다크모드
문서 신뢰 체인 백엔드 핸드오프
소유권과 경계
마이그레이션 20260718110617_document_trust_chain.sql의 owner는 platform-core/document-trust다. 계약관리 전용 하위 테이블이 아니라 모든 상품이 사용할 수 있는 플랫폼 능력이다.
- 원본 상품은 불변 JSON snapshot과 source identity만 제공한다.
document_trust_requests는 원본 상품 테이블을 FK로 참조하지 않는다.- 원본 상품은 문서 신뢰 체인 없이도 commit을 완료한다.
- 문서 신뢰 체인은 원본 상품 entitlement와 Office 권한만 확인한다.
- PDF 생성, 타임스탬프, 전자문서 보관은 각각 별도 provider-neutral adapter다.
UI·public 문서·event payload에는 외부 사업자명이나 인증정보를 넣지 않는다.
데이터 모델
document_trust_requests
한 source revision의 문서 및 증명 상태를 소유한다. source identity는 (source_product_key, source_aggregate_type, source_aggregate_id, source_revision, document_kind)이며 Office 안에서 유일하다.
불변 영역은 Company/Office·관리코드, source identity와 snapshot, 문서 종류·제목·snapshot contract version, 타임스탬프·보관 요청 여부다.
text
generation_queued
→ pdf_ready
→ timestamp_queued → timestamped
→ archive_queued → archived
각 처리 상태 → failed → 같은 stage queuedPDF 결과는 storage key, 소문자 64자리 SHA-256, byte size, 생성시각을 함께 요구한다. 타임스탬프와 보관도 token/receipt storage key와 식별번호·완료시각이 모두 있어야 한다. 실패는 failure_stage, 안전한 오류 코드와 사용자용 메시지를 함께 가진다.
document_trust_events
request별 증가 sequence를 가진 append-only 이력이다. 생성, stage 완료·실패, retry를 기록한다. UPDATE/DELETE trigger가 이력 변조를 거부한다.
private.document_trust_commands
Create/Retry idempotency ledger다. (company_id, command_name, request_key) exact replay를 지원하며 다른 payload는 document_trust_idempotency_conflict다.
Public·worker 명령
create_document_trust_request(...): authenticated 관리자가 snapshot을 확정하고 PDF stage를 큐에 넣는다.retry_document_trust_request(...):failedstage만 같은 adapter로 다시 큐잉한다.private.record_document_trust_stage_result(...): service role worker 전용. expected revision과 현재 stage를 검증하고 다음 optional stage를 큐잉한다.
모든 adapter event는 platform integration outbox를 사용한다.
| stage | adapter key | 필수 성공 결과 |
|---|---|---|
pdf-generation-v1 | storageKey, sha256, byteSize | |
| 타임스탬프 | timestamp-evidence-v1 | tokenStorageKey, serial |
| 전자문서 보관 | electronic-archive-v1 | receiptStorageKey, archiveRecordKey |
adapter 이름은 내부 capability 계약이며 특정 외부 사업자를 의미하지 않는다. adapter credential은 event/result JSON에 넣지 않는다.
Snapshot 계약
계약관리 최초 소비 계약은 contract-document.v1이다.
json
{
"schemaVersion": "contract-document.v1",
"issuer": { "companyId": "…", "companyName": "…", "officeId": "…", "officeName": "…" },
"contract": { "id": "…", "managementCode": "…", "versionNumber": 2, "displayName": "…" },
"parties": [{ "partyId": "…", "role": "customer", "displayName": "…", "snapshot": {} }]
}재귀 sensitive-key 검사에서 provider/vendor/secret/credential/API key/token/password/certificate key 계열 key를 거부한다. worker result도 같은 검사를 거친다.
Storage와 다운로드
document-trust-private는 private bucket이다. worker만 service role로 업로드한다. object key는 다음 경로를 사용한다.
text
{company_id}/{office_id}/{request_id}/{artifact-file}authenticated SELECT 정책은 경로의 세 scope가 request와 일치하고, 사용자가 해당 source product를 읽을 수 있을 때만 허용한다. 프론트는 60초 signed URL을 만들고 download filename을 지정한다. 서비스 역할 키는 브라우저에 절대 전달하지 않는다.
PDF 생성기
scripts/contract-document-pdf.mjs는 contract-document.v1 JSON을 읽고 production build의 EDS print CSS를 주입해 Chrome page.pdf()로 A4 PDF를 만든다. 생성 뒤 같은 byte의 SHA-256·byte size를 .evidence.json으로 출력한다.
bash
npm run build
npm run contract-document:pdf -- snapshot.json output.pdf운영 worker도 동일 snapshot contract와 EDS print artifact를 사용해야 한다. PDF를 업로드한 뒤 byte를 다시 읽어 계산한 해시를 stage result에 전달한다.
검증
- SQL smoke:
supabase/tests/document_trust_chain_smoke.sql - migration 구조:
src/composables/__tests__/documentTrustMigration.spec.js - repo 계약:
src/composables/__tests__/documentTrustRepo.spec.js - PDF snapshot/escaping:
src/composables/__tests__/contractDocument.spec.js
Smoke는 exact replay, direct write 차단, PDF→타임스탬프→보관 전이, 3개 adapter delivery, RLS, source product entitlement를 검사한다.