00 — Architecture Freeze V1

Status: FROZEN — 2026-06-12. This is the final architecture before coding begins. It supersedes conflicting statements elsewhere in the corpus. Changes from here require a new ADR that explicitly amends this freeze.

Optimized for correctness, consistency, and solo-developer feasibility. No new features were added and the platform was not redesigned — this freeze only reconciles contradictions found in the pre-development review and reduces scope to a buildable V1.


1. What this freeze does

The pre-development architecture review identified 5 blocking issues — internal contradictions on the two hardest subsystems (storage, sync) plus a security trap and data-model drift. All five are resolved below, the affected ADRs / data model / service boundaries / diagrams are rewritten to agree, and all 38 ADRs are reclassified into Accepted / Proposed / Deferred so that “Accepted” once again means “binds the V1 build.”


2. The 5 blockers — resolutions

# Blocker (review) Resolution (frozen) Artifacts changed
1 Two storage models — whole-blob (data model) vs chunk/pack/placement (ADRs 0017/0018/0019/0020), both “Accepted” Whole-object content-addressed blobs (BLAKE3), per tenant. No chunking/packing/placement in V1. FR-C3 delta = whole-object transfer in V1. ADR-0018 (rewritten, whole-object), ADR-0017 & 0020 Deferred, data model 08
2 Data-model I2 said “delete at refcount = 0” — the naive GC ADR-0019 calls unsafe Safe GC: deletion authorized by orphaned state + grace period + atomic zero-reference re-confirmation (CAS); dedup hit in the window flips back to committed. Refcount is a hint, never the authority. ADR-0019 (rewritten, whole-object), data model I2
3 Change journal described as both “source of truth” and “projection of at-least-once events” Journal is source of truth, CHANGE.seq assigned inside the File commit transaction (gap-free, totally ordered per tenant by construction). File owns/writes the journal; Sync reads it. ADR-0008 (rewritten), ADR-0024 (aligned), data model I4, service boundaries 05, diagrams 06
4 RLS tenant context + connection pooling unresolved (“session GUC” → cross-tenant bleed under transaction-mode pooling) Transaction-local SET LOCAL tenant context (PgBouncer transaction-mode safe); missing context = default-deny (no rows); non-BYPASSRLS app role; new pooled-bleed CI test. ADR-0038 (new), ADR-0007 (fixed), data model I3
5 Data model out of sync with ADRs 0014 / 0016 / 0037 BLAKE3 hash; composite PK (tenant_id, content_hash); TENANT_KEY + BLOB.enc_algo/dek_version (per-tenant envelope encryption); SHARE.link_token_hash (store hash only). Materialized-path move cost stated honestly. data model 08, ADR-0014, ADR-0037

3. V1 scope boundary

V1 = roadmap P0–P2 (walking skeleton → core product → correct sync). This is the portfolio-credible deliverable; it satisfies the hard correctness criteria. Everything beyond is documented direction, not committed build.

In V1 (built) Out of V1 (Proposed / Deferred)
bitvaultd modular monolith; in-process event bus NATS JetStream (P3), extracted services (P4)
Postgres (truth + journal + outbox), one object store (MinIO/S3) Chunking, packing, placement/federation, multi-provider
Commit protocol + safe whole-object GC Resumable multipart (Proposed), previews
RLS multi-tenancy (SET LOCAL) Schema/DB-per-tenant escalation
Sync: journal-at-commit, cursors, conflicted-copy, safety guards Sub-file delta sync
Postgres-FTS search OpenSearch content search (P3)
OAuth 2.1 + argon2id + API tokens; RBAC; public links (hashed token) MFA/passkeys/DPoP step-up (Proposed); sharing abuse program (P3)
Envelope encryption at rest (per-tenant DEK) Client-side E2E
OpenTelemetry from commit #1
Docker Compose (lite/standard) self-host Helm/K8s SaaS, GitOps, IaC, supply-chain, DR, env-promotion (P4)

Dependency tiers in V1: lite (Postgres + MinIO) and standard (+ Redis). The full tier (OpenSearch + extracted workers) is Deferred.


4. ADR reclassification (all 38)

Legend — Accepted: binds the V1 build. Proposed: agreed direction, not committed to V1 (authored into V1 only if its phase is reached). Deferred: out until a named P3+ forcing function.

