Merge main: the trunk renamed the seats and made them data
Both branches changed the seat set from the same starting point, so every number collided and every `mesh-*` name existed twice. The trunk's numbers and names win: this branch's records became 0129/0130 and its migrations 0037/0038, and the hardcoded rename map gave way to the trunk's `seat_alias` table — a rename is a row now (ADR 0122), not a recompile. Three of my checks were wrong and the merge is what showed it: A seat with an empty protocol is a marker, not an incomplete declaration. Most node-scoped seats are markers — which module is this machine's packet filter — and refusing one refused most of the set, the showcase module included. A mistyped field name is already refused by the parser, so an empty protocol was written as one deliberately. A claim on a seat this manifest does not declare is not the parser's to judge. A module may hold a seat another module declared; that is the whole reason ADR 0126 has callers name the seat and not its provider. Whether the seat exists is a fact about the catalogue, so the refusal is at registration, where every declaration is in view. And a seat may share a name with the provision it delivers. `git`, the npm registry and the artifact store still do, because renaming a delivering seat cascades to every consumer requiring it, with a window where a holder stops resolving mid-flight. The trunk deferred exactly those three on purpose. Full suite green against a real NATS and store.
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
-- Every bus user's password hash, because the file has to be written again.
|
||||
--
|
||||
-- novox/hq design 25 §4, task 1.7. On the bus the mesh runs on today an account is created by a
|
||||
-- management call: the mesh mints a password, hands it over, seals the plaintext to whoever will
|
||||
-- use it, and keeps nothing. That works because the broker remembers.
|
||||
--
|
||||
-- The bus being built has no management call — its users are a file the controller composes, and
|
||||
-- **the whole file is written every time any of it changes**. So the first person's access change
|
||||
-- would silently blank every module's password. The hash has to outlive its own minting, which is
|
||||
-- state the mesh did not need before and does now.
|
||||
--
|
||||
-- Keyed by username, because the username is exactly what the composed file needs and what a
|
||||
-- principal derives from its own identity. Nothing else about the user is here: **permissions are
|
||||
-- not stored.** They are derived from what each module declares, every time the file is written
|
||||
-- (ADR 0043) — a stored copy would be a second account of a user's authority, able to disagree
|
||||
-- with the first, and the disagreement would be invisible until somebody compared a file with a
|
||||
-- manifest.
|
||||
--
|
||||
-- The hash and not the password. A file on a node's disk holds the hash, and so does this: a
|
||||
-- credential recoverable from the mesh's store is one whose blast radius is the store's.
|
||||
create table bus_user (
|
||||
username text primary key,
|
||||
-- kind and what it names, so a user whose subject is gone can be found and removed: a module
|
||||
-- unassigned, a node forgotten, a token spent. Recorded rather than parsed back out of the
|
||||
-- username, because a name is for the server and a parser over it would be a second grammar.
|
||||
kind text not null,
|
||||
node text not null default '',
|
||||
module text not null default '',
|
||||
password_hash text not null,
|
||||
minted_at timestamptz not null default now()
|
||||
);
|
||||
|
||||
-- Finding every user of one kind, and every user belonging to one node — which is what removing a
|
||||
-- node, or composing after an assignment, asks.
|
||||
create index bus_user_kind on bus_user (kind);
|
||||
create index bus_user_node on bus_user (node) where node <> '';
|
||||
Reference in New Issue
Block a user