Add web server & collaboration spec (ADRs C012-C020) and foundation crates#25
Merged
Merged
Conversation
…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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
server (tiered encryption, Loro relay, sovereignty posture)
ADR-C015 capability gates, RBAC Role/Action matrix (ADR-C017), EU-pinned
Residency type (ADR-C019)
crypto-agile KeyWrap trait with algorithm-tagged WrappedDek (ADR-C014),
AeadKeyWrap (Tier 0/1 symmetric KEK) and X25519KeyWrap (Tier 2
zero-knowledge sharing)
tamper-detection verification (ADR-C020)
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_014eNuucay71HTkHJisfmSqG