Skip to Content
플랫폼 아키텍처데이터 격리 모델

데이터 격리 모델

AXE Labs 는 3-tier 데이터 격리를 사용합니다:

Tier격리 단위구현
1. OS-levelcustomermacmini 1대 = customer 1개
2. Database schemaentity (회사)PostgreSQL schema-per-entity
3. Row-level (일부)tenant_idRLS (magnet 등에서)

Tier 1 — OS-level (customer)

각 customer 는 자기 macmini 위에서 frame · blueprint 등 동일 스택을 실행합니다. customer 간 cross-talk 없음.

customermacminiDBschema 셋
axeaxe-macminiframe-postgresshared + axec + axev + axtest
realchoicerealchoice-macmini (예정)(별도)shared + realchoice

backup 도 macmini 별로 분리되며, customer 간 ring backup 은 운영자 명시 승인 + 양방향 SSH 합의 후에만.

Tier 2 — Schema-per-entity (frame)

frame 의 PostgreSQL 안에는:

frame DB ├── shared (cross-entity) │ ├── entity ← 등록된 entity 메타 (axec, axev, realchoice) │ ├── format_profile ← 파일 포맷 캐시 (vendor + column_signature) │ ├── account_template ← 표준 계정과목 (KSME) │ ├── idempotency_record ← 멱등 키 │ └── oauth_authorization_codes ← OAuth proxy (D-ops-15, 현재 dormant) ├── axec (개별 entity schema) │ ├── account, journal, journal_line │ ├── raw_transaction, bank_account, source_file │ ├── fiscal_period, open_item │ ├── evidence, audit_log │ └── ... (전체 16 개 migration) ├── axev (axec 와 동일 구조) │ └── ... └── axtest (테스트 전용) └── ...

Cross-entity 쿼리 불가: 각 MCP tool 호출이 SET search_path TO {entity_id}, shared 로 격리. 사용자가 axec 권한만 가지면 axev schema 접근 불가.

Migration 패턴

Alembic 의 dual-env:

디렉토리적용 시점대상
alembic/versions/frame migrate 시 1회shared schema
alembic/entity_versions/frame register-entity 시 + 매 migrate각 entity schema (axec, axev, …)

frame migrate 실행 시:

  1. upgrade_shared() → shared schema 마이그
  2. 등록된 모든 entity 에 대해 upgrade_entity(entity_id) → entity schema 마이그

Tier 3 — Row-level (선택적)

magnet 같이 multi-tenant 단일 schema 에서 RLS 사용:

-- magnet sql/050_multi_tenant_baseline.sql ALTER TABLE campaign_metrics_daily ENABLE ROW LEVEL SECURITY; CREATE POLICY tenant_isolation ON campaign_metrics_daily USING (tenant_id = current_setting('app.tenant_id')::bigint);

DB 연결 단계에서 SET app.tenant_id = <id> 주입. 사용자가 잘못된 tenant_id 로는 read/write 불가.

frame 은 schema-per-entity 가 더 강한 격리 (cross-schema 권한 자체 분리), magnet 은 RLS 가 멀티 brand 통합 운영에 더 적합 → 서비스 별 적절한 격리 모델 선택.

audit_log — 모든 쓰기 추적

각 entity schema 에 audit_log 테이블이 있으며, 모든 쓰기 (insert/update/delete) 가 자동 기록됩니다.

-- entity-side audit_log 의 컬럼 audit_log ( id bigserial, table_name text, op text, -- INSERT | UPDATE | DELETE old_data jsonb, new_data jsonb, actor text, -- frame.actor session var entity_context text, -- 'axec' ts timestamptz default now() )

frame.actor 는 entity_session context manager 에서 설정되며, MCP tool 호출의 TokenClaims.sub 가 자동으로 들어갑니다.

Append-only 강제

raw_transaction, journal_line 같은 핵심 테이블에는 DELETE/UPDATE 권한이 DB 사용자 단에서 REVOKE 되어 있습니다. 잘못된 분개는 reverse_journal 로 역분 (새 journal 생성), 절대 수정/삭제 안 함.

PII 암호화 (pgcrypto)

개인정보 (security_holder, audit log 의 일부 raw_data) 는 pgcrypto.pgp_sym_encrypt 로 entity 별 별도 passphrase 로 암호화됩니다.

# docker-compose.yml env FRAME_PII_PASSPHRASE_AXEC: ${FRAME_PII_PASSPHRASE_AXEC:-} FRAME_PII_PASSPHRASE_AXEV: ${FRAME_PII_PASSPHRASE_AXEV:-} FRAME_PII_PASSPHRASE_REALCHOICE: ${FRAME_PII_PASSPHRASE_REALCHOICE:-} FRAME_PII_SALT_DIR: /root/.frame

각 entity 의 passphrase 는 운영자 Keychain (security add-generic-password) 에 저장되고 컨테이너 launch wrapper 가 env 로 주입. passphrase 분실 = PII 영구 손실 (이는 의도된 설계 — 운영자가 DB dump 만으로는 PII 복원 불가).

Evidence blob 저장 (content-addressed file store, D-frame-2)

큰 파일 (evidence PDF·통장 xls·세금계산서 등) 은 Postgres bytea 가 아니라 content-addressed file store .local/files/<hash[:2]>/<hash>/<filename> 에 저장. DB 의 evidence/source_file row 는 content_hash (SHA-256) + portable storage_url (relative <hash[:2]>/<hash>/<filename>) 만 보유.

  • 컨테이너 마운트: host .local/files ↔ 컨테이너 /app/.local/files bind mount (FRAME_FILE_STORAGE_PATH). read 시 frame.ingest.pipeline.resolve_storage_path() 가 portable url 을 live store 기준 absolute 로 매핑 → host·컨테이너 어디서 실행해도 같은 blob 해석.
  • 금지: local://<host 절대경로> (외부 OneDrive/Downloads 포인터 — 컨테이너 read 불가 + rename/move 시 깨짐), CWD 의존 absolute 경로 (host /Users/... vs 컨테이너 /app/... 불일치). 모든 ingest write-path (pipeline/card/fund/hometax/entity_meta/resolution/ops.evidence) 가 _store_file_locally 로 통일.
  • 무결성 probe: GET /frame/health/storage 가 모든 entity schema 의 evidence storage_url 을 resolve 해 blob 존재 확인 — bind-mount 회귀로 blob 이 사라지면 (2026-05-14 incident) 503 + missing-list. blob 은 절대 삭제 안 함 (append-only 정신).

정합성 검사

frame integrity-check --entity axec 가 4가지 검사를 수행:

  1. balanceSUM(debit) == SUM(credit) per journal
  2. journal — 모든 journal_line 이 valid account 참조
  3. account — chart-of-accounts 와 journal_line 의 일관성
  4. fiscal_period — period 의 starting/ending balance 정합

cron 으로 매일 자동 실행 + Slack alert.

Last updated on