# Endpoint access-control model (visibility / whitelist / grants)

## Status

accepted (2026-06-02)

**Amended 2026-06-15 (ADR-0020):** the reply-vs-new correlation below is keyed on
the `<EVENT>` envelope's **`from` attribute** (correlate an inbound `from` against
the endpoint's prior outbound), **not** the `__REPLY_TO__` token. `__REPLY_TO__`
was a mis-elevated relic and is removed from spt-core; the sender-identity
granularity is unchanged (the relic carried the same `from`).

## Context

Three access/discovery concepts accreted around the multi-subnet model and risked overlapping into a muddle: **endpoint visibility** (ADR-0006 — is an endpoint routable in a subnet at all?), a new **per-endpoint access whitelist** (who may *remotely reach* an endpoint), and the existing **consent grant store** (may *this agent* do *this high-risk thing*?). A new **resource advertisement** (free-text service blurb) also needed to know which access layer gates it. Folding the whitelist into the grant store was tempting but the two differ on two axes — subject granularity (origin **node/user** vs **agent**) and default polarity (**open** vs **deny**) — so merging would corrupt both. This ADR fixes the layering.

## Decision

Model access as **three orthogonal, nested gates**, evaluated in order:

1. **Subnet visibility** (ADR-0006, per-`(endpoint, subnet)`) — is the endpoint routable in this subnet at all? *Excluded* = invisible + unroutable.
2. **Endpoint access whitelist** (new, per-endpoint) — may *your node* (later: *your user*) remotely reach it?
3. **Capability grants** (existing consent store, `capability × agent × target-node`, default-deny) — may *this agent* perform *this high-risk capability* (remote-exec, instantiate-anywhere)?

**Resource advertisement** (the free-text discovery blurb / "subnet resource registry") is gated by gates 1–2: an endpoint a viewer can't reach never advertises what it can do.

### Access whitelist semantics

- **Origin-node gate.** It checks the **node the inbound interaction originates from** (where the operator is acting), **not** the sender endpoint's identity or home — *"the operator must be on a whitelisted machine; the sender endpoint's location is irrelevant."*
- **Stateful-firewall model**, not a blanket inbound block:
  - **Outbound** → any **visible** node (the whitelist never restricts who the endpoint talks *to*).
  - **Inbound reply** correlated to the endpoint's own prior outbound (`__REPLY_TO__`) → allowed from any visible node ("established/related").
  - **Unsolicited/direct inbound** (new control or message) → only from a **whitelisted** node ("new inbound").
- **Same-node operation is always allowed** (you're at the hardware; the home node never whitelists itself). The whitelist gates **remote (cross-node)** reach only.
- **Default empty = open** (any subnet-visible node — current behavior); setting a whitelist *restricts*.
- **Node-tier ships M4** (Ed25519 node pubkeys). **User-tier is deferred** to the per-(subnet, user) model (ADR-0006 cross-user seam); the schema reserves an inert `users` field until then.
- **Distinct from grants, same plumbing:** enforced at the target endpoint's node, synced as security material near the trust store (not the context repo), subnet-settable, revocable — but a **different table + polarity** (origin-node / default-open vs agent-subject / default-deny).

## Consequences

- New v1 requirements registered: `REQ-SEC-1` (access whitelist, M4 node-tier), `REQ-INST-14` (resource advertisement, M4), `REQ-PAIR-7` (subnet icon, M4 data / frontend render) — all `required_stages = []` until M4.
- The routing/messaging path (M4) gains a whitelist check on inbound: classify reply-vs-new (via `__REPLY_TO__` correlation) before applying the origin-node gate.
- The registry projection that backs resource advertisement must apply the visibility (and later whitelist) filter per viewer.
- **Forward (cross-user, ADR-0006 seam):** the whitelist's `users` tier and the "nodes belonging to whitelisted users" semantics activate with the per-(subnet, user) model.
- Builds on ADR-0006 (visibility, multi-subnet) and the existing consent grant store (CONTEXT §Consent & security gates).
