SQLite storage and disaster recovery
- Status: Specification v0.5 — September 2026. The SQL and model tests are executable; database commands, services, migrations, and hardware recovery are not implemented.
- Authority: This document owns SQLite schemas, connection policy, reconstruction records, and database maintenance. STATE-AND-RECOVERY owns artifact identity, trust, privilege, and the transaction state machine. KEY-RUNBOOK owns signing authority and compromise response.
1. Ownership and authority
SQLite stores projections of durable decisions and verified objects. It does not establish trust, authorize privileged effects, or replace recovery journals. Each owner has its own files and reconstruction stream. The offline root Pi keeps its existing ceremony records; it gains no database. Farm agents exchange the existing signed job/result protocol and never open the coordinator's database.
Diagram source
flowchart LR
U[Prefix owner] --> C[Client state]
U --> K[Disposable client cache]
T[Authoritative trust records] --> K
J[Prefix records and journals] --> C
H[Authorized system helper] --> S[Protected system state]
R[Protected trust and journals] --> H
C -. untrusted plan input .-> H
A[Agent job and result protocol] --> F[Farm coordinator]
E[Retained plans and gate evidence] --> F
F --> P[Publisher]
P --> N[Release signer]
N --> P
O[Offline root ceremonies] --> N
O --> P
Arrows show data flow, not inherited authority. The helper authenticates a complete closure independently. The publisher and signer independently enforce authorization, gates, expected base, and environment. Their private keys and credentials remain outside SQLite and outside database backup sets.
1.1 Files, owners, and creation
<system> is /Library/Application Support/aslice/system. <coordinator>,
<publisher>, and <signer> are absolute local service-state roots selected during
provisioning and recorded in owner-controlled configuration. They must be distinct
directories, even on a shared machine. A database path is never a network share.
| CLI role / SQL companion | Active location | Owner and access | Creation trigger |
|---|---|---|---|
client-state / schema |
<prefix>/db/state.sqlite |
Prefix owner writes; that owner's manager and inspection read; committed execution catalog supplies shim dispatch | Explicit prefix initialization |
client-cache / schema |
<prefix>/cache/db/cache.sqlite |
Prefix owner; fetcher and solver read/write | First authenticated refresh or solve |
system-state / schema |
<system>/db/state.sqlite |
Root helper writes; authorized helper inspection reads | First authorized protected operation |
coordinator / schema |
<coordinator>/db/coordinator.sqlite |
Coordinator service account writes; authorized operator reads | Farm initialization |
publisher / schema |
<publisher>/db/publisher.sqlite |
Publisher service account writes; authorized operator reads | Publisher provisioning |
release-signer / schema |
<signer>/db/signer.sqlite |
Release signer account writes; authorized operator reads | Signer provisioning after trusted-state import |
Directories use owner-only access (0700), database and sidecar files 0600, with equivalent ACL restrictions. Root-controlled paths have no unprivileged-writable ancestor. Check ownership, regular-file type, canonical location, and symlink traversal before opening any database or sidecar. Database inspection does not grant another role's permissions. Root launches the fixed helper vocabulary, never arbitrary user SQL in the privileged process.
For protected inspection, the helper's fixed export operation produces only the authorized diagnostic projection in a private inspection directory for the caller. The caller runs SQL in an unprivileged, resource-limited inspection process. Service operators similarly query owner-approved exports; they do not submit SQL over the job/result or signing protocols. Exports retain role/instance/head identity and their authorization scope, and are removed after inspection.
The paths above are logical active slots. Versioned copies live under the same
owner's db/versions/<migration-id>/; activation of the slot is journaled. Cache
versions live below cache/db/versions/. Schema creation alone does not initialize
an installation: the owner must insert database_identity before activation.
1.2 Identity and reconstruction
The client-cache and coordinator schemas have user_version = 3; the other four roles remain at 2. Their application_id values, in table
order above, are 1095977985 through 1095977990. database_identity binds the role,
schema version, immutable 32-lowercase-hex instance identifier, and stable owner
identity. For clients the instance is the prefix identity; protected state has a
separate root identity. Services retain their provisioned identity across recovery.
Owner identity binds the provisioned principal and host/prefix record, not a UID
alone. Restoring onto a new host requires an authorized ownership mapping recorded
in the restore journal. Copying a database never creates a new prefix identity.
| Role | Reconstruction sources | Consequence of deletion |
|---|---|---|
| Client state | Prefix initialization record, compact choices/history, generation manifests, exact artifact manifests and materialization receipts, unresolved journals | Stop conflicting mutations and GC; retain access to established unaffected selections and closures; unavailable required selection state fails promptly without fallback |
| Client cache | Currently authenticated index/recipe objects and separate trust receipts; fresh solver work | Rebuild on demand; retained trust and user choices are unchanged |
| System state | Protected initialization record, independently verified closure manifests, prefix registrations, effect journals, before-images, service declarations and receipts | Stop protected mutations and GC; preserve closures and reconcile actual effects |
| Coordinator | Archived orchard commits, signed plans/jobs, enrollment/capability evidence, attempts/results, quarantine and gate records | Stop dispatch; rebuild, expire old leases, then admit reconnecting agents |
| Publisher | Candidates, promotions, durable queue/fence records, timestamp reservations, exact signed objects, activation/acknowledgement evidence, current repository pointer | Stop all publication and refresh until the base and high-water state agree |
| Release signer | Trusted configuration/root chain, candidate bindings, every reservation including unpublished/consumed versions, signed objects and acknowledgements | Stop signing; reconcile independently retained latest public signing state |
None of these sources can regenerate lost unpublished payload bytes or missing before-images. A retained manifest identifies missing bytes; it is not a backup of them. Git plus build results cannot reconstruct quarantine decisions or signing reservations.
2. Schema conventions
The six SQL companions contain the complete DDL: columns, types, keys, constraints,
indexes, and views. Run a companion only against an empty staging database. All
tables are STRICT. Primary and foreign keys deliberately include repository and
environment wherever a relation is scoped; a dev row cannot stand in for prod.
Repository strings are existing repository namespaces, resolved against retained
trust identity bindings; environments are dev, staging, and prod. Reusing a
namespace for a different trust identity requires explicit recovery, never an SQL
rename. Artifact identities remain sha256: plus 64 lowercase hex digits;
build_id remains 64 lowercase hex digits, as defined by
STATE-AND-RECOVERY §1.
These schemas add no fields to public manifests, indexes, locks, or TUF metadata.
Every role contains five common tables:
| Table | Purpose and lifecycle |
|---|---|
database_identity |
Provisioning writes one row; every connection checks it against configuration, application ID, and supported schema before using projections |
maintenance_tasks |
Disposable per-task attempt/success/outcome bookkeeping; reconstruction marks every task due; excluded from logical digests and replay |
objects |
Verified digest, byte length, and descriptive kind for external immutable objects; importer inserts after hashing; inspection and reference validation read |
scopes |
Repository/environment pairs admitted by the owner; serves as the parent for scoped foreign keys, never a trust grant |
replay_head |
Last fully applied record sequence/digest; replay updates it in the same SQL transaction as the projection; cache leaves it at zero |
objects is a reference registry, not a blob store. Resolve digests through the
owner's fixed content-addressed object roots; no row supplies an arbitrary filename.
Store exact signed bytes, large manifests, backups, recipes, solver inputs/results,
and evidence outside SQLite. Hash and measure bytes again when consumed. Kind is
descriptive; the consumer validates the required format and semantic role. A digest
may serve multiple uses. Length and checksum do not authenticate a source.
SQL enforces structural invariants. Owner code additionally validates canonical
manifest hashes, package names against existing schemas, complete closures, DAG
acyclicity, required gate sets, monotonic reservations, state transitions, signatures,
and compare-and-swap conditions. Foreign keys cannot prove those properties. No
writer uses INSERT OR REPLACE to hide a conflicting identity. An identical retry
compares the complete immutable record; different bytes at the same identity stop
replay and retain both records as evidence.
3. Client state
Diagram source
erDiagram
scopes ||--o{ artifacts : contains
artifacts ||--o{ bindings : consumes
artifacts ||--o{ bindings : provides
profiles ||--o{ generations : retains
generations ||--o{ members : includes
artifacts ||--o{ members : identifies
generations ||--o| active_generations : activates
profiles ||--o{ requests : records
requests ||--o| holds : holds
profiles ||--o{ runtime_defaults : selects
profiles ||--o{ profile_priorities : prefers
artifacts ||--o{ gc_roots : retains
objects ||--o{ history : explains
Bindings join exact artifacts, including explicit cross-repository dependencies.
Each binding retains its dependency repository, environment, and qualified name;
there is no inferred cross-environment fallback. Manifests, rather than matching
build_id values, authorize the binding.
| Tables | Readers, writers, transaction boundary, and retention |
|---|---|
artifacts, bindings |
Store import verifies and inserts artifact identity, origin, full package-record reference, materialization receipt, and bindings together. Install/solver/why/leaves/GC read them; the external package record retains flags, variants, recipe, and compatibility requirements. Retain while reachable, including dependencies; delete only through journaled GC after checking every owner |
profiles, generations, members, active_generations |
Install, uninstall, upgrade, rollback, and machine apply project manifests and activation in one transaction after pointer operations. History, list, and GC read; shims use the corresponding committed execution catalog. Retain generation rows while policy retains their manifests; pruning records the removal |
requests, holds |
Install/mark/uninstall and one-argument pin/unpin write durable choices even without a generation change. Autoremove/upgrade read. Clearing a choice requires an explicit record; dependencies remain distinguishable from requested roots |
runtime_defaults, profile_priorities |
Default and profile prefer journal changes together with their projections and the committed execution catalog. Shims read committed defaults from that catalog; generation construction reads the preferred qualified provider for each colliding name. A missing provider cannot silently win a collision. Session use and project pins remain outside this database |
history |
Every completed mutation or attention outcome inserts operation ID, command, time, outcome, before/after state digests, and terminal record. History/log read. Compact rows and their records remain indefinitely; verbose log retention is unchanged |
gc_roots |
Transactions, protected-reference synchronization, launch registrations, execution views, process leases, and backups register roots. GC reads the recursive retained_artifacts view and independently checks protected roots. Expiry alone never establishes process death; uncertain liveness retains the root |
User choices describe current intent independently of generation manifests.
Rollback applies the recorded target managed state under existing command semantics;
it does not erase later history. before_digest and after_digest may be equal for
a no-op and null for a failed operation without an established state. The history
sequence identifies its terminal record, not wall-clock ordering.
An operation that stops with needs-attention retains that history entry. Its
later authorized resolution has a new operation ID and references the earlier
record; it does not replace the earlier outcome.
Representative query (also exercised by the tests):
SELECT profile, repository, name, artifact_id, on_request
FROM installed ORDER BY profile, repository, name;
4. Disposable client cache
Diagram source
erDiagram
scopes ||--o{ snapshots : scopes
snapshots ||--o| current_snapshots : selects
snapshots ||--o{ packages : indexes
solve_cache ||--o{ solve_inputs : binds
snapshots ||--o{ solve_inputs : supplies
scopes ||--o| trust_projections : projects
| Tables | Readers, writers, transaction boundary, and retention |
|---|---|
snapshots, packages |
Refresh imports a verified snapshot, receipts, and searchable package/recipe references together. Search/info and solver read. Cleanup removes eligible dependent rows in bounded transactions before removing the snapshot (§10.2) |
current_snapshots |
Authenticated refresh selects one current snapshot per repository/environment in the import transaction. Cleanup protects this reference and active work |
solve_cache, solve_inputs |
Solver stores the exact input and result object references together. The input digest binds all snapshots, recipes, installed bindings, requests/holds, OS/CPU, policy, configuration, and solver version. Recheck inputs and trust before reuse; a snapshot hash alone is insufficient |
trust_projections |
Authenticated trust reconciliation replaces descriptive projections. Repo inspection reads them; capability checks use authoritative trust/ instead. Deletion conveys no revocation or grant |
All cache rows are disposable; routine cleanup protects current snapshots and active work under §10.2. No cached verdict permits stale metadata, stale policy, or offline use without the retained verification receipts required by STATE-AND-RECOVERY §8. Search uses the indexed name column; FTS is not a schema dependency.
SELECT repository, environment, name, summary
FROM search_packages WHERE name LIKE 'python%' ORDER BY repository, environment, name;
5. Protected system state
Diagram source
erDiagram
closures ||--o{ closure_members : contains
scopes ||--o{ closure_members : scopes
prefixes ||--o{ prefix_references : retains
closures ||--o{ prefix_references : referenced
prefixes ||--o{ effects : owns
closures ||--o{ effects : executes
effects ||--o{ services : registers
objects ||--o{ recovery_receipts : proves
| Tables | Readers, writers, transaction boundary, and retention |
|---|---|
closures, closure_members |
Helper imports independently verified closure inventory and authorization references. Helper service launch/rollback/GC read. Keep all active, retained, and unresolved closures |
prefixes, prefix_references |
Helper registers stable prefix identity, canonical path, UID, and active/retained/transaction references under the system lock. Cross-prefix GC consults these records; a missing client database does not release them |
effects, services |
Helper projects expected before/after fingerprints, backup metadata objects, unique managed targets, and launchd plist bindings. Services use the same closure as their owning effect. Reconcile one authorized transaction at a time; SQL state alone never triggers an external write |
recovery_receipts |
Helper records operation state and durable journal receipt in the same projection transaction. Recover/doctor/check read. Keep compact receipts indefinitely, including rolled-back and attention outcomes |
Effect and service rows describe the current managed footprint. Historical fingerprints and declaration versions remain in durable records after replacement or removal. Before-image objects include file metadata and an explicit absent-file marker when appropriate. Resolved backup retention follows existing policy; unresolved operations retain all before-images. No new database changes privileged consent or system-volume requirements.
SELECT label, prefix_id, closure_digest, state FROM managed_services ORDER BY label;
6. Farm coordinator
Diagram source
erDiagram
plans ||--o{ jobs : schedules
job_kinds ||--o{ jobs : classifies
jobs ||--o{ job_dependencies : depends
jobs ||--o{ job_requirements : requires
workers ||--o{ worker_capabilities : proves
workers ||--o{ attempts : runs
jobs ||--o{ attempts : retries
attempts ||--o| results : returns
jobs ||--o{ quarantine : decides
jobs ||--o{ gate_evidence : evaluates
| Tables | Readers, writers, transaction boundary, and retention |
|---|---|
plans, jobs, job_dependencies, job_requirements, job_kinds |
Farm plan inserts a complete authenticated DAG and requirements together. Scheduler reads dependencies and capability requirements; reject cycles and cross-plan edges. Job identity is the existing job-manifest hash |
workers, worker_capabilities |
Enrollment and capability verification project identity and evidence. Scheduler reads; agent self-report alone cannot establish a capability. Keep enrollment/revocation and capability history in records |
scheduling, worker_inventory |
Project authenticated dispatch priority, continuous ready time, supersession/cancellation evidence, and physical worker identity. Jobs without complete scheduling/capability evidence are blocked. Security priority and independent-builder eligibility are reverified from retained records |
| attempts | Dispatch/heartbeat/expiry serialize under the coordinator writer. One active attempt per job; tokens are unique and expiry uses the coordinator's clock. Record attempt and lease before dispatch. After recovery mark every inherited active lease expired before redispatch; never trust restored clock-relative liveness |
| results | Result admission authenticates worker/attempt/job bindings and inserts once per job-manifest hash. Byte-identical duplicates return the retained result. Conflicting duplicates are retained in evidence and quarantined, never overwrite the accepted result. Expired attempts can supply evidence but cannot complete a replacement lease |
| quarantine, gate_evidence | Gate processing writes evidence references and append-only quarantine decisions together. Only an authorized release decision clears a hold. Required gates derive from retained policy, so absent rows mean pending. Signed job results alone do not establish a passing gate |
Compact scheduling, attempts, result identities, quarantine decisions, and gate receipts survive indefinitely in records. Old SQL jobs may be archived only behind a verified checkpoint; unresolved jobs and evidence remain available. Build logs and payloads follow their existing retention policy. The coordinator's success is not publisher authorization.
SELECT job_digest, attempt, worker_id FROM lease_status
WHERE expires_at <= :now ORDER BY job_digest;
7. Publisher
Diagram source
erDiagram
scopes ||--o{ candidates : authorizes
candidates ||--o{ promotions : source_and_destination
candidates ||--o| publication_queue : queues
scopes ||--o| fencing : serializes
candidates ||--o{ timestamp_reservations : reserves
timestamp_reservations ||--o| activations : publishes
activations ||--o| acknowledgements : reconciles
| Tables | Readers, writers, transaction boundary, and retention |
|---|---|
candidates, promotions |
Repo build verifies candidate, frozen inventory, authorization, and base. Promotion binds the same repository and inventory across dev→staging or staging→prod and requires successful source evidence. A new base or changed publication metadata gets a fresh candidate; immutable payload inventory cannot change under promotion |
publication_queue |
Publisher queues releases, unchanged-content renewals, and timestamp refreshes. A waiting candidate holds no writer lease. Queue transitions and corresponding records commit together |
fencing |
Publisher increments the durable epoch before handing out a writer. Activation checks epoch, unexpired ownership, and expected active snapshot under the publication lock. The repository activation adapter must reject stale epochs too; an SQLite comparison alone cannot fence an old remote writer |
timestamp_reservations |
Reserve monotonically per repository/environment before signing, including daily refresh. Bind exact unsigned bytes and candidate; save exact signed bytes before use. Consumed or interrupted versions are never reused, including after key rotation |
activations, acknowledgements |
Persist the observed atomic timestamp activation and delivery acknowledgement; inspect/recover reconciles the actual published snapshot and signer. Never infer publication from queue state or a signature alone |
All candidate bindings, reservations, fence epochs, promotions, and publication evidence are retained indefinitely in compact records. SQL history may move behind verified checkpoints but active queue/base and all version high-water projections remain available. Current and in-flight immutable public objects follow repository retention. Compare returned signer state with retained public records on every recovery; uncertain latest state stops publication.
SELECT repository, environment, queue_sequence, state, snapshot_digest
FROM publication_status ORDER BY repository, environment, queue_sequence;
8. Release signer
Diagram source
erDiagram
scopes ||--o{ candidates : scopes
candidates ||--o{ reservations : reserves
reservations ||--o| signed_metadata : signs
candidates ||--o| signing_outcomes : concludes
candidates ||--o{ slice_signatures : retains
candidates ||--o| publication_acknowledgements : observes
| Tables | Readers, writers, transaction boundary, and retention |
|---|---|
candidates |
Signer admission independently verifies candidate digest, authorization, inventory, expected base, and trusted policy before projection |
reservations |
Signer durably reserves targets/snapshot versions and exact unsigned bytes before invoking keys. Same version/different bytes is a refusal. Interrupted reservations resume only identical bytes or become consumed. Highest reserved, not merely highest published, is the floor |
signing_outcomes, signed_metadata, slice_signatures |
Store exact returned metadata and detached slice-signature references, with outcome, before returning a response. A retry returns saved bytes. A signed outcome requires every required object; enforce this semantic check before response |
publication_acknowledgements |
Authenticated publisher receipt and independently checked repository state establish the last published base. Lost acknowledgement triggers reconciliation, not another signature at an old version |
Keep every reservation, candidate binding, outcome, exact signed object, and acknowledgement indefinitely, including rejected and signed-but-unpublished work. Private key operations remain in the existing signer boundary. No key blob, unlock secret, or key-backup path is a column. Root signing remains offline and is not represented by this database.
SELECT repository, environment, metadata_role, version
FROM version_high_water ORDER BY repository, environment, metadata_role;
9. Durable records and replay
Each durable role has an owner-controlled records/ stream beside db/; existing
transaction journals and trust/ remain authoritative in their own locations.
Records describe logical state transitions, never raw SQL. The cache has no stream.
An independent recovery set outside the active prefix retains reconstruction
records, trust evidence, transaction receipts, and manifests, with protected copies
for privileged evidence and an export for disk-loss recovery. Application Support
directories are the approved location; §9.3 defines the ordered multi-copy protocol.
The adjacent stream alone cannot satisfy prefix-loss recovery.
See STATE-AND-RECOVERY §5.1.
Protect streams with the same ownership boundary as their database and retain
compact history indefinitely. Checkpoints accelerate replay; they do not authorize
discarding compact records.
A record is canonical RFC 8785 JSON with these fields: format (1), role,
instance_id, sequence (positive safe integer), previous (prior record digest,
null only for sequence 1), operation_id (32 lowercase hex digits), phase,
command, occurred_at (UTC epoch seconds), scope (repository/environment or
null for an instance-wide operation), before, after (logical state digests or
null), changes, and evidence. changes is an ordered array of typed domain
operations whose before/after references bind complete canonical values, with null
as the explicit absence/deletion marker;
evidence is a sorted, duplicate-free array of digest/byte-length references.
Record identity is SHA-256 of the canonical bytes, stored separately from the
bytes themselves. Names are <sequence>-<digest-hex>.json. Initialize the stream
with owner/instance identity and empty state before admitting mutations.
Domain operations cover generation/member/binding changes, request marking and removal, holds, defaults, profile priorities, GC references, effect/service and prefix registration, farm enrollment/plan/attempt/result/gate/quarantine changes, candidate/promotion/queue/fence changes, reservations, signing outcomes, and activation/acknowledgement receipts. Each kind has a versioned decoder and validates against its owning contract; unknown kinds or formats stop replay. Trust changes reference independently authenticated trust records rather than embedding a grant in an ordinary choice record. Session selections and project files are not replayed.
The writer serializes stream allocation under the role lock. It writes a temporary
record, flushes it, renames to its final name without replacement, flushes the
directory, and advances a flushed stream-head record containing sequence/digest.
Validate both the head and all finalized tail files: a crash between rename and
head update may leave a valid contiguous tail to recover. Duplicate sequence with
different bytes, gaps, broken predecessors, unsupported formats, or an operation
with incompatible terminal outcomes produce needs-attention. Checksums detect
damage; independently retained heads/backup manifests detect a truncated tail.
Local checksums alone cannot prove that an attacker did not rewrite user-owned state.
9.1 Commit boundaries
Diagram source
sequenceDiagram
participant W as Owner writer
participant J as Durable records and journal
participant F as Pointers and external effects
participant D as SQLite projection
W->>J: Prepared intent and verified before-images, flush
W->>F: Apply under expected fingerprints, activate pointers
W->>D: BEGIN IMMEDIATE, project tentative after-state, COMMIT
W->>F: Reconcile managed state
W->>J: Committed terminal record, flush
W->>D: History and replay head in one transaction
W->>F: Bounded service health checks, retain mutation ownership
Preserve prefix-then-system lock ordering for operations touching both owners. Service roles serialize through their own owner locks and existing publication protocol; there is no transaction spanning WAL databases. Cross-owner journals bind the same operation and participant receipts. Missing participants prevent success.
The durable terminal record is the commit decision. A choice-only operation uses
the same prepared/terminal discipline without generation or pointer changes.
Before returning success, both terminal record and final SQL projection must be
durable. SQL may temporarily lead or lag the record stream; every opener checks for
an unresolved journal or head mismatch before exposing shim/installed state.
Do not expose tentative state as committed. A mismatch blocks affected selections,
not every runtime: established unaffected selections and verified closures remain
usable under STATE-AND-RECOVERY §5.2.
Keep session/project/default precedence and fail promptly if required state is
unavailable; no implicit fallback is introduced. The separately retained committed
execution catalog and affected-selection gates supply that read path independently
of SQLite; §9.3 orders its durable publication. Diagnostic reads report mismatches.
If the terminal commit is absent, recovery reverses effects
and tentative choices under the existing fingerprint rules, except for explicit
finalization of a verified protected-volume pending-reboot intent under
SYSTEM-VOLUMES §4; committed operations
reconcile their after-state. SQL rollback alone does not undo filesystem changes.
Reservations and fence allocations are special irreversible decisions: their flushed allocation records consume the number even if the enclosing operation never commits. Append these before any signature or writer handoff. An abandoned candidate records consumption; replay never releases its versions. Quarantine holds are similarly retained until an explicit authorized release record. Missing gate evidence remains pending.
Contention and cancellation follow §10.1, including committed operations whose SQL projection remains incomplete.
9.2 Replay and checkpoints
Replay verifies identity, format, sequence, predecessor, checksum, byte length, and trusted external references before applying a record. It applies one complete logical transition and its head in one SQL transaction. Replaying an already applied sequence with the same digest is a no-op; another digest is a conflict. Require the transition's before-state to match the reconstructed state. Terminal history inserts once by operation ID. A prepared operation without a terminal decision is unresolved, not silently dropped. Rebuild into staging and run recovery against real pointers and fingerprints before activation.
A checkpoint binds role/instance/schema, manager and SQLite build identities, sequence/head digest, logical projection digest, checkpoint object digest/length, and the retained-record range it covers. Authenticate its provenance through the owner's independently retained backup inventory; a checkpoint supplied by an untrusted database is not evidence. Verify its prefix records and replay its suffix. Unknown or conflicting high-water provenance forbids signer/publisher resumption. No replay reconstructs a TOFU decision or lowers retained trust versions.
9.3 Independent records and durable copy updates
The outside-prefix owner-controlled stream is the transaction decision history; the adjacent prefix stream is a mirror, and SQLite is a projection. A transaction has one operation ID, one ordered history, and one terminal decision. Protected participants keep their own root-controlled receipts bound to that ID and history; a user record cannot confer protected authority. Prefix initialization binds the recovery location and owner identity. Independently retained checkpoints and export inventories establish known heads; a stream's self-reported head alone cannot prove that history was not truncated or rewritten.
Each finalized record uses the temporary-file, flush, no-replacement rename, and directory-flush sequence in §9. A head advances only after its referenced records and required objects are durable. Validate finalized tails as well as heads after interruption. Apply the following order under effective mutation ownership:
- Allocate the operation and sequence from the validated outside-prefix history. Inventory the actual base and required participants. Persist complete intent, authorization references, before-images, object inventory, and the affected execution-catalog gate outside the prefix. Persist required privileged evidence under the protected owner and obtain its bound preparation receipt. Mirror preparation into the prefix. No live write precedes these required flushes.
- Apply each journaled effect under its expected fingerprint. Persist completion receipts before advancing progress. The protected helper independently verifies authorization and actual state and writes its own receipt before acknowledging a privileged step. A lost acknowledgement is resolved by querying that receipt and inspecting state; it does not authorize repeating an external effect blindly.
- Reconcile pointers and effects, record the tentative projection, and prepare the next execution catalog. Require durable receipts from every participating owner showing the verified after-state and prepared commit boundary. These receipts are not a terminal commit by themselves. Flush the single commit decision in the outside-prefix history, binding all participant receipts, state and catalog digests. This is the commit point; a crash before it invokes pre-commit reversal (with the explicit protected-volume finalization exception in §9.1), and a crash after it preserves the commit. Unknown or inaccessible decision evidence permits neither guessing rollback nor declaring success.
- Mirror the decision into the prefix, acknowledge it in protected records through the helper, and reconcile the final projection/head and catalog selection. The helper accepts only a decision matching its own authorized intent and receipts; user-supplied state never substitutes for protected evidence. Clear affected catalog gates only after their state agrees. Do not report full completion until required mirrors, participant acknowledgements, and projections are durable. Mirror failure after the commit point reports committed with recovery pending.
- Run bounded service checks while retaining mutation ownership. Append check outcomes and saved recovery choices without changing the commit decision. Report health failure or incomplete verification explicitly. Release ownership only after helpers stop writing and the operation reaches a safe stopping point.
An unambiguous verified contiguous suffix repairs a lagging copy automatically. Compare owner/instance, sequence, predecessor, operation, object digests, known independent heads, and protected counterpart receipts. Never choose by timestamp, filename ordering, or copy majority. A gap, incompatible terminal decisions, missing required evidence, or an unexplained newer head preserves both copies and blocks the affected repair. If the outside-prefix copy is lost, a surviving mirror is usable only after those same independent checks establish the decision history. Without them, use guided evidence recovery; do not promote a surviving file merely because it is readable.
Exports capture the same coordinated boundary as §11, including the committed execution catalog, unresolved gates and choices, required before-images, and protected components. Finalize the verified inventory last. A partial export lists missing participants and is not a complete recovery set. Local redundancy does not establish resistance to disk loss or compromise of the owner account.
The storage layout, canonical JSON record/receipt schemas, flush obligations, and export validation are specified in STATE-AND-RECOVERY §10.2. Per-filesystem adapters, record/receipt decoders, and crash-injection evidence must pass the release gates before the protocol can be described as implemented or durable on hardware.
10. SQLite connection and migration policy
Bundle a pinned SQLite 3.51.3 source release, or an explicitly reviewed newer
fixed release, with the manager and service builds. The release manifest must pin
the source checksum, sqlite_source_id(), compiler options, and VFS build; those
build artifacts remain implementation work. Do not link legacy macOS system
SQLite. SQLite's WAL documentation identifies
the WAL-reset corruption fix in 3.51.3 and backports; the project baseline uses the
fixed mainline. See the offline SQLite account.
Minimum schema features are STRICT tables (3.37.0), foreign keys, recursive CTEs,
partial indexes, and the online backup API. Production readers must use the pinned
fixed build as well as understand the current role/schema version; accepting STRICT syntax alone is
not sufficient. Schema-test Python may use another SQLite build and reports its
version; that is not a production qualification. Older managers may open only
their retained compatible database copy. Unknown application IDs, schema versions,
or identity mismatches fail before domain queries; db schema may show the shipped
schema without opening a damaged database.
Every writer connection checks journal_mode=WAL, enables foreign_keys=ON,
trusted_schema=OFF, and uses prepared statements with bound data. Durable roles
use synchronous=FULL; only the cache may use NORMAL. On macOS enable
fullfsync=ON and checkpoint_fullfsync=ON and verify the selected VFS and
filesystem's behavior in platform acceptance. Unknown/ignored required settings
are a refusal. Durability still depends on the storage stack. The
PRAGMA reference explains the
FULL/NORMAL distinction; defaults are not a project configuration policy.
Use short BEGIN IMMEDIATE write transactions after acquiring domain locks. Use the
shared lock-wait allowance as specified in §10.1; expiry reports db_busy and
resolves the durable phase before releasing ownership. Replan on a stale base;
never repeat external effects merely because SQL returned busy. Surface disk-full
and I/O errors, stop mutations/GC, preserve sidecars and journals, and determine
the durable phase through recovery.
Autocheckpoint at 1,000 pages; attempt PASSIVE checkpoint after a batch and on idle. Inspect WAL growth and busy readers. Owner maintenance can request RESTART/TRUNCATE only after readers drain; it must report an incomplete checkpoint. Query readers have a 5-second deadline and 10,000-row/16-MiB output limit. Do not delete WAL/SHM files manually or copy only the live main file. These choices follow the WAL concurrency and checkpoint rules.
Inspection opens an owner-produced consistent snapshot read-only, never marks a
live database immutable, and enables defensive mode, query-only, disabled trusted
schema, and disabled extension loading. Prepare exactly one statement with no
non-whitespace tail. The authorizer permits SELECT/READ/RECURSIVE and an explicit
side-effect-free built-in function allowlist; deny ATTACH/DETACH, all PRAGMAs,
transactions, writes, DDL, virtual-table creation, and extension/file/network
functions. Apply SQL length, memory, expression-depth, and execution limits.
db schema and db check use separate fixed statements. sqlite3_stmt_readonly()
is an additional check, not the authorization policy. Use the
SQLite security guidance and
authorizer API; runtime enforcement
and adversarial tests remain required.
10.1 Contention and safe stopping
Busy interactive commands display the known owner and offer wait or exit.
Unattended commands wait only with --wait; otherwise they return
contention promptly. A configured timeout alone does not authorize waiting.
After waiting or recovery, revalidate the base, trust, and plan; materially changed
operations require fresh confirmation. Prefix ownership spans preparation through
post-commit service checks, including surviving helpers until they stop writing.
The approved waiting/stopping command surface and protocol are in
STATE-AND-RECOVERY §10.2.
Authorized foreground waits use db.lock_timeout = "30s"; the common
--lock-timeout DURATION option overrides it for one invocation. Accept a
nonnegative integer followed by ms, s, or m; reject other forms and overflow.
0s tries once without waiting. One operation controller counts cumulative
lock-wait time across prefix, system, service-owner, and SQLite waits. Useful work
does not consume this allowance, and entering another layer does not reset it.
Use monotonic elapsed time and cancellable backoff starting at 10 ms, doubling to
a 250 ms cap and clipped to the remaining allowance. Preserve prefix-then-system
ordering. After one second of cumulative waiting, print a stderr waiting message;
update every five seconds thereafter with role, operation, elapsed wait, and known
owner identity. Say unknown when the owner cannot be established; a PID alone
does not establish authority. JSON output remains one object, with progress on stderr.
Owner-lock contention and SQLite BUSY consume the same allowance. SQLite may
return busy without invoking its busy handler, so the operation controller handles
every result; a connection-local timeout cannot implement this contract. Enable
extended result codes. For BUSY_SNAPSHOT, end the stale read transaction and
revalidate/replan before any effects; after effects begin, enter recovery instead.
For internal LOCKED conflicts, finalize conflicting statements or end the local
transaction and diagnose the connection error; do not sleep and retry blindly.
Disable shared-cache connections. Finalize statements and end read transactions
promptly. Runtime shims fail promptly when required state is unavailable and do
not inherit foreground mutation waits. See the
SQLite contention evidence.
Normal commands bypass a busy disposable cache when authenticated inputs are available: verify receipts, freshness, policy, and complete solver inputs as usual, work in memory, and skip cache writes. Without those inputs, report unavailable verified data; contention never authorizes stale or unverified use. Explicit cache inspection or maintenance reports contention rather than substituting another data source.
Timeout and cancellation are resolved by durable phase, not by the last SQL call:
| Durable phase | Timeout or cancellation outcome |
|---|---|
| Before preparation or external changes | End SQL work, release locks, report no managed-state change |
| Prepared, no live effects | Resolve prepared intent as aborted/rolled back before releasing ownership |
| External effects begun, no durable commit | Stop forward execution; enter fingerprint-checked recovery; never replay external effects because SQL was busy |
| Durable commit exists, projection incomplete | Preserve the commit decision and reconcile forward; report committed with recovery pending if reconciliation cannot finish |
| Durable commit exists, service checks running | Stop checks safely; report committed installation and incomplete verification, never an unchanged or rolled-back install |
| Complete, optional maintenance busy | Preserve foreground success and defer maintenance |
Recovery receives one separate, bounded 30-second cumulative lock-wait allowance,
including resolution of prepared intent; it is not renewed by retries or repeated
cancellation. Cancellation requests a safe stopping point, not abandonment of
effects. The initiating user or an authenticated administrator may request stopping;
the controller must quiesce helpers before rollback or manual repair. If recovery
cannot finish, retain journals, backups, and GC roots, report needs-attention, and
block conflicting mutations. Working packages and external repair tools remain
available. A committed operation cannot
be described as rolled back merely because cancellation arrived late.
Contention with no unresolved recovery exits 4; recovery-required outcomes exit 1;
safely completed cancellation exits 130. Optional maintenance after completed work
preserves exit 0. Diagnostics in data expose phase (unprepared, prepared,
effects, committed, or complete), committed (boolean), recovery_required
(boolean), and retry_safe (boolean). Retry is safe only after resolution and base
revalidation; committed work must not be resubmitted as a fresh mutation.
10.2 Automatic maintenance and cleanup
Run maintenance after successful mutations and in existing service idle loops. Add no client daemon or launchd job, and never request elevation solely for housekeeping. Attempt owner and SQL locks without waiting, yield to foreground work, and share a 100 ms work budget across tasks. Check a monotonic deadline between tasks and batches and use an SQL progress handler to interrupt work. This limits scheduling and interruptible SQL, not the wall-clock duration of storage synchronization. An interrupted batch rolls back; completed batches remain. Optional maintenance failures do not undo command success; report corruption or I/O failures separately and block later mutations when database health is uncertain.
Attempt cleanup hourly, optimization daily, and PRAGMA quick_check weekly when
due. Keep PASSIVE checkpoint attempts after batches and on idle; readers that
prevent completion leave checkpoint work deferred. Rotate due tasks by oldest
attempt (null first, then task name) so a cleanup backlog cannot starve another
task. A deferred or interrupted task remains due; each invocation tries it at most
once. Use bounded PRAGMA optimize with the pinned build's analysis limit and the
same progress deadline. Full integrity and external-reference checks belong to
db check; a quick check is not proof of reference or trust validity.
maintenance_tasks records UTC epoch seconds for last attempt and last success,
outcome, and deferred reason. Success advances only after the whole task completes;
a partial checkpoint or cleanup backlog is deferred. Bookkeeping is disposable,
excluded from logical-state digests and authoritative replay. Reconstruction seeds
all tasks due. If locks prevent writing an attempt, retain that observation in
memory and report it; flush it on the next writable opportunity, without waiting
solely to record a deferral. A backward clock jump clears future bookkeeping times
and makes tasks due rather than suppressing them indefinitely. These local records
are not telemetry.
Cleanup transactions delete at most 100 rows total, including dependent rows.
Large snapshot projections are removed in bounded child batches while owner
coordination protects current snapshots and active work; parent rows survive
until all references are gone. No cascading delete may evade the batch bound.
Before removing children, set the solve or snapshot's cleanup_pending marker
under exclusive owner coordination. Marked entries cannot be selected as current,
reused by solves, or exposed by search, even after interruption. The search view
excludes marked snapshots; owner code enforces the other selection rules. Complete
authenticated reimport may clear the marker atomically. This keeps a partially
removed projection from appearing complete between batches.
Cache inserted_at and accessed_at are UTC epoch seconds; initialize them on
authenticated insertion and coalesce access updates during writable operations.
Inspection never writes access times. In-memory active-work pins protect the
owner's readers even when timestamps have not been flushed. Other processes hold
shared owner coordination while consuming cache rows; cleanup requires exclusive
coordination and defers if those readers remain. Revalidate eligibility under the
lock. Access timestamps never move backward; retain future timestamps after a
clock correction until their age can be established conservatively.
| Role | Eligible cleanup and retained material |
|---|---|
| Client cache | Remove unused solves and superseded snapshot projections when last access is at least 30 days old (30 × 86,400 seconds). Protect current_snapshots, active work, and snapshots still referenced by retained solves. Expiry is a verification rule, not cleanup authority |
| Client/system state | Remove obsolete projections only after governing GC, decommission, or recovery has authorized removal. Expiry alone does not release process or protected references; compact history and recovery receipts remain |
| Coordinator | Lease expiry belongs to scheduler recovery. Completed jobs, attempts, quarantine decisions, and evidence do not become disposable through age; archival requires retained records and the existing authorized boundary |
| Publisher/release signer | Preserve reservations, version floors, candidate bindings, signing outcomes, and publication evidence indefinitely. Stale candidates do not authorize erasing history |
| All roles | Remove object-registry rows only after checking every SQL and retained external reference. Housekeeping deletes no external payloads, trust records, compact history, or recovery evidence |
10.3 Manual maintenance, compaction, and schema 2
aslice db maintain [--dry-run] runs eligible cleanup, optimization, lightweight
checks, and checkpoint work for the selected role. It uses the foreground lock
allowance and cancellable bounded batches, without the automatic 100 ms total
budget. It reports remaining/deferred work rather than claiming completion.
--dry-run inspects and reports eligibility and work estimates without writes,
including bookkeeping. It grants no authority to remove protected references.
auto_vacuum=NONE is explicit in every schema. Cleanup removes eligible rows;
deleted pages remain reusable inside the file. Checkpointing transfers WAL pages,
optimization updates planner information, and compaction reclaims database-file
space. None substitutes for another. Only aslice db compact [--dry-run] requests
full compaction; automatic maintenance never vacuums.
Compaction uses the versioned-copy mechanism below. Under owner coordination,
capture a consistent backup, vacuum the staging copy with no open statements or
transaction, validate logical equivalence, identity, replay head, integrity,
foreign keys, and external references, then journal activation. Keep the owner
boundary through activation; drain/reopen readers against the selected version.
Preserve the original database and known-good recovery entry point. Before work,
report snapshot size, vacuum workspace, retained-original size, and free-space
requirements per filesystem. Reserve space for the snapshot plus up to twice its
size for vacuum workspace, in addition to the retained original and measured
sidecars/journal margin; refuse insufficient space. --dry-run reports the estimate
without staging or activation. Failure before activation leaves the active file
intact; interruption during activation enters existing journal recovery. No
filename or modification time decides which copy is active. See the
VACUUM evidence.
The historical schema-1-to-2 transition advanced all six roles without changing application IDs or
public formats. Migrate through a validated copy, never by editing the active file.
Rebuild database_identity with its version-2 constraint, preserve instance/owner
and all domain rows, and add seeded maintenance tasks. Cache lifecycle timestamps
start at migration time (not fabricated historical access); rebuild current-snapshot
references from authenticated current inputs, refusing activation if these cannot
be established. Unknown cache rows may instead be discarded and reimported after
verification. Compare logical state excluding maintenance bookkeeping and cache
lifecycle fields; validate every retained reference and both version markers.
Diagram source
sequenceDiagram
participant S as Known-good supervisor
participant O as Old manager and database
participant N as Versioned staging copy
participant J as Recovery journal
S->>O: Lock and capture coordinated boundary
O->>N: SQLite backup API snapshot
S->>N: Migrate or compact, validate, run manager health checks
S->>J: Prepare activation with old and new identities
S->>N: Activate database and manager pair
S->>J: Reconcile and commit
Note over O,N: Retain old pair and recovery entry point
Migrations run on a versioned copy, including migrations that appear additive. Validate integrity, foreign keys, semantic equivalence, replay cursor, object references, and manager health before journaled activation. No destructive in-place migration is permitted. Failure preserves the old pair. Downgrade after new writes requires an explicit compatible replay/conversion path; selecting an old snapshot must not erase newer choices, reservations, or trust. Without that path refuse.
10.4 Security and scheduler projection version 3
Client-cache version 3 adds advisory_assessments keyed by repository/environment,
snapshot, advisory, artifact, and component (empty component means whole artifact).
Rows retain repository identity, advisory object digest, expiry, and vulnerability
status for active-closure queries. Recompute remediation from current holds,
platform and available candidates; it is not repository authority. Clear dependent
assessment rows before snapshot eviction. Cache rows cannot authorize an advisory:
reverify the retained TUF target, freshness, scope, and assessment evidence.
Coordinator version 3 names the former kind table job_kinds and the jobs column
kind. The scheduling projection independently records priority, continuous ready
time, authenticated authorization, supersession, and cancellation receipts.
worker_inventory binds enrolled worker IDs to physical builder IDs and retained
capability evidence. Query these with job requirements, dependencies, attempts and
gate evidence under BUILD-INFRA. Scheduler rows cannot grant
security priority, qualify hardware, or waive a gate without authenticated evidence.
Reject unsupported role/schema versions before reading domain rows or admitting mutation. Do not reinterpret old lanes as priorities. Reconstruct client-cache 3 from currently authenticated snapshots and advisory targets in a new file. Reconstruct coordinator 3 from retained plans, job kinds, authorization, readiness, lease history, capability and gate receipts. Missing priority/readiness/physical identity evidence keeps jobs blocked pending explicit classification or qualification; do not fabricate timestamps, consent, or independent builders. Expire inherited leases, reconcile completed and cancelled attempts, then activate the verified copy under the existing recovery procedure. Retain the old file as evidence.
The schema-1-to-2 migration above remains a historical regression contract;
tests/fixtures/database-v2 freezes its DDL. It is not an automatic 2-to-3 migration.
The remaining roles need no new persistent tables: promotion and selections are
retained authenticated records, addressed through their existing object projections.
11. Backup sets and restore
A backup set is a consistent SQLite snapshot plus the durable material required to interpret it. Use SQLite's online backup API into a new staging file; check every API result and require completion. Raw copying of a live main file is unsupported. A local snapshot supports operator-error recovery; disk/site loss requires independently verified copies on separate storage and at a separate location, following the never-lose set.
11.1 Coordinated boundaries
| Set | Barrier and required participants |
|---|---|
| Prefix | Prefix mutation lock; resolved transactions, generation/choice head, trust-state checkpoint, referenced store/manifest/materialization objects, and local effects/backups |
| Prefix with protected effects | Prefix then system lock; one boundary ID plus both owners' heads, protected closures, prefix references, journals, before-images, and service declarations. Root material stays in a separately protected component |
| System | System lock after any participating prefix locks in canonical prefix-ID order; root trust, every prefix reference, unresolved journals and closures. Never discard references to unavailable prefixes |
| Coordinator | Stop dispatch and record admission at a known sequence; plans, enrollment, attempts, results, quarantine, gate policy/evidence. Exported leases are treated as expired on recovery |
| Publisher and signer | Pause publication, renewal, timestamp allocation, and signing admission; capture both heads, reservations, fence high-water, exact signed objects, current repository pointer and acknowledgements. A disconnected signer leaves the set incomplete for signing recovery |
| Cache | One consistent snapshot only, optional; no recovery authority and no need to preserve solves |
A standalone role backup is still useful but must name missing participants. It
cannot claim whole-prefix or signing recovery completeness. Quiesce at a resolved
boundary where possible. If an operation cannot resolve, retain its complete
prepared intent, phase receipts, and before-images and mark the set recovery-required.
Never wait for another owner while holding locks in reverse order.
The version-1 canonical JSON backup manifest binds set_id, creation time, status,
each role/instance/owner, application/schema IDs, manager/SQLite source identities,
database digest/length, stream sequence/head and retained ranges, trust checkpoints,
active pointer fingerprints, all required object digests/lengths, participant
boundary IDs, and any unresolved operations or missing components. Include restore
tool identities and compatible manager/database pairs. Verify all objects before
flushing and finalizing the manifest; store its digest in the independent backup
inventory. Interrupted copies have no finalized manifest and are not restorable
sets. Encrypt private administrative records in transit/storage as required by
their owner; database backup contains no private keys. Key backup follows the
separate runbook.
11.2 Ordered restore procedure
- Stop the affected writers and GC/dispatch/publication. Preserve damaged files, WAL/SHM, journals, configuration, and pointers as evidence. Record incident and selected backup identities; do not overwrite originals.
- Authenticate the backup manifest against the independent inventory. Establish the latest retained heads from live records, other copies, and counterpart receipts. A stale backup is usable only with a continuous verified suffix to the known latest state. If latest state cannot be established, refuse automatic restore rather than treating absence as an empty history.
- Restore into a new owner-controlled staging directory. Check permissions,
symlinks, role/instance/schema, manager compatibility, manifest checksums/lengths,
integrity_check,foreign_key_check, all object references, trust provenance, record continuity, and domain invariants. Refuse mixed repository/environment identities and missing required participants. Preserve the prior active pair. - Replay the verified suffix and reconstruct missing projections. Reconcile
incomplete transactions against actual filesystem pointers and fingerprints,
including protected effects. Conflicting external edits or missing before-images
produce
needs-attention; never overwrite them to make the backup match. - Produce a preview binding set digest, current heads, target identities, ownership mapping, affected paths, effects, and unresolved issues. Explicit confirmation authorizes exactly that preview. Reacquire locks and revalidate its base; any change invalidates the confirmation.
- Journal activation of the validated database/manager pair and any reconciled
effects, reconcile, and commit before service health checks. Manager/database
validation remains a pre-commit gate. Keep the prior pair until recovery
policy permits retirement. After interruption,
aslice recoverresumes from the journal and inspects pointers; it never guesses from the newest filename. - Verify clients/services, GC roots, farm pending gates, and current publication state as applicable. Take and independently verify a new complete backup set before resuming signing/publication after disaster recovery.
SQLite salvage can recover altered, deleted, or constraint-violating rows. Treat its output only as evidence for independent comparison and reconstruction, never as an automatically trusted restore.
12. Recovery decisions and failure matrix
Diagram source
flowchart TD
A[Stop affected mutations and preserve evidence] --> B{Only cache lost?}
B -- yes --> C[Rebuild from authenticated inputs; retain trust]
B -- no --> D{Trusted records and latest head intact?}
D -- yes --> E[Rebuild or restore staging and replay]
D -- no --> F{Independent complete backup and suffix?}
F -- yes --> E
F -- no --> G[Needs attention; authority recovery if required]
E --> H{Fingerprints and before-images sufficient?}
H -- no --> G
H -- yes --> I[Inspect journals and validate evidence]
I --> K{Durable commit exists?}
K -- yes --> L[Reconcile forward; retain commit]
K -- no --> M[Fingerprint-checked reversal]
L --> J[Preview, confirm restore, journal activation]
M --> J
L -- recovery wait exhausted --> G
M -- recovery wait exhausted --> G
Reconstruction through recover follows the same validation gates as restore.
Automatic crash reconciliation uses already-authorized transaction intent. Explicit
backup restore requires the confirmation in §11.2; neither path weakens consent
for protected effects or trust rebootstrap.
Interactive recovery offers recover and continue once; unattended use requires explicit authorization. Resume saved recovery progress automatically when durable evidence and actual state agree. Inventory files, services, registrations, permissions, and transaction progress before grouping actual conflicts by repository, package, or service. Persist recommended choices, validated current-state acceptance, displaced-content references, and accepted deviations in recovery records. These are additional record/decoder requirements, not implemented SQL schema changes. Manual repair waits for helper quiescence and preserves a mutation gate across exits and reboots while external repair tools remain usable.
A damaged-prefix rebuild recovers selections separately from executable authority. Verify artifacts independently, prepare beside the original, and review missing artifacts, verified replacements, and omissions together. Isolated verified closures may be usable before normal activation or affected privileged integration is allowed. Report repaired, usable with listed unresolved repairs, or replacement prepared but activation blocked. The authoritative workflow and recovery mechanisms are in STATE-AND-RECOVERY §5.1 and STATE-AND-RECOVERY §10.2.
| Failure | Ordered action and success condition |
|---|---|
| Process crash / interrupted SQL | Preserve sidecars; let the compatible owner open/recover WAL; inspect journals before exposing affected state or mutating; preserve unaffected working access; reverse uncommitted external effects or reconcile committed state |
| Missing/corrupt cache | Close users, preserve suspect cache for diagnosis, create new cache, revalidate inputs against retained trust; no TOFU reset |
| Missing/corrupt state with intact records | Stage fresh schema, replay initialization and complete stream, verify manifests/objects, reconcile journals, then activate through recovery |
| Stale backup | Compare independently retained heads; replay complete suffix. Without suffix refuse stale activation, even if SQLite integrity passes |
| Damaged/gapped/conflicting records | Preserve both versions; locate independent records and compare authenticated evidence. Stop at first disagreement; salvage cannot fill an authority gap |
| Missing before-images / external edits | Stop inverse writes at named targets; retain journal and GC roots. Operator supplies verified backup or resolves the external conflict through a new authorized operation |
| Disk full / I/O failure | Stop writes and GC; preserve durable intent and sidecars; establish storage health/free space without deleting unresolved backups; recover based on actual durable phase |
| Incomplete migration / interrupted restore | Retained supervisor or recovery entry point checks pair and journal; discard only proven unused staging after evidence retention, or reconcile activated copy. Old pair remains available |
| Whole prefix lost | Restore complete prefix set plus protected participant when present; verify store bytes and materialization; replay choices/history and reconcile system references. Missing user payloads or local builds remain missing, not reconstructed from names |
| Protected root lost | Reprovision trusted helper and protected trust from independent records; restore verified closures and before-images, inspect live effects, then rebuild references. Client claims cannot authorize imports or declare effects undone |
| Coordinator lost | Restore records/objects, verify plan DAGs and enrollment, expire inherited leases, requeue unresolved jobs, and reconcile duplicate results. Retained quarantine and missing gates prevent promotion |
| Publisher lost | Fence old workers at the publication adapter, restore coordinated public state, compare active timestamp/snapshot and signer heads, reconcile acknowledgements. Uncertain timestamp state invokes key runbook recovery |
| Release signer lost | Restore latest independently retained public signing state and separately recover keys under the runbook; compare unpublished reservations and exact bytes. No signing until high-water state is established |
| Trust lost or suspect | Fail closed for new trust decisions; recover authenticated chains from independent records. Irretrievably lost history requires an independently authenticated recovery bundle establishing current trust and safe version floors; disclose lost history. Installed software is not automatically deleted |
| Signing-version high-water uncertain | Stop signing/publication; reconcile off-device reservations and counterpart receipts. Key rotation alone does not reconstruct version floors. If continuity cannot be established, use deliberate authority recovery/rebootstrap; never reset to zero |
For timestamp state that is unavailable or untrustworthy, preserve the existing KEY-RUNBOOK requirement to replace the timestamp key through a root update. Establish safe version continuity as part of that recovery; new keys do not waive client rollback protection.
13. Command and output contracts
The specified command family is aslice-db(1).
--role accepts exactly the six roles in §1.1 and defaults to client-state.
--prefix selects a client instance; --instance selects a configured service or
system instance. Reject selectors that conflict with the role. Paths supplied by
an untrusted caller cannot select a different owner's database.
| Command | Contract |
|---|---|
aslice db list |
List all configured roles visible to this caller, including absent/blocked status; never create files. With --role, filter to it |
aslice db schema |
Show shipped role DDL/schema identity; --live reads the selected validated schema through fixed inspection |
aslice db query SQL |
Execute one bounded read-only statement on the selected role's consistent inspection snapshot; no interactive SQL shell |
aslice db check |
Fixed identity/schema/integrity/FK/reference/continuity/semantic checks; report missing participants, stale projections, maintenance status/deferred reasons, freelist pages, and estimated reclaimable bytes (freelist_count × page_size, not guaranteed savings); never repair |
aslice db maintain [--dry-run] |
Eligible bounded cleanup, optimization, lightweight checks, and checkpoints; dry-run reports without writes |
aslice db compact [--dry-run] |
Explicit validated-copy compaction; report space estimate before work, retain original and recovery path |
aslice db backup DEST |
Owner performs coordinated capture into a new destination; emit manifest digest, boundary heads, completeness, missing participants, and byte counts |
aslice db restore SET --dry-run |
Validate and preview without activation; emit a digest of the complete proposed action |
aslice db restore SET --confirm DIGEST |
Authorize that exact preview and invoke recovery/activation; interactive omission prompts with the same preview, noninteractive omission refuses |
aslice recover --role ROLE reconstructs a missing projection or resolves an
interrupted operation using retained records. It defaults to client state, discovers
required protected participants, and uses their authorization boundary. Cache
rebuild never resets trust. check offers this command when records are intact;
otherwise it identifies the missing evidence and applicable runbook. Service roles
are operator interfaces, not new client access to farm accounts.
--json produces one object with format: 1, command, role, instance_id,
status (ok, refused, needs-attention, or error), data, and errors.
Errors have stable code, message, remedy, and optional path. List has null
role/instance and an array of per-instance entries in data. Query data contains
ordered columns, positional rows, and truncated; SQL null maps to JSON null,
BLOB to an object with base64 bytes, and integers outside JSON's safe range to
decimal strings. Duplicate column names are preserved by positional rows. On a
limit, discard partial rows, set truncated: true, and return failure. Human output
uses tables and stderr diagnostics; scripts consume JSON. No command reports a
failed check or partial backup as success.
Exit codes: 0 success, 1 check failure/needs-attention/I/O failure, 2 usage or unsafe
query/unsupported schema, 3 authorization/confirmation refusal, 4 busy or changed
base without unresolved recovery; 130 safely completed cancellation. Recovery-required
outcomes use 1 even after timeout or cancellation. Stable error codes include db_busy, db_identity_mismatch,
db_schema_unsupported, db_query_denied, db_query_limit, db_corrupt,
db_records_incomplete, db_stale_backup, db_evidence_missing,
db_external_conflict, db_confirmation_required, and db_io_error.
14. Verification and acceptance
Run python -m unittest discover -s tests -p test_database.py. The suite creates
temporary databases from all six companions, checks valid fixtures and rejected
identities/relations/reservations, exercises views and representative queries, and
models record replay, lease expiry, duplicate results, stale restore, and migration
copies. Multi-connection tests exercise writer contention, stale snapshots, local
statement conflicts, and incomplete checkpoints with active readers. Deterministic
models cover cumulative wait/cancellation budgets, durable-phase outcomes, cache
bypass verification, fair maintenance scheduling, cleanup bounds and retention,
schema-1-to-2 copies, and compaction with interruption and space refusal. Model
helpers live only in tests; they are not database runtime services.
The current models do not implement the guided recovery, explicit waiting, helper-ownership, post-commit service-check, or isolated-replacement contracts added in this revision. Passing them validates their existing modeled scope only. The additional pending runtime scenarios and UX targets are specified in STATE-AND-RECOVERY §10.1, including lost trust, disagreeing copies, working access, grouped conflicts, manual repair, reviewed activation, repeated interruptions, and durable-transition failures.
Before shipping, implement and test the domain decoders, connection authorizer, service fencing adapter, backup coordinator, restore supervisor, and actual CLI. Inject failures at every journal/SQL/fsync/pointer/signature/activation boundary; test disk-full, I/O errors, malicious schema and SQL, long readers, corrupt records, lost acknowledgements, and independently restored backups. Complete HFS+/APFS and macOS 10.11–12 power-loss drills and separate Pi/service-host drills with the pinned build. Rendering diagrams, passing schema tests, and SQLite's upstream guarantees do not satisfy these platform and security acceptance gates.