DOC is CyberOS's e-signature workflow + immutable archival service. The cryptographic signing act is delegated to an eIDAS-conformant QTSP (qualified trust-service provider) per jurisdiction; DOC owns everything else: document upload + SHA-256 integrity hashing, template definition + auto-fill from CRM data, multi-party workflow (signer order, parallel vs sequential), identity verification (WebAuthn for CyberOS-resident users; VNeID for Vietnamese citizens; SMS-OTP fallback), per-document hash-chained audit log, and S3 Object-Lock archival with residency-pinning. Three certificate chains are supported: eIDAS QTSP (qualified electronic signature, QES) for the EU; Adobe AATL for US/non-EU enterprise; Vietnamese CA chain (VnPay, MK Group, Viettel-CA) for VN per Decree 130/2018. Migration paths from DocuSign, Adobe Sign, and HelloSign preserve original audit trails without re-signing. The default retention is 10 years; longer per applicable law (e.g. employment contracts under VN labour law).

At a glance

ItemDetail
StatusPlanned - P4 long-term
Cert chains3: eIDAS, AATL, VN CA
Identity proof4 methods: WebAuthn, VNeID, SMS-OTP, email-link
Retention10 years (eIDAS / ESIGN minimum)
ArchiveS3 Object-Lock, Compliance mode + residency pin
Migration3 sources: DocuSign, Adobe Sign, HelloSign
i18nvi + en, + regional certs
Depends onAUTH, memory, QTSP partner; CRM, HR, INV consumers

The bigger picture - three strategic roles

DOC ships P4 because legally-binding e-signatures require regulated trust-service providers - and CyberOS will not pretend to be one. The architecture is partner-routed: DOC owns the workflow (upload, template, multi-party order, archival, lifecycle); QTSP partners (eIDAS/AATL/VN CA) provide the cryptographic signing. This separation makes DOC compliance-clean and provider-portable.

Role 1 - Document repository. Versioned, ACL'd, 10-year retention. Documents stored in S3 Object-Lock-Compliance bucket with residency pinning per tenant. Versioned at every modification; immutable post-archive. ACL aligns with module ownership (HR contracts -> HR scope; CRM contracts -> CRM scope; ESOP grants -> cap-table scope). 10-year retention satisfies eIDAS + ESIGN minimum.

Role 2 - E-sign workflow. Partner-routed cryptography, CyberOS-owned workflow. DOC owns the workflow: upload, template assembly, multi-party signing order, identity verification (WebAuthn / VNeID / SMS-OTP / email-link), audit chain. The cryptographic act delegates to eIDAS QTSP (EU), AATL CA (US), or Vietnamese CA (VN). DocuSign / Adobe Sign / HelloSign migration brings legacy signed docs in without re-signing.

Role 3 - Contract lifecycle. HR + CRM + ESOP integration, expiry + renewal. Every contract carries lifecycle metadata: type (employment / NDA / MSA / SOW / ESOP grant), parties, effective dates, renewal date, expiry, parent contract. Expiry alerts fire 90 days before; renewal proposals draft via CUO. Cross-module: HR contracts auto-link to Member onboarding; CRM contracts link to Engagement creation in PROJ; ESOP grants archive with cap-table audit trail.

DOC partner-routed signing flow

Diagram source (Mermaid, flattened during migration):

