Skip to content

Add web server & collaboration spec (ADRs C012-C020) and foundation crates#25

Merged
kevincarlson merged 5 commits into
mainfrom
claude/adr-spec-implementation-mm43ai
Jul 2, 2026
Merged

Add web server & collaboration spec (ADRs C012-C020) and foundation crates#25
kevincarlson merged 5 commits into
mainfrom
claude/adr-spec-implementation-mm43ai

Conversation

@kevincarlson

Copy link
Copy Markdown
Member
  • docs/adr/LOKI_WEB_SERVER_SPEC.md: ratified v1 spec for the collaboration
    server (tiered encryption, Loro relay, sovereignty posture)
  • loki-model: WorkspaceId/DocumentId/UserId newtypes, EncryptionTier with
    ADR-C015 capability gates, RBAC Role/Action matrix (ADR-C017), EU-pinned
    Residency type (ADR-C019)
  • loki-crypto: per-document DEK envelope encryption (XChaCha20-Poly1305),
    crypto-agile KeyWrap trait with algorithm-tagged WrappedDek (ADR-C014),
    AeadKeyWrap (Tier 0/1 symmetric KEK) and X25519KeyWrap (Tier 2
    zero-knowledge sharing)
  • loki-server-audit: append-only hash-chained audit entries with
    tamper-detection verification (ADR-C020)
  • stub manifests for the remaining server crates (store/collab/auth/api/bin)

Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_014eNuucay71HTkHJisfmSqG

claude added 5 commits July 1, 2026 23:44
…rates

- docs/adr/LOKI_WEB_SERVER_SPEC.md: ratified v1 spec for the collaboration
  server (tiered encryption, Loro relay, sovereignty posture)
- loki-model: WorkspaceId/DocumentId/UserId newtypes, EncryptionTier with
  ADR-C015 capability gates, RBAC Role/Action matrix (ADR-C017), EU-pinned
  Residency type (ADR-C019)
- loki-crypto: per-document DEK envelope encryption (XChaCha20-Poly1305),
  crypto-agile KeyWrap trait with algorithm-tagged WrappedDek (ADR-C014),
  AeadKeyWrap (Tier 0/1 symmetric KEK) and X25519KeyWrap (Tier 2
  zero-knowledge sharing)
- loki-server-audit: append-only hash-chained audit entries with
  tamper-detection verification (ADR-C020)
- stub manifests for the remaining server crates (store/collab/auth/api/bin)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014eNuucay71HTkHJisfmSqG
- loki-server-store: WorkspaceStore/UserStore/DocumentStore/MemberStore/
  OplogStore/AuditStore ports with Postgres (SQLx runtime queries,
  embedded migrations) and in-memory implementations; BlobStore over
  object_store with the {doc}/snap/{version} layout (ADR-C016); GDPR
  crypto-shredding and advisory-locked audit appends (ADR-C020)
- loki-server-collab: binary frame protocol, FanOutBus port with
  InMemoryBus and PgNotifyBus (pointer-based NOTIFY for updates, inline
  base64 for awareness with a typed oversize error), per-document local
  hub, testable DocRelay (persist-then-broadcast, ADR-C013), and the
  axum WebSocket adapter (ADR-C012)
- loki-server-auth: OIDC bearer verification pinned to issuer+audience
  (RS256/ES256 in production, static key source, JWKS fetch recorded as
  TODO), AuthContext claims mapping, and the RBAC require() helper
  (ADR-C017)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014eNuucay71HTkHJisfmSqG
- loki-server-api: /v1 routes (workspaces, documents, members, blobs,
  collab WS upgrade, export, GDPR export/erase) with bearer-auth
  middleware (JIT user provisioning), RBAC via require_doc_role
  (non-members see 404, not 403), problem+json errors with stable
  urn:appthere:loki:error:* codes, and the ADR-C015 tier gate: export on
  a zero-knowledge document is a typed 409 e2ee-capability-disabled;
  Tier-2 role grants require the client-driven DEK re-wrap (422 without)
