# Decision records (ADRs): 158 files in decisions/, number and title. Generated by landing/scripts/sync-captures.ts. # Published in full here: 0034, 0035, 0062, 0077, 0085 (the ones the page cites). 0001 Rust as the implementation language (TypeScript only for the PWA UI) 0002 Cryptographic library per primitive 0003 KEM: X-Wing (pinned draft) inside RFC 9180 HPKE 0004 Hybrid signatures: composite ML-DSA-65 + Ed25519 with per-purpose context 0005 AEAD: ChaCha20-Poly1305 with derived per-message keys and key commitment ("AEAD-C1") 0006 KDF and hash functions 0007 Encoding of signed and hashed objects: deterministic CBOR in a "sign-the-bytes" envelope 0008 Randomness and memory hygiene 0009 Crypto agility and suite versioning 0010 Signed objects carry the exact signed bytes (no canonical JSON) 0011 One hash-chained account log with relay receipts and checkpoint gossip 0012 Device roster validity is decided by log position, not timestamps 0013 Enrollment: commit-reveal transcript SAS, account binding, approver friction 0014 Account Secret epochs and per-host Tessera shares 0015 Secret policy is an approver-signed, versioned record; callers identified by peer credentials 0016 Exposure modes: proxy by default, injection pinned and opt-in 0017 Request/grant hardening: sealed scope, verifiable UV, safe defaults, epoch revocation 0018 Lease time: honest-host enforcement plus derived short-lived credentials 0019 The relay never serves executable approver code 0020 Opaque Web Push for wakeup, WebSocket for live sessions 0021 Bridge credential custody: no standing access, Custos-owned sign-in 0022 Bridge scoping, token caching, reference integrity, and the value of Tessera for bridged secrets 0023 Local host hardening: peer credentials, pipe/socket ACLs, non-dumpable daemon, process-tree kill 0024 Wire format: det-CBOR sign-the-bytes envelope, one log-entry object, checkpoint as receipt 0025 PoC live updates: signed long-poll on the account log (WebSocket and Web Push deferred) 0026 Handles: the approver-signed policy is the unit of access; bridged handles back onto a bridge-credential secret 0027 WebAuthn user verification on every widening approver action, verified by hosts with the limited verification algorithm 0028 PoC scope cut: what is built tonight, what is deferred, and why it is safe to defer 0029 Non-cryptographic dependency pins, TLS client configuration and licence allowlist 0030 The host daemon runs as its own OS user ("service mode"); same-user "dev mode" is labelled and limited 0031 Fork plus withholding: state the real guarantee, add a human-compared log fingerprint, keep witnesses deferred 0032 Granted window and use limit are plaintext in the log; requested scope stays sealed 0033 The spec is normative for bytes; a separate verification path for self-signed objects 0034 Harvest-now-decrypt-later: values rest on X-Wing; the log keeps public-key wraps of `a_i` 0035 Fatigue control derived from the log and enforced on the phone; grants bound to the requesting session 0036 WebAuthn UV proves a biometric on the approver origin, not what the user saw; XSS is an origin compromise 0037 Egress hardening: agent and host hints are untrusted; once grants bind the operation; identity encoding; no loopback egress 0038 The approver can only choose grants inside the allowed envelope; handles can expire (session storage) 0039 Suite C1 is frozen only after the published vectors of the specifications it pins pass 0040 Handles that share a backing secret must have the same tier and env 0041 Fork check compares prefixes at the approver's size, runs as the daemon user, and a refused reduction is an alarm 0042 Service-mode layout: socket outside the state directory; the daemon never opens client paths; host-context work runs in the client 0043 A client uid that can become root does not get "service mode"; every bridge needs isolation = 1 0044 WebAuthn user verification may be the device passcode: split the phone thief into A6a and A6c 0045 "Until HH:MM" is converted by the daemon with jiff; request durations and grant `not_before` are defined 0046 Bind the approvers' and the hosts' copies of a policy body with a signed hash commitment 0047 Approval-screen defaults, round 2: verified-only scope sentence, capped night-shift uses, per-card number entry, closed env choice, hint after typing 0048 Root-owned daemon paths with an ancestor check, `sudo -k` in every documented host command, live escalation check before bridges 0049 Enrollment round 3: role chosen first, approver adds use a code shown by the existing approver, authenticated `as-transfer`, ungrindable device fingerprint 0050 Number matching round 3: six digits, no trust styling, never coalesced 0051 Daemon local state: lock and freeze as their own files, a short-lived `flock`, pages applied on a clone before they are persisted 0052 Host-side audit shows public fields only; the morning report with reasons and agent facts is a phone screen 0053 One PolicyBody ciphertext under a per-version policy key; byte bounds that fit the envelope 0054 Write guard round 3: overlapping rules, confirm only for unsafe methods, one handle per single-use grant, the approver sees the write body 0055 Coverage gate runs as a non-root user plus one root-only test binary; the 1Password bridge is Linux-only in the PoC 0056 Owner-priority scope overrides of the PoC cut (§13): 1Password on macOS dev mode, Windows compile target, Lowkey Vault test, executed fuzzing 0057 Lockfile: security-fix exceptions to the 14-day rule, two licence additions, one unmaintained build-time crate, and dev/fuzz pins 0058 Relay implementation choices: request limits, mailbox validation and lifetime, durable checkpoint time, rate limits 0059 CLI and MCP client choices: socket resolution, `.env` grammar, bridge commands, MCP tool contract 0060 Daemon implementation choices: state lock, ancestor check scope, macOS ACL detection, use-before-value order, wire details, test layout 0061 custos-wasm bindings and approver PWA: API shape, test strategy, simulated relay, pins 0062 End-to-end proof: the `tests-e2e` crate, what is real and what is simulated, demos 0063 Daemon review round 1: fail-closed use path, freshness only from consistent checkpoints, log recovery, buffer wiping, two-uid tests 0064 Spec conformance review round 1: missing property tests, claim citations, stale spec text, relay rate 0065 Core review round 1: method-override normalisation and body refusal, exact-size seed buffers, missing KATs, structure-aware log fuzzing, redactor property 0066 MCP daemon calls: per-call timeout and `outcome_unknown` 0067 Daemon review round 2: root-equivalent groups, host-only report acknowledgement, bounded agent-driven state 0068 Spec conformance review round 2: Windows is daemon-only, and every test name in the plan is a real test 0069 Core review round 2: wiped redactor needles and base64 buffers, drop names in the coalescing key, `;` refused in egress paths, KDF goldens, an authorization oracle for log fuzzing 0070 CLI daemon calls: per-call timeout, `outcome unknown` exit 5; shared ACL parser 0071 Refused reductions are a persisted queue; panic steps never stop each other 0072 Core review round 3: crypto fuzz targets that reach verification, wiped SHA-256/HMAC states, negative Azure grammar tests 0073 Daemon review round 3: append receipts that cover the entry, freshness only from new checkpoints, URL-override headers dropped, `op` path ACLs, `verify` on a busy lock 0074 Relay: device rate-limit buckets in their own table, apart from anonymous per-address keys 0075 Approver review round 4: a relay's `422` never drops a reduction; the build carries its own CSP 0076 rpc: `secret.propose` is parsed and built without unwiped copies of the value 0077 Every request result carries the fingerprint; a late drop is collected by polling 0078 Core review round 4: oldest and newest checkpoint per size, linear-time fragment redaction, enrollment goldens 0079 Daemon review round 4: bounded new arrivals, two-clock freshness, a grant-manager task, transient state-lock waits, PQ key share first, JSON secrets without scratch copies 0080 wasm review round 4: the simulated relay enforces skew and replay, the approver signs with a strictly increasing time, `restore` wipes the vault 0081 Relay: client address behind one trusted proxy (`--trusted-proxy`, right-most `X-Forwarded-For`) 0082 cli: secret values are read, encoded and sent without unwiped heap copies 0083 wasm review round 5: a Deny overtaken by a grant is owed as its revocation, a gap page is stale, and the wasm boundary hands over no unwiped secret copies 0084 Daemon review round 5: bounded relay bodies, a log written from what extended the state, request creation under the queue, sync warning with back-off, escalation re-checked at every acceptance 0085 Content-free Web Push wakes approvers; `inspect --search` closes the bstr proof hole 0086 Bounded checkpoint tracker: keep 64 sizes, refuse below the floor 0087 Daemon call deadline for `http.request`; response scrub on the blocking pool 0088 Service-mode walkthrough: two real uids, the built binaries, no network, a recorded transcript 0100 Exposure ladder L0–L5, per-handle approver-signed exposure rules, delivery binding 0101 `custos://` reference grammar; `op://` recognition and vault-scoped 1Password sources 0102 SSH agent in the daemon with an own minimal codec and verified session binding 0103 SSH key custody: daemon-held Ed25519 by default, certificates as the short-lived option 0104 SSH approval timing: grant-before-use for agents, approve-on-sign held ≤ 90 s 0105 Git commit and tag signing through `custos-ssh-sign`: the daemon signs the payload it shows 0106 Local approver = approver device of class `local` with a Secure Enclave UV signature 0107 Local approver placement: own OS user service + Swift Secure Enclave helper; PoC vs product signing 0108 Derived-credential adapters; PoC providers github-app, aws-sts, entra-token (ssh-cert should) 0109 Helper protocols: strict parsing, exact destinations, labelled as leaking 0110 `custos run` / `custos inject`: controlled environment and file delivery 0111 Reveal: per use, never leased, per target 0112 Resolver, recipe registry and resolver-gated escalation 0113 MCP server instructions, new tools and the agent snippet 0114 1Password bridge in macOS service mode, gated on a macOS two-uid test 0115 macOS one-command install 0116 VAPID JWT cache that attackers cannot flush; push rows deleted only for the endpoint that failed 0117 Security and protocol, round 3: fixed UV keys, per-entry caps, one session rule with parents, binding classes, chained rebases, local number matching, commit-then-reveal enrollment, CA generations 0118 Developer and implementation paths, round 3: `custos eject`, tool shims, persisted sessions and resume records, scoped `IdentityAgent`, SSH-first git, FIFO creation, re-delivery, recipe binding, project leases from `custos run`, npm, IAM, `who_can_approve` 0119 Security and protocol, round 4: grant shapes, enrollment peer check, requester labels and conditional number matching, amend-aware rebase chains 0120 Developer and implementation paths, round 4: terminal modes of `custos run`, argument-aware shims, `IdentityAgent` that works without the variable, background fetch by operation, gh accounts and scopes, Touch ID in projects 0121 Security and protocol, round 5: lineage-based race rule, peer check on every approver-app connection, local re-delivery through helper bindings, mint and re-delivery summaries, one-entry bundles 0122 Developer and implementation paths, round 5: `.env` framework cascade, browser-exposed variables, L4 project values, offline start, polling commands, agent-session leases, SSH migration that keeps the key, signing that waits, git and `ps` parsing 0130 Surrogate channel at L0: stand-in keys swapped by a local intercepting proxy 0131 Surrogate templates, account-wide keyed tag with handle and epoch, checksum policy, pinned and ephemeral classes 0132 Intercepting proxy in custosd: CONNECT with session credentials, in-memory CA with name-constrained intermediates, intercept only rule hosts, per-process trust 0133 AWS: L2 STS through credential_process in the PoC; SigV4 verify-then-re-sign of a surrogate pair as [Should] 0134 Use blocks: batched use entries for leased L0 traffic 0135 Alert entry (kind 14) and surrogate tripwires 0136 Discovery runs as the user, offline, with own Rust detectors, confidence tiers and daemon-keyed fingerprints 0137 Migration: ladder-ordered replacement, span-splice rewriter without plaintext backups, git rules, approval defaults and rotation cards 0138 Local approver hardening: single-use local grants in the PoC, wrapped Secure Enclave handle, enrollment in the approver uid, reductions limited to the co-located host 0139 Persisted proxy sessions, project sessions, bundle requests and "Start my workday" 0140 Proxy deployment: upstream chaining, merged CA bundles, per-tool shell-init, persisted rotating roots, constrained and inspect roots, inspect-all opt-in, own X.509 encoder 0141 Surrogate tag keys: random, per tier, with generations and rotation; templates that copy no secret-dependent structure; ≥ 64 tag bits; shape spec 0142 Use blocks and alerts, round 1: plaintext `batchable` bit, fixed k = 50, per-severity alert budgets, local-only plaintext findings, foreign stand-ins in LLM bodies, credential-minting templates 0143 Rewriter round 1: splice and edit kinds, symlinks, git-tracked files left unchanged, restore rule by default, daemon-side equality oracle, previews without fragments, AWS config file 0144 Batch UV for policy entries: one user verification for a scan's adoptions and for a tag-key rotation 0145 Developer paths, round 1: psql through a pgpass tmpfile, AWS session-token mode and SSO advice, GitHub attribution and `github-user`, Azure stated plainly, kubectl, containers, Touch ID availability, `custos setup` and words 0146 SSH, round 1: unverified destinations once-only with approver pinning; git signing per repository with rebase grants and visible waiting; hybrid ML-DSA-44 signing key as [Should] 0147 Crate layout and test plumbing: pure `custos-core`, `custos-local`, `HostKeyVerifier`, `test-upstream` feature, format clarifications, effort and cuts 0148 Security and protocol, round 2: bundle entries and per-handle caps, whole-argv and reference binding for leases, a local approver without the Account Secret, local grant shapes, name constraints on the proxy root, mint facts, rebase entries, restore on demand, honest limits 0149 Developer and implementation paths, round 2: `custos start` and `custos fix`, project sessions keyed on root and remote, shell exports to `~/.custos/env`, gh keyring and git keychain, Azure caches left alone, psql service setup, relay dependence and resume, background fetches, proxy ports and framing, stable bundles, app-owned configs, scanner budgets, test plumbing 0150 Native iOS and Android approver apps over the shared Rust core (UniFFI, two facades) 0151 Push transports: APNs direct for iOS, FCM HTTP v1 for Android, Web Push for the PWA 0152 End-to-end encrypted notification hint under the policy key 0153 Android delivery: system-drawn generic notification by default, opt-in preview mode 0154 No decision from a notification: approval only on the in-app card 0155 Hardware-bound UV key for native class-1 approvers and its composition with C1 0156 Enrollment of a native approver 0157 Interruption levels, channels and fatigue controls on native approvers 0158 Native approver PoC: scope, distribution, provisioning and tests 0159 Native approvers design round 1: lock-screen trust from the phone's own table, keychain groups, owner build, relay-side mute, two-step hardware signing, push-key handling 0170 macOS dev-mode installer: user LaunchAgents, manifest uninstall, loopback approver origin 0171 claude-guard: a credential-read guard for the monitoring Claude Code session 0172 Value-free monitoring: `custos status --json`, `custos audit tail --json`, `custos monitor` 0173 Agent-written text in monitoring output: scrub at the daemon, withhold on request, bounded commands only 0173 Monitor guard: settings writer, pre-flight and permission backstop 0174 claude-guard round-1 hardening: every tool gated, a monitor profile, shape-preserving redaction 0180 Native SwiftUI Control Center with a `MenuBarExtra(.window)` and a full window, shipped outside the Mac App Store without App Sandbox 0181 The Control Center renders value-free daemon view models over the existing sockets, verifies what it must not trust through a UniFFI core facade, and checks the daemon's code identity 0182 The Control Center is the local approver app: one bundle, one code identity, one Secure Enclave key 0183 Local approval card: Deny by default, 1-second input guard, number entry for terminal requests, "Nobody typed this" with no pre-selection for background requests, words only on the card, the Touch ID sheet and the phone, nothing approvable from the menu bar 0184 Install, repair and update from the app: the reviewed, signed script is copied to a root-owned directory, hash-checked there and run in Terminal under `sudo -k`, with no privileged helper; the product moves to a signed `.pkg` with launch constraints 0185 Updates: a Custos-signed release manifest verified by the app, installed through the reviewed script; Sparkle is not used; the app and the daemons are version-locked 0186 Control Center visual language: quiet-ledger as the base with living-shield and vault-door grafts; facts with their age instead of padlocks; ink primary actions; a keyhole-in-a-ring mark whose menu bar shape changes per state; log head in hex; boundary map as a secondary view 0187 Vault in the app shows metadata only; the L5 reveal to the Control Center is a once request, phone-approved by default, logged before it is shown, displayed for 30 s, with a 45-s clipboard clear and no capture-proof claim 0188 Presence: a shape-changing menu bar icon, a Dock icon while something needs you, notifications without request cues, occlusion fallback, configurable shortcuts ⌥⌘K and ⌥⌘. 0189 Control Center tests: Swift Testing at 100 % lines of app logic, snapshot tests of every design state, XCUITest flows against a fake daemon, schema contract tests, a UI canary test; PoC scope and ranked cut list 0190 Owner decisions on three open points of the local-credentials design