# 0053 — Access enforcement degrades CLOSED

Date: 2026-07-29
Status: accepted
Amends: ADR-0009 (endpoint access control model)

<!-- Ruled at the access-control grill 2026-07-29 (operator). CONTEXT.md
"control-surface modes" carries the resolution chain; this ADR records the degrade
flip and why the original ruling no longer holds. -->

## Context

ADR-0009 deliberately ruled the access whitelist's degrade **open**: a corrupt or
missing `access.json` admits traffic, documented as the opposite of the grant
store's fail-closed degrade. The justification was explicit: the whitelist was "a
same-subnet convenience boundary layered inside subnet membership, not the
system's outer wall" — every member node was the one user's own machine.

The **shared subnet** ruling (2026-07-28, glossary) breaks that premise. Member
nodes now belong to different human operators, and the access layer is the gate
against **agents** — unintended collaboration, disturbance of unrelated agents. A
boundary that evaporates when its store file corrupts is not a boundary an
operator can reason about.

## Decision

**A corrupt or missing access store degrades CLOSED — always, regardless of the
effective mode.** The whole resolution chain (explicit entries → endpoint mode →
node mode → captured subnet mode) presumes a readable store; when the store cannot
be read, enforcement refuses unlisted-subject traffic rather than admitting it.
The stateful-firewall reply exemption (replies to the endpoint's own recent
outbound) survives the degrade — it keys on correlation state, not the rule store,
and keeps a degraded node's own conversations alive.

A degraded store must surface loudly (diagnostic on the refusing node) — silence
plus refusal reads as network failure and burns diagnosis time.

**Mint-on-first-load** (amendment, 2026-07-29, W1 build): "missing degrades
closed" would otherwise brick every node that *upgrades into* per-surface access
control, since none of them has an `access.json` yet — the ruling would refuse all
unsolicited cross-node traffic across the fleet on the release that shipped it.
So absence is split by whether the node has ever had a store: **before the first
mint** — the load path writes an empty v2 store (atomic temp+rename, the same
save path as every edit) and proceeds under the unconfigured baseline, the chain's
implicit-open bottom; **after it** — the store was minted and then vanished, which
atomic writes make impossible in normal operation, so it is an anomaly and
degrades closed per the rule above. A mint that *fails* degrades closed as well: a
node that cannot write its own policy cannot vouch for one. The discriminator is
per-store-path and per-process, so a restart re-mints (boot is a first load) while
a deletion under a running daemon is caught. The reply exemption is unchanged
throughout.

## Consequences

- Flips a deliberate, documented ADR-0009 ruling; `spt-store::access` load-path
  behavior and its tests change contract.
- The gate's load path now has a **write** side effect (the baseline mint) on a
  node's first check — deliberate, and the reason the upgrade path is safe.
- A personal single-user fleet with zero configured rules now blocks unsolicited
  cross-node traffic if the store file corrupts — accepted: the failure is loud,
  local, and repairable (`spt endpoint access open` / store re-mint), and the
  alternative (silent admission on a shared subnet) is the worse lie.
- Aligns the access store's degrade with the grant store's — one posture for all
  security material: fail toward refusal.
