Core System Architecture Part 74 — Anonymous Network Database, Persistent State, Transaction Boundaries, Schema Evolution & Storage-Engine Architecture
SIAR — Secure, Local-First, Multi-Transport Communication Platform
Series: Core System Architecture
Part: 74
Status: Architecture specification
Primary language: Rust
Builds on: Core System Architecture Parts 4–6, 9, 31–33, 35, 40, 46–50, 57, 61–73
Primary purpose: define SIAR's database and persistent-state architecture across client devices, provider infrastructure, control-plane services, mailboxes, federation gateways, tenants, and archival systems, including authoritative vs derived state, transactional boundaries, storage-engine abstractions, idempotency, schema evolution, migrations, crash recovery, retention/deletion integration, encryption, replication, and operational correctness.
1. Purpose
Persistent state is where correctness becomes durable.
A distributed anonymous system may correctly process a message in memory and still fail if persistence semantics are wrong.
Failure examples:
message sent before durable write
duplicate delivery after crash
stale schema during rolling upgrade
deleted object resurrected by old replica
tenant data queried without scope
mailbox ACK returned before storage commit
migration partially applied
cache treated as authority
The governing principle is:
Persistent state must have explicit ownership, authority, transaction boundaries, versioning, and crash-recovery semantics. Storage engines are replaceable mechanisms; invariants are not.
2. Architectural Position
Domain / Protocol State Machine
│
▼
Persistence Interfaces
│
┌──────┼──────┐
│ │ │
SQL KV Object Store
│ │ │
└──────┼──────┘
▼
Durable / Derived State
3. Core Separation
Keep distinct:
authoritative state
derived state
ephemeral state
cache
index
archive
backup
replica
4. State Authority Classes
#![allow(unused)] fn main() { pub enum StateAuthority { Authoritative, Derived, Ephemeral, Rebuildable, } }
5. Authoritative
Loss affects correctness.
Examples:
account authority state
outbox
mailbox object metadata
tenant policy
governance policy version
6. Derived
Can be rebuilt from authoritative source.
Examples:
search index
materialized view
cached route score
7. Ephemeral
Never part of durable truth.
Examples:
typing
presence
in-flight retry timer
8. Rebuildable
Persisted for performance but disposable.
Examples:
thumbnail cache
denormalized metrics
9. Hard Rule
No component may treat cache/index state as authoritative unless explicitly declared.
10. Persistent State Registry
Machine-readable registry.
#![allow(unused)] fn main() { pub struct PersistentStateDescriptor { pub state_type: StateTypeId, pub authority: StateAuthority, pub durability: DurabilityRequirement, pub lifecycle: LifecyclePolicyId, pub encryption: EncryptionRequirement, } }
11. No Undeclared Durable State
Hard rule.
12. Storage Domains
#![allow(unused)] fn main() { pub enum StorageDomain { ClientLocal, MailboxProvider, RelayState, Directory, ControlPlane, Federation, Tenant, Archive, } }
13. Client Local Store
Stores:
message history
conversation metadata
outbox
receipts
contacts
device state
14. Provider Store
Stores minimum opaque service state.
15. Control Plane Store
Stores:
infrastructure state
config versions
deployment state
tenant metadata
16. No User Content In Control Plane
Hard rule.
17. Storage Engine Classes
#![allow(unused)] fn main() { pub enum StorageEngineClass { Sql, KeyValue, Object, AppendOnlyLog, } }
18. SQL
Best for:
relational metadata
transactions
constraints
queries
migrations
19. KV
Best for:
high-throughput lookup
small scoped records
caches
replay windows
20. Object Storage
Best for:
attachments
encrypted large blobs
backups
archives
21. Append-Only Log
Best for:
audit
event sourcing in narrow domains
transparency logs
22. No One-Database Dogma
Hard rule.
23. Engine Choice
Driven by invariants.
24. SQL Baseline
PostgreSQL for infrastructure/control-plane metadata is a strong default.
25. Client SQL
SQLite/rusqlite-like embedded SQL can be used.
26. Alternative Embedded DB
Allowed if ACID, migration, corruption-recovery, and encryption requirements are met.
27. Domain Independence
Core domain crates do not import database driver types.
28. Repository Trait
#![allow(unused)] fn main() { pub trait Repository<T> { type Id; fn get(&self, id: &Self::Id) -> Result<Option<T>, StorageError>; } }
29. Transaction Abstraction
#![allow(unused)] fn main() { pub trait StorageTransaction { fn commit(self) -> Result<(), StorageError>; fn rollback(self) -> Result<(), StorageError>; } }
30. Transaction Boundary
Defined by domain invariant.
31. Example: Send Message
Must atomically persist:
message record
outbox entry
attachment references
before transmission starts.
32. Persist-Before-Send
Hard rule.
33. Message Send Transaction
Conceptually:
#![allow(unused)] fn main() { pub struct PersistedSendIntent { pub message: MessageRecord, pub outbox: OutboxRecord, pub attachment_refs: Vec<AttachmentRef>, } }
34. Commit Then Dispatch
begin tx
→ insert message
→ insert outbox
→ update conversation sequence
→ commit
→ signal transport worker
35. No Send Inside Transaction
Preferred.
36. Why
Network call is nondeterministic/slow.
37. Transactional Outbox
Core pattern.
38. Outbox State
#![allow(unused)] fn main() { pub enum OutboxState { Pending, InFlight, Delivered, FailedRetryable, FailedPermanent, } }
39. Crash Recovery
Pending survives restart.
40. InFlight Recovery
On restart:
requeue with idempotency/dedup
41. Delivery ACK
Must map to stable MessageId.
42. Inbox Pattern
Receiver persists before app-level ACK.
43. Receive Transaction
validate
→ dedup check
→ persist message
→ persist delivery state
→ commit
→ send app-level ACK
44. ACK-Before-Commit Forbidden
Hard rule.
45. Idempotency
Mandatory.
46. Idempotency Key
Protocol-generated stable operation/message ID.
47. Idempotency Record
#![allow(unused)] fn main() { pub struct IdempotencyRecord { pub operation: OperationId, pub result_digest: Option<ContentDigest>, } }
48. Idempotent Apply
Duplicate operation yields same semantic result.
49. No Duplicate Side Effect
Hard rule.
50. Deduplication Scope
Protocol-specific.
51. Message Dedup
By MessageId.
52. Payment/Credit Dedup
By SpendId/OperationId.
53. Admin Command Dedup
By AdminCommandId.
54. Schema Version
Every durable schema versioned.
#![allow(unused)] fn main() { pub struct SchemaVersion(pub u32); }
55. Migration Version
Separate.
#![allow(unused)] fn main() { pub struct MigrationVersion(pub u32); }
56. Data Format Version
Separate from DB schema version.
57. Protocol Version
Separate from DB schema version.
58. Hard Rule
Do not conflate API, protocol, and storage schema versions.
59. Schema Evolution Principle
Use:
expand
→ migrate
→ contract
60. Expand
Add compatible schema first.
61. Migrate
Backfill/dual-write if necessary.
62. Contract
Remove old schema only after fleet compatibility window.
63. Rolling Upgrade
Mixed-version nodes expected.
64. Backward Read Compatibility
Needed during rollout.
65. Forward Read Compatibility
Useful where practical.
66. No Breaking Schema Change In Single Step
Hard rule for shared production stores.
67. Migration Metadata
#![allow(unused)] fn main() { pub struct MigrationDescriptor { pub from: SchemaVersion, pub to: SchemaVersion, pub reversible: bool, pub online: bool, } }
68. Reversible
Explicit.
69. Irreversible Migration
Requires backup/recovery plan.
70. Migration State
#![allow(unused)] fn main() { pub enum MigrationState { Pending, Running, Validating, Completed, Failed, } }
71. Migration Lock
Prevent concurrent incompatible migration.
72. Advisory Lock / Leader
Possible.
73. No Per-Request Auto-Migration
Hard rule.
74. Client DB Migration
Runs at startup/update.
75. Client Migration Requirements
transactional where possible
crash-safe
backup or rollback strategy
76. Migration Journal
Durable.
77. Crash Mid-Migration
Must resume or rollback.
78. Hard Rule
Unknown partial migration state cannot open app normally.
79. Read-Only Recovery Mode
Useful.
80. Schema Compatibility Gate
#![allow(unused)] fn main() { pub trait SchemaCompatibilityGate { fn check( &self, binary: SoftwareVersion, schema: SchemaVersion, ) -> Result<(), StorageError>; } }
81. Binary Refuses Unsupported Schema
Hard rule.
82. Data Migration Backfill
Chunked.
83. Why
Avoid giant transactions.
84. Backfill Checkpoint
#![allow(unused)] fn main() { pub struct MigrationCheckpoint { pub migration: MigrationVersion, pub cursor: Option<OpaqueCursor>, } }
85. Online Migration
Rate-limited.
86. Production Traffic Priority
Higher than backfill.
87. Migration Observability
Aggregate.
88. No user-content logging.
89. Transaction Isolation
Choose by invariant.
90. PostgreSQL Isolation Levels
Possible:
Read Committed
Repeatable Read
Serializable
91. Default
Read Committed may suffice for simple CRUD.
92. Stronger Invariants
Use:
constraints
locking
serializable transaction
93. Example
One-time credit spend:
must prevent double spend
94. Unique Constraint
Prefer DB-enforced invariant.
95. Compiler + DB
Use both.
96. No Application-Only Uniqueness
Hard rule where DB can enforce.
97. Constraint Types
primary key
unique
foreign key
check
not null
98. Database Constraint
Part of domain safety.
99. Foreign Keys
Useful, but avoid cross-tenant leakage.
100. Tenant-Scoped Composite Key
Example:
(tenant_id, object_id)
101. Multi-Tenant Isolation
Part 69.
102. Tenant Scope Required In Key
For shared stores.
103. RLS
Defense in depth.
104. No Query Without Tenant Context
Hard rule.
105. Connection Pool
Bounded.
106. Pool Trait
#![allow(unused)] fn main() { pub trait ConnectionPool { fn acquire(&self) -> Result<DbConnection, StorageError>; } }
107. No Unbounded Pool
Hard rule.
108. Pool Backpressure
Required.
109. Transaction Timeout
Required.
110. Long Transactions
Avoid.
111. Why
Lock contention/vacuum issues.
112. Large Object Transfer
Not inside SQL transaction.
113. Two-Phase App Workflow
Example attachment:
upload encrypted blob
→ persist manifest reference transactionally
114. Orphan Blob
GC later.
115. No Blob Commit Coupled To Remote Store Transaction
Hard rule.
116. Saga Pattern
For cross-store operations.
117. Saga State
#![allow(unused)] fn main() { pub enum SagaState { Started, StepCompleted(u32), Compensating, Completed, Failed, } }
118. Saga Journal
Durable.
119. Idempotent Steps
Mandatory.
120. Compensation
Best effort, domain-safe.
121. No Distributed 2PC Requirement
Preferred.
122. Why
Complexity/availability.
123. Use Local Transaction + Outbox + Saga
Recommended.
124. Object Store Consistency
Must understand provider semantics.
125. Object Address
Content-addressed or opaque random ID.
126. Sensitive Mode
Opaque random object ID often better.
127. Content Addressing
May leak equality.
128. Strict Privacy
Disable cross-user dedup.
129. Blob Metadata DB
Authoritative reference metadata in SQL/KV.
130. Blob Bytes
Object store.
131. Attachment Transaction
Persist:
manifest
chunk refs
ownership refs
132. Garbage Collection
Part 67.
133. Refcount
If used, update transactionally.
134. Refcount Race
Must test.
135. Prefer Mark/Sweep For Complex Sharing
Where safer.
136. Mailbox Store
Requires:
durable deposit
bounded TTL
idempotent fetch ACK
137. Mailbox Deposit Transaction
validate quota
→ store ciphertext
→ insert metadata
→ commit
→ return deposit ACK
138. Deposit ACK Before Commit Forbidden
Hard rule.
139. Mailbox Fetch
Read without deleting immediately.
140. Fetch ACK
Separate.
141. Delete After Client Durable Persist
Preferred.
142. At-Least-Once Delivery
Acceptable with dedup.
143. Exactly-Once
Do not claim globally.
144. Hard truth.
145. Directory Store
Mostly signed snapshots/state.
146. Append-Only History
Useful for audit.
147. Snapshot State
Current authoritative view.
148. Transparency Log
Append-only.
149. Governance Store
Strong consistency.
150. Quorum decisions.
151. No Eventually Consistent Governance Authority State
Hard rule.
152. Control Plane Store
Strongly consistent metadata preferred.
153. Runtime Nodes
Consume signed cached config.
154. Control Plane Unavailable
No unsafe write-through fallback.
155. Federation Store
Separate per peer/domain.
156. No One Global Federation DB
Hard rule.
157. Federation Queue
Durable.
158. Per-Domain Bulkhead.
159. Retry State
Stored.
160. No Cross-Domain User Identity Table
Hard rule.
161. Client Local-First Database
Core offline truth.
162. Client DB Stores
messages
conversations
contacts
outbox
receipts
local settings
163. Client DB Encryption
Required for sensitive durable state.
164. Key Source
Device secure store.
165. No DB Encryption Key In DB
Hard rule.
166. Database File
Encrypted at rest.
167. Attachments
Separate encrypted blob store.
168. Search Index
Derived.
169. FTS
Can be separate table/index.
170. Deleted Message
Must remove from FTS.
171. Embeddings
Derived and lifecycle-bound.
172. No Durable AI Index Without Lifecycle Policy
Hard rule.
173. Event Log
Part 4.
174. Event Sourcing
Use selectively.
175. Good Candidates
sync history
audit
governance transitions
176. Not Every Domain
Hard rule.
177. Event Record
#![allow(unused)] fn main() { pub struct DurableEvent { pub event_id: EventId, pub version: EventSchemaVersion, pub payload: Bytes, } }
178. Event Schema
Versioned.
179. Event Replay
Deterministic.
180. Snapshot
Can accelerate.
181. Snapshot Must Be Derivable
For event-sourced domain.
182. No Event Log Containing Sensitive Plaintext By Default
Hard rule.
183. WAL
Database internal.
184. Backup Implication
WAL may contain deleted/old data.
185. Encryption Important.
186. PostgreSQL WAL
Treat as sensitive durable storage.
187. SQLite WAL
Likewise.
188. Checkpoint/Vacuum
Operational lifecycle.
189. Encryption At Rest
Layered.
190. Options
database-level encryption
filesystem encryption
application-level field/blob encryption
191. Application-Level Encryption
Needed when storage provider must not see content.
192. Database Encryption
Protects lost disk.
193. Both Can Coexist.
194. Field-Level Encryption
Use carefully.
195. Searchability Tradeoff
Encrypted fields harder to query.
196. Prefer Minimal Server-Side Query On User Content
Hard rule.
197. Key Rotation
Part 66.
198. Rewrap DEKs when possible.
199. Database Key Rotation
Online where supported.
200. No Full-DB Downtime If Avoidable
Goal.
201. Backup Architecture
Part 33/67.
202. Logical Backup
Preferred for portability.
203. Physical Backup
Useful for infrastructure recovery.
204. Physical Backup Contains Engine internals.
205. User Content
Encrypted.
206. Backup Consistency
Use snapshot/transaction boundaries.
207. No Fuzzy Inconsistent Backup
Hard rule.
208. Point-In-Time Recovery
Useful for PostgreSQL.
209. PITR
Must respect deletion anti-resurrection.
210. Restore Filter/Deletion Ledger
Part 67.
211. No Restore To Old State Without Applying Revocations/deletions.
212. Hard rule.
213. Replication
Engine-level or application-level.
214. PostgreSQL Replication
Good for control-plane HA.
215. Mailbox Replication
May be application-aware.
216. Client Sync
Application-level.
217. Replication Factor
Policy.
218. No More Replication Than Needed
Privacy/cost.
219. Strong Consistency
Use where authority requires.
220. Eventual Consistency
Use where tolerant.
221. Consistency Requirement
#![allow(unused)] fn main() { pub enum ConsistencyRequirement { Strong, Causal, Eventual, LocalOnly, } }
222. Governance
Strong.
223. Tenant policy
Strong.
224. Presence
Ephemeral/eventual.
225. Search Index
Eventual.
226. Message Local History
Local strong.
227. Multi-Device Sync
Conflict-resolved eventual.
228. Conflict Resolution
Domain-specific.
229. No Generic Last-Write-Wins For All Data
Hard rule.
230. Why
Clock skew/data loss.
231. Conflict Policy Registry
#![allow(unused)] fn main() { pub enum ConflictPolicy { AppendOnly, MonotonicVersion, DomainMerge, TombstoneWins, LastWriterWinsAllowed, } }
232. TombstoneWins
For deletion.
233. MonotonicVersion
For policy/config.
234. AppendOnly
For audit/event log.
235. LWW
Only low-risk fields.
236. Timestamp
Not sole ordering authority.
237. Prefer
sequence
epoch
version
238. Schema Ownership
Each crate/domain owns its schema.
239. No Giant Shared SQL Schema Ownership
Hard rule.
240. Migration Module
Lives with owning crate.
241. Cross-Domain Foreign Keys
Use sparingly.
242. Why
Tight coupling.
243. Integration Boundary
Use IDs/events.
244. Monolithic Workspace
Still modular schema ownership.
245. Storage Adapter
Per domain.
246. Example Layout
crates/
messaging-store/
identity-store/
mailbox-store/
governance-store/
247. Database Connection Sharing
Infrastructure optimization.
248. Domain interface still separate.
249. Repository Error Model
#![allow(unused)] fn main() { pub enum StorageError { NotFound, Conflict, ConstraintViolation, Unavailable, Corruption, UnsupportedSchema, TransactionAborted, EncryptionFailure, Internal, } }
250. No Raw DB Error Leakage
Hard rule.
251. Error Normalization
Required.
252. Corruption Detection
Critical.
253. Checksums
Use where engine provides.
254. Application-Level Integrity
For encrypted blobs/manifests.
255. Corruption State
#![allow(unused)] fn main() { pub enum CorruptionState { None, Suspected, Confirmed, } }
256. Confirmed Corruption
Read-only/quarantine affected store.
257. No Continue Blindly
Hard rule.
258. Recovery
Restore/rebuild depending authority class.
259. Derived Store Corrupt
Rebuild.
260. Authoritative Store Corrupt
Recover from replica/backup.
261. Corruption Incident
Part 65/70.
262. Storage Health
Separate from service health.
263. Health Metrics
commit latency
pool saturation
replication lag
disk usage
error rate
264. No User Query Logging
Hard rule.
265. Slow Query Logging
Use normalized query fingerprint.
266. No parameter values.
267. Query Plan Regression
Monitor.
268. Index Strategy
Evidence-driven.
269. No Over-Indexing Sensitive Fields
Hard rule.
270. Index Leakage
Indexes can reveal:
existence
cardinality
frequency
271. Server-Side User Content Indexing
Minimize.
272. Client Search
Preferred.
273. Partitioning
Infrastructure DB.
274. Partition By
tenant
time
provider
depending domain.
275. No Partition By Global User ID
Hard rule.
276. Mailbox Partition
By opaque mailbox shard.
277. Tenant DB Shard
TenantId.
278. Federation Queue Partition
FederationDomainId.
279. Shard Movement
Explicit migration.
280. No Ad-Hoc Resharding During Incident
Hard rule.
281. Capacity Planning
Part 63.
282. DB Sizing Inputs
writes/s
reads/s
rows
bytes
retention
index overhead
replication
283. Storage Headroom
Required.
284. Disk Full
Must fail safely.
285. Mailbox Disk Full
Reject new deposits before corruption.
286. Client Disk Full
Keep existing data safe.
287. Migration Disk Headroom
Account for temporary duplication.
288. No Migration Without Space Preflight
Hard rule.
289. Vacuum/Compaction
Engine-specific.
290. Maintenance Scheduling
Low priority.
291. Compaction Privacy
No user-level metrics.
292. KV Compaction
Can create write amplification.
293. Benchmark.
294. Storage Engine Capability Model
#![allow(unused)] fn main() { pub struct StorageCapabilities { pub transactions: bool, pub snapshot_reads: bool, pub ttl: bool, pub atomic_compare_swap: bool, pub encryption_at_rest: bool, } }
295. Domain Requirement Check
#![allow(unused)] fn main() { pub trait StorageCapabilityValidator { fn validate( &self, requirements: &StorageRequirements, capabilities: &StorageCapabilities, ) -> Result<(), StorageError>; } }
296. No Engine Used If It Cannot Meet Invariant
Hard rule.
297. Pluggable Storage
Possible.
298. But
Not lowest-common-denominator semantics.
299. Domain States Requirements Explicitly.
300. Example
Mailbox requires:
durable atomic metadata write
TTL or retention scheduler
idempotent key
301. Embedded Store Requirement
Client requires:
ACID
crash recovery
migration
encryption
302. Store Replacement
Migration/export-import.
303. No Direct File Copy Between Different Engines
Hard rule.
304. Canonical Logical Export
Versioned.
305. Import Validation
Strict.
306. Store Adapter Version
#![allow(unused)] fn main() { pub struct StorageAdapterVersion(pub u16); }
307. Data Access Layer
Use typed queries.
308. Raw SQL
Contained.
309. No SQL String Construction From User Input
Hard rule.
310. Prepared Queries
Preferred.
311. SQLx/Diesel/rusqlite
Implementation choice.
312. Pure Rust Preference
Use pure-Rust client libs where practical.
313. Native SQLite
May use FFI depending crate.
314. FFI isolated.
315. Migration Testing
Required.
316. Test Matrix
fresh install
N-1→N
N-2→N where supported
crash mid migration
rollback
mixed version
317. Data Fixtures
Synthetic.
318. No Production DB Snapshot In CI
Hard rule.
319. Property Tests
Examples:
persist-before-send
ACK-after-commit
tombstone beats stale write
tenant scope preserved
320. Fuzzing
Fuzz:
migration metadata
logical export/import
event payload
storage adapter input
321. Fault Injection
Inject:
disk full
fsync failure
connection loss
transaction abort
replica lag
322. Crash Tests
After each transaction step.
323. Concurrency Tests
Examples:
double spend
duplicate message
delete vs update
migration vs request
324. Loom
Useful for in-memory transaction coordination.
325. Kani
Useful for version/constraint logic.
326. TLA+
Useful for:
outbox/inbox retry
replicated delete
schema rolling upgrade
327. Benchmarking
Part 63.
328. Benchmark Realistic Security
Encryption enabled.
329. DB Performance Tests
Measure:
commit p99
pool saturation
migration throughput
recovery time
330. Storage SLO
Per domain.
331. Data Lifecycle Integration
Part 67.
332. Every table/object namespace maps to lifecycle policy.
333. Retention Job
Uses lifecycle engine.
334. No ad-hoc cron delete logic.
335. Privacy Integration
Part 56.
336. Consent-controlled state has scope.
337. Legal Hold Integration
Part 55/67.
338. Hold only scoped records.
339. Multi-Tenant Integration
Part 69.
340. Tenant-scoped repositories.
341. Supply-Chain Integration
Part 73.
342. Migration binary/artifact exact and signed.
343. Attestation Integration
Part 71.
344. Critical DB host state verified.
345. Disaster Recovery Integration
Part 70.
346. Restore drills validate database recovery.
347. Database Secret Management
Part 61/72.
348. DB credentials scoped.
349. No Shared Superuser Credential
Hard rule.
350. Role Separation
application role
migration role
backup role
admin role
351. Application Role
Least privilege.
352. Migration Role
Elevated, temporary.
353. Backup Role
Read only where possible.
354. Admin Role
Human/admin only.
355. No App Running As DB Owner
Hard rule.
356. Schema Ownership
Migration service/role.
357. Credential Rotation
Routine.
358. Connection Pool Reacts to rotation.
359. No Long-Lived Static Password
Preferred.
360. TLS
DB connections encrypted.
361. Mutual auth where practical.
362. Database Network
Private/internal.
363. No Public DB Port
Hard rule.
364. Client DB
Local only.
365. Database Observability
Safe metrics:
query class latency
pool usage
replication lag
disk usage
migration state
366. Forbidden telemetry
No:
message text
contact IDs
raw mailbox IDs
SQL parameter values
367. Database Audit
Infrastructure/admin actions.
368. No user query content.
369. Storage Privacy Invariants
Mandatory.
370. Examples
server does not need plaintext to route encrypted message
search stays client-side where possible
cross-user blob dedup disabled strict mode
371. Initial Production Storage Profile
Recommended:
Client:
encrypted embedded SQL
encrypted blob store
derived FTS/index
Infrastructure:
PostgreSQL for control-plane/relational authority
KV where bounded high-throughput state needs it
object storage for encrypted blobs/backups
372. No Mandatory Single Vendor
Hard rule.
373. Storage Portability
Logical schemas/adapters.
374. Data Export
RON/Postcard can be used for internal logical snapshots where appropriate.
375. JSON
Only external interoperability.
376. Postcard
Good for compact binary internal records.
377. RON
Good for human-reviewed metadata/config, not giant datasets.
378. DB Record Encoding
Native SQL columns preferred for queryable metadata.
379. Blob Column
Use for opaque versioned payload where appropriate.
380. No Whole Domain Row As Unversioned Blob
Hard rule.
381. Versioned Payload
#![allow(unused)] fn main() { pub struct VersionedBlob { pub version: u16, pub bytes: Vec<u8>, } }
382. Decode Bounds
Mandatory.
383. Migration Of Blob Formats
Separate from SQL schema migration.
384. Persistent State Ownership Graph
Machine-readable.
385. State Owner
#![allow(unused)] fn main() { pub struct StateOwnership { pub state_type: StateTypeId, pub owning_crate: CrateId, pub schema_version: SchemaVersion, } }
386. No Shared Anonymous Ownership
Hard rule.
387. Data Store Inventory
Part 67/73 style.
388. Inventory Includes
engine
location
authority
encryption
retention
backup
389. Storage Inventory
No user data.
390. Storage Incident Classes
#![allow(unused)] fn main() { pub enum StorageIncident { Corruption, CapacityExhaustion, ReplicationFailure, MigrationFailure, CredentialCompromise, UnauthorizedAccess, DataResurrection, } }
391. Data Resurrection
Privacy/security critical.
392. Migration Failure
Freeze writes if needed.
393. Capacity Exhaustion
Degrade safely.
394. Unauthorized Access
Rotate credentials/investigate.
395. DB Compromise
Assume server-side metadata exposure.
396. Application-level encrypted payloads limit content exposure.
397. No Server DB Compromise Should Reveal E2EE Plaintext
Hard rule.
398. Storage Testkit
Dedicated.
399. Test Engines
At least:
embedded SQL adapter
PostgreSQL adapter
object-store mock
400. Contract Tests
Same repository semantics across adapters.
401. Storage Contract
#![allow(unused)] fn main() { pub trait StorageContractTest { fn run<A: StorageAdapter>( adapter: A, ) -> ContractTestReport; } }
402. Contract Scenarios
atomicity
idempotency
rollback
crash recovery
migration
403. No Adapter Accepted Without Contract Suite
Hard rule.
404. Crate Layout
Recommended:
crates/
├── siar-storage-core/
├── siar-storage-registry/
├── siar-storage-sql/
├── siar-storage-kv/
├── siar-storage-object/
├── siar-transaction/
├── siar-outbox/
├── siar-inbox/
├── siar-schema/
├── siar-migration/
├── siar-storage-recovery/
├── siar-storage-observability/
└── siar-storage-testkit/
405. siar-storage-core
Owns:
authority classes
capabilities
errors
406. siar-storage-registry
Persistent state ownership/requirements.
407. siar-storage-sql
SQL adapters.
408. siar-storage-kv
KV adapters.
409. siar-storage-object
Blob/object adapters.
410. siar-transaction
Transaction/saga abstractions.
411. siar-outbox
Transactional outbox.
412. siar-inbox
Dedup/persist/ACK.
413. siar-schema
Schema versions/compatibility.
414. siar-migration
Migration engine/checkpoints.
415. siar-storage-recovery
Corruption/restore/rebuild.
416. siar-storage-observability
Privacy-safe metrics.
417. siar-storage-testkit
Crash/fault/contract/migration tests.
418. Security, Privacy & Correctness Invariants
Mandatory:
1. Every durable state type has an explicit owner, authority class, lifecycle policy, and storage requirements.
2. Message send uses persist-before-send transactional outbox semantics.
3. Message/mailbox ACKs are never emitted before required durable commit.
4. All externally retried side effects are idempotent and deduplicated by stable operation/message IDs.
5. Cache/search/index state is never silently treated as authoritative.
6. Database/API/protocol schema versions are independent and migration-compatible.
7. Shared production schema changes follow expand→migrate→contract and support mixed-version rollout.
8. Deleted/tombstoned state cannot be resurrected by stale replicas, backups, or offline writes.
9. Tenant-scoped stores enforce tenant context structurally and through database policy where available.
10. Critical server compromise must not reveal E2EE plaintext stored only as encrypted payloads.
11. Application services do not run with database-owner/superuser privileges.
12. Storage-engine replacement is allowed only when the new engine satisfies declared domain invariants and passes the storage contract test suite.
419. Initial Production Scope
Implement first:
persistent-state registry
embedded encrypted SQL client store
PostgreSQL infrastructure store
transactional outbox/inbox
stable MessageId/OperationId dedup
bounded connection pools
typed repositories
schema/version registry
expand-migrate-contract migration engine
migration checkpoints
object-store adapter
lifecycle/deletion hooks
tenant-scoped repository layer
backup/restore anti-resurrection
storage contract testkit
Then add:
specialized KV adapters
online backfill controller
advanced cross-store saga engine
formal migration verification
automated storage capability negotiation
multi-engine portability tooling
420. Definition of Done
Part 74 is complete when:
- all durable state has an explicit authority classification
- domain crates are independent from specific database drivers
- message send/receive persistence is crash-safe
- outbox/inbox/idempotency semantics are explicit
- provider mailbox ACKs follow durable commit
- schema, migration, protocol, and API versions are separate
- expand→migrate→contract supports rolling upgrades
- migrations are crash-safe and checkpointed
- derived indexes/caches are rebuildable
- lifecycle/deletion/tombstone rules integrate with storage
- tenant-scoped repository isolation is structural
- storage encryption and credential separation are defined
- backups/restores cannot resurrect deleted/revoked state
- corruption/recovery procedures are explicit
- SQL/KV/object-store adapters are capability-validated and contract-tested
- database observability avoids user-content leakage
421. Final Architecture
DOMAIN / PROTOCOL LOGIC
│
▼
PERSISTENCE INTERFACES
│
┌──────────────┼──────────────┐
│ │ │
SQL KV Object Store
│ │ │
└──────────────┼──────────────┘
▼
AUTHORITY / LIFECYCLE LAYER
│
┌────────────┼────────────┐
│ │ │
Outbox/Inbox Migration Retention
│ │ │
└────────────┼────────────┘
▼
DURABLE STATE
Persistent-state safety model:
explicit authority
+
typed repositories
+
transactional boundaries
+
idempotency
+
versioned migrations
+
encryption
+
lifecycle integration
+
crash recovery
not:
write some rows, call the network, and hope retries or upgrades do not duplicate or lose state
422. Final Principle
The database is not the architecture.
The architecture is the set of durable invariants that every storage engine must preserve.
The correct model is:
domain-owned state
+
explicit transaction boundaries
+
idempotent distributed workflows
+
versioned schemas
+
safe migration
+
privacy-aware storage
This architecture gives SIAR a durable, local-first, multi-engine storage foundation that supports clients, mailboxes, control-plane services, tenants, federation, backups, and archives without tying correctness to a single database product.