flowchart LR SOURCE["Source  
HR · CRM · ESOP · LEGAL"] DOC["✍ DOC  
workflow · template · identity"] SIGN_EU["🇪🇺 eIDAS QTSP  
(EU residency)"] SIGN_US["🇺🇸 AATL CA  
(US residency)"] SIGN_VN["🇻🇳 VNeID + VN CA  
(VN residency)"] ARCHIVE["📦 S3 Object-Lock  
Compliance · 10yr"] memory["🧠 memory  
contract event audit"] LIFECYCLE["⏰ Lifecycle  
expiry · renewal · alerts"] SOURCE --> DOC DOC -- "EU tenant" --> SIGN_EU DOC -- "US tenant" --> SIGN_US DOC -- "VN tenant" --> SIGN_VN SIGN_EU --> ARCHIVE SIGN_US --> ARCHIVE SIGN_VN --> ARCHIVE ARCHIVE --> LIFECYCLE LIFECYCLE --> SOURCE DOC --> memory classDef hub fill:#fde68a,stroke:#451a03,stroke-width:3px,color:#451a03 classDef mod fill:#e0e7ff,stroke:#3730a3 classDef partner fill:#fef2f2,stroke:#b91c1c classDef memory fill:#fef6e0,stroke:#9c750a class DOC hub class SOURCE,ARCHIVE,LIFECYCLE mod class SIGN_EU,SIGN_US,SIGN_VN partner class memory memory

Auto vs human-in-loop operations matrix

Operation| How it happens| Why this split ---|---|--- Document upload| Manual Member action| Intent; auto-upload from other modules also requires authoring trigger. Template assembly| Auto from template engine| Templates parameterised; CUO drafts variable fields. Identity verification| Auto via WebAuthn / VNeID / SMS-OTP / email-link| Multi-factor enforced per signer; never bypassed. Cryptographic signing| Auto via QTSP partner API| The QTSP/CA is the trust anchor; CyberOS never holds the signing key. High-value signing (>= tenant threshold)| Manual 2nd-factor + dual approval| Anti-fraud per Decree 130/2018; signer + counter-signer if applicable. Expiry alert| Auto 90 / 30 / 7 days before| Renewal lead time; CUO drafts renewal proposal. Renewal proposal| Auto draft; Manual review + send| CUO drafts terms; legal/AM reviews. Archival| Auto at signing complete| Object-Lock + audit chain row. DSAR export| Auto bundle per request| Per-subject signature events; chain-preserved. Document deletion (purge)| Blocked until retention expires| 10-year retention is statutory; deletion not allowed prior.

Why DOC exists

Consultancies sign documents all day: MSAs, SOWs, employment contracts, NDAs, change orders, supplier agreements, COIs, retention letters. They send them through DocuSign or Adobe Sign at $30-$50 per seat per month, and the resulting signed PDFs land in someone's email or shared drive - never in the same audit chain as the workflow that produced them, never re-discoverable by the AI surface, never bound to the CRM record. DOC fixes this by making "sign this document" a first-class CyberOS workflow: the template is defined in DOC, the signer field auto-pulls from CRM, the QTSP partner does the actual crypto, and the resulting PDF + cert chain + audit log lands back in DOC, citable from memory, and visible to the AM in CHAT. The cost saving (no per-seat DocuSign) is incidental; the integration is the value.

The bet is that the dollars saved on DocuSign are nice but the real value is the binding: a signed MSA appears as a CRM activity, an employment contract appears in HR, a SOW appears under PROJ.engagement, an INV-issued purchase order links to the signed-doc record. Future-AI surfaces - "show me every signed agreement with Acme Corp" - become a single GraphQL query, not a Sharepoint hunt.

What it does - 5W1H2C5M

A structured decomposition of DOC's scope.

Axis| Question| Answer ---|---|--- 5W - What| What is DOC?| A document-management + e-signature workflow service. Owns: documents, templates, signing sessions, signature records, identity-verification events, audit log, archive. Delegates cryptographic signing to QTSP partners. 5W - Who| Who uses it?| AMs: initiate MSA / SOW signing. HR: contract signing. Finance: PO + invoice signing. Counterparties: external signers via email link. CLO: reviews high-value docs pre-send. 5W - When| When does it run?| On-demand for signing-session create. Continuous for archival jobs and certificate-chain validation. Nightly for retention-policy enforcement (purge after retention expiry per legal). 5W - Where| Where does it run?| P4: SG-1 primary + VN-hanoi-1 partition. S3 Object-Lock bucket per residency. QTSP API calls outbound to partner regions. 5W - Why| Why a separate module?| Because every other module needs to attach signed documents to its primitives (CRM.deal -> MSA, HR.employee -> contract, INV.invoice -> signed PO). Owning the surface once means every consumer reads the same signature audit chain. 1H - How| How does it work?| Document upload -> SHA-256 integrity hash -> template overlay (auto-fill from CRM/HR data) -> signing session created with ordered signer list -> identity verification per signer (WebAuthn/VNeID/SMS) -> QTSP signing call -> certificate + timestamp returned -> PDF stamped + AATL/CAdES-format embedded -> archive to S3 Object-Lock. 2C - Cost| Cost budget?| P4: QTSP partner cost ~$0.50-$2.00 per signature (passes through). Infra ~$80 / month (Fargate + S3 storage + Object-Lock + KMS). 2C - Constraints| Constraints?| (a) eIDAS conformity required for QES - partner-only. (b) VN Decree 130/2018 - signature must use VN-licenced CA for legal effect in VN. (c) 10-year minimum retention. (d) PDPL Art. 14 DSAR - export support for signers' own data. (e) GDPR Art. 17 - purge with archival exemption for legal docs. 5M - Materials| Stack?| Rust 1.81, axum, sqlx, PostgreSQL 16, S3 (Object-Lock Compliance), KMS, pdfium for PDF manipulation, CAdES + PAdES formats, OpenTelemetry. QTSP partners: GlobalSign (EU), DigiCert (US-AATL), VnPay (VN), MK Group (VN), Viettel-CA (VN). 5M - Methods| Method choices?| PAdES-B-LT (long-term) format for archived PDFs - includes cert chain + revocation info + LTV. Per-document hash-chained audit log mirrors memory chain semantics. Multi-signer workflows are state machines. 5M - Machines| Deployment?| Fargate tasks (workflow), separate task for archival jobs. S3 Object-Lock buckets per residency. KMS-wrapped encryption for at-rest documents. 5M - Manpower| Who maintains?| 0.3 FTE at P4; legal counsel reviews QTSP partner contracts annually; security review of cert-chain handling annually. 5M - Measurement| How measured?| (NFR pending) signature non-repudiation - cosign verifies any archive byte-for-byte; (NFR pending) zero archive loss over 10 years; KPI signature-time p95 (<= 5 min including human-confirm).

Architecture

DOC is a Rust service split into three planes: workflow (document, template, session, signer state machine), partner-broker (QTSP routing per jurisdiction), and archive (S3 Object-Lock + LTV PAdES validation). The architecture is partner-routed: signature crypto never happens inside CyberOS; identity verification is normalised across WebAuthn / VNeID / SMS-OTP / email-link.

Diagram source (Mermaid, flattened during migration):

graph TB subgraph UI ["UI surfaces"] SPA["Signer SPA  
(React + pdf.js)"] TPL["Template designer"] INBOX["Counterparty link"] end subgraph DOC ["DOC service (Rust · axum)"] DCRUD["doc.rs  
document CRUD"] TPL_S["template.rs  
field overlay"] SESSION["session.rs  
signing-session state machine"] SIGNER["signer.rs  
per-signer flow"] IDV["idv.rs  
identity verification dispatcher"] BROKER["broker.rs  
QTSP routing"] ARCHIVE["archive.rs  
PAdES-LT + S3 Object-Lock"] MIGRATE["migrate.rs  
DocuSign / AdobeSign import"] PDF["pdf.rs  
pdfium wrapper"] end subgraph IDV_BACKENDS ["Identity-verification backends"] WA["AUTH WebAuthn"] VNEID["🇻🇳 VNeID API"] SMS["SMS-OTP provider"] EMAIL["Email-link"] end subgraph QTSP ["QTSP partners (delegated crypto)"] GS["GlobalSign QTSP (EU)"] DG["DigiCert AATL (US/non-EU)"] VNPAY["VnPay CA (VN)"] MKG["MK Group CA (VN)"] VTL["Viettel-CA (VN)"] end subgraph STORES ["Stores"] PG[("PostgreSQL 16  
doc.document · template  
session · signer · signature  
audit_event")] S3[("S3 Object-Lock Compliance  
10y retention  
residency-pinned")] KMS[("AWS KMS  
document encryption keys")] end subgraph SINKS ["Audit"] memory["🧠 memory  
doc.* rows"] OBS["👁 OBS"] end SPA --> DCRUD TPL --> TPL_S INBOX --> SIGNER DCRUD --> PDF DCRUD --> S3 DCRUD --> KMS TPL_S --> PG SESSION --> PG SESSION --> SIGNER SIGNER --> IDV IDV --> WA IDV --> VNEID IDV --> SMS IDV --> EMAIL SIGNER --> BROKER BROKER --> GS BROKER --> DG BROKER --> VNPAY BROKER --> MKG BROKER --> VTL BROKER --> ARCHIVE ARCHIVE --> S3 ARCHIVE --> memory SESSION --> memory DOC --> OBS MIGRATE --> S3 MIGRATE --> PG classDef planned fill:#fde68a,stroke:#78350f classDef store fill:#f5f3ff,stroke:#7c3aed classDef sink fill:#f5ede6,stroke:#45210e classDef partner fill:#fef2f2,stroke:#7f1d1d class SPA,TPL,INBOX,DCRUD,TPL_S,SESSION,SIGNER,IDV,BROKER,ARCHIVE,MIGRATE,PDF planned class PG,S3,KMS store class memory,OBS sink class GS,DG,VNPAY,MKG,VTL,WA,VNEID,SMS,EMAIL partner

Internal components

Component| Path (planned)| Responsibility ---|---|--- doc.rs| services/doc/src/doc.rs| Document CRUD - upload, version, SHA-256 hash, encrypt-at-rest via KMS, store on S3. template.rs| services/doc/src/template.rs| Template definition: signer fields, text fields, date fields, conditional fields. Auto-fill rules pulling from CRM/HR/INV. session.rs| services/doc/src/session.rs| Signing session: ordered signer list, parallel/sequential mode, deadline, reminder schedule. signer.rs| services/doc/src/signer.rs| Per-signer state machine: invited -> identity_verified -> signed / rejected / expired. idv.rs| services/doc/src/idv.rs| Identity-verification dispatcher. Routes based on signer profile + document risk-tier. WebAuthn / VNeID / SMS-OTP / email-link. vneid.rs| services/doc/src/vneid.rs| VNeID API client. CCCD (Citizen ID) validation; biometric assertion for high-value docs (Decision 06/2022/QĐ-TTg). broker.rs| services/doc/src/broker.rs| QTSP routing. Picks partner per (jurisdiction x document_type x signer_country). Failover between partners on outage. pades.rs| services/doc/src/pades.rs| PAdES-B-LT formatter. Embeds cert chain + OCSP revocation info + DSS dictionary for long-term validation. archive.rs| services/doc/src/archive.rs| S3 Object-Lock Compliance archival. Residency-pinned bucket. 10-year retention by default; per-tenant override. audit_chain.rs| services/doc/src/audit_chain.rs| Per-document hash-chained audit log. Each signature event chains to previous; export bundle for legal discovery. retention.rs| services/doc/src/retention.rs| Retention policy enforcement. Nightly job: identifies docs past retention + legal hold lifted; purges with audit row. migrate.rs| services/doc/src/migrate.rs| DocuSign / Adobe Sign / HelloSign import. Preserves original audit trail; flags imported docs as "external-trust". fraud.rs| services/doc/src/fraud.rs| Notary-fraud detection: velocity check, impossible-travel for signers, device fingerprint, anomaly score. migrations/| services/doc/migrations/| sqlx migrations. RLS by tenant_id. Indices on (doc_id), (session_id, signer_id), (status).

DOC-INV-001 - Signed-document integrity is byte-for-byte verifiable. Every archived PDF MUST cosign-verify against (a) its SHA-256 stored at archive time, (b) its cert chain stored with the document, and (c) its memory audit-chain leaf. The verification path is implemented in cyberos-doc verify --archive-id ... and is exercised quarterly by an automated chaos job that randomly picks 1% of archived docs and verifies them. Failure halts retention purges until investigation completes.

Data model

Documents, templates, signing sessions, signers, signatures, audit events, archive records. PostgreSQL holds metadata; PDF content (encrypted) lives on S3 Object-Lock.

Diagram source (Mermaid, flattened during migration):

erDiagram TENANT ||--o{ DOCUMENT: "owns" TENANT ||--o{ TEMPLATE: "defines" DOCUMENT ||--o| TEMPLATE: "instantiated from" DOCUMENT ||--o{ SIGNING_SESSION: "has" SIGNING_SESSION ||--o{ SIGNER: "lists" SIGNER ||--o| SIGNATURE: "produces" SIGNATURE ||--o{ AUDIT_EVENT: "logs" SIGNING_SESSION ||--o{ AUDIT_EVENT: "logs" DOCUMENT ||--o{ AUDIT_EVENT: "logs" DOCUMENT ||--o| ARCHIVE: "archived as" TEMPLATE ||--o{ TEMPLATE_FIELD: "defines" TENANT { uuid id PK string slug string default_residency int default_retention_years } DOCUMENT { uuid id PK uuid tenant_id FK string name string s3_key "encrypted at rest" string sha256_original string status "draft | in_signing | executed | expired | revoked" uuid template_id FK timestamp created_at uuid created_by FK } TEMPLATE { uuid id PK uuid tenant_id FK string name string jurisdiction "EU | US | VN | OTHER" string s3_key "template PDF" timestamp created_at } TEMPLATE_FIELD { uuid id PK uuid template_id FK string field_type "signature | text | date | checkbox | initial" string name int page int x int y int width int height string auto_fill_source "crm.contact.name | hr.member.email | …" } SIGNING_SESSION { uuid id PK uuid document_id FK string mode "sequential | parallel" timestamp deadline string status "open | completed | expired | cancelled" string memory_chain } SIGNER { uuid id PK uuid session_id FK int order_index string email string display_name string country "VN | EU | US | …" string idv_method "webauthn | vneid | sms_otp | email_link" string idv_proof_id "external reference" string status "invited | viewed | identity_verified | signed | rejected | expired" timestamp invited_at timestamp signed_at } SIGNATURE { uuid id PK uuid signer_id FK string qtsp_partner "globalsign | digicert | vnpay | mkgroup | viettelca" bytea cert_chain "X.509 chain DER" string timestamp_authority "RFC 3161 TSA" bytea signature_blob "CAdES detached or PAdES embedded" string sha256_signed_pdf timestamp signed_at string memory_chain } AUDIT_EVENT { uuid id PK uuid document_id FK string actor_type "human | agent | system | qtsp" uuid actor_id string event_type "uploaded | template_applied | session_created | invited | viewed | idv_attempted | idv_passed | signed | rejected | archived | purge_blocked | purged" string event_chain_hash string prev_chain_hash bytea details_json timestamp ts } ARCHIVE { uuid id PK uuid document_id FK string s3_bucket "cyberos-doc-archive-vn-hanoi" string s3_key string sha256_archived string pades_b_lt_format date retention_until bool legal_hold timestamp archived_at }

Per-document audit chain (mirrors memory semantics)

/ each audit_event row carries:
/ prev_chain_hash = previous event's chain hash for this doc
/ event_chain_hash = SHA-256(canonical(this_event_minus_chain) || prev_chain_hash)
/
/ `cyberos-doc verify --doc-id …` walks the chain end-to-end:
/ - validates SHA-256 of original at upload event
/ - validates each event's chain hash
/ - validates QTSP cert chain at signed event
/ - validates timestamp authority RFC 3161 token
/ - validates final archive SHA-256 against archive event
/
/ Tampering at any point fails verification with a precise diagnosis row.

API surface

GraphQL subgraph for cross-module reads (CRM.deal -> contract list, HR.member -> contract history), REST for the signer SPA, MCP tools for natural-language workflow ("send the standard MSA to acme@...").

GraphQL subgraph (federated)

extend schema
 @link(url: "https://specs.apollo.dev/federation/v2.5", import: ["@key", "@external", "@shareable", "@requiresScopes"])

type Document @key(fields: "id") {
 id: ID!
 name: String!
 status: DocStatus!
 template: Template
 sessions: [SigningSession!]!
 auditEvents: [AuditEvent!]!
 archive: Archive
 createdAt: DateTime!
}

type Template @key(fields: "id") {
 id: ID!
 name: String!
 jurisdiction: Jurisdiction!
 fields: [TemplateField!]!
}

type TemplateField {
 fieldType: FieldType!
 name: String!
 autoFillSource: String
}

type SigningSession @key(fields: "id") {
 id: ID!
 document: Document!
 mode: SessionMode!
 status: SessionStatus!
 deadline: DateTime!
 signers: [Signer!]!
}

type Signer @key(fields: "id") {
 id: ID!
 orderIndex: Int!
 email: String!
 displayName: String!
 country: String!
 idvMethod: IdvMethod!
 status: SignerStatus!
 signature: Signature
}

type Signature {
 qtspPartner: QtspPartner!
 timestampAuthority: String!
 signedAt: DateTime!
}

type AuditEvent {
 eventType: AuditEventType!
 actorType: ActorType!
 ts: DateTime!
 eventChainHash: String!
}

type Archive {
 s3Bucket: String!
 retentionUntil: Date!
 legalHold: Boolean!
 padesFormat: String!
}

enum DocStatus { DRAFT IN_SIGNING EXECUTED EXPIRED REVOKED }
enum SessionMode { SEQUENTIAL PARALLEL }
enum SessionStatus { OPEN COMPLETED EXPIRED CANCELLED }
enum SignerStatus { INVITED VIEWED IDENTITY_VERIFIED SIGNED REJECTED EXPIRED }
enum IdvMethod { WEBAUTHN VNEID SMS_OTP EMAIL_LINK }
enum QtspPartner { GLOBALSIGN DIGICERT VNPAY MKGROUP VIETTELCA }
enum Jurisdiction { EU US VN OTHER }
enum FieldType { SIGNATURE TEXT DATE CHECKBOX INITIAL }
enum ActorType { HUMAN AGENT SYSTEM QTSP }
enum AuditEventType { UPLOADED TEMPLATE_APPLIED SESSION_CREATED INVITED VIEWED IDV_ATTEMPTED IDV_PASSED SIGNED REJECTED ARCHIVED PURGE_BLOCKED PURGED }

extend type Deal @key(fields: "id") {
 id: ID! @external
 documents: [Document!]! @requiresScopes(scopes: [["doc.read"]])
}

extend type Member @key(fields: "id") {
 id: ID! @external
 contracts: [Document!]! @requiresScopes(scopes: [["doc.read"]])
}

type Query {
 document(id: ID!): Document @requiresScopes(scopes: [["doc.read"]])
 myPendingSignatures: [Signer!]! @requiresScopes(scopes: [["doc.read"]])
 templates(jurisdiction: Jurisdiction): [Template!]! @requiresScopes(scopes: [["doc.read"]])
}

type Mutation {
 uploadDocument(name: String!, sha256: String!): UploadUrl! @requiresScopes(scopes: [["doc.write"]])
 createSession(input: SessionInput!): SigningSession! @requiresScopes(scopes: [["doc.write"]])
 cancelSession(id: ID!, reason: String!): SigningSession! @requiresScopes(scopes: [["doc.write"]])
 setLegalHold(documentId: ID!, hold: Boolean!): Document!
 @requiresScopes(scopes: [["doc.legal_hold"]])
}

REST endpoints

Method| Path| Purpose ---|---|--- POST| /doc/upload| Multipart upload - returns doc_id + s3_key. POST| /doc/{id}/template/{tpl}| Apply template overlay; resolve auto-fill from CRM/HR. POST| /doc/{id}/session| Create signing session. GET| /doc/sign/{signer_token}| External signer landing page (counterparty). POST| /doc/sign/{signer_token}/idv| Identity-verification challenge response. POST| /doc/sign/{signer_token}/finalize| Capture signature; trigger QTSP call. GET| /doc/{id}/audit-bundle.zip| Legal-discovery bundle: PDF + cert chain + audit log. POST| /doc/migrate/docusign| Import legacy DocuSign envelope (preserve original trail). POST| /doc/migrate/adobesign| Import legacy Adobe Sign. POST| /doc/migrate/hellosign| Import legacy HelloSign. GET| /doc/{id}/verify| Cosign verify of archived PDF. POST| /doc/{id}/legal-hold| Apply / lift legal hold. POST| /doc/admin/retention-sweep| Run nightly retention sweep manually.

MCP tool catalogue

Tool name| Inputs| Outputs| Annotations ---|---|---|--- cyberos.doc.list| filter, status?| Document| readonly, scope=doc.read cyberos.doc.start_signing| template_id, deal_id, signers| {session_id, signer_links}| destructive, scope=doc.write, human-confirm cyberos.doc.status| session_id| {session, signers}| readonly cyberos.doc.cancel_session| session_id, reason| {ok}| destructive, scope=doc.write, human-confirm cyberos.doc.find_template| name_match, jurisdiction| Template| readonly cyberos.doc.legal_discovery_bundle| doc_id| {zip_url}| destructive, scope=doc.legal_export cyberos.doc.verify_archive| doc_id| {ok, diagnostics}| readonly cyberos.doc.set_legal_hold| doc_id, hold| {ok}| destructive, scope=doc.legal_hold, human-confirm

Key flows

Flow 1 - Document upload + template fill

sequenceDiagram autonumber participant AM as Account Manager participant SPA as DOC SPA participant D as doc.rs participant T as template.rs participant CRM as 🏢 CRM participant KMS as AWS KMS participant S3 as S3 (residency-pinned) participant B as 🧠 memory AM->>SPA: upload "Acme MSA.pdf" + select template "MSA-EU-v3" SPA->>D: POST /doc/upload (multipart, sha256) D->>KMS: encrypt at rest (envelope) D->>S3: PutObject (encrypted body) D->>B: append doc.uploaded row {sha256, by:AM} SPA->>T: POST /doc/{id}/template/MSA-EU-v3 T->>CRM: query Deal.contact for auto-fill (name, email, address) CRM-->>T: contact rows T->>T: overlay fields onto template canvas; SHA-256 final body T->>B: append template.applied row T-->>SPA: ready-to-send preview

Flow 2 - Multi-party signing (sequential)

sequenceDiagram autonumber participant AM as Account Manager participant S as session.rs participant Sa as Signer A (CEO of Acme) participant Sb as Signer B (CyberSkill CEO) participant I as idv.rs participant BK as broker.rs participant QTSP as eIDAS QTSP (GlobalSign) participant B as 🧠 memory AM->>S: createSession(mode=sequential, signers=[A, B], deadline=14d) S->>B: append session.created row S->>Sa: email magic link (signer order_index=1) Sa->>I: opens link · IDV via email-link (T2 doc) I-->>Sa: identity verified Sa->>S: views + signs S->>BK: route signature(country=EU, doc_type=MSA) BK->>QTSP: sign call (cert chain, RFC 3161 timestamp) QTSP-->>BK: CAdES blob + cert chain + TSA token BK->>S: signature stored S->>B: append signer.signed row {A} S->>Sb: email magic link (order=2) Sb->>I: WebAuthn (CyberSkill member, AAL3) I-->>Sb: identity verified Sb->>S: views + signs S->>BK: route again BK->>QTSP: sign QTSP-->>BK: signature blob BK->>S: signature stored S->>S: all signers complete → session.complete S->>B: append session.completed row S->>S: trigger archive flow (Flow 5)

Sequential mode means signer B's magic link is not emitted until A signs. Parallel mode emits both at once; first-to-sign rule + reminder cadence still apply.

Flow 3 - Identity verification + sign with VNeID

sequenceDiagram autonumber participant S as Signer (VN citizen) participant SPA as DOC SPA participant I as idv.rs participant V as vneid.rs participant VNEID as 🇻🇳 VNeID API participant BK as broker.rs participant VNCA as VN CA (e.g., VnPay) participant B as 🧠 memory S->>SPA: open signing page SPA->>I: getIdvChallenge(country=VN, risk=high) I->>V: prepare VNeID challenge (CCCD + biometric) V->>VNEID: createIdentityVerification VNEID-->>V: deeplink to VNeID app V-->>SPA: deeplink + QR S->>VNEID: open app, scan, biometric capture VNEID-->>V: idv_proof_id, attributes (full_name, CCCD, DOB) V->>I: pass idv I->>B: append idv.passed row {method:vneid, proof_id} I-->>SPA: ready to sign S->>SPA: confirm sign SPA->>BK: route signature (country=VN) BK->>VNCA: sign call (signer attrs from VNeID) VNCA-->>BK: CAdES blob + cert chain (VN root) BK->>B: append signed row

Flow 4 - Long-term archival (PAdES-B-LT + S3 Object-Lock)

sequenceDiagram autonumber participant S as session.rs (all signed) participant P as pades.rs participant A as archive.rs participant KMS as AWS KMS participant S3 as S3 Object-Lock Compliance participant TSA as RFC 3161 TSA participant B as 🧠 memory S->>P: assemble PAdES-B-LT P->>P: embed cert chain (all signers) P->>P: embed OCSP responses (revocation info) P->>TSA: outer timestamp (long-term validation) TSA-->>P: TSA token P->>P: DSS dictionary + LTV P->>A: hand off PDF A->>KMS: encrypt at rest A->>S3: PutObject with Object-Lock retain-until = now + 10y; LegalHold=OFF S3-->>A: ETag + VersionId A->>B: append archive.completed row {s3_key, retention_until, sha256} A->>S: archive ready

Object-Lock Compliance mode means even the AWS account root cannot delete the object before retention expiry. Legal hold is a separate flag that can be applied later to extend protection.

Flow 5 - DocuSign migration (preserve original trail)

sequenceDiagram autonumber participant CLO as CLO participant M as migrate.rs participant DS as DocuSign API participant A as archive.rs participant S3 as S3 Object-Lock participant B as 🧠 memory CLO->>M: cyberos-doc migrate-docusign --since 2020-01-01 M->>DS: list envelopes (paged) loop per envelope DS-->>M: envelope_id, signed PDF URL, audit log M->>DS: GET signed PDF + cert chain + audit JSON DS-->>M: bytes + metadata M->>M: validate DocuSign cert chain (AATL trust) M->>A: archive original PDF (no re-sign) A->>S3: PutObject (Object-Lock) M->>B: append migration.imported row {source:docusign, original_envelope_id, trust:external} end M->>CLO: report N migrated · 0 failures

Migration preserves the original DocuSign audit trail in audit_event.details_json. Documents are flagged trust=external so consumers know the signature was produced by another provider.

Signing-session lifecycle

A session traverses six states; the per-signer state machine sits inside Open. Every transition writes an audit event.

stateDiagram-v2 [*] --> Draft: document uploaded + template applied Draft --> Open: createSession Open --> Completed: all signers signed Open --> Expired: deadline passed Open --> Cancelled: AM revoked OR signer rejected Completed --> Archived: PAdES-LT + S3 Object-Lock Archived --> [*] Expired --> [*] Cancelled --> [*] state Open { [*] --> Invited Invited --> Viewed: signer opens link Viewed --> IdvAttempted: starts identity verification IdvAttempted --> IdvPassed: verified IdvAttempted --> IdvFailed: too many retries IdvPassed --> Signed: signer captures signature IdvPassed --> Rejected: signer declines Signed --> [*] Rejected --> [*] IdvFailed --> [*] }

Per-state actions

State| Trigger| Side-effects ---|---|--- Draft| document + template ready| Audit row template.applied; preview rendered. Open| createSession success| First signer notified (sequential) or all signers notified (parallel); reminders scheduled. Invited| signer email sent| Magic-link token issued; expiry = session deadline. Viewed| signer opens link| Audit row signer.viewed with IP + UA + device fingerprint. IdvPassed| identity verified| Audit row idv.passed with method + proof_id. Signed| QTSP signature returned| Audit row signer.signed with cert chain + TSA token. Completed| all signers signed| Trigger archive flow; downstream consumers (CRM / HR / INV) notified. Archived| PAdES-LT written to S3 Object-Lock| Audit row archive.completed; retention timer starts. Expired| deadline reached without all signing| Audit row session.expired; AM notified; document remains in PG (no archive). Cancelled| AM revoke OR signer reject| Audit row session.cancelled with reason; all signer tokens invalidated.

Functional requirements

The CyberOS task catalogue is being rebuilt one feature at a time via the open task-author Agent Skill.

Previous task enumerations were archived 2026-05-14 and are no longer reflected on this page. Specific tasks land here as they are re-authored.

Non-functional requirements

DOC NFRs centre on signature non-repudiation and long-term archival durability.

NFR ID| Concern| Target| Measurement ---|---|---|--- (NFR pending)| Signature non-repudiation (cosign verifies archive byte-for-byte)| 100% of archives| quarterly chaos audit (random 1%) (NFR pending)| Cross-tenant document leak| = 0| RLS verification suite (NFR pending)| Archive durability (no archive loss over 10 years)| 11x9s (S3 Object-Lock guarantee)| S3 SLA (NFR pending)| Audit-chain integrity| = 0 broken chains| nightly walker job (NFR pending)| uploadDocument server-canonical p95| <= 3 s (10 MB PDF)| k6 (NFR pending)| End-to-end sign (after IDV) p95| <= 8 s| RUM (NFR pending)| QTSP partner round-trip p95| <= 5 s (network)| per-partner SLO (NFR pending)| DOC availability| >= 99.9% (P4)| SLO monitor (NFR pending)| QTSP partner failover RTO| <= 60 s on partner outage| chaos test (NFR pending)| eIDAS QES conformity (annual audit)| passed| external auditor (NFR pending)| VN Decree 130/2018 cert chain validity| 100%| VN CA SLA

Dependencies

DOC depends on AUTH (WebAuthn), memory (audit), KMS + S3 (archival), and at least one QTSP partner per jurisdiction. Consumed by CRM (deal contracts), HR (employment contracts), INV (signed POs), and PORTAL (client signing).

Diagram source (Mermaid, flattened during migration):

graph LR subgraph upstream ["DOC depends on"] AUTH["🔐 AUTH  
WebAuthn + RBAC"] memory["🧠 memory  
audit chain"] OBS["👁 OBS"] KMS["🔑 AWS KMS"] S3["🗄 S3 Object-Lock"] VNEID["🇻🇳 VNeID API"] QTSP["🤝 QTSP partners  
GlobalSign · DigiCert · VnPay · MK · Viettel-CA"] TSA["RFC 3161 TSA"] CRM["🏢 CRM  
contact auto-fill"] HR_UP["👥 HR  
member auto-fill"] end DOC["✍️ DOC"] subgraph downstream ["Used by"] CRM_D["🏢 CRM  
deal contracts"] HR_D["👥 HR  
employment contracts"] INV_D["💰 INV  
signed POs"] PORTAL["🌐 PORTAL  
client signing"] MEMORY_D["🧠 memory  
doc citations"] end AUTH --> DOC memory --> DOC OBS --> DOC KMS --> DOC S3 --> DOC VNEID --> DOC QTSP --> DOC TSA --> DOC CRM --> DOC HR_UP --> DOC DOC --> CRM_D DOC --> HR_D DOC --> INV_D DOC --> PORTAL DOC --> MEMORY_D classDef planned fill:#fde68a,stroke:#78350f classDef partner fill:#fef2f2,stroke:#7f1d1d class AUTH,memory,OBS,KMS,S3,CRM,HR_UP,DOC,CRM_D,HR_D,INV_D,PORTAL,MEMORY_D planned class VNEID,QTSP,TSA partner

Compliance scope

DOC is the most regulation-heavy module after AUTH. Signature legality across jurisdictions is its entire job.

Regulation / standard| Article / clause| DOC feature that satisfies it ---|---|--- eIDAS (EU Reg. 910/2014)| Art. 25 - Legal effects of e-signatures| QES via partner QTSP for EU; AdES default for non-QES use cases. eIDAS| Art. 32 - Validation of QES| PAdES-B-LT embedded cert chain + OCSP + LTV timestamp. eIDAS| Art. 34 - Long-term preservation| S3 Object-Lock Compliance 10y; LTV outer timestamp. EU ESI standards| ETSI EN 319 142 - PAdES baseline| PAdES-B-LT format implemented. US ESIGN Act (15 U.S.C. §7001)| Consent + intent + record retention| Explicit consent UI; intent captured at sign; 10-year retention. US UETA| State e-signature recognition| AATL cert chain via DigiCert. Vietnam Decree 130/2018/NĐ-CP| E-signature legal recognition| VN CA chain (VnPay / MK / Viettel-CA); compliant cert profile. Vietnam Decision 06/2022/QĐ-TTg| National digital identity (VNeID)| VNeID integration via vneid.rs for VN citizens. Vietnam Law 91/2025/QH15 (PDPL)| Art. 14 - DSAR| DSAR export bundles documents + audit chains. GDPR (EU 2016/679)| Art. 15 - Right of access| Same surface as PDPL. GDPR| Art. 17 - Right to erasure| Purge supported with legal-hold + retention-period exemptions; audit fact of erasure remains. ISO/IEC 27001:2022| A.5.34 - Privacy in development| Documents encrypted at rest via KMS; access RLS-keyed. ISO 14533-1 (PAdES)| Long-term archival profiles| PAdES-B-LT format. SOC 2 Type II| CC6.1 - Logical access| RBAC + multi-factor for high-value signing.

Risk entries

DOC-specific risks in the risk register.

ID| Risk| Likelihood| Impact| Owner| Mitigation ---|---|---|---|---|--- R-DOC-001| Spoofing / Repudiation - forged signature| Low| Catastrophic| CLO| eIDAS QTSP cert chain; WebAuthn binding; multi-factor for high-value docs; quarterly external audit. R-DOC-002| Notary-fraud / impersonation via SMS-OTP only| Medium| High| CSO| SMS-OTP restricted to low-value docs; high-value requires WebAuthn or VNeID biometric. R-DOC-003| QTSP partner outage during signing campaign| Medium| Medium| CTO| Multi-partner per jurisdiction; broker.rs failover; 60s RTO chaos-tested. R-DOC-004| VNeID API breaking change| Medium| High| CTO| Adapter pattern in vneid.rs; integration tests on every release; VN CA fallback for non-biometric path. R-DOC-005| S3 Object-Lock misconfiguration allows premature deletion| Low| Catastrophic| CSO| Compliance mode (not Governance); CI test verifies retention-until; account-root cannot override. R-DOC-006| Legal-hold lift granted to non-CLO| Low| High| CSO| (task pending) scope=doc.legal_hold limited to CLO + DPO; quarterly access review. R-DOC-007| Cert chain expires; PAdES-LT cannot validate decades later| Medium| High| CTO| PAdES-B-LT includes LTV outer timestamp; periodic re-stamping job at year 9 (1y before retention end). R-DOC-008| Cross-tenant document leak via shared S3 bucket| Low| Catastrophic| CSO| Per-tenant bucket prefix; IAM scoped per tenant; pre-signed-URL audience-bound. R-DOC-009| Migration import accepts forged DocuSign trail| Low| High| CLO| Imports flagged trust=external; cert chain validated against AATL root; CLO approves import batches. R-DOC-010| GDPR Art. 17 purge collides with retention obligation| Medium| Medium| DPO| Purge requires CLO sign-off + retention-exempt classification; rejection logged with reason. R-DOC-011| CRM/HR/ESOP-triggered document creation fires without underlying lifecycle context| Medium| Medium| CTO| Each cross-module trigger validates source record state at submission; reject document creation if source record incomplete. R-DOC-012| Renewal proposal CUO-drafted with stale terms (e.g. expired discount)| Medium| Low| CPO| Renewal proposal carries terms-version stamp; CUO reads from active rate-card / contract templates only; AM final review. R-DOC-013| Expiry alert latency - 90-day notice misses a contract| Low| High| CTO| Nightly batch double-checks all active contracts; 90/30/7 day cascade; OBS alarm on any missed cascade step. R-DOC-014| Multi-jurisdiction contract (VN tenant signs with EU customer) - which cert chain governs?| Medium| Medium| CLO| Per-contract cert-chain declaration; co-signer cert chains both attached; legal precedence rules per type documented in KB runbook. R-DOC-015| Migrated DocuSign trail fails LTV after import - provenance breaks at archive| Medium| High| CLO| Imports flagged trust=external + cert-chain captured at import; LTV re-validation at year 9; failures escalate to CLO.

KPIs

DOC KPIs cover signing speed, completion rate, audit integrity, and compliance posture.

KPI| Formula| Source| Target ---|---|---|--- Session completion rate| completed / opened (per 30d)| doc.signing_session| >= 75% Time-to-first-signature p95| histogram| OBS| <= 24 h Time-to-completion p95| histogram| OBS| <= 7 d QTSP partner success rate| signed / attempted| broker.rs| >= 99.5% Identity-verification pass rate| idv.passed / idv.attempted| idv.rs| >= 90% Archive verification success| quarterly chaos audit| chaos job| = 100% Cosign chain validation success| nightly walker| OBS| = 100% Fraud-detection alerts / month| count| fraud.rs| tracked Migration trust-external flag rate| external / total| doc.document| declining trend Cross-module trigger validation rate| cross-module-triggered docs with source record validated / total| OBS| = 1.0 (hard floor) Renewal proposal terms-stamp coverage| proposals with active terms-version stamped / total| memory audit| = 1.0 Expiry cascade completeness| contracts hit by all 3 cascade steps (90/30/7) / total expiring| nightly batch| = 1.0 Multi-jurisdiction cert-chain declaration rate| multi-juris contracts with explicit cert-chain declared / total| contract metadata| = 1.0 LTV re-validation pass rate (year 9)| migrated trails passing LTV at year 9 / total imports| quarterly audit| >= 0.95 (escalate failures)

RACI matrix

DOC is owned by the CLO seat (legal effect) with CTO for engineering and DPO for data-subject rights.

Activity| CEO| CLO| CTO| CSO| DPO| CFO ---|---|---|---|---|---|--- Service design + spec| A| R| R| C| C| I Implementation| I| C| A/R| C| I| I QTSP partner selection + contracts| C| A/R| R| C| I| C Legal-hold application / lift| C| A/R| I| C| R| I Retention-policy review (10y default)| C| A/R| I| I| C| I eIDAS / Decree 130 conformity audit| C| A/R| C| R| C| I Notary-fraud incident response| C| R| R| A| C| I DSAR fulfilment (DOC scope)| I| C| I| I| A/R| I Migration import (DocuSign / Adobe Sign)| C| A| R| I| I| I

R = Responsible, A = Accountable, C = Consulted, I = Informed.

Planned CLI surface

Admin CLI cyberos-doc. Every destructive action writes a chained memory audit row.

1. Upload + apply template

$ cyberos-doc upload "Acme MSA.pdf" --template MSA-EU-v3 --deal deal-2026-0912

[uploaded] doc_id=DOC-018824 sha256=… bytes=412_223
[template] MSA-EU-v3 applied; 4 fields auto-filled from CRM
[audit] memory seq=21884 chain=…

2. Create a multi-party signing session

$ cyberos-doc session create \
 --doc DOC-018824 --mode sequential --deadline 14d \
 --signer "ceo@acme.com:Alice Smith:US:webauthn" \
 --signer "stephen@cyberskill.com:Stephen:VN:webauthn"

[session created] S-009311 order: [1:alice, 2:stephen]
[invited] alice@acme.com (magic link sent)
[reminder] scheduled +3d, +7d, +13d

3. Check session status

$ cyberos-doc session status S-009311

[status] open
[signers]
 1. alice@acme.com [SIGNED · 2026-05-13 09:14 · WebAuthn AAL3 · GlobalSign QTSP]
 2. stephen@cyberskill.com [VIEWED · 2026-05-13 11:02 · awaiting WebAuthn]
[deadline] 2026-05-27 (+13d)

4. Verify a signed-document archive

$ cyberos-doc verify --doc DOC-018824

[archive] s3:/cyberos-doc-archive-vn-hanoi/2026/05/DOC-018824.pdf
[sha256] ✓ matches archive event
[chain] ✓ 7 events, all chain-hashes valid
[certs] ✓ GlobalSign QTSP root trusted
[ocsp] ✓ revocation info valid
[tsa] ✓ RFC 3161 token valid
[overall] PASS

5. Export legal-discovery bundle

$ cyberos-doc bundle --doc DOC-018824 --output discovery.zip

[bundle]
 doc-018824.pdf 412 KB (PAdES-B-LT)
 doc-018824-audit.json 18 KB (7 chained events)
 doc-018824-cert-chain.pem 6 KB (full chain to root)
 doc-018824-tsa-token.tsr 4 KB
 doc-018824-readme.md 2 KB (verification instructions)
[total] 442 KB
[written] discovery.zip

6. Migrate DocuSign envelopes

$ cyberos-doc migrate docusign --since 2020-01-01 --account "Acme Corp"

[connect] DocuSign OAuth ok
[scan] 324 envelopes since 2020-01-01
[import] 324 imported · 0 failures
[trust] all flagged trust=external
[audit] memory seq=21899 chain=…

7. Apply legal hold

$ cyberos-doc legal-hold --doc DOC-018824 --on --reason "Acme litigation 2026-104"

[hold applied] DOC-018824 protected beyond retention window
[next purge] blocked — legal_hold=true
[audit] memory seq=21902 chain=… (CLO co-sign required: stephen@cyberskill.com confirmed)

8. DSAR export for a signer

$ cyberos-doc dsar-export --subject alice@acme.com --output dsar.zip

[dsar] signed_documents: 12 (since 2024)
[dsar] audit_events: 96 rows
[dsar] cert_chains: 12 PEM files
[dsar] written: dsar.zip (4.8 MB)

Phase status & estimates

ItemDetail
StatusPlanned - P4 long-term
Est. LoC (Rust)~9,500 (services/doc + PAdES + broker)
Est. LoC (TS)~3,200 (signer SPA + template designer)
Planned tests140+ (incl. eIDAS conformity suite)
External libs~20 (pdfium, cms, oid-registry, CAdES)
P4 budget~$80/mo + $0.50-$2 / signature (Fargate + S3 + KMS + QTSP pass-thru)

Capability| Status ---|--- Document upload + SHA-256 + KMS encrypt| planned - P4 Template designer + auto-fill from CRM/HR| planned - P4 Multi-party signing (sequential + parallel)| planned - P4 eIDAS QTSP integration (GlobalSign / Cryptomathic)| planned - P4 Adobe AATL chain (DigiCert)| planned - P4 VN CA chain (VnPay / MK Group / Viettel-CA)| planned - P4 VNeID integration for VN citizens| planned - P4 WebAuthn / SMS-OTP / email-link IDV| planned - P4 PAdES-B-LT archival with LTV outer timestamp| planned - P4 S3 Object-Lock Compliance, residency-pinned| planned - P4 DocuSign / Adobe Sign / HelloSign migration| planned - P4 Notary-fraud detection (velocity + device)| planned - P4 Legal-hold + retention sweep| planned - P4 Cosign verification end-to-end| planned - P4 Quarterly external eIDAS audit| planned - P4+

References

Changelog

History lives in the changelog; this page describes only the current state.


Generated from modules/doc/docs/index.md — edit the markdown source, not this file (TASK-DOCS-002).