Accepted (26)

# Title Note
0001 Modular monolith first Foundational
0002 Monorepo Foundational
0003 gRPC internal / REST external; proto contract Contracts from day 1
0004 Postgres = metadata source of truth
0005 Object storage abstraction One adapter (MinIO/S3) in V1
0006 Outbox + event bus Outbox + in-proc bus V1; NATS deferred to P3
0007 Multi-tenancy RLS Revised: SET LOCAL (see 0038)
0008 Change-journal sync + conflicted copy Revised: journal written at commit
0009 Search as derived index PG-FTS V1; OpenSearch deferred to P3
0010 AuthN/AuthZ: OAuth 2.1 + RBAC
0011 Direct-to-storage presigned URLs
0012 Tiered deployment packaging lite/standard V1; full + Helm/K8s deferred
0013 Observability: OpenTelemetry From commit #1
0014 Envelope encryption at rest Revised: schema footprint; per-tenant DEK
0015 API versioning (URI /v1)
0016 Content hashing: BLAKE3
0018 Deduplication scope Revised: whole-object, per-tenant
0019 Garbage collection Revised: whole-object grace + CAS
0022 Three-tree reconciliation (client planner) P2
0023 Local sync database (SQLite) P2
0024 Cursor delta pull Revised: aligned to journal-at-commit
0025 File watching strategy P2
0026 Conflict resolution policy (conflicted copy) P2
0027 Sync safety guards P2
0037 Public sharing security Revised: hashed token; abuse program deferred
0038 RLS connection pooling (SET LOCAL) New — blocker-4

Proposed (2)

# Title Why not yet committed
0021 Resumable / multipart uploads V1 ships single-shot presigned PUT with a size cap; multipart is the agreed next increment (P1/P2)
0036 Authentication policy (MFA/passkeys/step-up/DPoP) Basic authn is V1 (ADR-0010); this hardening is agreed but not committed to V1

Deferred (10)

# Title Re-opens at
0017 Content-defined chunking (FastCDC) + packing Large-file delta/dedup efficiency (post-V1)
0020 Storage placement & federation Multi-region/residency or 2nd provider (P5+/NG9)
0028 GitOps with ArgoCD P4 (extraction & scale)
0029 Progressive delivery (Argo Rollouts) P4
0030 Secrets management (External Secrets Operator) P4 (K8s/SaaS); V1 uses env/keyfile secrets
0031 IaC with OpenTofu P4
0032 CI/CD supply-chain (SBOM/cosign/SLSA) P4; V1 CI = build/test/lint
0033 Backup & DR P4; V1 = pg_dump + object versioning
0034 Environment promotion (build once, promote digest) P4 (multi-env); V1 single-env
0035 Machine identity (workload identity) P4

5. Reconciled correctness invariants (the test list)

# Invariant (data model 08 §2) Test
I1 No committed version without HEAD-verified bytes; VERSION + BLOB ref + CHANGE(seq) + OUTBOX are one transaction Dual-write chaos (kill between PUT and commit)
I2 Deletion needs orphaned + grace + atomic zero-ref re-confirm; dedup hit flips back; refcount is a hint GC race (re-upload during grace → not deleted)
I3 Tenant isolation via RLS + transaction-local SET LOCAL; no context ⇒ no rows Forged-tenant_id test and pooled-bleed test
I4 CHANGE.seq assigned in the commit tx ⇒ gap-free total order per tenant; journal is source of truth Conflict harness; read-your-writes on next pull
I5 Namespace ops are byte-free (stable NODE.id); subtree move is O(descendants) path rewrite, not O(1) Move/copy refcount + path-update test
I6 Search/notifications/meters are rebuildable projections; journal + namespace are not derived Rebuild search index from journal

6. Consistency guarantee

Before this freeze the corpus claimed to be “cross-linked and consistent” but contradicted itself on storage (two models), GC (safe vs unsafe), the journal (truth vs projection), the hash algorithm, the share-token storage, and the encryption schema. After this freeze:

Affected documents carry an Architecture Freeze V1 (2026-06-12) banner pointing here.


7. Coding may begin

The five blocking conditions from the review are met. The remaining review items (scalability/operability hardening — outbox partitioning, quota reserve, key-shred runbook) are non-blocking and tracked against their phases. Build P0 next.