publish.Publish opens the transfer naming every part's digest and sends only the parts the holder answers it lacks, in chunks of at most 760 KiB (a message under the bus's 1 MiB once base64), in sequence with the last marked; a part whose bytes are not its digest is refused before any of it travels. The seat's messages are mesh-contracts'; the call is the caller's own. Its test takes the place of the vault's get test, merged into the vault's table. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
120 lines
5.4 KiB
Go
120 lines
5.4 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"slices"
|
|
"testing"
|
|
)
|
|
|
|
// The vault's provision is one the controller itself dereferences — every minted credential is
|
|
// sealed with it — so it is delivered by a seat of the mesh's own, and a second provider is a second
|
|
// claimant refused by name rather than a candidate for a pin (novox/hq ADR 0161, issue 106).
|
|
func TestTheVaultsSeatDeliversSecret(t *testing.T) {
|
|
seat, known := SeatNamed("mesh-vault")
|
|
if !known {
|
|
t.Fatal("mesh-vault is not in the mesh's own set")
|
|
}
|
|
if seat.Scope != ScopeMesh || seat.Delivers != "secret" {
|
|
t.Fatalf("mesh-vault is %s-scoped and delivers %q; one per mesh, delivering secret", seat.Scope, seat.Delivers)
|
|
}
|
|
vault := Manifest{Module: "mesh-vault", Provides: []Offer{{Name: "secret", Scope: ScopeMesh}},
|
|
Claims: []Claim{{Name: "mesh-vault", Scope: ScopeMesh}}}
|
|
if err := CanHold(vault, seat); err != nil {
|
|
t.Fatalf("the vault, claiming its seat and providing secret, was refused: %v", err)
|
|
}
|
|
another := Manifest{Module: "other-vault", Provides: []Offer{{Name: "secret", Scope: ScopeMesh}}}
|
|
if err := CanHold(another, seat); err == nil {
|
|
t.Fatal("a provider of secret that does not claim the seat was allowed to hold it")
|
|
}
|
|
}
|
|
|
|
// The vault takes values in and rotates them (novox/hq ADR 0349 clauses 8 to 10, issue 675): the seat promises
|
|
// secret-ask, issued and rotate, each with the arguments of its steps; a holder serving them is accepted, and,
|
|
// optional while the vault that runs does not, so is one that does not yet. And the move of the controller's secrets
|
|
// (design 57's V3, issue 676): move-in, asked in two steps, never a value in the clear. accept and move-asks, which no
|
|
// holder served and nothing called, are not the seat's (ADR 0620 §3); retire is, optional (design 32).
|
|
func TestTheVaultsSeatPromisesItsIntakeVerbs(t *testing.T) {
|
|
seat, _ := SeatNamed("mesh-vault")
|
|
want := map[string][]string{
|
|
// get, on request and reply (ADR 0349 §11, issue 674); a vault not serving it yet still holds the seat
|
|
// (TestTheVaultsSeatDeliversSecret).
|
|
"get": {"name", "public_key"},
|
|
"secret-ask": {"node", "module", "name", "desk", "asked_by", "outcome", "call", "sealed"},
|
|
"issued": {"provision", "provider", "module", "call", "sealed", "holders"},
|
|
"rotate": {"node", "module", "name", "provider", "provision", "by"},
|
|
// 774/S37/AC1: the enrolment verbs mesh-enrolment asks (ADR 0587 §3, §5).
|
|
"enrolment-token": {"node", "expires", "sealed", "drop", "tunnel"},
|
|
"node-account": {"node", "sealing_key", "keys_digest", "retire"},
|
|
"sign": {"kind", "node", "serving_key", "reply"},
|
|
// 774/S38: the rotation verbs (ADR 0583 §2, §3, §5).
|
|
"rotate-node": {"node", "why", "act"},
|
|
"node-account-confirm": {},
|
|
"abandon": {"node"},
|
|
"move-in": {"node", "module", "name", "provider", "provision", "consumer", "consumer_module", "local", "origin",
|
|
"generation", "made_at", "call", "sealed", "read_back_to"},
|
|
// And own (issue 1126): a machine's node-engine asks its own machine's secrets, sealed to its stated key.
|
|
"own": {"credentials"},
|
|
"retire": {"node", "module", "name", "warrant"},
|
|
// Issue 786: node-key, optional while the vault that runs does not serve it.
|
|
"node-key": {"key"},
|
|
}
|
|
for _, gone := range []string{"accept", "move-asks"} {
|
|
for _, v := range seat.Serves {
|
|
if v.Name == gone {
|
|
t.Errorf("the vault's seat still promises %s, which no holder serves (ADR 0620 §3)", gone)
|
|
}
|
|
}
|
|
}
|
|
for name, args := range want {
|
|
var verb *Verb
|
|
for i := range seat.Serves {
|
|
if seat.Serves[i].Name == name {
|
|
verb = &seat.Serves[i]
|
|
}
|
|
}
|
|
if verb == nil {
|
|
t.Errorf("the vault's seat does not promise %s", name)
|
|
continue
|
|
}
|
|
if !verb.Optional {
|
|
t.Errorf("%s is not optional while the vault that runs does not serve it", name)
|
|
}
|
|
props, _ := verb.Input["properties"].(map[string]any)
|
|
for _, arg := range args {
|
|
if props[arg] == nil {
|
|
t.Errorf("%s takes no %s", name, arg)
|
|
}
|
|
}
|
|
if props["value"] != nil {
|
|
t.Errorf("%s takes a value in the clear", name)
|
|
}
|
|
// The first tunnel the token carries (ADR 0587 §3, insight of 2026-11-02): an object of the hub's key, its endpoint
|
|
// and the node's address.
|
|
if name == "enrolment-token" {
|
|
tunnel, _ := props["tunnel"].(map[string]any)
|
|
if tunnel["type"] != "object" || !slices.Equal(tunnel["required"].([]string), []string{"address", "hub_endpoint", "hub_key", "key", "range"}) {
|
|
t.Errorf("enrolment-token's tunnel is %v", tunnel)
|
|
}
|
|
}
|
|
}
|
|
// The holders `issued` names (issue 749) are a list of {node, module}.
|
|
for _, verb := range seat.Serves {
|
|
if verb.Name != "issued" {
|
|
continue
|
|
}
|
|
props, _ := verb.Input["properties"].(map[string]any)
|
|
holders, _ := props["holders"].(map[string]any)
|
|
items, _ := holders["items"].(map[string]any)
|
|
each, _ := items["properties"].(map[string]any)
|
|
if holders["type"] != "array" || each["node"] == nil || each["module"] == nil {
|
|
t.Errorf("issued's holders are %v", holders)
|
|
}
|
|
}
|
|
serving := Manifest{Module: "mesh-vault", Provides: []Offer{{Name: "secret", Scope: ScopeMesh}},
|
|
Claims: []Claim{{Name: "mesh-vault", Scope: ScopeMesh,
|
|
Serves: []string{"get", "secret-ask", "issued", "rotate", "sign", "enrolment-token", "node-account",
|
|
"rotate-node", "node-account-confirm", "abandon", "move-in", "node-key", "own"}}}}
|
|
if err := CanHold(serving, seat); err != nil {
|
|
t.Fatalf("the vault serving its verbs was refused: %v", err)
|
|
}
|
|
}
|