# CONSUMER_INTERFACE_GATES.md Cross-project concerns raised by, or raised against, the systems this compiler expects to sit beside. Non-normative. | | | |---|---| | Created | 2026-09-12 | | Status | A register. Nothing here blocks current work. | | Peer | Kane Fabric's `docs/CONSUMER_INTERFACE_GATES.md`, at `5df8801` | | Normative counterpart | `IDENTITY-CONTRACT.md` | --- ## 0. What this file is for, and what it is not for A gate is a disagreement, an unmade decision, or an assumption that two projects hold differently and have not yet had to reconcile. Recording one is not the same as scheduling work on it, and it is not an invitation to negotiate. The failure this prevents is specific. When two systems meet, the pull is to fix the mismatch immediately — usually by one side quietly adopting the other's model, in a commit that looks like integration and is actually a surrendered boundary. A register makes the mismatch visible without making it urgent, so that when it does have to be resolved, both sides can see what was known at the time and who it belonged to. Kane Fabric wrote the first of these against this project. This is the same instrument pointed the other way, plus the defects that review found in our own document. **Entries are not requirements on anyone.** An entry owned by another project is a statement that we are waiting, not that they are late. --- ## 1. Defects in our own contract, found by external review These are corrections we owe, not disagreements. Both are addressed in the same commit that adds this file; they are recorded because the *class* of mistake will recur. ### G-1 — an illustrative value read as an expectation `IDENTITY-CONTRACT.md` gave `kane-fabric/oidc` as an example authentication method. Kane Fabric has no OIDC service, no user database, and no person-authentication role. The example named a capability in another project that does not exist and was never asked for. An example in an interface contract is read as an expectation by the next person to implement against it. This is the same failure as the "acceptable for a development name" sentence that survived three documents: a plausible clause, never challenged, hardening into a constraint. **Corrected.** Method strings are now specified by shape rather than by example. **Owner:** this project. **Blocks:** nothing. **Standing lesson:** an interface document should contain no example that names a system outside it. ### G-2 — a jurisdiction baked into the wire format The headers were `X-Kane-Auth-Email` and `X-Kane-Auth-Method`. Two things wrong. The contract insists at length that the compiler must never learn a membership concept, then puts a county in the header name — a deployment fact in an invariant place. And `-Email` names a format rather than a role, in a field that is explicitly allowed to hold something else (see G-3). **Corrected** to `X-Mechcomp-Auth-Id` and `X-Mechcomp-Auth-Method`. The receiver is the invariant; the jurisdiction is not. **Residue:** the header lands in `design_record.Author.email`, a field named for what it holds today. Renaming it is a format change to every stored record and is deliberately not done now. Noted so the mismatch is on record rather than discovered. **Owner:** this project. **Blocks:** nothing. --- ## 2. Open gates ### G-3 — persistent person identity against epoch-scoped participation **The disagreement.** This compiler records a stable identifier for the author of a design, in a document built to remain legible for years. The civic direction is address-bound, opaque and epoch-scoped participation, which deliberately avoids durable person identity. These are not two spellings of one idea. They disagree about whether a person should remain identifiable over time. **What already accommodates it.** Nothing in `design_record.py` requires the string to be an email. `Author.email` holds an identifier; `verified` and `method` state what it is worth. An opaque, epoch-scoped token fits the existing field without a schema change, and `Author.handle` already exists for a display name that is explicitly not the identity. **What does not dissolve.** A design record is meant to be readable by someone who has none of this software, years from now. An epoch-scoped identifier is meant to stop resolving. A record naming a token whose epoch has closed has an author line that can never again be resolved to anyone — which may be exactly right as privacy policy, and is still a permanent loss of provenance for a physical object somebody is holding. That is a conflict between two goods. It is not a bug in either design and it cannot be settled by whichever side writes its document second. **What this project will not do:** pre-emptively adopt either model, or add an identity-mode switch to the record format before there is a second consumer to justify one. **Owner:** joint — this project and whatever membership system reaches production first. **Blocks:** nothing today. Becomes blocking at the first implementation of `IDENTITY-CONTRACT.md` §7, and is cheap until then only because no record with a verified author yet exists anywhere. ### G-4 — the chain may grow, and exactly one hop may decide `IDENTITY-CONTRACT.md` names CT 101 as the decider because CT 101 is what exists. Additional containers are expected to insert themselves between the browser and this application, possibly several, possibly owned by other projects. The invariant that matters is not which box decides. It is that **exactly one does, and that it is the last hop before the application.** Two intermediaries both setting the identity headers is a forgery vector wearing the costume of a deployment change: the second one wins, the first one believes it decided, and nothing in the chain reports a conflict. **Corrected in the contract** by restating the strip and the decision as properties of the chain rather than duties assigned to a named container. **Residue:** nothing verifies the property. A check that the application is reachable only from its declared decider would be a `ct-baseline.sh`-shaped test — binary, checkable, not a matter of judgement — but that script is host property covering three projects and adding to it is a CIVICVS decision. Carried as `HANDOFF.md` open question 11, which asks the same thing about working-tree ownership. **Owner:** this project for the contract; CIVICVS for whether it is enforced. **Blocks:** nothing. ### G-5 — what happens when the decider cannot answer The contract specified `2xx` and `401`. It said nothing about the eligibility endpoint being unreachable, slow, or returning something unparseable. Across a chain of unknown length and multiple owners, unreachable is the ordinary case rather than the exotic one. Fail-open and fail-closed are both defensible and they are not the same system. Discovering which one was built during an outage is the worst way to find out. **Corrected in the contract:** anything that is not an affirmative permission is a refusal. Unreachable, timed out, malformed, and `5xx` all deny. **Residue:** this makes the eligibility endpoint a hard dependency for every gated route. Whether that is acceptable is a question for whoever operates the membership system, and the answer may be that gated routes need a degraded mode. Not designed. Recorded. **Owner:** joint. **Blocks:** nothing today; `/m/` is not yet gated. ### G-6 — method strings need a vocabulary, not examples If `X-Mechcomp-Auth-Method` is free text, every consumer invents its own, and a record written by one deployment is uninterpretable by another. If it is an enumeration, this project is dictating mechanisms to systems it refuses to know anything about. **Corrected in the contract** by specifying a shape — issuer, mechanism, and what was actually verified — while leaving every value open. **Residue:** a shape is not a vocabulary. Two deployments can comply and still disagree about what "verified" means. That is tolerable while the record states the method verbatim and makes no claim beyond it, and it stops being tolerable the moment anything filters or aggregates on the field. **Owner:** this project. **Blocks:** nothing. --- ## 3. Raised against us, and correctly not ours Recorded so that a successor does not mistake them for work, and does not rediscover them as though they were new. ### N-1 — buildings against delivery-point geography Kane Fabric observes that membership is described as attaching to buildings while this compiler insists it must never know what a building is. That is not a contradiction, it is the boundary working. The compiler receives a decision, not the facts behind it. Whether "building" is the right primitive against parcel and delivery-point geography is membership semantics, upstream of anything here, and a correction there requires no change to `IDENTITY-CONTRACT.md`. **Not ours.** The compiler's only stake is that the answer stays an answer. ### N-2 — the local secure origin MS5 needs a browser to establish a secure context with a local edge while central infrastructure is unavailable. This project has centrally terminated public TLS and internal proxy TLS, which solves a different problem and offers nothing to that one. **Not ours.** No claim is made that our TLS architecture is a model for it. ### N-3 — the `wg-pk` fleet question Peer lifecycle, key replacement, address allocation and hub ownership do not extrapolate from the current manual administration. This project depends on the hub as transport and deliberately refuses to make it an authorization authority — `IDENTITY-CONTRACT.md` §2 — but the fleet design is estate work. **Not ours.** Our only requirement is that the hub stays transport. ### N-4 — credential hierarchies stay separate Proxy TLS, edge TLS, WireGuard keys, membership credentials and signing authority have no reason to merge. We agree, and note it here so that nobody proposes a unification on our behalf. **Not ours, and not wanted.** --- ## 4. The shape of the eventual junction Recorded because both projects independently drew the same picture, which is the strongest evidence available that it is right. ``` geography facts | v a membership or participation system | v a minimal authorization decision | v this compiler ``` One direction. The compiler's need for authentication does not oblige the geography layer to acquire authentication, and the geography layer's model of participation does not reach this application except as a verdict. Where the two must not meet: this compiler and geography, directly. If a future design has us reading parcels, delivery points or buildings, the membership layer has been bypassed and G-3 through G-6 are all void.