Project
Decision 0001
Status: accepted, 2026-10-01
Context #
The write guard blocks a request only when it matches a write_signatures entry. Anything that
matches nothing passes. The first real host profile listed the writes it knew of, channel by
channel. A review found that most of one channel's writes passed: the inventory had been taken
from one call shape, and calls written another way were missed. Two more channels (identity
provider account changes, a file-processing service) were missing entirely.
Decision #
For every channel where reads and writes share a method and host (RPC-style APIs that POST every
operation, callable cloud functions, GraphQL), write one signature that matches the whole channel
and list the reads in allow_signatures, by exact name or by a verb the backend's own model
declares. A new operation is then blocked and shows up in writes.json, instead of passing
silently. Allow entries are anchored to their host, because an allow entry overrides every write
signature.
Keep a replay check next to the profile that loads it through profile.mjs, judges requests with
the guard's own decideRequest / decideControl, and takes its expected answers from the host's
source (every live operation and function name), never from the profile. Run it after every
profile change and after every host release.
Consequences #
- A new read is blocked until it is added, which is the safe direction; a read-only run that touches it is refused and names it.
- Functions with side effects outside the app's data (model quota, mail) stay blocked; a feature
that needs them runs
scoped-write, where calls are recorded, not stopped. - The check is host code and lives with the host profile, not in this repository.