diff --git a/go/seats/meshvault/contract.go b/go/seats/meshvault/contract.go index e5beead..14bd5c1 100644 --- a/go/seats/meshvault/contract.go +++ b/go/seats/meshvault/contract.go @@ -250,7 +250,22 @@ type SignInput struct { } // SignAnswer is what sign answers. -type SignAnswer = map[string]any +type SignAnswer struct { + // the authority's certificate that signed it, as PEM + Authority string `json:"authority,omitempty"` + // the node certificate as PEM, for a node certificate + Certificate string `json:"certificate,omitempty"` + // sha256: of the bytes signed + Digest string `json:"digest"` + // node-certificate or enrolment, as asked + Kind string `json:"kind"` + // the machine, for a node certificate + Node string `json:"node,omitempty"` + // the signature in base64, for an enrolment + Signature string `json:"signature,omitempty"` + // the signer's fingerprint + Signer string `json:"signer"` +} // EnrolmentTokenInputTunnel is the node's first tunnel, built by mesh-enrolment at issue and carried in the token: the hub's public key, the hub's endpoint, the node's tunnel address, its own tunnel key and the network's range. type EnrolmentTokenInputTunnel struct { @@ -281,7 +296,20 @@ type EnrolmentTokenInput struct { } // EnrolmentTokenAnswer is what enrolment-token answers. -type EnrolmentTokenAnswer = map[string]any +type EnrolmentTokenAnswer struct { + // the bus's address the machine dials + Broker string `json:"broker,omitempty"` + // a drop left neither the token nor its hash + Dropped bool `json:"dropped,omitempty"` + // the fingerprint of the bus's certificate + Fingerprint string `json:"fingerprint,omitempty"` + // the node the token is for + Node string `json:"node"` + // the token sealed to the operator key, when asked sealed, relayed as it is + Sealed string `json:"sealed,omitempty"` + // the vault's public signing key in base64 + Signer string `json:"signer,omitempty"` +} // NodeAccountInput is what node-account takes. type NodeAccountInput struct { @@ -296,7 +324,16 @@ type NodeAccountInput struct { } // NodeAccountAnswer is what node-account answers. -type NodeAccountAnswer = map[string]any +type NodeAccountAnswer struct { + // the account sealed to the node's sealing key, in base64 + Account string `json:"account,omitempty"` + // the login minted + Login string `json:"login,omitempty"` + // the joining node + Node string `json:"node"` + // a retire left no account + Retired bool `json:"retired,omitempty"` +} // RotateNodeInput is what rotate-node takes. type RotateNodeInput struct { @@ -347,6 +384,22 @@ type RetireAnswer struct { Retired bool `json:"retired"` } +// SignedHolderEvent is the body of the holder's event signed. +type SignedHolderEvent struct { + // when it was signed, RFC 3339 + At string `json:"at"` + // the caller the vault proved + Caller string `json:"caller"` + // sha256: of the bytes signed + Digest string `json:"digest"` + // node-certificate or enrolment + Kind string `json:"kind"` + // the machine, for a node certificate + Node string `json:"node,omitempty"` + // the signer's fingerprint + Signer string `json:"signer"` +} + // OwnRetiredHolderEvent is the body of the holder's event own-retired. type OwnRetiredHolderEvent struct { // the ask's id diff --git a/go/seats/meshvault/contract_test.go b/go/seats/meshvault/contract_test.go index 63f54dc..a358263 100644 --- a/go/seats/meshvault/contract_test.go +++ b/go/seats/meshvault/contract_test.go @@ -29,8 +29,8 @@ func TestFixtures(t *testing.T) { decode: func(b []byte) (any, error) { var v map[string]any; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.mod\\.[a-z0-9-]+\\.event\\.own-replaced$")}, {name: "account-issued under the vault's own subjects", subject: "mesh.mod.mesh-vault.event.account-issued", given: "{}", wire: "{}", decode: func(b []byte) (any, error) { var v map[string]any; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.mod\\.[a-z0-9-]+\\.event\\.account-issued$")}, - {name: "signed under the vault's own subjects", subject: "mesh.mod.mesh-vault.event.signed", given: "{}", wire: "{}", - decode: func(b []byte) (any, error) { var v map[string]any; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.mod\\.[a-z0-9-]+\\.event\\.signed$")}, + {name: "signed under the vault's own subjects", subject: "mesh.mod.mesh-vault.event.signed", given: "{\"at\":\"2026-11-04T13:00:00Z\",\"caller\":\"mesh-enrolment\",\"digest\":\"sha256:0000000000000000000000000000000000000000000000000000000000000000\",\"kind\":\"enrolment\",\"signer\":\"sha256:1111111111111111111111111111111111111111111111111111111111111111\"}", wire: "{\"at\":\"2026-11-04T13:00:00Z\",\"caller\":\"mesh-enrolment\",\"digest\":\"sha256:0000000000000000000000000000000000000000000000000000000000000000\",\"kind\":\"enrolment\",\"signer\":\"sha256:1111111111111111111111111111111111111111111111111111111111111111\"}", + decode: func(b []byte) (any, error) { var v SignedHolderEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.mod\\.[a-z0-9-]+\\.event\\.signed$")}, {name: "rotation-begun under the vault's own subjects", subject: "mesh.mod.mesh-vault.event.rotation-begun", given: "{}", wire: "{}", decode: func(b []byte) (any, error) { var v map[string]any; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.mod\\.[a-z0-9-]+\\.event\\.rotation-begun$")}, {name: "node-account-ready under the vault's own subjects", subject: "mesh.mod.mesh-vault.event.node-account-ready", given: "{}", wire: "{}", @@ -41,6 +41,12 @@ func TestFixtures(t *testing.T) { decode: func(b []byte) (any, error) { var v OwnRetiredHolderEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.mod\\.[a-z0-9-]+\\.event\\.own-retired$")}, {name: "retire on a warrant", subject: "", given: "{\"ask\":\"a1\",\"module\":\"messenger\",\"name\":\"token\",\"node\":\"ace\",\"warrant\":\"w-1\"}", wire: "{\"ask\":\"a1\",\"module\":\"messenger\",\"name\":\"token\",\"node\":\"ace\",\"warrant\":\"w-1\"}", decode: func(b []byte) (any, error) { var v RetireInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil}, + {name: "a node certificate signed", subject: "", given: "{\"authority\":\"-----BEGIN CERTIFICATE-----\",\"certificate\":\"-----BEGIN CERTIFICATE-----\",\"digest\":\"sha256:0000000000000000000000000000000000000000000000000000000000000000\",\"kind\":\"node-certificate\",\"node\":\"ace\",\"signer\":\"sha256:1111111111111111111111111111111111111111111111111111111111111111\"}", wire: "{\"authority\":\"-----BEGIN CERTIFICATE-----\",\"certificate\":\"-----BEGIN CERTIFICATE-----\",\"digest\":\"sha256:0000000000000000000000000000000000000000000000000000000000000000\",\"kind\":\"node-certificate\",\"node\":\"ace\",\"signer\":\"sha256:1111111111111111111111111111111111111111111111111111111111111111\"}", + decode: func(b []byte) (any, error) { var v SignAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil}, + {name: "an enrolment token's trust", subject: "", given: "{\"broker\":\"tls://192.0.2.1:4222\",\"fingerprint\":\"sha256:2222222222222222222222222222222222222222222222222222222222222222\",\"node\":\"ace\",\"signer\":\"AAAA\"}", wire: "{\"broker\":\"tls://192.0.2.1:4222\",\"fingerprint\":\"sha256:2222222222222222222222222222222222222222222222222222222222222222\",\"node\":\"ace\",\"signer\":\"AAAA\"}", + decode: func(b []byte) (any, error) { var v EnrolmentTokenAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil}, + {name: "a node account retired", subject: "", given: "{\"node\":\"ace\",\"retired\":true}", wire: "{\"node\":\"ace\",\"retired\":true}", + decode: func(b []byte) (any, error) { var v NodeAccountAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil}, } for _, c := range cases { v, err := c.decode([]byte(c.given)) diff --git a/go/seats/seats.go b/go/seats/seats.go index fb51082..6b5673a 100644 --- a/go/seats/seats.go +++ b/go/seats/seats.go @@ -197,13 +197,13 @@ func List() []Seat { Output: map[string]any{"description": "the holder's answer", "type": "object"}}, {Name: "sign", Description: "Sign with the vault's key or authority (novox/hq ADR 0550 §2): 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.", Optional: true, Input: map[string]any{"properties": map[string]any{"kind": map[string]any{"description": "node-certificate or enrolment", "type": "string"}, "node": map[string]any{"description": "the machine, for a node certificate: the caller's own, or the node mesh-enrolment enrols", "type": "string"}, "reply": map[string]any{"description": "the redeem answer's bytes in base64, for an enrolment", "type": "string"}, "serving_key": map[string]any{"description": "from mesh-enrolment: the serving key's public half the enrolling node presented", "type": "string"}}, "required": []string{"kind"}, "type": "object"}, - Output: map[string]any{"description": "the holder's answer", "type": "object"}}, + Output: map[string]any{"description": "for a node certificate the certificate as PEM and the authority that signed it, for an enrolment the signature in base64; always the signer's fingerprint and the digest; never a key's private half", "properties": map[string]any{"authority": map[string]any{"description": "the authority's certificate that signed it, as PEM", "type": "string"}, "certificate": map[string]any{"description": "the node certificate as PEM, for a node certificate", "type": "string"}, "digest": map[string]any{"description": "sha256: of the bytes signed", "type": "string"}, "kind": map[string]any{"description": "node-certificate or enrolment, as asked", "type": "string"}, "node": map[string]any{"description": "the machine, for a node certificate", "type": "string"}, "signature": map[string]any{"description": "the signature in base64, for an enrolment", "type": "string"}, "signer": map[string]any{"description": "the signer's fingerprint", "type": "string"}}, "required": []string{"kind", "signer", "digest"}, "type": "object"}}, {Name: "enrolment-token", 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.. 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. The token carries the first tunnel mesh-enrolment built at issue (ADR 0587 §3, insight of 2026-11-02).", Optional: true, Input: map[string]any{"properties": map[string]any{"drop": map[string]any{"description": "\"true\": drop the node's token and its hash", "enum": []string{"true", "false"}, "type": "string"}, "expires": map[string]any{"description": "when the token expires (RFC 3339), as the enrolment's key says", "type": "string"}, "node": map[string]any{"description": "the node the token is for", "type": "string"}, "sealed": map[string]any{"description": "\"true\": answer the token sealed to the operator key", "enum": []string{"true", "false"}, "type": "string"}, "tunnel": map[string]any{"additionalProperties": false, "description": "the node's first tunnel, built by mesh-enrolment at issue and carried in the token: the hub's public key, the hub's endpoint, the node's tunnel address, its own tunnel key and the network's range", "properties": map[string]any{"address": map[string]any{"description": "the joining node's tunnel address", "type": "string"}, "hub_endpoint": map[string]any{"description": "the hub's endpoint, host:port", "type": "string"}, "hub_key": map[string]any{"description": "the hub's tunnel public key", "type": "string"}, "key": map[string]any{"description": "the joining machine's own tunnel public key, as given at issue", "type": "string"}, "range": map[string]any{"description": "the private network's range, a prefix", "type": "string"}}, "required": []string{"address", "hub_endpoint", "hub_key", "key", "range"}, "type": "object"}}, "required": []string{"node"}, "type": "object"}, - Output: map[string]any{"description": "the holder's answer", "type": "object"}}, + Output: map[string]any{"description": "what the joining machine must trust, never the token: the bus's address, its certificate's fingerprint and the vault's public signing key; the token sealed to the operator key when asked sealed; or that a drop left neither the token nor its hash", "properties": map[string]any{"broker": map[string]any{"description": "the bus's address the machine dials", "type": "string"}, "dropped": map[string]any{"description": "a drop left neither the token nor its hash", "type": "boolean"}, "fingerprint": map[string]any{"description": "the fingerprint of the bus's certificate", "type": "string"}, "node": map[string]any{"description": "the node the token is for", "type": "string"}, "sealed": map[string]any{"description": "the token sealed to the operator key, when asked sealed, relayed as it is", "type": "string"}, "signer": map[string]any{"description": "the vault's public signing key in base64", "type": "string"}}, "required": []string{"node"}, "type": "object"}}, {Name: "node-account", Description: "Mint a joining node's bus account 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.. Asked by mesh-enrolment alone. Never the value in the clear.", Optional: true, Input: map[string]any{"properties": map[string]any{"keys_digest": map[string]any{"description": "the digest of every public key the node presented (delivery.KeysDigest)", "type": "string"}, "node": map[string]any{"description": "the joining node", "type": "string"}, "retire": map[string]any{"description": "\"true\": retire the account minted for this node", "enum": []string{"true", "false"}, "type": "string"}, "sealing_key": map[string]any{"description": "the sealing key's public half the node presented, which the account is sealed to", "type": "string"}}, "required": []string{"node"}, "type": "object"}, - Output: map[string]any{"description": "the holder's answer", "type": "object"}}, + Output: map[string]any{"description": "the login minted and the account sealed to the sealing key the node presented, never in the clear; or that a retire left no account", "properties": map[string]any{"account": map[string]any{"description": "the account sealed to the node's sealing key, in base64", "type": "string"}, "login": map[string]any{"description": "the login minted", "type": "string"}, "node": map[string]any{"description": "the joining node", "type": "string"}, "retired": map[string]any{"description": "a retire left no account", "type": "boolean"}}, "required": []string{"node"}, "type": "object"}}, {Name: "rotate-node", Description: "Begin a node's account rotation to its other login: answered only for a call carrying a hand act recorded through mesh-hand-acts, which the vault reads back by its id. Makes the other login's value sealed to that node, writes it rotating in node-logins and says rotation-begun; says node-account-ready once the bus node's have shows a list holding both logins. One node at a time across the mesh, the bus node last: a call while a rotation runs waits until it is done.", Optional: true, Input: map[string]any{"properties": map[string]any{"act": map[string]any{"description": "the id of the hand act recorded for it in mesh-hand-acts", "type": "string"}, "node": map[string]any{"description": "the node whose account rotates", "type": "string"}, "why": map[string]any{"description": "why, in a sentence", "type": "string"}}, "required": []string{"node", "why", "act"}, "type": "object"}, Output: map[string]any{"description": "the holder's answer", "type": "object"}}, diff --git a/seats/mesh-vault/fixtures/events.json b/seats/mesh-vault/fixtures/events.json index 342fd91..ef46519 100644 --- a/seats/mesh-vault/fixtures/events.json +++ b/seats/mesh-vault/fixtures/events.json @@ -39,7 +39,13 @@ "name": "signed under the vault's own subjects", "of": "holder-event:signed", "subject": "mesh.mod.mesh-vault.event.signed", - "given": {} + "given": { + "caller": "mesh-enrolment", + "kind": "enrolment", + "digest": "sha256:0000000000000000000000000000000000000000000000000000000000000000", + "signer": "sha256:1111111111111111111111111111111111111111111111111111111111111111", + "at": "2026-11-04T13:00:00Z" + } }, { "name": "rotation-begun under the vault's own subjects", diff --git a/seats/mesh-vault/fixtures/verbs.json b/seats/mesh-vault/fixtures/verbs.json index a031109..49b29f3 100644 --- a/seats/mesh-vault/fixtures/verbs.json +++ b/seats/mesh-vault/fixtures/verbs.json @@ -9,5 +9,35 @@ "warrant": "w-1", "ask": "a1" } + }, + { + "name": "a node certificate signed", + "of": "verb:sign.answer", + "given": { + "kind": "node-certificate", + "node": "ace", + "certificate": "-----BEGIN CERTIFICATE-----", + "authority": "-----BEGIN CERTIFICATE-----", + "signer": "sha256:1111111111111111111111111111111111111111111111111111111111111111", + "digest": "sha256:0000000000000000000000000000000000000000000000000000000000000000" + } + }, + { + "name": "an enrolment token's trust", + "of": "verb:enrolment-token.answer", + "given": { + "node": "ace", + "broker": "tls://192.0.2.1:4222", + "fingerprint": "sha256:2222222222222222222222222222222222222222222222222222222222222222", + "signer": "AAAA" + } + }, + { + "name": "a node account retired", + "of": "verb:node-account.answer", + "given": { + "node": "ace", + "retired": true + } } ] diff --git a/seats/mesh-vault/seat.json b/seats/mesh-vault/seat.json index 4cb5d2c..5a115a6 100644 --- a/seats/mesh-vault/seat.json +++ b/seats/mesh-vault/seat.json @@ -327,7 +327,42 @@ }, "answer": { "type": "object", - "description": "the holder's answer" + "description": "for a node certificate the certificate as PEM and the authority that signed it, for an enrolment the signature in base64; always the signer's fingerprint and the digest; never a key's private half", + "properties": { + "kind": { + "type": "string", + "description": "node-certificate or enrolment, as asked" + }, + "node": { + "type": "string", + "description": "the machine, for a node certificate" + }, + "certificate": { + "type": "string", + "description": "the node certificate as PEM, for a node certificate" + }, + "authority": { + "type": "string", + "description": "the authority's certificate that signed it, as PEM" + }, + "signature": { + "type": "string", + "description": "the signature in base64, for an enrolment" + }, + "signer": { + "type": "string", + "description": "the signer's fingerprint" + }, + "digest": { + "type": "string", + "description": "sha256: of the bytes signed" + } + }, + "required": [ + "kind", + "signer", + "digest" + ] } }, { @@ -402,7 +437,36 @@ }, "answer": { "type": "object", - "description": "the holder's answer" + "description": "what the joining machine must trust, never the token: the bus's address, its certificate's fingerprint and the vault's public signing key; the token sealed to the operator key when asked sealed; or that a drop left neither the token nor its hash", + "properties": { + "node": { + "type": "string", + "description": "the node the token is for" + }, + "broker": { + "type": "string", + "description": "the bus's address the machine dials" + }, + "fingerprint": { + "type": "string", + "description": "the fingerprint of the bus's certificate" + }, + "signer": { + "type": "string", + "description": "the vault's public signing key in base64" + }, + "sealed": { + "type": "string", + "description": "the token sealed to the operator key, when asked sealed, relayed as it is" + }, + "dropped": { + "type": "boolean", + "description": "a drop left neither the token nor its hash" + } + }, + "required": [ + "node" + ] } }, { @@ -439,7 +503,28 @@ }, "answer": { "type": "object", - "description": "the holder's answer" + "description": "the login minted and the account sealed to the sealing key the node presented, never in the clear; or that a retire left no account", + "properties": { + "node": { + "type": "string", + "description": "the joining node" + }, + "login": { + "type": "string", + "description": "the login minted" + }, + "account": { + "type": "string", + "description": "the account sealed to the node's sealing key, in base64" + }, + "retired": { + "type": "boolean", + "description": "a retire left no account" + } + }, + "required": [ + "node" + ] } }, { @@ -586,7 +671,44 @@ }, { "name": "signed", - "subject": "mesh.mod..event.signed" + "subject": "mesh.mod..event.signed", + "body": { + "type": "object", + "description": "who asked, the kind, the node and the digest of what was signed; never a key", + "properties": { + "caller": { + "type": "string", + "description": "the caller the vault proved" + }, + "kind": { + "type": "string", + "description": "node-certificate or enrolment" + }, + "node": { + "type": "string", + "description": "the machine, for a node certificate" + }, + "digest": { + "type": "string", + "description": "sha256: of the bytes signed" + }, + "signer": { + "type": "string", + "description": "the signer's fingerprint" + }, + "at": { + "type": "string", + "description": "when it was signed, RFC 3339" + } + }, + "required": [ + "caller", + "kind", + "digest", + "signer", + "at" + ] + } }, { "name": "rotation-begun", diff --git a/ts/src/mesh-vault.ts b/ts/src/mesh-vault.ts index 7c51701..c78145a 100644 --- a/ts/src/mesh-vault.ts +++ b/ts/src/mesh-vault.ts @@ -234,7 +234,22 @@ export interface SignInput { } /** is what sign answers. */ -export type SignAnswer = Record; +export interface SignAnswer { + /** the authority's certificate that signed it, as PEM */ + "authority"?: string; + /** the node certificate as PEM, for a node certificate */ + "certificate"?: string; + /** sha256: of the bytes signed */ + "digest": string; + /** node-certificate or enrolment, as asked */ + "kind": string; + /** the machine, for a node certificate */ + "node"?: string; + /** the signature in base64, for an enrolment */ + "signature"?: string; + /** the signer's fingerprint */ + "signer": string; +} /** the node's first tunnel, built by mesh-enrolment at issue and carried in the token: the hub's public key, the hub's endpoint, the node's tunnel address, its own tunnel key and the network's range */ export interface EnrolmentTokenInputTunnel { @@ -265,7 +280,20 @@ export interface EnrolmentTokenInput { } /** is what enrolment-token answers. */ -export type EnrolmentTokenAnswer = Record; +export interface EnrolmentTokenAnswer { + /** the bus's address the machine dials */ + "broker"?: string; + /** a drop left neither the token nor its hash */ + "dropped"?: boolean; + /** the fingerprint of the bus's certificate */ + "fingerprint"?: string; + /** the node the token is for */ + "node": string; + /** the token sealed to the operator key, when asked sealed, relayed as it is */ + "sealed"?: string; + /** the vault's public signing key in base64 */ + "signer"?: string; +} /** is what node-account takes. */ export interface NodeAccountInput { @@ -280,7 +308,16 @@ export interface NodeAccountInput { } /** is what node-account answers. */ -export type NodeAccountAnswer = Record; +export interface NodeAccountAnswer { + /** the account sealed to the node's sealing key, in base64 */ + "account"?: string; + /** the login minted */ + "login"?: string; + /** the joining node */ + "node": string; + /** a retire left no account */ + "retired"?: boolean; +} /** is what rotate-node takes. */ export interface RotateNodeInput { @@ -331,6 +368,22 @@ export interface RetireAnswer { "retired": boolean; } +/** is the body of the holder's event signed. */ +export interface SignedHolderEvent { + /** when it was signed, RFC 3339 */ + "at": string; + /** the caller the vault proved */ + "caller": string; + /** sha256: of the bytes signed */ + "digest": string; + /** node-certificate or enrolment */ + "kind": string; + /** the machine, for a node certificate */ + "node"?: string; + /** the signer's fingerprint */ + "signer": string; +} + /** is the body of the holder's event own-retired. */ export interface OwnRetiredHolderEvent { /** the ask's id */ diff --git a/ts/src/seats.ts b/ts/src/seats.ts index 746e817..a514e20 100644 --- a/ts/src/seats.ts +++ b/ts/src/seats.ts @@ -1345,7 +1345,42 @@ export const list: readonly Seat[] = [ "name": "sign", "optional": true, "output": { - "description": "the holder's answer", + "description": "for a node certificate the certificate as PEM and the authority that signed it, for an enrolment the signature in base64; always the signer's fingerprint and the digest; never a key's private half", + "properties": { + "authority": { + "description": "the authority's certificate that signed it, as PEM", + "type": "string" + }, + "certificate": { + "description": "the node certificate as PEM, for a node certificate", + "type": "string" + }, + "digest": { + "description": "sha256: of the bytes signed", + "type": "string" + }, + "kind": { + "description": "node-certificate or enrolment, as asked", + "type": "string" + }, + "node": { + "description": "the machine, for a node certificate", + "type": "string" + }, + "signature": { + "description": "the signature in base64, for an enrolment", + "type": "string" + }, + "signer": { + "description": "the signer's fingerprint", + "type": "string" + } + }, + "required": [ + "kind", + "signer", + "digest" + ], "type": "object" } }, @@ -1420,7 +1455,36 @@ export const list: readonly Seat[] = [ "name": "enrolment-token", "optional": true, "output": { - "description": "the holder's answer", + "description": "what the joining machine must trust, never the token: the bus's address, its certificate's fingerprint and the vault's public signing key; the token sealed to the operator key when asked sealed; or that a drop left neither the token nor its hash", + "properties": { + "broker": { + "description": "the bus's address the machine dials", + "type": "string" + }, + "dropped": { + "description": "a drop left neither the token nor its hash", + "type": "boolean" + }, + "fingerprint": { + "description": "the fingerprint of the bus's certificate", + "type": "string" + }, + "node": { + "description": "the node the token is for", + "type": "string" + }, + "sealed": { + "description": "the token sealed to the operator key, when asked sealed, relayed as it is", + "type": "string" + }, + "signer": { + "description": "the vault's public signing key in base64", + "type": "string" + } + }, + "required": [ + "node" + ], "type": "object" } }, @@ -1457,7 +1521,28 @@ export const list: readonly Seat[] = [ "name": "node-account", "optional": true, "output": { - "description": "the holder's answer", + "description": "the login minted and the account sealed to the sealing key the node presented, never in the clear; or that a retire left no account", + "properties": { + "account": { + "description": "the account sealed to the node's sealing key, in base64", + "type": "string" + }, + "login": { + "description": "the login minted", + "type": "string" + }, + "node": { + "description": "the joining node", + "type": "string" + }, + "retired": { + "description": "a retire left no account", + "type": "boolean" + } + }, + "required": [ + "node" + ], "type": "object" } },