The control plane's signing key, and a second context to hold it
Everything is blocked on what a node presents to prove which node it is. This builds the other direction, which is not blocked: what a node believes. identity is the second of the seven contexts. It holds an Ed25519 signing key the control plane generates once, whose public half now travels in every enrolment token. A node believes a declaration because it carries a signature that key made -- pinning only the broker would make the control plane's authority transitive, and since the host applies whatever the link delivers, a compromised broker forging declarations is the whole machine. Establishing the key is idempotent, and it has to be: a second key generated by a restart is a mesh where every node holds the wrong public half, so every declaration is refused by every node with nothing visibly wrong. The guarantee is a partial unique index plus a read-back, not the check before the insert -- six processes racing to establish all agree on one key, and there is a test that runs them. Tokens are now one line of base64 carrying three of their four parts. The missing two are the broker's address and its certificate fingerprint, both step 5 of the bootstrap. The command prints the token and names what is missing rather than emitting something that looks usable. The second context also tests a claim this repository had made and never checked: that a context reaches only its own store. Two databases, two credentials, no setting that reaches both. Running migrate with one stops and names the grant it lacks -- verified, not asserted. Assembling a token needs a node record from one and a key from the other, and neither reads the other's store; the process holding both grants asks each for its part. 45 tests, none skipped. Fault injection found one test whose property is enforced somewhere other than where I injected -- idempotency comes from the database constraint, not from the early return, which is what the code comment already said.
This commit is contained in:
@@ -26,7 +26,8 @@ argument that is not settled there.
|
||||
| | |
|
||||
|---|---|
|
||||
| `inventory` | node records and enrolment tokens — **built, as far as identity** |
|
||||
| `config`, `connectivity`, `provisioning`, `delivery`, `observability`, `identity` | not built |
|
||||
| `identity` | the control plane's own signing key — **built, and no further** |
|
||||
| `config`, `connectivity`, `provisioning`, `delivery`, `observability` | not built |
|
||||
| the interface every surface speaks to | not built; its shape is not decided |
|
||||
|
||||
```
|
||||
@@ -35,6 +36,7 @@ mesh-control node add <name> create a node record
|
||||
mesh-control node list the nodes this mesh knows about
|
||||
mesh-control token issue --node <name> a one-time right to join, for an existing record
|
||||
mesh-control token issue --new <name> create the record and issue for it
|
||||
mesh-control identity show this control plane's signing key
|
||||
mesh-control version what this binary is
|
||||
```
|
||||
|
||||
@@ -56,10 +58,26 @@ one — two live tokens are two machines able to join as the same node.
|
||||
Redemption is a single statement that both finds a live token and spends it, so eight concurrent
|
||||
attempts on one secret produce exactly one winner. There is a test that runs them.
|
||||
|
||||
**What a token is missing is three of its four parts.** ADR 0004 requires the broker's address,
|
||||
the fingerprint of its certificate, and the control plane's signing identity. None of the three
|
||||
exists yet, so `token issue` prints the secret **and says so**, rather than producing something
|
||||
that looks complete and cannot be used.
|
||||
**A token now carries three of its four parts**, and is one line of base64 a person can copy. The
|
||||
signing key is real: an Ed25519 key this control plane generates once and keeps, whose public half
|
||||
travels in every token. A node believes a declaration because it carries a signature that key made
|
||||
— and pinning only the broker would not do, because it would make the control plane's authority
|
||||
transitive, so a compromised broker could forge declarations, and since the host applies whatever
|
||||
the link delivers that is the whole machine.
|
||||
|
||||
**Still missing: the broker's address and its certificate fingerprint.** Both are step 5 of the
|
||||
substrate bootstrap and neither exists. `token issue` prints the token **and names what is
|
||||
missing**, rather than producing something that looks complete and cannot be used.
|
||||
|
||||
### Two contexts, and the rule between them is real
|
||||
|
||||
`identity` is the second context and it exists partly to test a claim this repository had made and
|
||||
never checked: that a context reaches only its own store. It holds `MESH_STORE_IDENTITY`;
|
||||
`inventory` holds `MESH_STORE_INVENTORY`; there is no setting that reaches both and no way to ask
|
||||
for one. Run `migrate` with only one and it stops, naming the grant it does not have.
|
||||
|
||||
A token needs a node record from one and a signing key from the other. Neither reads the other's
|
||||
store — the process holding both grants asks each for its part.
|
||||
|
||||
### Where this stops, and why there
|
||||
|
||||
|
||||
Reference in New Issue
Block a user