- loki-server: single Axum binary (ADR-C012) — env config validated at
  startup (EU residency pin per ADR-C019, Tier 2 rejected as deployment
  default per ratified decision 6.1), Postgres pool + embedded
  migrations, S3/memory object store, PgNotifyBus wiring, RSA-key OIDC
  verifier, graceful SIGINT/SIGTERM shutdown
- six end-to-end handler tests over the in-memory ports (auth, RBAC,
  tier gating, GDPR, validation)
- CLAUDE.md: document the server crate group and its deliberate TODOs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014eNuucay71HTkHJisfmSqG
…ral)

Compaction:
- doc_meta.snapshot_seq (migration 0002): a move-forward-only pointer
  guard — set_snapshot(id, ptr, up_to) only advances, so a slow
  concurrent compactor loses the race instead of regressing the snapshot
  below the truncated oplog; losers skip truncation and delete their blob
- loki-server-collab Compactor: merges current snapshot + oplog tail via
  Loro, stores the new snapshot, truncates covered entries; Tier-2
  documents are never touched (server holds ciphertext); corrupt payloads
  abort the pass without truncating; run_periodic scans
  OplogStore::docs_with_backlog on a fixed cadence
- PUT /v1/documents/{doc}/snapshot: client-produced snapshot upload —
  how Tier-2 documents get compacted; forward-only with a typed 409
  snapshot-superseded; snapshot_seq exposed in document metadata so
  clients resume the collab WebSocket with after=snapshot_seq
- loki-server: compactor task wired with LOKI_COMPACT_INTERVAL_SECS /
  LOKI_COMPACT_MIN_ENTRIES (0 disables, with an honest warning)

JWKS (closes TODO(oidc-jwks)):
- KeySource is now async and returns owned keys so sources can refresh
- JwksKeySource: fetches the IdP's JWKS document over HTTPS, caches it,
  refetches on unknown kid (key rotation), throttles refetches so forged
  kids cannot hammer the IdP, and keeps serving cached keys through
  fetch failures; kid-less tokens resolve when the set has one key
- loki-server config: LOKI_OIDC_JWKS_URL (recommended) or
  LOKI_OIDC_RSA_PEM_FILE (static fallback), exactly one

Also splits the in-memory store into memory/{mod,content}.rs to stay
under the 300-line ceiling. 63 tests pass; clippy -D warnings clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014eNuucay71HTkHJisfmSqG
…tion

- docs/adr/LOKI_HEADLESS_SERVER_SPEC.md: ratified v1 headless spec
- loki-convert (ADR-C024): conversion matrix orchestrating the existing
  import/export crates — DOCX/ODT to each other and to EPUB/PDF
  (PDF/X-1a/X-3/X-4 profile selection), XLSX/ODS both ways; unsupported
  pairs return a typed ConversionUnsupported before any parsing;
  PPTX/ODP/ODG gated per ratified decision 5.1; pdf-a2b is a typed
  ProfileUnsupported until the krilla migration (TODO(headless-c022));
  integration tests run real DOCX->ODT->DOCX, DOCX->PDF/X-4, DOCX->EPUB,
  and ODS<->XLSX conversions
- loki-print (ADR-C023): blocking IPP client over rustls — Print-Job
  dispatch of rendered PDF with copies/sides/media/print-color-mode
  attributes (friendly media names map to PWG keywords),
  Get-Job-Attributes polling with typed JobFailed/JobTimeout; DNS-SD
  discovery deferred (TODO(headless-c023-discovery))
- loki-headless: clap CLI with convert/render/print/formats; print
  renders non-PDF inputs through loki-convert first (render -> PDF ->
  IPP); end-to-end tests drive the compiled binary on real files; the
  apalis worker mode, HTTP endpoint, vello_cpu thumbnails, fail-closed
  fonts, and TEE attestation are recorded as in-code TODOs
- loki-server-api export route TODO relabelled to headless-c025 (the
  queue ADR) now that the conversion engine exists

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014eNuucay71HTkHJisfmSqG
@kevincarlson kevincarlson merged commit 70ef320 into main Jul 2, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants