Architecture decision records

Architecture decision records

One decision per file, each in Status / Context / Decision / Consequences form. A record is amended by a later record that supersedes it, never edited to change its meaning; corrections of fact land in the evolution log.

Record Decision Status
0001 Thin-core architecture — one Go binary owning state, sequencing, keys, and recovery; everything else a pinned subprocess Accepted
0002 Project name exocomp, after the Control D ctrld collision ruled out the working name Accepted
0003 Secrets envelope: age recipients → DEK → HKDF subkeys, with a recovery kit that closes the credential loop Accepted
0004 OpenTofu HTTP state backend implemented in SQLite, sidestepping the S3 conditional-write gap Accepted
0005 Job sandboxing via nono's enforcing tier; jobs are denied every powerful local socket Accepted
0006 uv build-time Python toolchain plus a doctor-gated operator escape hatch in the data volume Accepted
0007 Audit hash chain deferred; revival requires an off-host-anchored head Accepted
0008 Multi-step choreography via a step ledger on the job store; DBOS deferred Accepted
0009 LAN listeners require locally-minted bearer tokens; TLS by default Accepted
0010 Git-centric source model; deploy/fleet keys; state-only vs run-in-server workspaces Accepted
0011 Project deps from root requirements.txt/pyproject.toml, uv-resolved into hash-gated caches; uv ships at runtime Accepted
0012 Litestream as an optional fast-RPO sidecar replicating exocomp.db's WAL; restic stays authoritative; unencrypted-remote refused without ack; sidecar not embedded Accepted
0013 OpenBao Transit as an optional additive seal for the DEK (auto-unseal); keyring.age recovery preserved; unreachable is an explicit error, never silent fallback Accepted
0014 Agent-facing surface: one core, three thin faces (CLI --output json, HTTP /api/v1, MCP) sharing Decision 0009 auth, the one queue, and the internal/wire envelope contract Accepted
0015 Ansible collections go per-source: content-hash-keyed cache dir, resolved at claim time, GC'd with the source lifecycle; Decision 0011's shared collections decision superseded Accepted
0016 exocomp ships as a self-contained base image that is also an artifact carrier; uv is bootstrapped when absent, ansible is PATH-delegated with PYTHONPATH, and an operator's image is captured for recovery Accepted
0017 One internal/probe vocabulary for checks and gates; checks gain dns/tls/metric; the SSRF guard is per-face and Location is opt-in per kind Accepted
0018 Opt-in aggregate check events carrying the present host set, with --limit-hosts expansion governed by the trusted-source line Superseded by 0019
0019 Aggregate check events, event.hosts, and --limit-hosts removed; per-host events are simpler, and migration 0041 stays as inert schema Accepted
0020 Reload first, restart after drain, recover interrupted jobs; schedules reload without a restart Accepted
0021 Keep the host scope write-only; the request response itself is the polling surface Accepted
0022 Withdraw the v1.0 label; the release line is pre-1.0 from v0.1.0, and release-please owns later versions Accepted

Some decisions are recorded elsewhere because they are shape rather than choice: the platform support tiers are in architecture/platform-contract.md.