Add the vault's enrolment verbs to its seat, so mesh-enrolment asks the vault to mint and sign and holds nothing
ADR 0587 §3 and §5 (changing ADR 0561 §5) have the vault make the token and its hash (enrolment-token), answer it sealed to the operator key for tokens to relay (sealed, §8), drop both at every end (drop), and mint, answer again and retire a joining node's account (node-account), with mesh-enrolment their only caller; sign names mesh-enrolment for a joining node's certificate (with the serving key it presented, since the node has no record yet) and for a redeem answer. Both verbs optional until the vault that runs serves them. Their answers' shapes are in go/delivery. Tested as rows of TestTheVaultsSeatPromisesItsIntakeVerbs, no new test function. (issue 774, 774/S37) Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
+28
-6
@@ -233,13 +233,35 @@ var defaultSeats = append([]Seat{
|
||||
"controller's, never anew for one the vault holds a value or an adopted hash for. Never the value.",
|
||||
Input: schema(map[string]string{"user": "the bus user, e.g. ace.mesh-build-go"}, []string{"user"})},
|
||||
{Name: "sign", Optional: true, Description: "Sign with the vault's key or authority (novox/hq ADR 0550 §2): " +
|
||||
"kind node-certificate signs the certificate of the calling machine's internal name for the serving key " +
|
||||
"its record states, built from bus state alone and asked by that machine's node-engine only; kind " +
|
||||
"enrolment signs an enrolment reply under its own context, for a caller the adoption record names. " +
|
||||
"Answers the certificate or the signature and the signer's fingerprint, and says signed; never a key.",
|
||||
"kind node-certificate signs the certificate of a machine's internal name, asked by that machine's " +
|
||||
"node-engine for the serving key its record states, built from bus state alone, or by mesh-enrolment for " +
|
||||
"a node it enrols, for the serving key the node presented at redeem (ADR 0587 §3); kind enrolment signs a " +
|
||||
"redeem answer under its own context, for mesh-enrolment alone. Answers the certificate or the signature " +
|
||||
"and the signer's fingerprint, and says signed; never a key.",
|
||||
Input: schema(map[string]string{"kind": "node-certificate or enrolment",
|
||||
"node": "the machine, for a node certificate: the caller's own",
|
||||
"reply": "the enrolment reply's bytes in base64, for an enrolment"}, []string{"kind"})},
|
||||
"node": "the machine, for a node certificate: the caller's own, or the node mesh-enrolment enrols",
|
||||
"serving_key": "from mesh-enrolment: the serving key's public half the enrolling node presented",
|
||||
"reply": "the redeem answer's bytes in base64, for an enrolment"}, []string{"kind"})},
|
||||
// Enrolment (novox/hq ADR 0561 §5, ADR 0587 §3, §5; issue 774): mesh-enrolment is the only caller the vault's
|
||||
// manifest names for these two.
|
||||
{Name: "enrolment-token", Optional: true, Description: "Make a one-time token for a node mesh-enrolment " +
|
||||
"issues on the operator's warrant: its secret kept with its copy sealed to the operator key and its hash " +
|
||||
"written as the bus user enrol.<node>. Answers only what the joining machine must trust (the bus's " +
|
||||
"address, the bus certificate's fingerprint, the vault's public signing key), never the token; with " +
|
||||
"sealed, the token sealed to the operator key, for tokens to relay; with drop, the token and its hash " +
|
||||
"dropped, at the enrolment's end. Asked by mesh-enrolment alone, only from the lease write.",
|
||||
Input: schema(map[string]string{"node": "the node the token is for",
|
||||
"expires": "when the token expires (RFC 3339), as the enrolment's key says",
|
||||
"sealed": "\"true\": answer the token sealed to the operator key",
|
||||
"drop": "\"true\": drop the node's token and its hash"}, []string{"node"}, "sealed", "drop")},
|
||||
{Name: "node-account", Optional: true, Description: "Mint a joining node's bus account node.<node>, sealed " +
|
||||
"to the sealing key it presented, and write its hash; asked again for the same node and keys, answers the " +
|
||||
"account it already minted; with retire, the account it minted for a revoked enrolment retired. Writes the " +
|
||||
"node's node-logins key naming node.<node>. Asked by mesh-enrolment alone. Never the value in the clear.",
|
||||
Input: schema(map[string]string{"node": "the joining node",
|
||||
"sealing_key": "the sealing key's public half the node presented, which the account is sealed to",
|
||||
"keys_digest": "the digest of every public key the node presented (delivery.KeysDigest)",
|
||||
"retire": "\"true\": retire the account minted for this node"}, []string{"node"}, "retire")},
|
||||
}},
|
||||
// The owner of every config value (novox/hq ADR 0348 §9, design 57 K3): its holder alone writes the buckets
|
||||
// mesh-config_values, _resolved, _predefined and _proposals, and serves the one verb `config` (§1, §10).
|
||||
|
||||
@@ -65,6 +65,10 @@ func TestTheVaultsSeatPromisesItsIntakeVerbs(t *testing.T) {
|
||||
"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"},
|
||||
"node-account": {"node", "sealing_key", "keys_digest", "retire"},
|
||||
"sign": {"kind", "node", "serving_key", "reply"},
|
||||
}
|
||||
for name, args := range want {
|
||||
var verb *Verb
|
||||
@@ -105,7 +109,7 @@ func TestTheVaultsSeatPromisesItsIntakeVerbs(t *testing.T) {
|
||||
}
|
||||
serving := Manifest{Module: "mesh-vault", Provides: []Offer{{Name: "secret", Scope: ScopeMesh}},
|
||||
Claims: []Claim{{Name: "mesh-vault", Scope: ScopeMesh,
|
||||
Serves: []string{"get", "accept", "secret-ask", "issued", "rotate"}}}}
|
||||
Serves: []string{"get", "accept", "secret-ask", "issued", "rotate", "sign", "enrolment-token", "node-account"}}}}
|
||||
if err := CanHold(serving, seat); err != nil {
|
||||
t.Fatalf("the vault serving its verbs was refused: %v", err)
|
||||
}
|
||||
|
||||
@@ -238,3 +238,31 @@ func (a RedeemAnswer) SignedBytes() []byte {
|
||||
raw, _ := json.Marshal(a)
|
||||
return raw
|
||||
}
|
||||
|
||||
// **The vault's enrolment verbs** (ADR 0561 §5, ADR 0587 §3, §5), asked by mesh-enrolment alone.
|
||||
const (
|
||||
EnrolmentTokenVerb = "enrolment-token"
|
||||
NodeAccountVerb = "node-account"
|
||||
)
|
||||
|
||||
// EnrolmentTokenAnswer is what enrolment-token answers: what the joining machine must trust (the bus's address, its
|
||||
// certificate's fingerprint, the vault's public signing key in base64), never the token; Sealed holds the token sealed
|
||||
// to the operator key when asked with sealed, relayed by tokens as it is; Dropped says a drop left neither the token
|
||||
// nor its hash.
|
||||
type EnrolmentTokenAnswer struct {
|
||||
Node string `json:"node"`
|
||||
Broker string `json:"broker,omitempty"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
Signer string `json:"signer,omitempty"`
|
||||
Sealed string `json:"sealed,omitempty"`
|
||||
Dropped bool `json:"dropped,omitempty"`
|
||||
}
|
||||
|
||||
// NodeAccountAnswer is what node-account answers: the login minted, the account sealed to the sealing key the node
|
||||
// presented (base64), never in the clear; Retired says a retire left no account.
|
||||
type NodeAccountAnswer struct {
|
||||
Node string `json:"node"`
|
||||
Login string `json:"login,omitempty"`
|
||||
Account string `json:"account,omitempty"`
|
||||
Retired bool `json:"retired,omitempty"`
|
||||
}
|
||||
|
||||
@@ -48,8 +48,8 @@ const (
|
||||
|
||||
// KindNodeCertificate is a node's certificate for its internal name, asked by that node's node-engine alone.
|
||||
KindNodeCertificate = "node-certificate"
|
||||
// KindEnrolment is an enrolment reply, signed under its own context; no caller is named until adoption's record
|
||||
// (ADR 0561 §5).
|
||||
// KindEnrolment is a redeem answer (RedeemAnswer), signed under its own context for mesh-enrolment alone (ADR 0587
|
||||
// §3, §6).
|
||||
KindEnrolment = "enrolment"
|
||||
// KindAuthority is the vault's own authority certificate, which the vault signs for itself.
|
||||
KindAuthority = "authority"
|
||||
|
||||
Reference in New Issue
Block a user