From 52ab209fcfc49af1c67a62599bb78988288a4951 Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 5 Nov 2026 15:10:17 +0100 Subject: [PATCH] =?UTF-8?q?seatlisttest=20equals=20the=20contracts=20on=20?= =?UTF-8?q?the=20join=20heads=20(renamed=20seats=20mesh-node-enroller,=20m?= =?UTF-8?q?esh-module-assigner;=20domain;=20B2's=20delivery=20seats);=20th?= =?UTF-8?q?e=20bootstrap=20is=20held=20equal=20to=20it=20by=20a=20test;=20?= =?UTF-8?q?the=20compiled-list=20golden=20is=20gone=20(B7c=20finding=202;?= =?UTF-8?q?=20ADR=200634=20=C2=A79,=20ADR=200636=20=C2=A77;=20issue=201966?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i --- go/catalogue/own_manifest_test.go | 5 +- go/catalogue/seats_test.go | 111 - go/catalogue/test_seats_test.go | 4 +- go/catalogue/testdata/seats-before-move.json | 4810 ----------------- go/catalogue/verbs_test.go | 6 +- go/seatlist/bootstrap/mesh-catalog.json | 141 +- go/seatlist/bootstrap/node-engine.json | 1052 +++- go/seatlist/bootstrap/operator-channel.json | 295 +- go/seatlist/seatlist_test.go | 21 + .../seatlisttest/parts/build-step.json | 721 +++ .../seats/container-image-registry.json | 137 +- .../seatlisttest/seats/mesh-catalog.json | 986 +++- .../seatlisttest/seats/mesh-conditions.json | 563 +- .../seatlisttest/seats/mesh-config.json | 49 + .../seats/mesh-delivery-checker.json | 390 ++ .../seatlisttest/seats/mesh-delivery.json | 346 +- .../seatlisttest/seats/mesh-hand-acts.json | 231 +- .../seatlisttest/seats/mesh-healers.json | 182 +- .../seats/mesh-module-assigner.json | 942 ++++ ...enrolment.json => mesh-node-enroller.json} | 189 +- .../seatlisttest/seats/mesh-placement.json | 138 - .../seatlisttest/seats/mesh-provisioner.json | 387 +- .../seatlisttest/seats/mesh-self-check.json | 129 +- .../seatlisttest/seats/mesh-vault.json | 137 +- .../seatlisttest/seats/mesh-wants.json | 234 +- .../seatlisttest/seats/mesh-watchdogs.json | 155 +- .../seatlisttest/seats/node-build-go.json | 83 +- .../seatlisttest/seats/node-build-image.json | 83 +- .../seatlisttest/seats/node-build-node.json | 83 +- .../seatlisttest/seats/node-engine.json | 1595 ++++++ .../seats/oci-artifact-registry.json | 137 +- .../seatlisttest/seats/operator-channel.json | 295 +- .../seats/the-private-network.json | 15 +- go/testdata/seat-names.txt | 1 - 34 files changed, 9407 insertions(+), 5246 deletions(-) delete mode 100644 go/catalogue/testdata/seats-before-move.json create mode 100644 go/seatlist/seatlisttest/parts/build-step.json create mode 100644 go/seatlist/seatlisttest/seats/mesh-module-assigner.json rename go/seatlist/seatlisttest/seats/{mesh-enrolment.json => mesh-node-enroller.json} (51%) delete mode 100644 go/seatlist/seatlisttest/seats/mesh-placement.json diff --git a/go/catalogue/own_manifest_test.go b/go/catalogue/own_manifest_test.go index 21ae5f8..dae109f 100644 --- a/go/catalogue/own_manifest_test.go +++ b/go/catalogue/own_manifest_test.go @@ -45,8 +45,11 @@ func TestOwnManifestUsesNoWordTheRunningControllerRefuses(t *testing.T) { added = append(added, r) } } + // The two seats the milestone renamed (ADR 0634 §9) are read by their new names; a node-engine built with the baseline + // reads the old ones, and a manifest of the new names is the milestone's to deliver after the engines. + renamed := []string{"seat:mesh-enrolment", "seat:mesh-placement"} for _, b := range baseline { - if !slices.Contains(reads, b) { + if !slices.Contains(reads, b) && !slices.Contains(renamed, b) { t.Errorf("the baseline's %s is no reader capability of this contract", b) } } diff --git a/go/catalogue/seats_test.go b/go/catalogue/seats_test.go index f899561..e4e5217 100644 --- a/go/catalogue/seats_test.go +++ b/go/catalogue/seats_test.go @@ -1,10 +1,6 @@ package catalogue import ( - "encoding/json" - "os" - "reflect" - "slices" "testing" ) @@ -34,111 +30,4 @@ func TestUseSeatsReplacesTheSetButNeverEmptiesIt(t *testing.T) { if len(Seats()) != 1 { t.Fatalf("the working set is %d seats, not the one that was loaded", len(Seats())) } - - // **The compiled set is the generated one, and the move changed nothing a reader sees** (design 26, SD-M3, SDK-8): - // every seat the list mesh-sdk compiled before the move held (testdata, written from 5a38a12) is in the set - // generated from mesh-contracts' definitions, entry for entry, its verbs' optional marks and a kind's code - // included. A seat defined whole may add verbs, events and answers; the vault drops accept and move-asks (ADR 0620 - // §3); three registry seats are new. - UseSeats(DefaultSeats()) - type golden struct { - Seat - Optional []string - ShapeCode []string - } - raw, err := os.ReadFile("testdata/seats-before-move.json") - if err != nil { - t.Fatal(err) - } - var compiled []golden - if err := json.Unmarshal(raw, &compiled); err != nil { - t.Fatal(err) - } - whole := []string{"npm-package-registry", "mesh-broker", "mesh-config", "mesh-vault", "mesh-delivery-checker", "node-engine"} - dropped := map[string]bool{"mesh-vault.accept": true, "mesh-vault.move-asks": true} - // Verbs a seat at the seat list's grain gained in the milestone, each optional until its holders serve it. - addedVerbs := map[string]bool{} - // A verb whose input the whole definition corrected: compose-candidate's input is the boolean the engine and the - // checker read, where the compiled list said a word (engine_compose_candidate_test.go reads it). - retyped := map[string]bool{"node-engine.compose-candidate": true} - // A seat whose provision retired delivers nothing since: the store's, artifact-store (ADR 0632 §2). - retired := map[string]bool{"mesh-artifact-store": true} - norm := func(v any) any { - b, _ := json.Marshal(v) - var out any - _ = json.Unmarshal(b, &out) - return out - } - var names []string - for _, want := range compiled { - names = append(names, want.Name) - got, ok := SeatNamed(want.Name) - if !ok { - t.Errorf("%s left the generated set", want.Name) - continue - } - if retired[want.Name] { - want.Delivers = "" - } - g := golden{Seat: got} - for _, v := range got.Serves { - if v.Optional { - g.Optional = append(g.Optional, v.Name) - } - } - for _, r := range got.Receives { - if r.Shape != nil && (r.Shape.Check != nil || r.Shape.Reads != nil) { - g.ShapeCode = append(g.ShapeCode, r.Kind) - } - } - g.Serves = slices.DeleteFunc(slices.Clone(g.Serves), func(v Verb) bool { return addedVerbs[want.Name+"."+v.Name] }) - g.Optional = slices.DeleteFunc(g.Optional, func(n string) bool { return addedVerbs[want.Name+"."+n] }) - if slices.Contains(whole, want.Name) { - want := &want - byName := map[string]Verb{} - for _, v := range got.Serves { - v.Output = nil - byName[v.Name] = v - } - var kept []Verb - for _, v := range want.Serves { - if dropped[want.Name+"."+v.Name] { - if _, still := byName[v.Name]; still { - t.Errorf("%s still serves %s", want.Name, v.Name) - } - continue - } - kv := byName[v.Name] - if retyped[want.Name+"."+v.Name] { - kv.Input = v.Input - } - kept = append(kept, kv) - } - g.Serves, g.Emits, g.Accepts = kept, want.Emits, want.Accepts - // A verb the whole seat added (new, optional while its holders catch up) is no verb of the compiled list. - g.Optional = slices.DeleteFunc(g.Optional, func(n string) bool { - return !slices.ContainsFunc(want.Serves, func(v Verb) bool { return v.Name == n }) - }) - if len(g.Optional) == 0 { - g.Optional = nil - } - want.Optional = slices.DeleteFunc(want.Optional, func(n string) bool { return dropped[want.Name+"."+n] }) - want.Serves = slices.DeleteFunc(slices.Clone(want.Serves), func(v Verb) bool { return dropped[want.Name+"."+v.Name] }) - } - if !reflect.DeepEqual(norm(g), norm(want)) { - gb, _ := json.Marshal(g) - wb, _ := json.Marshal(want) - t.Errorf("%s is not as the compiled list held it:\n got %s\nwant %s", want.Name, gb, wb) - } - } - var added []string - for _, s := range DefaultSeats() { - if !slices.Contains(names, s.Name) { - added = append(added, s.Name) - } - } - slices.Sort(added) - if want := []string{"container-image-registry", "go-package-registry", "oci-artifact-registry"}; !slices.Equal(added, want) { - t.Errorf("the generated set adds %v, want %v", added, want) - } } diff --git a/go/catalogue/test_seats_test.go b/go/catalogue/test_seats_test.go index 3862dce..4d6531e 100644 --- a/go/catalogue/test_seats_test.go +++ b/go/catalogue/test_seats_test.go @@ -18,8 +18,8 @@ func DefaultSeats() []Seat { return append([]Seat(nil), defaultSeats...) } const ( CatalogSeat = "mesh-catalog" CheckerSeat = "mesh-delivery-checker" - EnrolmentSeat = "mesh-enrolment" - PlacementSeat = "mesh-placement" + EnrolmentSeat = "mesh-node-enroller" + PlacementSeat = "mesh-module-assigner" DeliverySeat = "mesh-delivery" WantsSeat = "mesh-wants" HandActsSeat = "mesh-hand-acts" diff --git a/go/catalogue/testdata/seats-before-move.json b/go/catalogue/testdata/seats-before-move.json deleted file mode 100644 index 0bd3492..0000000 --- a/go/catalogue/testdata/seats-before-move.json +++ /dev/null @@ -1,4810 +0,0 @@ -[ - { - "Name": "mesh-store", - "Scope": "mesh", - "Replicated": false, - "Delivers": "postgres-database", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "databases", - "description": "Every database the store holds, with its on-disk size.", - "input": { - "properties": {}, - "type": "object" - }, - "replaces": [ - "psql -l" - ] - }, - { - "name": "query", - "description": "One read-only statement against one database the store holds.", - "input": { - "properties": { - "database": { - "description": "the database to query", - "type": "string" - }, - "sql": { - "description": "the read-only statement", - "type": "string" - } - }, - "required": [ - "database", - "sql" - ], - "type": "object" - }, - "replaces": [ - "psql" - ] - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0079" - }, - { - "Name": "mesh-broker", - "Scope": "mesh", - "Replicated": false, - "Delivers": "mesh-bus", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": null, - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0079" - }, - { - "Name": "mesh-vault", - "Scope": "mesh", - "Replicated": false, - "Delivers": "secret", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "get", - "description": "One secret, sealed to the public key the asker sends (the sealed box of ADR 0206), with its generation; answered only to an asker the vault allows, as the bus proved it. Never the value in the clear, and never to a private key.", - "input": { - "properties": { - "name": { - "description": "the secret's name", - "type": "string" - }, - "public_key": { - "description": "the asker's X25519 public key as SPKI PEM — never a private key", - "type": "string" - } - }, - "required": [ - "name", - "public_key" - ], - "type": "object" - } - }, - { - "name": "accept", - "description": "Take a value given at the controller's terminal, from the controller alone: name the secret and get a call and a key made for it, then send the call and the value sealed to that key. The vault alone opens it, and never remakes an accepted credential.", - "input": { - "properties": { - "by": { - "description": "who gave it at the terminal", - "type": "string" - }, - "call": { - "description": "the second step: the call the first step answered", - "type": "string" - }, - "module": { - "description": "the module, for a module's own secret", - "type": "string" - }, - "name": { - "description": "the secret's name in the module", - "type": "string" - }, - "node": { - "description": "the machine, for a module's own secret", - "type": "string" - }, - "origin": { - "description": "issued for a bus account the controller minted for a core or moved module and hands at once (ADR 0374, issue 787); left out for a value given at the terminal", - "type": "string" - }, - "provider": { - "description": "the provider's machine, for a provision's credential", - "type": "string" - }, - "provision": { - "description": "the provision", - "type": "string" - }, - "sealed": { - "description": "the second step: the value sealed to the call's key — never the value", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "secret-ask", - "description": "Ask the operator for a module's own secret at a desk, from the controller or a caller the vault's manifest names, named as the bus proved it (novox/hq ADR 0563 §8): the ask is recorded in the vault's store and bounded before the prompt opens, and what is typed is sealed to a key the vault made for the call and opens there alone.", - "input": { - "properties": { - "asked_by": { - "description": "who asked, as the bus named its caller", - "type": "string" - }, - "call": { - "description": "the second step: the call the first step answered", - "type": "string" - }, - "desk": { - "description": "the machine whose prompt asks", - "type": "string" - }, - "module": { - "description": "the module", - "type": "string" - }, - "name": { - "description": "the secret's name in the module", - "type": "string" - }, - "node": { - "description": "the machine the secret is for", - "type": "string" - }, - "outcome": { - "description": "instead of sealed: how the prompt ended", - "type": "string" - }, - "sealed": { - "description": "the second step: the value sealed to the call's key — never the value", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "issued", - "description": "Take the credential a provider's software issued for a provision, from the controller alone, which asks the provider's tool with a key the vault made for the call; kept as the provision's one credential, or, when the tool answers a value per holder named, each as that holder's own; the same value again writes nothing.", - "input": { - "properties": { - "call": { - "description": "the second step: the call the first step answered", - "type": "string" - }, - "holders": { - "description": "the provision's holders from that provider, each {node, module}: its tool may answer a value for each, kept as that holder's own and handed to it alone", - "items": { - "properties": { - "module": { - "description": "the consuming module", - "type": "string" - }, - "node": { - "description": "the holder's machine", - "type": "string" - } - }, - "type": "object" - }, - "type": "array" - }, - "module": { - "description": "the provider's module", - "type": "string" - }, - "provider": { - "description": "the provider's machine", - "type": "string" - }, - "provision": { - "description": "the provision", - "type": "string" - }, - "sealed": { - "description": "the second step: the value sealed to the call's key — never the value", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "rotate", - "description": "Make a secret anew. Refused for a value a provider's software issued and for an accepted credential. Never answers the value.", - "input": { - "properties": { - "by": { - "description": "who asked, when the controller carries the act", - "type": "string" - }, - "module": { - "description": "the module", - "type": "string" - }, - "name": { - "description": "the secret's name in the module", - "type": "string" - }, - "node": { - "description": "the machine, for a module's own secret", - "type": "string" - }, - "provider": { - "description": "the provider's machine", - "type": "string" - }, - "provision": { - "description": "the provision", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "move-in", - "description": "Take one value of the controller's store in, as it is, from the node-engine of a machine it was sealed to and no other caller: name the row, its origin and generation and get a call and a key made for it, then send the call, the value sealed to that key and a key to read back to. Never replaces a value the vault holds; answers what it kept, read back sealed to that key.", - "input": { - "properties": { - "call": { - "description": "the second step: the call the first step answered", - "type": "string" - }, - "consumer": { - "description": "the consumer's machine", - "type": "string" - }, - "consumer_module": { - "description": "the consuming module", - "type": "string" - }, - "generation": { - "description": "the controller's generation for the value", - "type": "string" - }, - "local": { - "description": "the credential's local name, when it has one", - "type": "string" - }, - "made_at": { - "description": "when it was made (RFC 3339)", - "type": "string" - }, - "module": { - "description": "its module", - "type": "string" - }, - "name": { - "description": "its name in the module", - "type": "string" - }, - "node": { - "description": "a module's own secret: its machine", - "type": "string" - }, - "origin": { - "description": "made, accepted or issued, as the controller's store says", - "type": "string" - }, - "provider": { - "description": "a pair credential: the provider's machine", - "type": "string" - }, - "provision": { - "description": "the provision", - "type": "string" - }, - "read_back_to": { - "description": "the second step: a raw X25519 public key the read-back is sealed to", - "type": "string" - }, - "sealed": { - "description": "the second step: the value sealed to the call's key — never the value", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "node-key", - "description": "State this machine's sealing key to the vault, in its own name, from the node-engine of the machine alone: at its start and whenever the key changes. A first or changed key is recorded and the machine's core secrets are said changed; the same key again changes nothing.", - "input": { - "properties": { - "key": { - "description": "the machine's sealing key, a raw X25519 public key in base64", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "own", - "description": "Answer the node-engine of one machine, about its own machine alone (as the bus proved the caller, never an argument), its modules' own secrets and the pair credentials it consumes or provides that it names, each sealed to the key it stated with node-key. A credential the vault does not hold is named missing; never a value in the clear.", - "input": { - "properties": { - "credentials": { - "description": "the pair credentials the machine's composition reads, each {role: consumer|provider, provision, consumer, consumer_module, provider, local}", - "items": { - "type": "object" - }, - "type": "array" - } - }, - "type": "object" - } - }, - { - "name": "move-asks", - "description": "Copy rows of the controller's record of asks at a desk into the vault's, as they are and once, from the controller alone. No value is in any of them.", - "input": { - "properties": { - "asks": { - "description": "the controller's rows: id, node, module, name, desk, asked_by, opened_at, ended_at, outcome", - "items": { - "type": "object" - }, - "type": "array" - } - }, - "type": "object" - } - }, - { - "name": "issue-account", - "description": "Mint one bus account the grants part wants, now (novox/hq ADR 0561 §9): only while the controller's lease is handed away, never a node's account or the controller's, never anew for one the vault holds a value or an adopted hash for. Never the value.", - "input": { - "properties": { - "user": { - "description": "the bus user, e.g. ace.mesh-build-go", - "type": "string" - } - }, - "required": [ - "user" - ], - "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.", - "input": { - "properties": { - "kind": { - "description": "node-certificate or enrolment", - "type": "string" - }, - "node": { - "description": "the machine, for a node certificate: the caller's own, or the node mesh-enrolment enrols", - "type": "string" - }, - "reply": { - "description": "the redeem answer's bytes in base64, for an enrolment", - "type": "string" - }, - "serving_key": { - "description": "from mesh-enrolment: the serving key's public half the enrolling node presented", - "type": "string" - } - }, - "required": [ - "kind" - ], - "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.\u003cnode\u003e. 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).", - "input": { - "properties": { - "drop": { - "description": "\"true\": drop the node's token and its hash", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "expires": { - "description": "when the token expires (RFC 3339), as the enrolment's key says", - "type": "string" - }, - "node": { - "description": "the node the token is for", - "type": "string" - }, - "sealed": { - "description": "\"true\": answer the token sealed to the operator key", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "tunnel": { - "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": { - "address": { - "description": "the joining node's tunnel address", - "type": "string" - }, - "hub_endpoint": { - "description": "the hub's endpoint, host:port", - "type": "string" - }, - "hub_key": { - "description": "the hub's tunnel public key", - "type": "string" - }, - "key": { - "description": "the joining machine's own tunnel public key, as given at issue", - "type": "string" - }, - "range": { - "description": "the private network's range, a prefix", - "type": "string" - } - }, - "required": [ - "address", - "hub_endpoint", - "hub_key", - "key", - "range" - ], - "type": "object" - } - }, - "required": [ - "node" - ], - "type": "object" - } - }, - { - "name": "node-account", - "description": "Mint a joining node's bus account node.\u003cnode\u003e, 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.\u003cnode\u003e. Asked by mesh-enrolment alone. Never the value in the clear.", - "input": { - "properties": { - "keys_digest": { - "description": "the digest of every public key the node presented (delivery.KeysDigest)", - "type": "string" - }, - "node": { - "description": "the joining node", - "type": "string" - }, - "retire": { - "description": "\"true\": retire the account minted for this node", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "sealing_key": { - "description": "the sealing key's public half the node presented, which the account is sealed to", - "type": "string" - } - }, - "required": [ - "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.", - "input": { - "properties": { - "act": { - "description": "the id of the hand act recorded for it in mesh-hand-acts", - "type": "string" - }, - "node": { - "description": "the node whose account rotates", - "type": "string" - }, - "why": { - "description": "why, in a sentence", - "type": "string" - } - }, - "required": [ - "node", - "why", - "act" - ], - "type": "object" - } - }, - { - "name": "node-account-confirm", - "description": "Confirm a node's switch to its new login: asked by the node-engine dialled as that login, the runtime's proof of the caller being the confirmation. The old login's hash is then removed, and rotation-done said once the bus node's have shows the list without it.", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "abandon", - "description": "Abandon a node's rotation before its old login is retired: the new login and its value dropped, the old one kept; a node that switched switches back first.", - "input": { - "properties": { - "node": { - "description": "the node whose rotation is abandoned", - "type": "string" - } - }, - "required": [ - "node" - ], - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0161", - "Optional": [ - "get", - "accept", - "secret-ask", - "issued", - "rotate", - "move-in", - "node-key", - "own", - "move-asks", - "issue-account", - "sign", - "enrolment-token", - "node-account", - "rotate-node", - "node-account-confirm", - "abandon" - ] - }, - { - "Name": "mesh-config", - "Scope": "mesh", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "config", - "description": "Set, clear, list or read the history of config values: mesh config or node config for a predefined key, or a module's own value on one node, each set with why, validated, and naming each module on each node whose value moves.", - "input": { - "properties": { - "action": { - "description": "list (when not given), set, clear or history", - "type": "string" - }, - "by": { - "description": "who gave the value at the controller's terminal", - "type": "string" - }, - "key": { - "description": "the config key", - "type": "string" - }, - "limit": { - "description": "for history: at most this many, newest", - "type": "number" - }, - "module": { - "description": "a module: its own value, with node", - "type": "string" - }, - "node": { - "description": "a node", - "type": "string" - }, - "value": { - "description": "for set: the value, of the key's type: a number, a string, a boolean, or for a trusted key an object or a list" - }, - "why": { - "description": "for set and clear: why", - "type": "string" - } - }, - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0348, ADR 0565", - "Optional": [ - "config" - ] - }, - { - "Name": "mesh-provisioner", - "Scope": "mesh", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "provisioning", - "description": "Read what the provisioner keeps: the registry (which domain answers which kind of placeholder), the needs each module on each node recorded, what is applied where, or the items of every collection, narrowed by node and module. Reads only.", - "input": { - "properties": { - "collection": { - "description": "for collect: a collection", - "type": "string" - }, - "module": { - "description": "a module: for collect, a contributor", - "type": "string" - }, - "node": { - "description": "a node", - "type": "string" - }, - "what": { - "description": "registry (when not given), needs, applied or collect", - "type": "string" - } - }, - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0349", - "Optional": [ - "provisioning" - ] - }, - { - "Name": "mesh-artifact-store", - "Scope": "mesh", - "Replicated": false, - "Delivers": "artifact-store", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": null, - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0075" - }, - { - "Name": "mesh-catalog", - "Scope": "mesh", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "modules", - "description": "Every module the trunk holds, each with its repository, its directory there, the merge commit it was last changed at and its fingerprint (or why it has none): the catalog of the mesh, read from its holder's state. Reads only.", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "module", - "description": "One module whole: its repository, directory, commit and fingerprint, and its manifest as the trunk holds it. Older versions are in its repository's history. Reads only.", - "input": { - "properties": { - "module": { - "description": "the module's name", - "type": "string" - } - }, - "required": [ - "module" - ], - "type": "object" - } - }, - { - "name": "provides", - "description": "Which modules provide a provision (a module provides its own name too) and which require it, from the module graph's provision edges. Reads only.", - "input": { - "properties": { - "provision": { - "description": "the provision, such as postgres-database", - "type": "string" - } - }, - "required": [ - "provision" - ], - "type": "object" - } - }, - { - "name": "dependents", - "description": "The modules whose build edges reach a module, directly or through others (a base, a mesh package it publishes, the build module that builds them): what a change of it builds again, from the module graph. Reads only.", - "input": { - "properties": { - "module": { - "description": "the module's name", - "type": "string" - } - }, - "required": [ - "module" - ], - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0604", - "Optional": [ - "modules", - "module", - "provides", - "dependents" - ] - }, - { - "Name": "mesh-delivery", - "Scope": "mesh", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "deliveries", - "description": "Every delivery not final, and those that ended in the last day, one line each: its id (owner/repository@commit), its state, its group, what it waits for and since when. Narrowed by state, repository or group.", - "input": { - "properties": { - "all": { - "description": "\"true\": the final ones of the last thirty days too", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "group": { - "description": "a group's id (its branch name)", - "type": "string" - }, - "repository": { - "description": "owner/repository", - "type": "string" - }, - "state": { - "description": "one state, e.g. delivering or held", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "times", - "description": "Removed in R6 (ADR 0539): refused by name, naming its replacement: the delivery view (nox-delivery): each machine's want and have, and since when", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "show", - "description": "One delivery or group whole: its delivery plan (what it builds, what each machine receives, what is not an ordinary send), every transition with when and why, the machine steps of its walk, its group and its order.", - "input": { - "properties": { - "id": { - "description": "a delivery's id, or a group's", - "type": "string" - } - }, - "required": [ - "id" - ], - "type": "object" - } - }, - { - "name": "groups", - "description": "Every delivery group: its members in order, why each pair is ordered (declared, built by, version skew, engine before controller, by name), its composed check and its state, derived from its members.", - "input": { - "properties": { - "all": { - "description": "\"true\": the groups that ended too", - "enum": [ - "true", - "false" - ], - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "what-if", - "description": "Removed in R6 (ADR 0539): refused by name, naming its replacement: the merge check's composition, which composes every machine a change touches", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "checks", - "description": "What the mesh's checks said of a pull request's head or of one commit: each of the commit's mesh statuses (mesh/check, the one a main requires from R4e, hq's mesh/repo-check, mesh/delivery, …) with its state, description and when it was set; the merge check's full verdict as the controller said it — each layer's summary, the machine that ran it, when, its build id and its report; and whether the branch's protection would let it merge, every required status being success.", - "input": { - "properties": { - "commit": { - "description": "a commit's sha, or the start of one, instead of a pull request", - "type": "string" - }, - "number": { - "description": "a pull request's number: its head is read", - "type": "string" - }, - "repository": { - "description": "owner/repository", - "type": "string" - } - }, - "required": [ - "repository" - ], - "type": "object" - } - }, - { - "name": "table", - "description": "The state table every delivery runs by: each transition with its guard, each state's bound and what healer H2 may do once it has passed; and the machine steps' table.", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "stalled", - "description": "Every delivery held past its state's bound, with the transition the table lets healer H2 take for it, or none.", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "recheck", - "description": "Check a rejected or ready delivery again: it goes back to proposed and the controller is asked for its check. With why.", - "input": { - "properties": { - "id": { - "description": "the delivery's id", - "type": "string" - }, - "why": { - "description": "why, kept with the transition", - "type": "string" - } - }, - "required": [ - "id", - "why" - ], - "type": "object" - } - }, - { - "name": "release", - "description": "Removed in R6 (ADR 0539): refused by name, naming its replacement: the assigner's asks (hold, restore, backup, record), answered on the phone or in the delivery view", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "stop", - "description": "Removed in R6 (ADR 0539): refused by name, naming its replacement: a bad fingerprint is held and its canary set back by the assigner (ADR 0429 §6, ADR 0539 clause 18)", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "close", - "description": "Healer H2's verb: take the one transition the table names for a delivery held past its bound. Refused for any other.", - "input": { - "properties": { - "id": { - "description": "the delivery's id", - "type": "string" - }, - "why": { - "description": "the condition it answers", - "type": "string" - } - }, - "required": [ - "id", - "why" - ], - "type": "object" - } - }, - { - "name": "retire-history", - "description": "Removed in R6 (ADR 0539): refused by name, naming its replacement: nothing to retire: the gate keeps no delivery after its merge", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "settle", - "description": "Ask the operator to settle deliveries held because their merge reached the trunk without a passing check: one ask on the operator's channel for all of them, as an Approve is asked. The verb moves nothing; on the operator's answer each goes to merged, and whether it is on the machines is derived as for any merge (novox/hq ADR 0556). Refused for a delivery not held, or held for another reason, naming why.", - "input": { - "additionalProperties": false, - "properties": { - "ids": { - "description": "the held deliveries' ids (owner/repository@commit)", - "items": { - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - "why": { - "description": "why they may be settled, shown to the operator", - "type": "string" - } - }, - "required": [ - "ids", - "why" - ], - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0239", - "Optional": [ - "times", - "what-if", - "checks", - "release", - "stop", - "retire-history", - "settle" - ] - }, - { - "Name": "mesh-placement", - "Scope": "mesh", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "placement", - "description": "Read placement: one node's key, the nodes one module is placed on, or every key, each with its revision, and how full the state is against its cap. With history, one node's or one module's changes in order: a revision the state keeps whole, an older one as the proposal's changes from its placement-changed event.", - "input": { - "properties": { - "history": { - "description": "\"true\": the changes in order, from the stream and the state's revisions", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "module": { - "description": "one module: the nodes it is placed on", - "type": "string" - }, - "node": { - "description": "one node", - "type": "string" - }, - "seats": { - "description": "\"true\": who holds each seat, and on which node (the state seat-holders); with node, that node's", - "enum": [ - "true", - "false" - ], - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "proposals", - "description": "Every open proposal of placement: its changes, the revision of each key it was laid over, its candidates, every node's answer, the differences computed and answered, its verdict and its ask. Narrowed to one by id.", - "input": { - "properties": { - "id": { - "description": "one proposal's id", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "propose", - "description": "Propose a change of placement, with why: laid over the current keys and refused at once, naming every finding, when the result cannot be placed (two modules on one host port, a provider not placed, a seat held once held twice, two nodes with one address, two hubs, a module the indexer does not hold, a node with no record). Otherwise the nodes it touches compose it in shadow and answer, and on a passing verdict the operator is asked to approve it. Who proposes is the caller, as the runtime proves it. An add-node from mesh-enrolment, whose warrant names its node and address, is written with no ask of its own: its Approve is the enrolment's (ADR 0587 §7).", - "input": { - "additionalProperties": false, - "properties": { - "changes": { - "description": "the changes, each an object with `op`: place (node, module, ports, bindings), take-off (node, module), pin (node, provision, provider {node, module}), unpin (node, provision), hold (node, module, claim, scope, kind, site), unhold (node, module, claim), decide (node, field, value: a decided field of the node, its address among them), config (node, module, mesh-wide, layer, mesh-wide-values, values), add-node (node, decided, keys: a joining node, mesh-enrolment's alone, the proposal's one change, with the enrolment's warrant, its tunnel key alone at the operator's yes; ADR 0587 §7), remove-node (node: a joining node never confirmed, taken off when its enrolment ends; mesh-enrolment's alone, on the same warrant)", - "items": { - "required": [ - "op", - "node" - ], - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "warrant": { - "description": "for an add-node alone: the enrolment's warrant {node, address, ask, warrant}, naming the change's node and address", - "required": [ - "node", - "address", - "ask", - "warrant" - ], - "type": "object" - }, - "why": { - "description": "why, shown to the operator with the proposal", - "type": "string" - } - }, - "required": [ - "why", - "changes" - ], - "type": "object" - } - }, - { - "name": "withdraw", - "description": "Withdraw an open proposal: its key and its candidates are deleted and placement-withdrawn carries it whole. With why.", - "input": { - "properties": { - "id": { - "description": "the proposal's id", - "type": "string" - }, - "why": { - "description": "why, kept in the event", - "type": "string" - } - }, - "required": [ - "id", - "why" - ], - "type": "object" - } - }, - { - "name": "again", - "description": "Judge an open proposal now with the answers that stand, and raise its ask again when its verdict passes and the operator's channel does not hold the ask. Nothing more: the recovery of a proposal whose judgement or ask was lost.", - "input": { - "properties": { - "id": { - "description": "the proposal's id", - "type": "string" - } - }, - "required": [ - "id" - ], - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0567", - "Optional": [ - "placement", - "proposals", - "propose", - "withdraw", - "again" - ] - }, - { - "Name": "mesh-delivery-checker", - "Scope": "mesh", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "try", - "description": "Try a pushed head before its pull request is opened: the checker judges it as it judges a pull request's head, merged onto its trunk's head, its unit and integration levels through the build modules, and owes the forge nothing. Answers the try's id at once; try-read reads where it stands. A try of the same head that waits or runs is the answer to a second ask; at most 4 wait or run at once, mesh-wide.", - "input": { - "properties": { - "head": { - "description": "the full commit to try, 40 or 64 hex digits", - "type": "string" - }, - "level": { - "description": "unit or integration: only this level; both when absent", - "type": "string" - }, - "repository": { - "description": "\u003cowner\u003e/\u003cname\u003e", - "type": "string" - }, - "tests": { - "description": "a go test -run pattern: only those tests run, and nothing is kept", - "type": "string" - }, - "trunk": { - "description": "the trunk commit to merge it onto, or the trunk's branch for its head now; the trunk's head when absent", - "type": "string" - } - }, - "required": [ - "repository", - "head" - ], - "type": "object" - }, - "replaces": [ - "sh merge-check.sh", - "docker run postgres" - ] - }, - { - "name": "try-read", - "description": "Where a try stands, as stored: queued, running, or its end (pass, fail, or error when it could not run), with what was said of it, the head and the trunk commit it was merged onto, the time it took and what failed first. Read-only.", - "input": { - "properties": { - "id": { - "description": "the try's id, try-\u003cdigits\u003e", - "type": "string" - } - }, - "required": [ - "id" - ], - "type": "object" - } - }, - { - "name": "try-end", - "description": "End a try that waits or runs, as error, by hand: say why (kept in what it says). Its place among the tries is free at once and the next try of its head asks anew with a new id. A try that ended already is refused.", - "input": { - "properties": { - "id": { - "description": "the try's id, try-\u003cdigits\u003e", - "type": "string" - }, - "why": { - "description": "why it is ended, in words", - "type": "string" - } - }, - "required": [ - "id", - "why" - ], - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0547", - "Optional": [ - "try", - "try-read", - "try-end" - ] - }, - { - "Name": "mesh-conditions", - "Scope": "mesh", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": [ - "condition-raised", - "condition-changed", - "condition-cleared" - ], - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "conditions", - "description": "What is wrong with the mesh now: every open condition, urgent first, then oldest, raised from the observers' findings (the watchman, the self-check, the healers, the assigner) and cleared when the finding goes, never by hand. Given a key, that one whole with its evidence; with history, every transition lately.", - "input": { - "properties": { - "days": { - "description": "with history: how many days back (default 7, at most 90)", - "type": "string" - }, - "history": { - "description": "\"true\": every raising, change, silence and clearing lately, oldest first", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "key": { - "description": "a condition's key: that one whole; with history, only its transitions", - "type": "string" - }, - "machine": { - "description": "only those about this machine", - "type": "string" - }, - "scope": { - "description": "only this scope: machine, plan, call, build, merge, provider, seat, bus, core, probe, mesh, delivery or module", - "type": "string" - }, - "severity": { - "description": "only urgent, or only warning", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "silence", - "description": "Stop one condition's messages for a while, a hand act with why: it stays open and in status, and the silence is kept as its transition in history.", - "input": { - "properties": { - "cause": { - "description": "the cause in a word (the condition's kind when absent)", - "type": "string" - }, - "for": { - "description": "how long: 30m, 4h, 2d; at most 7d", - "type": "string" - }, - "key": { - "description": "the condition's key", - "type": "string" - }, - "why": { - "description": "why, required and recorded", - "type": "string" - } - }, - "required": [ - "key", - "for", - "why" - ], - "type": "object" - } - }, - { - "name": "resync", - "description": "Read every observer's findings again and raise and clear what differs from the open conditions: the recovery of a keeper that missed a change.", - "input": { - "properties": {}, - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0564" - }, - { - "Name": "mesh-watchdogs", - "Scope": "mesh", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "signals", - "description": "Every watchdog of the signals table the watchman keeps, each a key that expires unless its signal renews it: its row, what renews it, its bound, the age of its newest signal, and the finding its expiry wrote.", - "input": { - "properties": { - "row": { - "description": "one row, e.g. S1", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "stuck", - "description": "Every want, build or step the watchman says stopped moving past its bound, with since when and the words of its finding.", - "input": { - "properties": { - "machine": { - "description": "only those on this machine", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "presence", - "description": "Every machine and tool runner the watchman sees connected, with its last word, and those it says silent; a machine that said it sleeps is shown as sleeping, not silent.", - "input": { - "properties": { - "machine": { - "description": "one machine", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "dead-letters", - "description": "Every message a consumer on the bus gave up on, kept in DEAD_LETTERS: whose consumer, the subject, how often it was handed over and when it was given up, newest first. With id: that one whole. With deliver or drop, a hand act with why.", - "input": { - "properties": { - "cause": { - "description": "with deliver or drop: the cause in a word (dead-letter when absent)", - "type": "string" - }, - "consumer": { - "description": "a consumer's name, or \u003cstream\u003e.\u003cconsumer\u003e: only what that one gave up on", - "type": "string" - }, - "deliver": { - "description": "a dead letter's id: deliver it again to the consumer that gave it up (needs why)", - "type": "string" - }, - "drop": { - "description": "a dead letter's id: drop it for good (needs why)", - "type": "string" - }, - "id": { - "description": "a dead letter's id, as the list gives it: that one whole, with its message", - "type": "string" - }, - "limit": { - "description": "how many to list, newest first (default 50); only when listing", - "type": "string" - }, - "why": { - "description": "with deliver or drop: why, required and recorded", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "resync", - "description": "Rebuild presence from the bus's connections, and the pulses and findings from the keys the watchman watches, and take every dead-letter notice waiting: the recovery of a watchman that missed a change.", - "input": { - "properties": {}, - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0564" - }, - { - "Name": "mesh-self-check", - "Scope": "mesh", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": [ - "checked" - ], - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "doctor", - "description": "The self-check: the last run's verdict at once, each probe passed, failed, deferred (its facts not on the bus yet, named) or could not run, and how long ago. With run, a run now; with probe, one probe's verdict whole. Runs every five minutes on its own; each failure is a finding.", - "input": { - "properties": { - "probe": { - "description": "one probe's id (D6): its verdict alone, with every finding", - "type": "string" - }, - "run": { - "description": "\"true\": run every probe now and answer the verdict", - "enum": [ - "true", - "false" - ], - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "probes", - "description": "The registry: what each probe asserts, where it reads its facts, and the finding it writes.", - "input": { - "properties": {}, - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0564" - }, - { - "Name": "mesh-healers", - "Scope": "mesh", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": [ - "healer-acted" - ], - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "healers", - "description": "The healers: each registered response to one kind of condition, its repair, its budget and what happens when it is spent; every act taken lately with its outcome, and the mesh-wide brake.", - "input": { - "properties": { - "days": { - "description": "how many days of acts back (default 7)", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "heal", - "description": "Take one healer's act for one open condition now, within its budget and the brake.", - "input": { - "properties": { - "key": { - "description": "the condition's key", - "type": "string" - }, - "why": { - "description": "why now, kept with the act", - "type": "string" - } - }, - "required": [ - "key", - "why" - ], - "type": "object" - } - }, - { - "name": "drill", - "description": "Record a drill: something broken on purpose to see the mesh raise and clear it, with what it tests. A drill is never a repair: no healer is wanted for it however often it is run.", - "input": { - "properties": { - "condition": { - "description": "the key of the condition the drill is meant to raise, if any (optional)", - "type": "string" - }, - "what": { - "description": "what was done on purpose, in a line", - "type": "string" - }, - "why": { - "description": "what the drill tests", - "type": "string" - } - }, - "required": [ - "what", - "why" - ], - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0564" - }, - { - "Name": "mesh-wants", - "Scope": "mesh", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": [ - "policy-changed" - ], - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "upgrade", - "description": "Whether each module's new builds wait for a person: its policy (roll, together or record) and where it comes from (a person, the module, the bus, its irreplaceable data, the default); the bus is never rolled. With module, that one; with module, policy and why, a person's choice for it, recorded as a hand act; with policy default, the choice taken back. With compare, each node's own composition and the assigner's findings.", - "input": { - "properties": { - "compare": { - "description": "\"true\": each node's own composition, by its digest, and the assigner's findings", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "module": { - "description": "one module", - "type": "string" - }, - "policy": { - "description": "with module: roll, together, record, or default to take a person's choice back", - "type": "string" - }, - "why": { - "description": "with policy: why, required and kept", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "bus", - "description": "What a bus upgrade would do: the fingerprint of the bus module each node runs against the indexer's newest, and the last bus steps from the hand-act log and the assigner's puts of the bus. Read-only: the step itself is the put on the operator's warrant.", - "input": { - "properties": {}, - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0565" - }, - { - "Name": "mesh-hand-acts", - "Scope": "mesh", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": [ - "act-recorded" - ], - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "hand-act", - "description": "Record one act done by hand: what, why, its cause in a word or a condition's kind (the word a second act for the same reason uses) and the condition it addresses. Who did it is the caller the runtime proves, never said by the caller. Said as act-recorded.", - "input": { - "properties": { - "cause": { - "description": "the cause in a word, or a condition's kind", - "type": "string" - }, - "condition": { - "description": "the key of the condition it addressed, if any (optional)", - "type": "string" - }, - "verb": { - "description": "the act's verb in the log: a repair when absent, `hand-act drill` for a drill, or a recorder's own", - "type": "string" - }, - "what": { - "description": "what was done, in a line", - "type": "string" - }, - "why": { - "description": "why it had to be done by hand", - "type": "string" - } - }, - "required": [ - "what", - "why" - ], - "type": "object" - } - }, - { - "name": "hand-acts", - "description": "What was done by hand lately, over this log and the controller's: who, why and the cause of each, and the causes recorded twice within a fortnight (a healer is wanted).", - "input": { - "properties": { - "cause": { - "description": "only this cause", - "type": "string" - }, - "days": { - "description": "how many days back (default 14)", - "type": "string" - }, - "warrants": { - "description": "\"true\": every warrant recorded", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "who": { - "description": "only acts whose who contains this", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "warranted", - "description": "Record what a module did on the operator's warrant: who chose, through which channel, with which proofs and which answer read from the router's own record of that module's ask, never from the caller; once per ask. Refused for an ask still open, ended without a choice, or not that module's.", - "input": { - "properties": { - "ask": { - "description": "its ask's id", - "type": "string" - }, - "asker": { - "description": "the module that asked", - "type": "string" - } - }, - "required": [ - "asker", - "ask" - ], - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0565" - }, - { - "Name": "mesh-enrolment", - "Scope": "mesh", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": [ - "enrolment-issued", - "node-enrolled", - "enrolment-refused", - "enrolment-revoked", - "enrolment-expired" - ], - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "issue", - "description": "Issue a one-time token for a new machine, on the operator's Approve: refused at once, naming every finding, for a name that is not a node name or that a node record, a placement key or an open enrolment holds, and for every call before the controller's lease is handed away. Otherwise reserves the lowest free private-network address, asks the operator under the seat's own name and, on the warrant, adds the node to placement with its tunnel key, asks the vault for the token and says enrolment-issued. The token itself is read only through tokens.", - "input": { - "properties": { - "expires": { - "description": "how long the token lasts, a duration such as 1h or 24h: one hour when not given, at most 24 hours", - "type": "string" - }, - "node": { - "description": "the new machine's name in the mesh", - "type": "string" - }, - "overlay_key": { - "description": "the new machine's private network (tunnel) public key, as `nox-mesh-host key` prints it", - "type": "string" - }, - "why": { - "description": "why it joins, shown to the operator with the ask", - "type": "string" - } - }, - "required": [ - "node", - "why", - "overlay_key" - ], - "type": "object" - } - }, - { - "name": "redeem", - "description": "Spend a token: asked by a joining node-engine dialled as enrol.\u003cnode\u003e alone, with the public halves of its keys and its identity key's proof over them. Spends the token, asks the vault for the node's account and certificate, writes the node into placement, and answers the account sealed to the node, its certificate and the vault's public half, signed by the vault. Asked again with the same keys it finishes what is not done and answers the same; with other keys it is refused.", - "input": { - "additionalProperties": false, - "properties": { - "node": { - "description": "the node the token was issued for", - "type": "string" - }, - "overlay_key": { - "description": "the private network key's public half", - "type": "string" - }, - "profile": { - "description": "what the machine is, as its record says it", - "type": "object" - }, - "proof": { - "description": "the identity key's signature over the redeem's proof bytes (delivery.RedeemProof), base64", - "type": "string" - }, - "public_key": { - "description": "the identity key's public half, base64", - "type": "string" - }, - "sealing_key": { - "description": "the sealing key's public half, which the account is sealed to", - "type": "string" - }, - "serving_key": { - "description": "the serving key's public half, which the certificate binds", - "type": "string" - }, - "tunnel": { - "description": "a tunnel found on the machine: everything but its private key", - "type": "object" - } - }, - "required": [ - "node", - "public_key", - "sealing_key", - "proof" - ], - "type": "object" - } - }, - { - "name": "confirm", - "description": "Confirm an enrolment: asked by the node-engine dialled as its own node login alone, the runtime's proof of that caller being the confirmation. Says node-enrolled and ends the enrolment.", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "revoke", - "description": "End an enrolment at any stage, with why, without an Approve: withdraws an open ask, deletes the node's key and its address, has the vault retire an account it already minted for it, records the act as a hand act and says enrolment-revoked.", - "input": { - "properties": { - "node": { - "description": "the node", - "type": "string" - }, - "why": { - "description": "why, recorded with the hand act", - "type": "string" - } - }, - "required": [ - "node", - "why" - ], - "type": "object" - } - }, - { - "name": "tokens", - "description": "Read every open enrolment, or one: its stage, expiry, ask and revision, the bucket's fill against its cap. For a node at live, its token sealed to the operator key, asked of the vault at that moment and relayed as the vault sealed it. With history, the ended enrolments from the seat's events, as far back as the events are kept.", - "input": { - "properties": { - "history": { - "description": "\"true\": the ended enrolments too", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "node": { - "description": "one node", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "again", - "description": "Raise an issue's ask again when the operator's channel does not hold it, and nothing more.", - "input": { - "properties": { - "node": { - "description": "the node whose ask is raised again", - "type": "string" - } - }, - "required": [ - "node" - ], - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0587" - }, - { - "Name": "mesh-artifact-sweep", - "Scope": "mesh", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": [ - "artifacts-gone" - ], - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "artifacts", - "description": "Every artifact the mesh made that lives in its artifact store, each with its repository, digest, kind (image or archive) and state: kept, and why (a want names it, or one of the five newest builds of its module in the builder's log), eligible (made, kept for no reason, not yet let go), or unrecorded (in the store and named by nothing the mesh made: never removed). Reads only.", - "input": { - "properties": { - "collected": { - "description": "\"true\": list the collected references too (counted always)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "repository": { - "description": "only this repository's references, as \u003cmodule\u003e/\u003cartifact\u003e (the counts stay whole)", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "held", - "description": "Whether the artifact store holds each reference given, asked with one HEAD each: held, gone (the store answered 404) or unknown with why. The builder asks it before it reuses a done key. Reads only.", - "input": { - "properties": { - "references": { - "description": "the artifacts' references, artifact-store://\u003cmodule\u003e/\u003cartifact\u003e/blobs/sha256:\u003chex\u003e: a list, or one text separated by commas", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "references" - ], - "type": "object" - } - }, - { - "name": "collect", - "description": "The artifact store's sweep, now: what is eligible is let go of. Without confirm a dry run: what would be let go, nothing held or deleted. With confirm and why: every kept archive held first, then each eligible artifact let go of, oldest first, an index with the platform manifests no kept index names, each deletion recorded in the hand-act log. Bounded by most (500 by default, at most 5000) and 45 seconds; what is left is said. Never touches a digest the mesh did not make.", - "input": { - "properties": { - "confirm": { - "description": "\"true\": let go of what is eligible (needs why); without it nothing is done", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "most": { - "description": "let go of at most this many (500 by default, at most 5000)", - "type": "string" - }, - "why": { - "description": "with confirm: why, required and recorded", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "mirrors", - "description": "The bases the mesh copied into its artifact store, each kept (a kept build stood on it), eligible (only builds no longer kept stood on it) or unrecorded (no build the builder's log holds stood on it: never removed), by what the builder's log says each build stood on. Records nothing by hand.", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "images", - "description": "Every container image one machine's want and have name, each with the resources naming it: what the machine keeps when it prunes its images. Reads only.", - "input": { - "properties": { - "node": { - "description": "the machine's name", - "type": "string" - } - }, - "required": [ - "node" - ], - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0571" - }, - { - "Name": "npm-package-registry", - "Scope": "mesh", - "Replicated": false, - "Delivers": "npm-package-registry", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": null, - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0109" - }, - { - "Name": "git", - "Scope": "mesh", - "Replicated": false, - "Delivers": "git", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": null, - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0111" - }, - { - "Name": "node-build-go", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": [ - "delivery", - "check", - "recheck" - ], - "Emits": null, - "PulledByKind": true, - "Yields": true, - "Serves": [ - { - "name": "current", - "description": "The Go asks this node runs now (each ask's id, kind, module and when it started), its build capacity, and whether it is paused, taking no new ask.", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "pause", - "description": "Take no new Go ask on this node until resumed; an ask running here ends in its place. Kept across a restart of the build module (its pause key). A capacity of 0 is the lasting way to keep a node from building.", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "resume", - "description": "Take Go asks again on this node.", - "input": { - "properties": {}, - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0525, ADR 0536, ADR 0553" - }, - { - "Name": "node-build-node", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": [ - "delivery", - "check", - "recheck" - ], - "Emits": null, - "PulledByKind": true, - "Yields": true, - "Serves": [ - { - "name": "current", - "description": "The Node asks this node runs now (each ask's id, kind, module and when it started), its build capacity, and whether it is paused, taking no new ask.", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "pause", - "description": "Take no new Node ask on this node until resumed; an ask running here ends in its place. Kept across a restart of the build module (its pause key). A capacity of 0 is the lasting way to keep a node from building.", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "resume", - "description": "Take Node asks again on this node.", - "input": { - "properties": {}, - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0525, ADR 0536, ADR 0553" - }, - { - "Name": "node-build-image", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": [ - "delivery", - "check", - "recheck" - ], - "Emits": null, - "PulledByKind": true, - "Yields": true, - "Serves": [ - { - "name": "current", - "description": "The image asks this node runs now (each ask's id, kind, module and when it started), its build capacity, and whether it is paused, taking no new ask.", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "pause", - "description": "Take no new image ask on this node until resumed; an ask running here ends in its place. Kept across a restart of the build module (its pause key). A capacity of 0 is the lasting way to keep a node from building.", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "resume", - "description": "Take image asks again on this node.", - "input": { - "properties": {}, - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0525, ADR 0536, ADR 0553" - }, - { - "Name": "mesh-dns-resolver", - "Scope": "mesh", - "Replicated": true, - "Delivers": "wildcard-resolution", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": null, - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0194, ADR 0223" - }, - { - "Name": "node-hostname", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "entries", - "description": "Every line of this machine's /etc/hosts, each marked whose it is: the operator's, or the block of the module or tool that writes it.", - "input": { - "properties": {}, - "type": "object" - }, - "replaces": [ - "cat /etc/hosts", - "getent hosts" - ] - }, - { - "name": "add", - "description": "Add one address and its names to the operator's lines of this machine's /etc/hosts — a name for this machine's own programs, not the mesh's.", - "input": { - "properties": { - "address": { - "description": "the IPv4 or IPv6 address", - "type": "string" - }, - "names": { - "description": "the names for it, separated by spaces", - "type": "string" - } - }, - "required": [ - "address", - "names" - ], - "type": "object" - }, - "replaces": [ - "edit /etc/hosts", - "HOSTALIASES" - ] - }, - { - "name": "remove", - "description": "Remove one name, or every line of one address, from the operator's lines of this machine's /etc/hosts. A line a module writes is refused, naming the module.", - "input": { - "properties": { - "name": { - "description": "a host name, or an address to remove every line of", - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "replaces": [ - "sed -i /etc/hosts" - ] - } - ], - "Receives": null, - "Paths": [ - { - "path": "/etc/hosts" - }, - { - "path": "/etc/hostname" - } - ], - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0199, ADR 0223" - }, - { - "Name": "node-intrusion-prevention", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "status", - "description": "Every jail on this machine with how many it is watching and holding now, and the totals since the jail started; one jail's detail when named.", - "input": { - "properties": { - "jail": { - "description": "one jail (optional)", - "type": "string" - } - }, - "type": "object" - }, - "replaces": [ - "fail2ban-client status" - ] - }, - { - "name": "banned", - "description": "Every address banned on this machine right now, with the jail that holds it and when the ban ends.", - "input": { - "properties": { - "jail": { - "description": "one jail (optional)", - "type": "string" - } - }, - "type": "object" - }, - "replaces": [ - "fail2ban-client banned" - ] - }, - { - "name": "ban", - "description": "Ban one address in one jail now, for the jail's ban time — an operator's act on the live ban list, which the mesh never writes itself.", - "input": { - "properties": { - "ip": { - "description": "the address", - "type": "string" - }, - "jail": { - "description": "the jail to hold it", - "type": "string" - } - }, - "required": [ - "ip", - "jail" - ], - "type": "object" - }, - "replaces": [ - "fail2ban-client set banip" - ] - }, - { - "name": "unban", - "description": "Let one address go, from one jail or from every jail when none is named.", - "input": { - "properties": { - "ip": { - "description": "the address", - "type": "string" - }, - "jail": { - "description": "one jail (optional)", - "type": "string" - } - }, - "required": [ - "ip" - ], - "type": "object" - }, - "replaces": [ - "fail2ban-client unban", - "fail2ban-client set unbanip" - ] - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0121" - }, - { - "Name": "node-packet-filter", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "rules", - "description": "The packet filter as this machine enforces it now: the nftables ruleset and, where the tool exists, the legacy filter's listings. Narrowed to one table or chain when asked.", - "input": { - "properties": { - "chain": { - "description": "one chain of that table (optional)", - "type": "string" - }, - "table": { - "description": "one nftables table, as `family name` (optional)", - "type": "string" - } - }, - "type": "object" - }, - "replaces": [ - "nft list ruleset", - "iptables -L", - "iptables-save" - ] - }, - { - "name": "reload", - "description": "Load the mesh's own filter again from the file the mesh writes, and answer with the mesh's table as loaded.", - "input": { - "properties": {}, - "type": "object" - }, - "replaces": [ - "nft -f" - ] - }, - { - "name": "remove", - "description": "Remove one rule set the mesh did not write, named exactly as the host reports it (novox/hq ADR 0168) — `chain X (iptables-legacy)` or `table ip6 filter, chain DOCKER-USER`. Refuses the mesh's tables, the runtime's own chains, a built-in chain and an active found firewall's chains. An operator's act, by name, never a flush.", - "input": { - "properties": { - "where": { - "description": "the rule set, as `node show` lists it", - "type": "string" - } - }, - "required": [ - "where" - ], - "type": "object" - }, - "replaces": [ - "nft delete", - "iptables -X" - ] - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0121" - }, - { - "Name": "node-service-manager", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "units", - "description": "The units the service manager knows in a scope, each with its load, active and sub state; narrowed to a pattern when asked.", - "input": { - "properties": { - "pattern": { - "description": "a glob the unit's name must match (optional)", - "type": "string" - }, - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - } - }, - "type": "object" - }, - "replaces": [ - "systemctl list-units" - ] - }, - { - "name": "status", - "description": "One unit as the service manager sees it now: its states, whether it starts at boot, its main process, and whether the mesh declares it.", - "input": { - "properties": { - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - } - }, - "required": [ - "unit" - ], - "type": "object" - }, - "replaces": [ - "systemctl status", - "systemctl is-active" - ] - }, - { - "name": "start", - "description": "Start one unit. For a unit the mesh declares, the answer says the host will restore what its declaration says at the next apply.", - "input": { - "properties": { - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - } - }, - "required": [ - "unit" - ], - "type": "object" - }, - "replaces": [ - "systemctl start" - ] - }, - { - "name": "stop", - "description": "Stop one unit; for a mesh-declared unit the answer says the host will restore its declared state.", - "input": { - "properties": { - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - } - }, - "required": [ - "unit" - ], - "type": "object" - }, - "replaces": [ - "systemctl stop" - ] - }, - { - "name": "restart", - "description": "Restart one unit.", - "input": { - "properties": { - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - } - }, - "required": [ - "unit" - ], - "type": "object" - }, - "replaces": [ - "systemctl restart" - ] - }, - { - "name": "enable", - "description": "Make one unit start at boot (or at the account's login, in user scope).", - "input": { - "properties": { - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - } - }, - "required": [ - "unit" - ], - "type": "object" - }, - "replaces": [ - "systemctl enable" - ] - }, - { - "name": "disable", - "description": "Stop one unit starting at boot (or at login, in user scope).", - "input": { - "properties": { - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - } - }, - "required": [ - "unit" - ], - "type": "object" - }, - "replaces": [ - "systemctl disable" - ] - }, - { - "name": "journal", - "description": "The last lines of one unit's journal (at most 2000), in a time window and narrowed to a priority and to lines holding a text when asked. A secret the unit printed is shown as [redacted: \u003cwhat it was\u003e].", - "input": { - "properties": { - "lines": { - "description": "how many lines from the end of what matches (default 100, at most 2000)", - "type": "string" - }, - "match": { - "description": "only the lines holding this text, as written — a fixed string, not a pattern (optional)", - "type": "string" - }, - "priority": { - "description": "only entries this severe or more: 0-7 or emerg, alert, crit, err, warning, notice, info, debug (optional)", - "type": "string" - }, - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "since": { - "description": "the window's start: an RFC 3339 time (2026-10-07T09:30:00Z) or relative to now (-30min, -2h, yesterday) (optional)", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - }, - "until": { - "description": "the window's end, in the same forms (optional; now when absent)", - "type": "string" - } - }, - "required": [ - "unit" - ], - "type": "object" - }, - "replaces": [ - "journalctl" - ] - }, - { - "name": "failed", - "description": "Every failed unit on this machine, in the system manager and in the operator account's; a manager that does not answer is reported with its error, never as nothing failed.", - "input": { - "properties": { - "scope": { - "description": "\"system\" or \"user\": only that manager (both when absent)", - "type": "string" - } - }, - "type": "object" - }, - "replaces": [ - "systemctl --failed" - ] - }, - { - "name": "reset-failed", - "description": "Clear one unit's failed record in its manager — its failed state and its start-limit count — and answer its state after. It removes no file and starts nothing.", - "input": { - "properties": { - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - } - }, - "required": [ - "unit" - ], - "type": "object" - }, - "replaces": [ - "systemctl reset-failed" - ] - }, - { - "name": "wanted-by", - "description": "What wants, requires or triggers one unit, read-only: the manager's reverse dependencies (WantedBy, RequiredBy, UpheldBy, BoundBy, TriggeredBy and the rest) and every enable link naming it in the scope's configuration directories (*.wants, *.requires, *.upholds), each with where it points and whether it dangles — the account's own directory included in user scope.", - "input": { - "properties": { - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - } - }, - "required": [ - "unit" - ], - "type": "object" - }, - "replaces": [ - "systemctl list-dependencies --reverse", - "systemctl show -p WantedBy", - "ls ~/.config/systemd/user/*.wants" - ] - }, - { - "name": "unlink-dangling", - "description": "Remove the dangling enable links named for one unit — links in a *.wants, *.requires or *.upholds directory of the account's or the machine's own configuration whose target does not exist, which `disable` leaves once the unit's file is gone — on the person's word: `why` is required. Each is recorded (its path, its target, the reason) before it is removed, and the manager reloaded; a link whose target exists, or one a package, a generator or the runtime placed, is left and said.", - "input": { - "properties": { - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - }, - "why": { - "description": "the person's reason for removing the links, kept in the record", - "type": "string" - } - }, - "required": [ - "unit", - "why" - ], - "type": "object" - }, - "replaces": [ - "rm ~/.config/systemd/user/*.wants/\u003cunit\u003e", - "unlink /etc/systemd/system/*.wants/\u003cunit\u003e" - ] - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0177", - "Optional": [ - "failed" - ] - }, - { - "Name": "node-package-manager", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": null, - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0207" - }, - { - "Name": "node-container-runtime", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": null, - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0166, ADR 0207" - }, - { - "Name": "node-environment", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": null, - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0203" - }, - { - "Name": "node-provisioner", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": null, - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0349" - }, - { - "Name": "node-login-shell", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "execute", - "description": "Run one command on this machine as the operator account, in a non-interactive login shell in its home; answers with what it printed and how it exited.", - "input": { - "properties": { - "command": { - "description": "the command line, as you would type it", - "type": "string" - }, - "timeout_seconds": { - "description": "give up after this long, at most 25 (default 20)", - "type": "string" - } - }, - "required": [ - "command" - ], - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0204", - "Optional": [ - "execute" - ] - }, - { - "Name": "node-power", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": null, - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0211" - }, - { - "Name": "node-hotkeys", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": null, - "Receives": [ - { - "Kind": "trigger", - "Comment": "#", - "Dirs": false, - "Shape": null, - "Offered": false - } - ], - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0212" - }, - { - "Name": "node-message-bus", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": null, - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0215" - }, - { - "Name": "node-backup", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "backed-up", - "description": "What this machine backs up: each module, what it declared, its last good night, how many restore points are kept and the repository's size.", - "input": { - "properties": { - "module": { - "description": "one module (optional)", - "type": "string" - } - }, - "type": "object" - }, - "replaces": [ - "restic snapshots" - ] - }, - { - "name": "now", - "description": "Take a backup now, of one module or of every module on this machine — before a migration, a retirement or anything else that could go wrong.", - "input": { - "properties": { - "module": { - "description": "one module (optional)", - "type": "string" - } - }, - "type": "object" - }, - "replaces": [ - "restic backup" - ] - }, - { - "name": "restore", - "description": "Restore one module's data from a restore point BESIDE the live data, never over it: each directory as \u003cpath\u003e.restored-\u003cdate\u003e. Swapping it in is a person's act. Lists the restore points when none is named.", - "input": { - "properties": { - "module": { - "description": "the module", - "type": "string" - }, - "path": { - "description": "one of the module's directories (all of them when omitted)", - "type": "string" - }, - "snapshot": { - "description": "the restore point (from `backed-up`; the newest when omitted)", - "type": "string" - } - }, - "required": [ - "module" - ], - "type": "object" - }, - "replaces": [ - "restic restore" - ] - } - ], - "Receives": [ - { - "Kind": "backup", - "Comment": "#", - "Dirs": true, - "Shape": null, - "Offered": false - }, - { - "Kind": "data", - "Comment": "#", - "Dirs": true, - "Shape": null, - "Offered": false - } - ], - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0214" - }, - { - "Name": "the-private-network", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": null, - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0110" - }, - { - "Name": "node-uplink", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "resolvers", - "description": "The machine's resolver file as it is now: the resolvers, search domains and options it lists, whether it is the file the mesh declares, and when it is not, who wrote it as far as the machine shows — its header, a backup a VPN client left beside it, a link in its place, a known writer running.", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "links", - "description": "Every network link on the machine: its state, its addresses, whether the default route leaves through it, and the resolvers and search domains the network manager knows for it — a VPN's tunnel included.", - "input": { - "properties": {}, - "type": "object" - } - } - ], - "Receives": null, - "Paths": [ - { - "path": "/etc/resolv.conf", - "steps-back-for": "node-resolver" - } - ], - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0117", - "Optional": [ - "resolvers", - "links" - ] - }, - { - "Name": "node-resolver", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "routes", - "description": "What this machine's own resolver sends where: the mesh's resolvers, which answer every name not routed elsewhere, and each link given servers of its own with the domains routed to them. Also the resolver file's outside writes it kept, newest first: when, who wrote it as far as the file says, whether a module took it, and when the resolver's own file was put back.", - "input": { - "properties": {}, - "type": "object" - }, - "replaces": [ - "resolvectl status", - "resolvectl dns", - "resolvectl domain" - ] - }, - { - "name": "route", - "description": "Send these domains, and every name under them, to these servers over this link — and only them: the link is never the machine's default route for names, and the mesh's own domain is refused. Replaces whatever the link was given before. A link that goes away takes its route with it.", - "input": { - "properties": { - "domains": { - "description": "the domains to route there, separated by spaces or commas", - "type": "string" - }, - "link": { - "description": "the network link the servers are reached over, by name (a VPN's tunnel interface)", - "type": "string" - }, - "servers": { - "description": "the servers' addresses, separated by spaces or commas", - "type": "string" - } - }, - "required": [ - "link", - "domains", - "servers" - ], - "type": "object" - }, - "replaces": [ - "resolvectl dns", - "resolvectl domain", - "resolvectl default-route" - ] - }, - { - "name": "unroute", - "description": "Take one link's route away: its domains go back to the mesh's resolvers. Nothing changes when the link has none.", - "input": { - "properties": { - "link": { - "description": "the network link, by name", - "type": "string" - } - }, - "required": [ - "link" - ], - "type": "object" - }, - "replaces": [ - "resolvectl revert" - ] - } - ], - "Receives": null, - "Paths": [ - { - "path": "/etc/resolv.conf" - } - ], - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0247" - }, - { - "Name": "node-nfs-server", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "exports", - "description": "Every share this machine exports: its name, its path, read-write or read-only, the owner every client is mapped to (uid and gid), each node it is exported to with that node's private address and access (only the nodes the server grants it to and that ask for it), what is granted and not asked for or asked for and not granted, and whether the kernel holds it now. Also the exports found that are not the mesh's: a dataset's sharenfs property, a line in /etc/exports.", - "input": { - "properties": {}, - "type": "object" - }, - "replaces": [ - "exportfs -v", - "cat /etc/exports", - "zfs get sharenfs" - ] - }, - { - "name": "clients", - "description": "Which machines have mounted which share now, as the NFS server knows its clients.", - "input": { - "properties": {}, - "type": "object" - }, - "replaces": [ - "ss -tn sport = :2049", - "cat /proc/fs/nfsd/clients/*/info" - ] - }, - { - "name": "test", - "description": "Whether this machine exports one share for the mesh now, and whether its NFS service is up; with an address, also whether the share is exported to that address: a node's own private address, when the server grants it the share and the node asks for it. What a machine mounting the share asks before it mounts.", - "input": { - "properties": { - "address": { - "description": "a node's private address, to ask whether the share is exported to it (optional)", - "type": "string" - }, - "share": { - "description": "the share, by its name", - "type": "string" - } - }, - "required": [ - "share" - ], - "type": "object" - }, - "replaces": [ - "showmount -e" - ] - }, - { - "name": "reload", - "description": "Have the kernel read this machine's export files again now (exportfs -ra) and answer the shares as it then holds them. The module's own process writes its export file; this is for after a change made outside it. Changes no shared path.", - "input": { - "properties": {}, - "type": "object" - }, - "replaces": [ - "exportfs -ra" - ] - }, - { - "name": "adopt", - "description": "Take over an export a person made by hand: clear a dataset's sharenfs property for a path the mesh's own export now serves — only when that export is live. Without confirm, says what it would do and changes nothing.", - "input": { - "properties": { - "confirm": { - "description": "\"true\": change it (needs why); anything else is a dry run", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "path": { - "description": "the shared path whose hand-made export is taken over", - "type": "string" - }, - "why": { - "description": "why, for the record", - "type": "string" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "replaces": [ - "zfs set sharenfs=off" - ] - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0263" - }, - { - "Name": "node-mounts", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "list", - "description": "Every mount point on this machine: its fstab line, the mesh's mount and automount units for it, its state (armed, mounted, absent, unreachable, failed) and since when, and which settings asked for it. A password in a mount's options is never shown.", - "input": { - "properties": {}, - "type": "object" - }, - "replaces": [ - "cat /etc/fstab", - "findmnt", - "systemctl list-units --type=mount" - ] - }, - { - "name": "test", - "description": "Whether one share's or occasional source's server answers from this machine now, without touching its mount point.", - "input": { - "properties": { - "name": { - "description": "the share or source, by its name", - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "replaces": [ - "showmount -e", - "ping" - ] - }, - { - "name": "mount", - "description": "Mount one share or occasional source now, rather than at its first access.", - "input": { - "properties": { - "name": { - "description": "the share or source, by its name", - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "replaces": [ - "mount" - ] - }, - { - "name": "unmount", - "description": "Release one share or occasional source now; its automount stays armed, so the next access mounts it again.", - "input": { - "properties": { - "name": { - "description": "the share or source, by its name", - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "replaces": [ - "umount" - ] - }, - { - "name": "adopt", - "description": "Take over a mount a person wrote by hand: comment out the /etc/fstab line for a mount point the mesh's own units now serve, keeping a copy of the file — only when the mesh's automount for it is armed. Without confirm, says what it would do and changes nothing.", - "input": { - "properties": { - "confirm": { - "description": "\"true\": change it (needs why); anything else is a dry run", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "mountpoint": { - "description": "the mount point whose fstab line is taken over", - "type": "string" - }, - "why": { - "description": "why, for the record", - "type": "string" - } - }, - "required": [ - "mountpoint" - ], - "type": "object" - }, - "replaces": [ - "sed -i /etc/fstab" - ] - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0263" - }, - { - "Name": "node-reads", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "stat", - "description": "Each component of one path or several, from / down: its type, owner and group (name and number), mode, whether it is a link and to what, and whether it is a mount point and of which file system. A link is not followed unless follow is \"true\". Read as the holder's own account, which holds no secret: a component it may not reach says so. At most 200 paths in one call.", - "input": { - "properties": { - "follow": { - "description": "\"true\": also say where the path leads once every link is followed", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "paths": { - "description": "one absolute path, or several separated by line breaks", - "type": "string" - } - }, - "required": [ - "paths" - ], - "type": "object" - }, - "replaces": [ - "stat", - "ls -ld", - "namei -l", - "findmnt -T" - ] - }, - { - "name": "readlink", - "description": "What one link points to, as written, and where the path leads once every link is followed, and whether that exists.", - "input": { - "properties": { - "path": { - "description": "an absolute path", - "type": "string" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "replaces": [ - "readlink", - "readlink -f", - "realpath" - ] - }, - { - "name": "list", - "description": "Every entry of one directory with its type, owner, group, mode, size and, for a link, its target. At most 2000 entries; a longer directory is cut and says so. A place a secret is kept is refused by name.", - "input": { - "properties": { - "path": { - "description": "an absolute path to a directory", - "type": "string" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "replaces": [ - "ls", - "ls -la" - ] - }, - { - "name": "read", - "description": "One text file, or a part of it by line: at most 256 KiB in one answer (64 KiB unless more is asked), cut at a line and said so. A binary file is refused; a place a secret is kept, or a known credential file, is refused by name; a line shaped like a secret is withheld and its number said. /proc and /sys are read as far as the holder's account may.", - "input": { - "properties": { - "bytes": { - "description": "the most bytes to answer, up to 262144 (optional, 65536 unless given)", - "type": "string" - }, - "from": { - "description": "the first line to answer, counted from 1 (optional)", - "type": "string" - }, - "lines": { - "description": "how many lines to answer (optional)", - "type": "string" - }, - "path": { - "description": "an absolute path to a file", - "type": "string" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "replaces": [ - "cat", - "head", - "tail", - "less" - ] - }, - { - "name": "search", - "description": "Find a fixed string (never a pattern) in one file or under one directory: every matching line with its file and number. Bounded: at most 500 matches (100 unless more is asked), 8 directories deep (4 unless more is asked), 5000 files and 4 MiB per file; links are not followed and binary files are skipped. A place a secret is kept is skipped and named; a matching line shaped like a secret is withheld. The answer says which bound it met.", - "input": { - "properties": { - "case": { - "description": "\"false\": match whatever the case (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "depth": { - "description": "how many directories deep, up to 8 (optional)", - "type": "string" - }, - "matches": { - "description": "the most matches to answer, up to 500 (optional)", - "type": "string" - }, - "path": { - "description": "an absolute path to a file or a directory", - "type": "string" - }, - "text": { - "description": "the fixed string to find", - "type": "string" - } - }, - "required": [ - "text", - "path" - ], - "type": "object" - }, - "replaces": [ - "grep", - "grep -r", - "grep -F" - ] - }, - { - "name": "account", - "description": "One account or group by name or number, as this node's account files say: an account's number, group, home, shell and every group it is in; a group's number and members. With neither, every account and group. Never a password field.", - "input": { - "properties": { - "group": { - "description": "a group's name or number (optional)", - "type": "string" - }, - "name": { - "description": "an account's name or number (optional)", - "type": "string" - } - }, - "type": "object" - }, - "replaces": [ - "getent passwd", - "getent group", - "id", - "groups" - ] - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0310" - }, - { - "Name": "node-files", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "stat", - "description": "Each component of one path or several, from / down: its type, owner and group (name and number), mode, whether it is a link and to what, and whether it is a mount point and of which file system. A link is not followed unless follow is \"true\". At most 200 paths in one call. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - "input": { - "properties": { - "follow": { - "description": "\"true\": also say where the path leads once every link is followed", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "paths": { - "description": "one absolute path, or several separated by line breaks", - "type": "string" - } - }, - "required": [ - "paths" - ], - "type": "object" - }, - "replaces": [ - "stat", - "ls -ld", - "namei -l", - "findmnt -T" - ] - }, - { - "name": "readlink", - "description": "What one link points to, as written, and where the path leads once every link is followed, and whether that exists. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - "input": { - "properties": { - "path": { - "description": "an absolute path", - "type": "string" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "replaces": [ - "readlink", - "readlink -f", - "realpath" - ] - }, - { - "name": "list", - "description": "Every entry of one directory with its type, owner, group, mode, size and, for a link, its target. At most 2000 entries; a longer directory is cut and says so. A place a secret is kept is refused by name. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - "input": { - "properties": { - "path": { - "description": "an absolute path to a directory", - "type": "string" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "replaces": [ - "ls", - "ls -la" - ] - }, - { - "name": "read", - "description": "One text file, or a part of it by line: at most 256 KiB in one answer (64 KiB unless more is asked), cut at a line and said so. A binary file is refused; a place a secret is kept, or a known credential file, is refused by name; a line shaped like a secret is withheld and its number said. /proc and /sys are read as far as the holder's account may. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - "input": { - "properties": { - "bytes": { - "description": "the most bytes to answer, up to 262144 (optional, 65536 unless given)", - "type": "string" - }, - "from": { - "description": "the first line to answer, counted from 1 (optional)", - "type": "string" - }, - "lines": { - "description": "how many lines to answer (optional)", - "type": "string" - }, - "path": { - "description": "an absolute path to a file", - "type": "string" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "replaces": [ - "cat", - "head", - "tail", - "less" - ] - }, - { - "name": "search", - "description": "Find a fixed string (never a pattern) in one file or under one directory: every matching line with its file and number. Bounded: at most 500 matches (100 unless more is asked), 8 directories deep (4 unless more is asked), 5000 files and 4 MiB per file; links are not followed and binary files are skipped. A place a secret is kept is skipped and named; a matching line shaped like a secret is withheld. The answer says which bound it met. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - "input": { - "properties": { - "case": { - "description": "\"false\": match whatever the case (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "depth": { - "description": "how many directories deep, up to 8 (optional)", - "type": "string" - }, - "matches": { - "description": "the most matches to answer, up to 500 (optional)", - "type": "string" - }, - "path": { - "description": "an absolute path to a file or a directory", - "type": "string" - }, - "text": { - "description": "the fixed string to find", - "type": "string" - } - }, - "required": [ - "text", - "path" - ], - "type": "object" - }, - "replaces": [ - "grep", - "grep -r", - "grep -F" - ] - }, - { - "name": "account", - "description": "One account or group by name or number, as this node's account files say: an account's number, group, home, shell and every group it is in; a group's number and members. With neither, every account and group. Never a password field. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - "input": { - "properties": { - "group": { - "description": "a group's name or number (optional)", - "type": "string" - }, - "name": { - "description": "an account's name or number (optional)", - "type": "string" - } - }, - "type": "object" - }, - "replaces": [ - "getent passwd", - "getent group", - "id", - "groups" - ] - }, - { - "name": "write", - "description": "Write one text file whole, replacing what it holds; the old content goes to the node's trash. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - "input": { - "properties": { - "content": { - "description": "the text to write", - "type": "string" - }, - "mode": { - "description": "the file's mode, such as 0644 (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "override": { - "description": "a reason, to change a path a module owns anyway (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "path": { - "description": "an absolute path", - "type": "string" - }, - "reason": { - "description": "why the file is changed, for the audit trail", - "type": "string" - } - }, - "required": [ - "path", - "content", - "reason" - ], - "type": "object" - }, - "replaces": [ - "tee", - "\u003e" - ] - }, - { - "name": "copy", - "description": "Copy a file or directory to a path on this node. A target on another node (\u003cnode\u003e:/path) is refused: copying between nodes comes in step 2 (issue 903). An existing target is overwritten, the old one kept in the node's trash for 7 days. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - "input": { - "properties": { - "from": { - "description": "an absolute path", - "type": "string" - }, - "override": { - "description": "a reason, to change a path a module owns anyway (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "reason": { - "description": "why the file is changed, for the audit trail", - "type": "string" - }, - "to": { - "description": "an absolute path on this node", - "type": "string" - } - }, - "required": [ - "from", - "to", - "reason" - ], - "type": "object" - }, - "replaces": [ - "cp", - "cp -a", - "ssh \u003cnode\u003e cp" - ] - }, - { - "name": "move", - "description": "Move or rename a file or directory. A target on another node is refused: copying between nodes comes in step 2 (issue 903). An existing target is overwritten, the old one kept in the node's trash for 7 days. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - "input": { - "properties": { - "from": { - "description": "an absolute path", - "type": "string" - }, - "override": { - "description": "a reason, to change a path a module owns anyway (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "reason": { - "description": "why the file is changed, for the audit trail", - "type": "string" - }, - "to": { - "description": "an absolute path", - "type": "string" - } - }, - "required": [ - "from", - "to", - "reason" - ], - "type": "object" - }, - "replaces": [ - "mv" - ] - }, - { - "name": "delete", - "description": "Delete a file or directory into the node's trash, where restore can bring it back. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - "input": { - "properties": { - "override": { - "description": "a reason, to change a path a module owns anyway (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "path": { - "description": "an absolute path", - "type": "string" - }, - "reason": { - "description": "why the file is changed, for the audit trail", - "type": "string" - }, - "recursive": { - "description": "\"true\": delete a directory with what it holds", - "enum": [ - "true", - "false" - ], - "type": "string" - } - }, - "required": [ - "path", - "reason" - ], - "type": "object" - }, - "replaces": [ - "rm", - "rm -r" - ] - }, - { - "name": "mkdir", - "description": "Make a directory. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - "input": { - "properties": { - "override": { - "description": "a reason, to change a path a module owns anyway (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "parents": { - "description": "\"true\": make the missing parents too", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "path": { - "description": "an absolute path", - "type": "string" - }, - "reason": { - "description": "why the file is changed, for the audit trail", - "type": "string" - } - }, - "required": [ - "path", - "reason" - ], - "type": "object" - }, - "replaces": [ - "mkdir", - "mkdir -p" - ] - }, - { - "name": "chmod", - "description": "Change the mode of a file or directory. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - "input": { - "properties": { - "mode": { - "description": "the mode, such as 0644 or u+x", - "type": "string" - }, - "override": { - "description": "a reason, to change a path a module owns anyway (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "path": { - "description": "an absolute path", - "type": "string" - }, - "reason": { - "description": "why the file is changed, for the audit trail", - "type": "string" - }, - "recursive": { - "description": "\"true\": also everything under a directory", - "enum": [ - "true", - "false" - ], - "type": "string" - } - }, - "required": [ - "path", - "mode", - "reason" - ], - "type": "object" - }, - "replaces": [ - "chmod" - ] - }, - { - "name": "chown", - "description": "Change the owner and group of a file or directory. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - "input": { - "properties": { - "group": { - "description": "a group's name or number (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "override": { - "description": "a reason, to change a path a module owns anyway (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "owner": { - "description": "an account's name or number", - "type": "string" - }, - "path": { - "description": "an absolute path", - "type": "string" - }, - "reason": { - "description": "why the file is changed, for the audit trail", - "type": "string" - }, - "recursive": { - "description": "\"true\": also everything under a directory", - "enum": [ - "true", - "false" - ], - "type": "string" - } - }, - "required": [ - "path", - "owner", - "reason" - ], - "type": "object" - }, - "replaces": [ - "chown", - "chgrp" - ] - }, - { - "name": "upload", - "description": "Send one file of this node into a bucket of the object store, over a short signed link and never over the bus; answers the key (the file's SHA-256 when none is given), the size and the SHA-256. At most 100 MiB. A place a secret is kept, a known credential file, or a link to one is refused by name, before anything is sent. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - "input": { - "properties": { - "bucket": { - "description": "the bucket the file goes into", - "type": "string" - }, - "key": { - "description": "the object's key (optional; the file's SHA-256 when not given)", - "type": "string" - }, - "path": { - "description": "an absolute path to a file", - "type": "string" - } - }, - "required": [ - "path", - "bucket" - ], - "type": "object" - } - }, - { - "name": "download", - "description": "Write one object of a bucket to a file of this node, over a short signed link and never over the bus; the old file goes to the node's trash. At most 100 MiB. The bytes must hash to sha256, or to the key when the key is a SHA-256; otherwise no file is written. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - "input": { - "properties": { - "bucket": { - "description": "the bucket", - "type": "string" - }, - "key": { - "description": "the object's key", - "type": "string" - }, - "override": { - "description": "a reason, to change a path a module owns anyway (optional)", - "type": "string" - }, - "path": { - "description": "an absolute path", - "type": "string" - }, - "reason": { - "description": "why the file is changed, for the audit trail", - "type": "string" - }, - "sha256": { - "description": "the SHA-256 the bytes must have (optional; the key when that is a SHA-256)", - "type": "string" - } - }, - "required": [ - "bucket", - "key", - "path", - "reason" - ], - "type": "object" - } - }, - { - "name": "trash", - "description": "What the node's trash holds: each deleted or replaced item with its id, original path and when it went.", - "input": { - "properties": {}, - "type": "object" - }, - "replaces": [ - "ls ~/.local/share/Trash" - ] - }, - { - "name": "restore", - "description": "Put an item of the node's trash back where it was; refuses when something is there now. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - "input": { - "properties": { - "id": { - "description": "an id from trash", - "type": "string" - }, - "override": { - "description": "a reason, to change a path a module owns anyway (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "reason": { - "description": "why the file is changed, for the audit trail", - "type": "string" - } - }, - "required": [ - "id", - "reason" - ], - "type": "object" - }, - "replaces": [ - "trash-restore" - ] - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0394" - }, - { - "Name": "node-engine", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": [ - "asked", - "done", - "refused" - ], - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "hand-over", - "description": "Hand a directory this machine uses as found to the mesh (novox/hq ADR 0563 §3, §5): the engine judges the path as it does every hand-over (absolute, as it states it, used as found, reached through no link) and refuses in words, or asks the operator on their channel at the level approve, binding the owner, group and mode it will set. Nothing changes on the call: only the operator's yes hands it over. One ask is open per path; acts shows where it stands.", - "input": { - "properties": { - "path": { - "description": "the directory's absolute path, as the engine states it", - "type": "string" - }, - "why": { - "description": "why it is asked, in words the operator reads", - "type": "string" - } - }, - "required": [ - "path" - ], - "type": "object" - } - }, - { - "name": "access-act", - "description": "Ask the operator for the take act on a library whose root holds data outside its group (novox/hq ADR 0328 §7, ADR 0563 §3, §4), on the library's home engine: the access as declared and its root as last declared, judged and refused in words or asked at the level approve. Only the operator's yes sets the library's group, group write and setgid under its root. One ask is open per access.", - "input": { - "properties": { - "access": { - "description": "the access, library:\u003cname\u003e", - "type": "string" - }, - "act": { - "description": "the act: take", - "type": "string" - }, - "why": { - "description": "why it is asked, in words the operator reads", - "type": "string" - } - }, - "required": [ - "act", - "access" - ], - "type": "object" - } - }, - { - "name": "judge-directories", - "description": "Which of this machine's declared directories the engine would refuse to make or keep, one line each (novox/hq ADR 0563 §2). Read-only: changes no owner or mode.", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "setuid-search", - "description": "Throw away the engine's last search for setuid programs and start a full one (novox/hq ADR 0563 §2); until it completes root-free answers that the machine is not judged yet. Refused while one runs, and when one was started within the hour, naming when the next may start.", - "input": { - "properties": { - "why": { - "description": "why it is asked, in words", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "root-free", - "description": "Whether this machine is root-free (novox/hq ADR 0259 §8, ADR 0563 §6): free only on the engine's own verdict, at most fifteen minutes old, that the account agents run as cannot become root without a person, and the login shell's execute not served here. Anything else is no pass, and says why. Answered by this machine's engine alone.", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "acts", - "description": "Every act this machine's engine was asked through this seat (novox/hq ADR 0563 §9): each hand-over, access act and setuid search, open, done or refused, with who asked, the ask and the operator's warrant. Read-only.", - "input": { - "properties": { - "id": { - "description": "one act, by its id", - "type": "string" - }, - "limit": { - "description": "at most this many, newest", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "packages", - "description": "The packages this machine's compositions declare, present or absent, each with the modules that declare it, as the engine keeps them in its key of `packages`; until the controller hands its lease away, beside the controller's mirror for this machine and their difference. With write, the key written again from what the engine composed.", - "input": { - "properties": { - "write": { - "description": "\"true\": write this machine's key again from what the engine composed", - "enum": [ - "true", - "false" - ], - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "composition", - "description": "One module's composition on this machine beside what runs of it, read-only: the composition its want's build has in the bucket composed-\u003cnode\u003e (the latest revision, or the one whose digest is given, from the revisions the bucket keeps), the digest of each kept revision, what runs (applied, its have, or the kept declaration), its want and have (phase running, failed, held or waiting-yes, and the digest that waits for a yes), and the move's judgement with its words: why a record module waits for a yes. With diff, every resource added, removed or changed, a provider's received file naming the consumers that join or leave.", - "input": { - "properties": { - "diff": { - "description": "\"true\": list the resources that differ from what runs", - "type": "string" - }, - "digest": { - "description": "a composition's digest (sha256:…), to read that revision rather than the latest", - "type": "string" - }, - "module": { - "description": "the module, as placed on this machine; node for the node's own", - "type": "string" - } - }, - "required": [ - "module" - ], - "type": "object" - } - }, - { - "name": "compose", - "description": "Compose this machine's modules now and say how it went, so a composition that failed, or a module held, recovers without a restart: what the pass refused or held, each with why, and when the engine composes again on its own after a seat that did not answer (a deadline, no responders: retried with a growing wait; a refusal never). With module, that module's apply is also tried again. Changes only what a pass the engine makes on its own would change.", - "input": { - "properties": { - "module": { - "description": "a module placed on this machine whose apply is also tried again (optional)", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "compose-candidate", - "description": "Compose a check's candidate on this machine in shadow, with its own sealed values and the indexer's manifest verdicts, and say per module placed here whether it composes (with its digest and whether the candidate changes it), is refused by the candidate or is held without it, each with why, and the verdict of the machine's own composition (novox/hq ADR 0593 §7). Read-only: applies nothing and writes nothing. Answered by this machine's engine alone, within the asker's bound.", - "input": { - "properties": { - "candidate": { - "description": "the key of the indexer's candidate, \u003cgroup\u003e.\u003cheads hash\u003e", - "type": "string" - }, - "input": { - "description": "\"true\": also answer the composition input the candidate was composed from, for the sandbox path", - "enum": [ - "true", - "false" - ], - "type": "string" - } - }, - "required": [ - "candidate" - ], - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": true, - "Decision": "novox/hq ADR 0563", - "Optional": [ - "packages", - "composition", - "compose", - "compose-candidate" - ] - }, - { - "Name": "node-login-manager", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "sessions", - "description": "The sessions the login manager offers on this machine, and which one the operator account starts by default.", - "input": { - "properties": {}, - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0208" - }, - { - "Name": "node-display-server", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "displays", - "description": "The monitors connected now, each with its identity, its modes and where it is placed; and the layout profile in force, if one matches.", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "layout", - "description": "The monitor layout profiles, keyed by the connected monitors' identities: list them, save the current arrangement under a name, or apply one.", - "input": { - "properties": { - "action": { - "description": "list, save or apply", - "enum": [ - "list", - "save", - "apply" - ], - "type": "string" - }, - "name": { - "description": "the profile to save or apply (save and apply only)", - "type": "string" - } - }, - "required": [ - "action" - ], - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0208" - }, - { - "Name": "node-display-session", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "reload", - "description": "Reload the session's configuration in place, keeping its windows.", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "workspaces", - "description": "The session's workspaces: each one's name, output, and whether it is visible or focused.", - "input": { - "properties": {}, - "type": "object" - } - }, - { - "name": "windows", - "description": "The session's windows: each one's title, class, workspace and whether it has focus; narrowed to one workspace when named.", - "input": { - "properties": { - "workspace": { - "description": "one workspace (optional)", - "type": "string" - } - }, - "type": "object" - } - } - ], - "Receives": [ - { - "Kind": "config", - "Comment": "#", - "Dirs": false, - "Shape": null, - "Offered": false - } - ], - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0208" - }, - { - "Name": "node-terminal-emulator", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "open", - "description": "Open a terminal window in the operator's session, running a command or the login shell, in a directory or the account's home.", - "input": { - "properties": { - "command": { - "description": "what to run in it (optional; the login shell when absent)", - "type": "string" - }, - "directory": { - "description": "where it starts (optional; the account's home when absent)", - "type": "string" - } - }, - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0208" - }, - { - "Name": "node-launcher", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "menu", - "description": "Put a menu of choices in front of the operator and answer with the one chosen, or nothing when the menu was dismissed — the dmenu-compatible contract.", - "input": { - "properties": { - "choices": { - "description": "the lines to choose between, in order", - "items": { - "type": "string" - }, - "type": "array" - }, - "prompt": { - "description": "what the menu asks (optional)", - "type": "string" - } - }, - "required": [ - "choices" - ], - "type": "object" - } - }, - { - "name": "secret", - "description": "Ask the operator for a value in a prompt that does not show what is typed, and answer it sealed to the key the asker gives — never in the clear — or cancelled when the prompt was dismissed or not answered in time.", - "input": { - "properties": { - "module": { - "description": "the module whose own secret is asked for", - "type": "string" - }, - "node": { - "description": "the machine the module runs on", - "type": "string" - }, - "seal_to": { - "description": "the asker's public sealing key: the answer is sealed to it", - "type": "string" - }, - "secret": { - "description": "the own secret's name", - "type": "string" - }, - "timeout_seconds": { - "description": "give up after this long (optional)", - "type": "string" - } - }, - "required": [ - "module", - "secret", - "node", - "seal_to" - ], - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0208", - "Optional": [ - "secret" - ] - }, - { - "Name": "node-notifier", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "send", - "description": "Show the operator a notification.", - "input": { - "properties": { - "body": { - "description": "its text (optional)", - "type": "string" - }, - "title": { - "description": "the notification's summary", - "type": "string" - }, - "urgency": { - "description": "low, normal (the default) or critical", - "enum": [ - "low", - "normal", - "critical" - ], - "type": "string" - } - }, - "required": [ - "title" - ], - "type": "object" - } - }, - { - "name": "history", - "description": "The notifications shown lately, newest first.", - "input": { - "properties": { - "limit": { - "description": "how many (optional, default 20)", - "type": "string" - } - }, - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0208" - }, - { - "Name": "node-lock-screen", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "lock", - "description": "Lock the operator's session now.", - "input": { - "properties": {}, - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0208" - }, - { - "Name": "node-clipboard", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": [ - { - "name": "history", - "description": "What the clipboard held lately, newest first.", - "input": { - "properties": { - "limit": { - "description": "how many (optional, default 20)", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "copy", - "description": "Put text on the operator's clipboard.", - "input": { - "properties": { - "text": { - "description": "the text", - "type": "string" - } - }, - "required": [ - "text" - ], - "type": "object" - } - } - ], - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0208" - }, - { - "Name": "node-bar", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": null, - "Receives": [ - { - "Kind": "block", - "Comment": "#", - "Dirs": false, - "Shape": { - "Places": [ - { - "Field": "bar", - "Values": [ - "bottom", - "top" - ] - }, - { - "Field": "place", - "Values": [ - "resources", - "status" - ] - } - ], - "Order": "order", - "Examples": [ - { - "bar": "bottom", - "place": "status", - "shows": "battery" - }, - { - "bar": "bottom", - "options": { - "device": "BAT0" - }, - "place": "status", - "shows": "battery" - }, - { - "bar": "bottom", - "options": { - "command": "echo \"a line\"", - "interval": 5 - }, - "place": "status", - "shows": "command" - }, - { - "bar": "bottom", - "options": { - "state": "power.draw" - }, - "place": "status", - "shows": "state" - }, - { - "bar": "bottom", - "options": { - "key": "a-machine", - "state": "power.draw" - }, - "place": "status", - "shows": "state" - } - ], - "Text": true - }, - "Offered": true - } - ], - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0208", - "ShapeCode": [ - "block" - ] - }, - { - "Name": "node-compositor", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": null, - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0208" - }, - { - "Name": "node-secret-service", - "Scope": "node", - "Replicated": false, - "Delivers": "", - "Accepts": null, - "Emits": null, - "PulledByKind": false, - "Yields": false, - "Serves": null, - "Receives": null, - "Paths": null, - "HeldByTheEngine": false, - "Decision": "novox/hq ADR 0208" - } -] diff --git a/go/catalogue/verbs_test.go b/go/catalogue/verbs_test.go index 7e7f4c7..8947700 100644 --- a/go/catalogue/verbs_test.go +++ b/go/catalogue/verbs_test.go @@ -51,12 +51,12 @@ func TestHoldingAMeshSeatRequiresServingItsVerbs(t *testing.T) { t.Errorf("a holder saying it serves mint: %v", err) } - // L3: the seat mesh-catalog promises four verbs, each optional while two claimants stand (ADR 0604 §1, §6). + // L3: the seat mesh-catalog promises five verbs (the fifth is the seat list, ADR 0636 §4), each optional while two claimants stand (ADR 0604 §1, §6). catalog, ok := SeatNamed(CatalogSeat) - if !ok || catalog.Scope != ScopeMesh || catalog.Decision != "novox/hq ADR 0604" || len(catalog.Serves) != 4 { + if !ok || catalog.Scope != ScopeMesh || catalog.Decision != "novox/hq ADR 0604" || len(catalog.Serves) != 5 { t.Fatalf("mesh-catalog: in the set %v, %+v", ok, catalog) } - four := []string{"modules", "module", "provides", "dependents"} + four := []string{"modules", "module", "provides", "dependents", "seat-definitions"} for i, v := range catalog.Serves { if v.Name != four[i] || !v.Optional || v.Input == nil { t.Errorf("mesh-catalog's verb %d is %+v, want %s, optional, with its input", i, v, four[i]) diff --git a/go/seatlist/bootstrap/mesh-catalog.json b/go/seatlist/bootstrap/mesh-catalog.json index d036149..9fb8846 100644 --- a/go/seatlist/bootstrap/mesh-catalog.json +++ b/go/seatlist/bootstrap/mesh-catalog.json @@ -16,7 +16,47 @@ }, "answer": { "type": "object", - "description": "what modules answers, as its description says" + "description": "every module the trunk holds, by name", + "properties": { + "modules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "module": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "path": { + "type": "string", + "description": "the module's directory in its repository, '.' for a root" + }, + "commit": { + "type": "string", + "description": "the merge commit it was last changed at" + }, + "fp": { + "type": "string", + "description": "its fingerprint (src2: and 64 hex digits); absent when it has none" + }, + "none": { + "type": "string", + "description": "why it has none" + } + }, + "required": [ + "module", + "repository", + "commit" + ] + } + } + }, + "required": [ + "modules" + ] } }, { @@ -37,7 +77,48 @@ }, "answer": { "type": "object", - "description": "what module answers, as its description says" + "description": "the module whole", + "properties": { + "module": { + "type": "object", + "properties": { + "module": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "path": { + "type": "string", + "description": "the module's directory in its repository, '.' for a root" + }, + "commit": { + "type": "string", + "description": "the merge commit it was last changed at" + }, + "fp": { + "type": "string", + "description": "its fingerprint (src2: and 64 hex digits); absent when it has none" + }, + "none": { + "type": "string", + "description": "why it has none" + }, + "manifest": { + "type": "object", + "description": "the module's manifest as the trunk holds it" + } + }, + "required": [ + "module", + "repository", + "commit" + ] + } + }, + "required": [ + "module" + ] } }, { @@ -84,7 +165,7 @@ }, { "name": "seat-definitions", - "description": "The seat list (ADR 0636 §4): with no seat, every trunk seat with its digest and the revision of the list; with a seat, that trunk definition resolved with the parts it includes. Reads only.", + "description": "The seat list (ADR 0636 \u00a74): with no seat, every trunk seat with its digest and the revision of the list; with a seat, that trunk definition resolved with the parts it includes. Reads only.", "optional": true, "input": { "properties": { @@ -163,7 +244,7 @@ } } ], - "description": "The catalog of the mesh: every module the trunk holds with its manifest, fingerprint and verdict, the packages published, and the seat list (ADR 0636 §1, §4).", + "description": "The catalog of the mesh: every module the trunk holds with its manifest, fingerprint and verdict, the packages published, and the seat list (ADR 0636 \u00a71, \u00a74).", "family": { "name": "mesh-catalog.events", "max-age-days": 7 @@ -344,8 +425,9 @@ "toolchains": { "type": "object", "additionalProperties": { - "description": "toolchain used" - } + "$ref": "#/defs/ToolchainUsed" + }, + "description": "by language: the toolchain FP2 was computed with" }, "paths": { "type": "object", @@ -543,7 +625,8 @@ }, "at": { "type": "string", - "description": "when (RFC 3339 time)" + "description": "when (RFC 3339 time)", + "format": "date-time" } }, "required": [ @@ -576,7 +659,8 @@ }, "at": { "type": "string", - "description": "when (RFC 3339 time)" + "description": "when (RFC 3339 time)", + "format": "date-time" }, "list": { "description": "the foundation's predefined list as kept" @@ -670,7 +754,8 @@ }, "at": { "type": "string", - "description": "when (RFC 3339 time)" + "description": "when (RFC 3339 time)", + "format": "date-time" } }, "required": [ @@ -735,7 +820,8 @@ }, "at": { "type": "string", - "description": "when (RFC 3339 time)" + "description": "when (RFC 3339 time)", + "format": "date-time" } }, "required": [ @@ -830,7 +916,8 @@ }, "at": { "type": "string", - "description": "when (RFC 3339 time)" + "description": "when (RFC 3339 time)", + "format": "date-time" } }, "required": [ @@ -904,7 +991,8 @@ }, "at": { "type": "string", - "description": "when (RFC 3339 time)" + "description": "when (RFC 3339 time)", + "format": "date-time" } }, "required": [ @@ -921,7 +1009,8 @@ }, "at": { "type": "string", - "description": "when (RFC 3339 time)" + "description": "when (RFC 3339 time)", + "format": "date-time" } }, "required": [ @@ -935,5 +1024,29 @@ "description": "the published packages", "history": 5 } - ] + ], + "defs": { + "ToolchainUsed": { + "type": "object", + "description": "a toolchain a fingerprint was computed with (ADR 0405 clause 7)", + "properties": { + "language": { + "type": "string" + }, + "version": { + "type": "string", + "description": "the installed toolchain's version" + }, + "recipe": { + "type": "string", + "description": "the build module's recipe version" + } + }, + "required": [ + "language", + "version", + "recipe" + ] + } + } } diff --git a/go/seatlist/bootstrap/node-engine.json b/go/seatlist/bootstrap/node-engine.json index 541d242..237a651 100644 --- a/go/seatlist/bootstrap/node-engine.json +++ b/go/seatlist/bootstrap/node-engine.json @@ -754,58 +754,84 @@ }, "composed": { "type": "string", - "description": "the composition's digest" + "description": "the digest of the composition the node runs" }, "build": { "type": "string" }, "phase": { "type": "string", - "description": "how far it has come" + "description": "how far it has come", + "enum": [ + "backing-up", + "applying", + "checking", + "running", + "failed", + "removed", + "held", + "waiting-yes" + ] }, "health": { "type": "string", - "description": "how it is" + "description": "what the module's own check says", + "enum": [ + "ok", + "failed", + "waits", + "unknown" + ] }, "detail": { "type": "string" }, "user-list": { - "type": "object", - "description": "the user list written" + "$ref": "#/defs/UserListWritten" }, "judgement": { - "type": "string" + "type": "string", + "description": "in phase waiting-yes, the judgement of the move", + "enum": [ + "recreates", + "moves-data", + "cannot-judge" + ] }, "waits": { "type": "string", - "description": "what it waits for" + "description": "in phase waiting-yes, the digest of the composition that waits" }, "build-alone": { - "type": "boolean" + "type": "boolean", + "description": "the move changes the build alone" }, "kept": { "type": "array", "items": { "type": "string" - } + }, + "description": "what the node keeps of the module as it runs" }, "sealed": { "type": "array", "items": { "type": "string" - } + }, + "description": "each value sealed to the node's key" }, "sealed-at": { - "type": "object" + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "the sealed values by slot" }, "pull-wait": { - "type": "object", - "description": "a pull the engine waits for" + "$ref": "#/defs/PullWait" }, "handed": { - "type": "object", - "description": "the accounts handed" + "$ref": "#/defs/AccountsHanded" } }, "required": [ @@ -826,7 +852,8 @@ "type": "integer" }, "composed": { - "type": "string" + "type": "string", + "description": "the digest of the node's own composition it runs" }, "windows": { "type": "object" @@ -844,8 +871,7 @@ "type": "object" }, "record": { - "type": "object", - "description": "the node's record: its tunnel and certificate" + "$ref": "#/defs/NodeRecord" }, "sealed": { "type": "array", @@ -854,17 +880,21 @@ } }, "sealed-at": { - "type": "object" + "type": "object", + "additionalProperties": { + "type": "string" + } }, "candidates": { "type": "object", "additionalProperties": { - "type": "object" - } + "$ref": "#/defs/CandidateAnswer" + }, + "description": "the node's answer to each placement candidate naming it, by proposal id" }, "composes": { "type": "string", - "description": "own, or the controller's" + "description": "own, or none (empty)" }, "composes-why": { "type": "string" @@ -890,53 +920,96 @@ "type": "object", "description": "one composition of a module on a node under a fingerprint", "properties": { - "V": { + "v": { "type": "integer" }, - "Node": { + "node": { "type": "string" }, - "Module": { + "module": { "type": "string" }, - "FP": { + "fp": { "type": "string" }, - "Data": { - "type": "boolean" + "data": { + "type": "boolean", + "description": "the module declares data" }, - "Resources": { + "resources": { "type": "array", "items": { "type": "object" - } + }, + "description": "the declaration resources, each saying its module" }, - "Filled": { + "filled_files": { "type": "array", "items": { "type": "string" - } + }, + "description": "the files the provisioner fills" }, - "Own": { + "plan": { "type": "object", - "description": "the node's own adoption, identities, groups, libraries and provisioner account" + "description": "the module's whole provisioning plan, catalogue.Plan, kept whole: it is the manifest layer's type" }, - "Plan": { - "type": "object", - "description": "the module's plan" - }, - "Refused": { + "refused": { "type": "string", - "description": "why the composition was refused" + "description": "why the module cannot be composed at this fingerprint" } }, "required": [ - "Node", - "Module", - "FP" + "v", + "node", + "module", + "fp", + "resources" ] }, "description": "one composition" + }, + { + "key": ".node", + "value": { + "type": "object", + "description": "the node's own composition: its resources and parts", + "properties": { + "v": { + "type": "integer" + }, + "node": { + "type": "string" + }, + "data": { + "type": "boolean", + "description": "the module declares data" + }, + "resources": { + "type": "array", + "items": { + "type": "object" + }, + "description": "the declaration resources, each saying its module" + }, + "filled_files": { + "type": "array", + "items": { + "type": "string" + }, + "description": "the files the provisioner fills" + }, + "own": { + "$ref": "#/defs/NodeOwn" + } + }, + "required": [ + "v", + "node", + "resources" + ] + }, + "description": "the node's own composition" } ], "description": "the compositions of one node, kept in a bucket per node named composed-" @@ -966,11 +1039,12 @@ "items": { "type": "array", "items": { - "type": "object" + "$ref": "#/defs/PlacedDataItem" } } }, "required": [ + "v", "node", "module", "digest", @@ -1012,7 +1086,12 @@ "absent": { "type": "boolean" } - } + }, + "required": [ + "package", + "modules", + "absent" + ] } } }, @@ -1043,36 +1122,36 @@ "type": "string" }, "network": { - "type": "object" + "$ref": "#/defs/NetworkHealth" }, "peers": { "type": "array", "items": { - "type": "object" + "$ref": "#/defs/PeerSeen" } }, "units": { - "type": "object" + "$ref": "#/defs/UnitsHealth" }, "relogin": { "type": "array", "items": { - "type": "object" + "$ref": "#/defs/NodeAccount" } }, "found": { "type": "array", "items": { - "type": "object" + "$ref": "#/defs/NodeDirectory" } }, "fallback": { - "type": "object" + "$ref": "#/defs/LoginFallback" }, "root": { "type": "array", "items": { - "type": "object" + "$ref": "#/defs/RootAccountVerdict" } }, "contract": { @@ -1080,6 +1159,7 @@ }, "at": { "type": "string", + "description": "when", "format": "date-time" } }, @@ -1698,6 +1778,874 @@ "at", "said" ] + }, + "UserListWritten": { + "type": "object", + "description": "the bus's user list as the engine of the bus node wrote it to the bus module's file", + "properties": { + "source": { + "type": "string", + "description": "the key the list was taken from" + }, + "revision": { + "type": "integer" + }, + "wanted": { + "type": "string", + "description": "the digest of the list the source holds, sha256:" + }, + "digest": { + "type": "string", + "description": "the digest of the file on the machine after the write" + }, + "users": { + "type": "integer" + }, + "at": { + "type": "string", + "description": "when", + "format": "date-time" + }, + "error": { + "type": "string", + "description": "why a write failed" + }, + "follows": { + "type": "string", + "description": "which list the node follows now" + }, + "key": { + "type": "string", + "description": "the bus node's sealing key, its public half" + }, + "unwatched": { + "type": "string", + "description": "why the bus node does not watch the grants part's key now" + }, + "handed": { + "$ref": "#/defs/AccountsHanded" + } + }, + "required": [ + "source", + "revision", + "wanted", + "users", + "at" + ] + }, + "AccountsHanded": { + "type": "object", + "description": "the hashes of the users of the list, sealed to the vault", + "properties": { + "from": { + "type": "string" + }, + "to": { + "type": "string", + "description": "the fingerprint of the vault's key" + }, + "users": { + "type": "integer" + }, + "box": { + "$ref": "#/defs/SealedBox" + }, + "at": { + "type": "string", + "description": "when", + "format": "date-time" + } + }, + "required": [ + "from", + "to", + "users", + "box", + "at" + ] + }, + "SealedBox": { + "type": "object", + "description": "a value sealed to a key", + "properties": { + "v": { + "type": "integer" + }, + "eph": { + "type": "string" + }, + "iv": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "ct": { + "type": "string" + } + }, + "required": [ + "v", + "eph", + "iv", + "tag", + "ct" + ] + }, + "PullWait": { + "type": "object", + "description": "the pull from a store a failed apply waits on", + "properties": { + "image": { + "type": "string" + }, + "seat": { + "type": "string", + "description": "the seat the pull came through" + }, + "holder": { + "type": "string", + "description": "that seat's holder, whose settling the apply waits for" + }, + "began": { + "type": "string", + "description": "when the pull began", + "format": "date-time" + }, + "retried": { + "type": "string", + "description": "what triggered the last try again, in words" + }, + "retried-at": { + "type": "string", + "description": "when the apply was last tried again", + "format": "date-time" + } + }, + "required": [ + "image", + "holder", + "began" + ] + }, + "NodeRecord": { + "type": "object", + "description": "what the machine observes of itself, for every node and composing part to read", + "properties": { + "v": { + "type": "integer" + }, + "node": { + "type": "string" + }, + "tunnel-key": { + "type": "string" + }, + "capabilities": { + "type": "array", + "items": { + "type": "string" + } + }, + "sealing-key": { + "type": "string" + }, + "serving-key": { + "type": "string" + }, + "certificate": { + "type": "string" + }, + "outward-links": { + "type": "array", + "items": { + "type": "string" + } + }, + "networks": { + "type": "array", + "items": { + "$ref": "#/defs/AttachedNetwork" + } + }, + "networks-said": { + "type": "boolean" + }, + "contract": { + "type": "integer" + }, + "authority": { + "type": "string" + }, + "trusts": { + "type": "array", + "items": { + "type": "string" + } + }, + "manifest-reads": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "v", + "node" + ] + }, + "AttachedNetwork": { + "type": "object", + "description": "a network attached to a link", + "properties": { + "link": { + "type": "string" + }, + "network": { + "type": "string" + } + }, + "required": [ + "link", + "network" + ] + }, + "CandidateAnswer": { + "type": "object", + "description": "the node's answer to one placement candidate naming it", + "properties": { + "proposal": { + "type": "string" + }, + "revision": { + "type": "integer" + }, + "verdict": { + "type": "string", + "description": "same, differs or refused", + "enum": [ + "same", + "differs", + "refused" + ] + }, + "same": { + "type": "array", + "items": { + "type": "string" + }, + "description": "modules whose composition is the one running" + }, + "differs": { + "type": "array", + "items": { + "$ref": "#/defs/CandidateDiffers" + } + }, + "added": { + "type": "array", + "items": { + "$ref": "#/defs/CandidateAdded" + } + }, + "removed": { + "type": "array", + "items": { + "type": "string" + }, + "description": "modules the candidate removes" + }, + "refusals": { + "type": "array", + "items": { + "$ref": "#/defs/CandidateRefusal" + } + }, + "held": { + "type": "array", + "items": { + "$ref": "#/defs/CandidateRefusal" + } + }, + "refused": { + "type": "string", + "description": "why the node cannot compose with the candidate at all" + }, + "returns": { + "type": "string" + }, + "note": { + "type": "string" + }, + "values_differ": { + "type": "integer" + } + }, + "required": [ + "proposal", + "revision", + "verdict" + ] + }, + "CandidateDiffers": { + "type": "object", + "description": "a module whose composition with the candidate differs from the one running", + "properties": { + "module": { + "type": "string" + }, + "composed": { + "type": "string" + }, + "running": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "why": { + "type": "string" + }, + "bodies": { + "type": "array", + "items": { + "$ref": "#/defs/CandidateBodies" + } + } + }, + "required": [ + "module", + "why" + ] + }, + "CandidateBodies": { + "type": "object", + "description": "the bodies of one resource that differ", + "properties": { + "id": { + "type": "string" + }, + "why": { + "type": "string" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "composed": { + "type": "string" + }, + "running": { + "type": "string" + } + }, + "required": [ + "id", + "why" + ] + }, + "CandidateAdded": { + "type": "object", + "description": "a module the candidate adds", + "properties": { + "module": { + "type": "string" + }, + "composed": { + "type": "string" + }, + "unissued": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "module" + ] + }, + "CandidateRefusal": { + "type": "object", + "description": "a module refused or held with the candidate", + "properties": { + "module": { + "type": "string" + }, + "why": { + "type": "string" + }, + "today": { + "type": "string" + } + }, + "required": [ + "module", + "why" + ] + }, + "NodeOwn": { + "type": "object", + "description": "what a node's own composition carries beside its resources", + "properties": { + "adoption": { + "$ref": "#/defs/Adoption" + }, + "identities": { + "type": "array", + "items": { + "$ref": "#/defs/DeclaredIdentity" + } + }, + "groups": { + "type": "array", + "items": { + "$ref": "#/defs/DeclaredGroup" + } + }, + "libraries": { + "type": "array", + "items": { + "$ref": "#/defs/DeclaredLibrary" + } + }, + "provisioner_account": { + "type": "string", + "description": "the account the provisioner's client runs as on the node" + } + } + }, + "Adoption": { + "type": "object", + "description": "the node is adopted and which of its modules are taken", + "properties": { + "taken": { + "type": "array", + "items": { + "type": "string" + }, + "description": "the modules taken" + }, + "untaken": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "by module, the resources left untaken" + } + }, + "required": [ + "taken" + ] + }, + "DeclaredIdentity": { + "type": "object", + "description": "the identity of a module assigned to the node", + "properties": { + "module": { + "type": "string" + }, + "name": { + "type": "string" + }, + "uid": { + "type": "integer" + }, + "gid": { + "type": "integer" + }, + "groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "runner": { + "type": "string" + } + }, + "required": [ + "module", + "name", + "uid", + "gid" + ] + }, + "DeclaredGroup": { + "type": "object", + "description": "a group", + "properties": { + "name": { + "type": "string" + }, + "gid": { + "type": "integer" + } + }, + "required": [ + "name", + "gid" + ] + }, + "DeclaredLibrary": { + "type": "object", + "description": "a library the node is reached by", + "properties": { + "library": { + "type": "string" + }, + "root": { + "type": "string" + }, + "group": { + "type": "string" + }, + "gid": { + "type": "integer" + }, + "home": { + "type": "boolean" + }, + "taken": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "runner": { + "type": "string" + } + }, + "required": [ + "library", + "root", + "group", + "gid", + "home" + ] + }, + "PlacedDataItem": { + "type": "object", + "description": "one data item a module declares, as placed on a node", + "properties": { + "module": { + "type": "string" + }, + "item": { + "type": "string" + }, + "class": { + "type": "string" + }, + "path": { + "type": "string", + "description": "the item's place on the node" + }, + "covered_by": { + "type": "string", + "description": "the path whose snapshot keeps it, empty when it is not backed up" + }, + "protection": { + "type": "string", + "description": "backup, redundancy, both or none" + }, + "measure": { + "type": "string", + "description": "walk, dataset or shallow" + } + }, + "required": [ + "module", + "item", + "class", + "path" + ] + }, + "NetworkHealth": { + "type": "object", + "description": "the node's network", + "properties": { + "state": { + "type": "string", + "description": "running, degraded or unknown" + }, + "since": { + "type": "string", + "description": "since when", + "format": "date-time" + }, + "parts": { + "type": "array", + "items": { + "$ref": "#/defs/NetworkPart" + } + } + }, + "required": [ + "state", + "since", + "parts" + ] + }, + "NetworkPart": { + "type": "object", + "description": "one part of the node's network", + "properties": { + "part": { + "type": "string" + }, + "state": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "said": { + "type": "string" + }, + "writer": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "toward": { + "type": "array", + "items": { + "type": "string" + } + }, + "targets": { + "type": "array", + "items": { + "type": "string" + } + }, + "since": { + "type": "string", + "description": "since when", + "format": "date-time" + }, + "streak": { + "type": "integer" + } + }, + "required": [ + "part", + "state", + "since" + ] + }, + "UnitsHealth": { + "type": "object", + "description": "the node's units", + "properties": { + "state": { + "type": "string" + }, + "failed": { + "type": "array", + "items": { + "$ref": "#/defs/FailedUnit" + } + }, + "unread": { + "type": "array", + "items": { + "type": "string" + } + }, + "away": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "state", + "failed" + ] + }, + "FailedUnit": { + "type": "object", + "description": "a unit that failed", + "properties": { + "unit": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "load": { + "type": "string" + }, + "result": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "since": { + "type": "string", + "description": "since when", + "format": "date-time" + } + }, + "required": [ + "unit", + "scope", + "since" + ] + }, + "PeerSeen": { + "type": "object", + "description": "a tunnel peer and whether it is seen", + "properties": { + "key": { + "type": "string" + }, + "addresses": { + "type": "array", + "items": { + "type": "string" + } + }, + "seen": { + "type": "boolean" + } + }, + "required": [ + "key", + "seen" + ] + }, + "NodeAccount": { + "type": "object", + "description": "an account the node needs to log in again", + "properties": { + "account": { + "type": "string" + }, + "module": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "since": { + "type": "string", + "description": "since when", + "format": "date-time" + } + }, + "required": [ + "account", + "reason", + "since" + ] + }, + "NodeDirectory": { + "type": "object", + "description": "a directory found where the node expected none", + "properties": { + "module": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "path": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "since": { + "type": "string", + "description": "since when", + "format": "date-time" + } + }, + "required": [ + "module", + "resource", + "path", + "reason", + "since" + ] + }, + "LoginFallback": { + "type": "object", + "description": "a login that fell back to another node", + "properties": { + "node": { + "type": "string" + }, + "from": { + "type": "string" + }, + "why": { + "type": "string" + }, + "at": { + "type": "string", + "description": "when", + "format": "date-time" + } + }, + "required": [ + "node", + "from", + "why", + "at" + ] + }, + "RootAccountVerdict": { + "type": "object", + "description": "the engine's verdict of whether an account's resource is root-free, as the node's health lists it", + "properties": { + "account": { + "type": "string" + }, + "module": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "state": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "since": { + "type": "string", + "description": "since when", + "format": "date-time" + } + }, + "required": [ + "account", + "module", + "resource", + "state", + "since" + ] } } } diff --git a/go/seatlist/bootstrap/operator-channel.json b/go/seatlist/bootstrap/operator-channel.json index 0bd8c21..3f33532 100644 --- a/go/seatlist/bootstrap/operator-channel.json +++ b/go/seatlist/bootstrap/operator-channel.json @@ -182,7 +182,56 @@ "key": ".", "value": { "type": "object", - "description": "one ask the router holds and how it stands" + "description": "one ask the router holds and how it stands, as the router's tool states it", + "properties": { + "key": { + "type": "string", + "description": "." + }, + "asker": { + "type": "string", + "description": "its asker" + }, + "headline": { + "type": "string", + "description": "its headline" + }, + "state": { + "type": "string", + "description": "how it stands: open, chosen, expired, cancelled, replaced or refused" + }, + "opened": { + "type": "string", + "description": "when it was opened, RFC 3339" + }, + "expires": { + "type": "string", + "description": "when it ends unanswered, RFC 3339" + }, + "offered_on": { + "type": "array", + "items": { + "type": "string" + }, + "description": "the channels it was offered on" + }, + "asker_told": { + "type": "boolean", + "description": "its asker has been told how it ended" + }, + "answer": { + "type": "string", + "description": "how it ended, in words" + } + }, + "required": [ + "key", + "asker", + "headline", + "state", + "opened", + "expires" + ] }, "description": "one ask the router holds and how it stands" } @@ -563,6 +612,250 @@ "subject", "state" ] + }, + "Button": { + "type": "object", + "description": "one answer a channel offers: its label and the router's one-time ticket for it", + "properties": { + "label": { + "type": "string", + "description": "the button's label" + }, + "ticket": { + "type": "string", + "description": "the router's one-time ticket for it" + } + }, + "required": [ + "label", + "ticket" + ] + }, + "Message": { + "type": "object", + "description": "the work a channel takes on the bench channel: shown with buttons (show), shown again in place (edit) or said (send); the words are the router's, shown as given", + "properties": { + "handle": { + "type": "string", + "description": "the router's name for it, the same across a show and its edits" + }, + "title": { + "type": "string", + "description": "its title" + }, + "body": { + "type": "string", + "description": "its body" + }, + "buttons": { + "type": "array", + "items": { + "$ref": "#/defs/Button" + }, + "description": "the answers it offers (none for a send)" + }, + "note": { + "type": "string", + "description": "its secondary words after the body and the buttons: who asked and when, what each answer does, when it expires, the ask's details" + }, + "urgent": { + "type": "boolean", + "description": "urgent" + }, + "silent": { + "type": "boolean", + "description": "shown without notifying" + }, + "reply": { + "type": "string", + "description": "the choice or link request this answers, by its id: the channel shows it where that was made" + }, + "to": { + "type": "string", + "description": "the account linked as the operator on this kind, where the channel sends what is not a reply; empty when none is linked" + }, + "secret": { + "type": "boolean", + "description": "the words carry something shown once (a link's code): the channel keeps no copy" + } + }, + "required": [ + "handle", + "title", + "body" + ] + }, + "Sender": { + "type": "object", + "description": "who a channel's service says sent something; the router alone judges whether that is the operator", + "properties": { + "identity": { + "type": "string", + "description": "the account" + }, + "display": { + "type": "string", + "description": "the name it shows" + }, + "authenticated": { + "type": "boolean", + "description": "whether the service authenticated it" + } + }, + "required": [ + "identity", + "authenticated" + ] + }, + "Choice": { + "type": "object", + "description": "a button chosen on a channel, said on the bench intake as the event `choice`", + "properties": { + "id": { + "type": "string", + "description": "the channel's own for this arrival, unique, so the router acts on it once" + }, + "ticket": { + "type": "string", + "description": "the ticket of the button" + }, + "handle": { + "type": "string", + "description": "the message's handle" + }, + "sender": { + "$ref": "#/defs/Sender" + }, + "at": { + "type": "string", + "description": "when, RFC 3339", + "format": "date-time" + } + }, + "required": [ + "id", + "ticket", + "sender", + "at" + ] + }, + "LinkAsked": { + "type": "object", + "description": "somebody on a channel asking to be linked as the operator, said on the bench intake as the event `link`", + "properties": { + "id": { + "type": "string", + "description": "the channel's own for this arrival" + }, + "sender": { + "$ref": "#/defs/Sender" + }, + "at": { + "type": "string", + "description": "when, RFC 3339", + "format": "date-time" + } + }, + "required": [ + "id", + "sender", + "at" + ] + }, + "Failed": { + "type": "object", + "description": "a message a channel could not deliver, said on the bench intake as the event `failed`", + "properties": { + "handle": { + "type": "string", + "description": "the message's handle" + }, + "why": { + "type": "string", + "description": "why" + }, + "permanent": { + "type": "boolean", + "description": "trying again cannot help" + }, + "at": { + "type": "string", + "description": "when, RFC 3339", + "format": "date-time" + } + }, + "required": [ + "handle", + "why", + "at" + ] + }, + "Standing": { + "type": "object", + "description": "what a channel says of itself at least every five minutes and whenever it changes, on the bench intake as the event `standing`", + "properties": { + "ready": { + "type": "boolean", + "description": "it can send now" + }, + "why": { + "type": "string", + "description": "why not" + }, + "edits-silently": { + "type": "boolean", + "description": "its edits notify nobody" + }, + "at": { + "type": "string", + "description": "when, RFC 3339", + "format": "date-time" + } + }, + "required": [ + "ready", + "at" + ] + }, + "Code": { + "type": "object", + "description": "a code a person typed on a channel, asked as the proof `code` on the bench intake by request and reply: never in an event, never kept", + "properties": { + "sender": { + "$ref": "#/defs/Sender" + }, + "code": { + "type": "string", + "description": "the code" + }, + "purpose": { + "type": "string", + "description": "what it is for" + } + }, + "required": [ + "sender", + "code", + "purpose" + ] + }, + "ProofAnswer": { + "type": "object", + "description": "the router's answer to a proof", + "properties": { + "accepted": { + "type": "boolean", + "description": "whether it was taken" + }, + "words": { + "type": "string", + "description": "words to say to the person" + } + }, + "required": [ + "accepted", + "words" + ] } } } diff --git a/go/seatlist/seatlist_test.go b/go/seatlist/seatlist_test.go index d1eca62..e196f4d 100644 --- a/go/seatlist/seatlist_test.go +++ b/go/seatlist/seatlist_test.go @@ -4,12 +4,14 @@ import ( "encoding/json" "errors" "fmt" + "reflect" "strings" "testing" stdio "git.novox.be/novox/mesh-sdk/go" "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-sdk/go/seatdef" + "git.novox.be/novox/mesh-sdk/go/seatlist/seatlisttest" ) // fakeOwner is the seat mesh-catalog: its verb answers from its table and the events are delivered by the test. @@ -265,3 +267,22 @@ func TestStartReturnsAtOnceAndTheReaderSaysItIsReadingUntilTheSeatAnswers(t *tes t.Fatalf("after the read: %+v", st) } } + +// The bootstrap and the tests' seat list are copies of the same contracts files: where a seat is in both, they are equal, +// so a seat that changes is replaced in both (ADR 0636 §7). +func TestTheBootstrapEqualsTheTestsSeatList(t *testing.T) { + tests := map[string]seatdef.Definition{} + for _, d := range seatlisttest.Definitions() { + tests[d.Seat] = d + } + for _, b := range Bootstrapped() { + got, ok := tests[b.Seat] + if !ok { + t.Errorf("%s is bootstrapped but not in seatlisttest", b.Seat) + continue + } + if !reflect.DeepEqual(b, got) { + t.Errorf("%s: bootstrap/%s.json differs from seatlisttest/seats/%s.json", b.Seat, b.Seat, b.Seat) + } + } +} diff --git a/go/seatlist/seatlisttest/parts/build-step.json b/go/seatlist/seatlisttest/parts/build-step.json new file mode 100644 index 0000000..5592037 --- /dev/null +++ b/go/seatlist/seatlisttest/parts/build-step.json @@ -0,0 +1,721 @@ +{ + "format": 1, + "part": "build-step", + "description": "What the three build seats share: the verbs that read and steer a node's builds, and the layouts of the asks they accept and the steps they keep. A seat lists its states against these (a part carries no states).", + "verbs": [ + { + "name": "current", + "description": "The asks this node runs now (each ask's id, kind, module and when it started), its build capacity, and whether it is paused, taking no new ask.", + "input": { + "type": "object", + "properties": {} + }, + "answer": { + "type": "object", + "description": "the steps running, the capacity and whether paused" + } + }, + { + "name": "pause", + "description": "Take no new ask on this node until resumed; an ask running here ends in its place. Kept across a restart of the build module. A capacity of 0 is the lasting way to keep a node from building.", + "input": { + "type": "object", + "properties": {} + }, + "answer": { + "type": "object", + "description": "whether the node is paused" + } + }, + { + "name": "resume", + "description": "Take asks again on this node.", + "input": { + "type": "object", + "properties": {} + }, + "answer": { + "type": "object", + "description": "whether the node is paused" + } + } + ], + "defs": { + "StepAsk": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "id": { + "type": "string" + }, + "kind": { + "type": "string", + "description": "build, check or compose" + }, + "language": { + "type": "string", + "description": "go, typescript or image" + }, + "asker": { + "type": "string" + }, + "at": { + "type": "string", + "description": "when (RFC 3339 time)", + "format": "date-time" + }, + "build": { + "type": "object", + "properties": { + "module": { + "type": "string" + }, + "path": { + "type": "string" + }, + "fp": { + "type": "string" + }, + "fetch": { + "$ref": "#/defs/Fetch" + }, + "artifacts": { + "type": "array", + "items": { + "$ref": "#/defs/AskArtifact" + } + }, + "publish": { + "type": "boolean" + }, + "toolchain": { + "$ref": "#/defs/ToolchainUsed" + } + }, + "required": [ + "module", + "artifacts" + ] + }, + "check": { + "type": "object", + "properties": { + "fetch": { + "$ref": "#/defs/Fetch" + }, + "levels": { + "type": "array", + "items": { + "type": "string", + "description": "unit or integration" + } + }, + "kept": { + "type": "boolean" + }, + "runner": { + "type": "string" + }, + "runner_when": { + "type": "string" + }, + "run": { + "type": "string" + }, + "packages": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "fetch", + "levels" + ] + }, + "compose": { + "type": "object", + "properties": { + "fetch": { + "$ref": "#/defs/Fetch" + }, + "dir": { + "type": "string" + }, + "package": { + "type": "string" + }, + "request": { + "description": "the compose-candidate request" + }, + "reader": { + "type": "string" + } + }, + "required": [ + "fetch", + "dir", + "package", + "request" + ] + }, + "needs": { + "type": "array", + "items": { + "type": "string" + } + }, + "packages": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "package to the tagged version a build pins" + } + }, + "required": [ + "id", + "kind", + "language", + "asker" + ], + "description": "one ask of a build module" + }, + "BuildAnswer": { + "type": "object", + "properties": { + "artifacts": { + "type": "array", + "items": { + "$ref": "#/defs/BuiltArtifact" + } + }, + "toolchain": { + "$ref": "#/defs/ToolchainUsed" + }, + "imports": { + "type": "array", + "items": { + "type": "string" + } + }, + "out-of-step": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "artifacts" + ] + }, + "CheckAnswer": { + "type": "object", + "properties": { + "fetch": { + "$ref": "#/defs/Fetch" + }, + "levels": { + "type": "array", + "items": { + "$ref": "#/defs/LevelAnswer" + } + } + }, + "required": [ + "fetch" + ] + }, + "ComposeAnswer": { + "type": "object", + "properties": { + "built": { + "type": "boolean" + }, + "key": { + "type": "string" + }, + "verdict": { + "description": "the compose-candidate verdict" + } + }, + "required": [ + "built" + ] + }, + "Step": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "id": { + "type": "string" + }, + "tool": { + "type": "string" + }, + "node": { + "type": "string" + }, + "state": { + "type": "string", + "description": "running, passed, failed or refused" + }, + "started": { + "type": "string", + "description": "when (RFC 3339 time)", + "format": "date-time" + }, + "ended": { + "type": "string", + "description": "when (RFC 3339 time)", + "format": "date-time" + }, + "reason": { + "type": "string" + }, + "result": { + "$ref": "#/defs/StepResult" + }, + "what": { + "type": "string" + }, + "phase": { + "type": "string" + }, + "phase-at": { + "type": "string", + "description": "when (RFC 3339 time)", + "format": "date-time" + }, + "owner": { + "type": "string" + } + }, + "required": [ + "id", + "tool", + "node", + "state", + "started" + ], + "description": "one step a build module runs on a node" + }, + "Toolchain": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "language": { + "type": "string", + "description": "go or typescript" + }, + "version": { + "type": "string" + }, + "recipe": { + "type": "string" + }, + "at": { + "type": "string", + "description": "when (RFC 3339 time)", + "format": "date-time" + } + }, + "required": [ + "language", + "version", + "recipe" + ], + "description": "the toolchain a node reports" + }, + "Pause": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "paused": { + "type": "boolean" + }, + "by": { + "type": "string" + }, + "at": { + "type": "string", + "description": "when (RFC 3339 time)", + "format": "date-time" + } + }, + "required": [ + "paused" + ], + "description": "whether the node takes no new ask" + }, + "Capacity": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "effective": { + "type": "integer" + }, + "configured": { + "type": "integer" + }, + "configured-from": { + "type": "string" + }, + "quota-cores": { + "type": "number" + }, + "quota-from": { + "type": "string" + }, + "cores-per-step": { + "type": "number" + }, + "by-cpu": { + "type": "integer" + }, + "build-hours": { + "type": "string" + }, + "outside-hours": { + "type": "boolean" + }, + "build-hours-fault": { + "type": "string" + }, + "ineligible": { + "type": "string" + }, + "why": { + "type": "string" + }, + "at": { + "type": "string", + "description": "when (RFC 3339 time)", + "format": "date-time" + } + }, + "required": [ + "effective", + "configured", + "why" + ], + "description": "how many steps the node runs at once" + }, + "Fetch": { + "type": "object", + "description": "what a build module's fetch tool is asked: the repository and the full commit, the pull request's ref when only its head carries the commit, the trunk commit to merge onto for a merge preview, the module's directory and named paths, the mesh packages to build against, and the sibling repositories placed beside", + "properties": { + "repository": { + "type": "string", + "description": "/" + }, + "commit": { + "type": "string", + "description": "the full commit" + }, + "ref": { + "type": "string", + "description": "the pull request's ref, when only its head carries the commit" + }, + "base": { + "type": "string", + "description": "the trunk commit a merge preview merges onto" + }, + "dir": { + "type": "string", + "description": "the module's directory in the repository (\".\" for its root), with paths" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + }, + "description": "the paths the module names, relative to dir" + }, + "packages": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "the version (a full commit) of each mesh package to build against, by its module path" + }, + "beside": { + "type": "array", + "items": { + "type": "object", + "properties": { + "repository": { + "type": "string" + }, + "commit": { + "type": "string" + } + }, + "required": [ + "repository", + "commit" + ] + }, + "description": "the sibling repositories placed beside the fetched tree" + } + }, + "required": [ + "repository", + "commit" + ] + }, + "ToolchainUsed": { + "type": "object", + "properties": { + "language": { + "type": "string" + }, + "version": { + "type": "string" + }, + "recipe": { + "type": "string" + } + }, + "required": [ + "language", + "version", + "recipe" + ], + "description": "the toolchain a build was made with: the language, the installed version and the build module's recipe" + }, + "AskArtifact": { + "type": "object", + "properties": { + "artifact": { + "type": "string" + }, + "from": { + "type": "string" + }, + "seat": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "required": [ + "artifact" + ], + "description": "one artifact of a build ask: built from the fetched tree, or copied from an upstream pin" + }, + "BuiltArtifact": { + "type": "object", + "properties": { + "artifact": { + "type": "string" + }, + "digest": { + "type": "string" + }, + "reference": { + "type": "string" + } + }, + "required": [ + "artifact", + "digest" + ], + "description": "one artifact a build made: its digest and the reference it was published under" + }, + "LevelAnswer": { + "type": "object", + "properties": { + "level": { + "type": "string" + }, + "judged": { + "type": "string" + }, + "digest": { + "type": "object", + "properties": { + "level": { + "type": "string" + }, + "packages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "package": { + "type": "string" + }, + "digest": { + "type": "string" + } + }, + "required": [ + "package", + "digest" + ] + } + }, + "beside": { + "type": "array", + "items": { + "type": "string" + } + }, + "without_tests": { + "type": "array", + "items": { + "type": "string" + } + }, + "judged_by": { + "type": "string" + }, + "needs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "package": { + "type": "string" + }, + "needs": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + }, + "kept": { + "type": "array", + "items": { + "type": "object", + "properties": { + "package": { + "type": "string" + }, + "digest": { + "type": "string" + }, + "tests": { + "type": "integer" + } + } + } + }, + "tests": { + "type": "array", + "items": { + "description": "one test's result" + } + }, + "unmet": { + "type": "array", + "items": { + "type": "object", + "properties": { + "package": { + "type": "string" + }, + "needs": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "required": [ + "level" + ], + "description": "one level of a check ask: the digest's answer, the kept verdicts taken, one test result per test step run, and the packages the node could not test" + }, + "StepResult": { + "type": "object", + "description": "what a step answered, by the kind of ask: a build's artifacts, toolchain, imports and out-of-step modules; a check's fetch and levels; a compose's built, key and verdict; a declined ask's declined, needs and lacks. The fields of the kinds do not meet, so one layout holds the four", + "properties": { + "artifacts": { + "type": "array", + "items": { + "$ref": "#/defs/BuiltArtifact" + } + }, + "toolchain": { + "$ref": "#/defs/ToolchainUsed" + }, + "imports": { + "type": "array", + "items": { + "type": "string" + } + }, + "out-of-step": { + "type": "array", + "items": { + "type": "string" + } + }, + "fetch": { + "$ref": "#/defs/Fetch" + }, + "levels": { + "type": "array", + "items": { + "$ref": "#/defs/LevelAnswer" + } + }, + "built": { + "type": "boolean" + }, + "key": { + "type": "string" + }, + "verdict": { + "description": "the compose-candidate verdict (the composer's own layout)" + }, + "declined": { + "type": "boolean", + "description": "the holder declined the ask: its node lacks what the ask needs" + }, + "needs": { + "type": "array", + "items": { + "type": "string" + }, + "description": "what the declined ask needs" + }, + "lacks": { + "type": "array", + "items": { + "type": "string" + }, + "description": "what the holder's node lacks of it" + } + } + } + } +} diff --git a/go/seatlist/seatlisttest/seats/container-image-registry.json b/go/seatlist/seatlisttest/seats/container-image-registry.json index 3de19d8..bc116b4 100644 --- a/go/seatlist/seatlisttest/seats/container-image-registry.json +++ b/go/seatlist/seatlisttest/seats/container-image-registry.json @@ -341,5 +341,140 @@ "held" ] } - } + }, + "states": [ + { + "name": "holds", + "template": "state", + "history": 1, + "description": "the bundles, archives and copied bases kept from retention's removal", + "key-forms": [ + { + "key": "..", + "value": { + "type": "object", + "description": "one hold: an untagged bundle, archive or copied base kept from retention's removal", + "properties": { + "seat": { + "type": "string" + }, + "name": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "digest": { + "type": "string" + }, + "by": { + "type": "string" + }, + "why": { + "type": "string" + }, + "since": { + "type": "string", + "description": "when (RFC 3339 time)" + } + }, + "required": [ + "seat", + "name", + "kind", + "digest", + "by", + "since" + ] + }, + "description": "one hold" + } + ] + }, + { + "name": "windows", + "template": "state", + "history": 1, + "description": "the direct-write windows, one per caller", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "description": "the direct-write window of one caller: open while a node holds it open", + "properties": { + "name": { + "type": "string", + "description": "the caller whose window it is" + }, + "open": { + "type": "boolean" + }, + "holding": { + "type": "array", + "items": { + "type": "string" + }, + "description": "the nodes holding it open" + }, + "since": { + "type": "object", + "additionalProperties": { + "type": "string", + "description": "when (RFC 3339 time)" + }, + "description": "node to when it began to hold" + }, + "closed": { + "type": "string", + "description": "when it last closed (RFC 3339 time)" + }, + "last_holding": { + "type": "array", + "items": { + "type": "string" + } + }, + "dropped": { + "type": "array", + "items": { + "type": "object", + "properties": { + "node": { + "type": "string" + }, + "by": { + "type": "string" + }, + "why": { + "type": "string" + }, + "at": { + "type": "string", + "description": "when (RFC 3339 time)" + } + }, + "required": [ + "node" + ] + }, + "description": "retired nodes dropped by hand" + }, + "why": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "of each node holding the window, why it holds it" + } + }, + "required": [ + "name" + ] + }, + "description": "one caller's window" + } + ] + } + ] } diff --git a/go/seatlist/seatlisttest/seats/mesh-catalog.json b/go/seatlist/seatlisttest/seats/mesh-catalog.json index 5f71e23..9fb8846 100644 --- a/go/seatlist/seatlisttest/seats/mesh-catalog.json +++ b/go/seatlist/seatlisttest/seats/mesh-catalog.json @@ -2,7 +2,7 @@ "format": 1, "seat": "mesh-catalog", "scope": "mesh", - "grain": "seat-list", + "grain": "whole", "domain": "Delivery", "decision": "novox/hq ADR 0604", "verbs": [ @@ -13,6 +13,50 @@ "input": { "properties": {}, "type": "object" + }, + "answer": { + "type": "object", + "description": "every module the trunk holds, by name", + "properties": { + "modules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "module": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "path": { + "type": "string", + "description": "the module's directory in its repository, '.' for a root" + }, + "commit": { + "type": "string", + "description": "the merge commit it was last changed at" + }, + "fp": { + "type": "string", + "description": "its fingerprint (src2: and 64 hex digits); absent when it has none" + }, + "none": { + "type": "string", + "description": "why it has none" + } + }, + "required": [ + "module", + "repository", + "commit" + ] + } + } + }, + "required": [ + "modules" + ] } }, { @@ -30,6 +74,51 @@ "module" ], "type": "object" + }, + "answer": { + "type": "object", + "description": "the module whole", + "properties": { + "module": { + "type": "object", + "properties": { + "module": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "path": { + "type": "string", + "description": "the module's directory in its repository, '.' for a root" + }, + "commit": { + "type": "string", + "description": "the merge commit it was last changed at" + }, + "fp": { + "type": "string", + "description": "its fingerprint (src2: and 64 hex digits); absent when it has none" + }, + "none": { + "type": "string", + "description": "why it has none" + }, + "manifest": { + "type": "object", + "description": "the module's manifest as the trunk holds it" + } + }, + "required": [ + "module", + "repository", + "commit" + ] + } + }, + "required": [ + "module" + ] } }, { @@ -47,6 +136,10 @@ "provision" ], "type": "object" + }, + "answer": { + "type": "object", + "description": "what provides answers, as its description says" } }, { @@ -64,7 +157,896 @@ "module" ], "type": "object" + }, + "answer": { + "type": "object", + "description": "what dependents answers, as its description says" + } + }, + { + "name": "seat-definitions", + "description": "The seat list (ADR 0636 \u00a74): with no seat, every trunk seat with its digest and the revision of the list; with a seat, that trunk definition resolved with the parts it includes. Reads only.", + "optional": true, + "input": { + "properties": { + "seat": { + "description": "the seat's name; none lists every trunk seat", + "type": "string" + } + }, + "type": "object" + }, + "answer": { + "type": "object", + "description": "with no seat: the revision of the list and every trunk seat with its digest; with a seat: whether it was found and its entry, the definition resolved", + "properties": { + "revision": { + "type": "integer" + }, + "seats": { + "type": "array", + "items": { + "type": "object", + "properties": { + "seat": { + "type": "string" + }, + "source": { + "type": "string", + "description": "trunk" + }, + "digest": { + "type": "string" + } + }, + "required": [ + "seat", + "source", + "digest" + ] + } + }, + "seat": { + "type": "string" + }, + "found": { + "type": "boolean" + }, + "entry": { + "type": "object", + "properties": { + "seat": { + "type": "string" + }, + "source": { + "type": "string", + "description": "trunk" + }, + "digest": { + "type": "string" + }, + "definition": { + "type": "object", + "description": "the definition, resolved with the parts it includes" + }, + "unparsed": { + "type": "string", + "description": "why the definition could not be read, when it could not" + } + }, + "required": [ + "seat", + "source", + "digest" + ] + } + } } } - ] + ], + "description": "The catalog of the mesh: every module the trunk holds with its manifest, fingerprint and verdict, the packages published, and the seat list (ADR 0636 \u00a71, \u00a74).", + "family": { + "name": "mesh-catalog.events", + "max-age-days": 7 + }, + "events": [ + { + "name": "seat-definition-changed", + "body": { + "type": "object", + "properties": { + "seat": { + "type": "string" + }, + "source": { + "type": "string", + "description": "trunk" + }, + "digest": { + "type": "string" + } + }, + "required": [ + "seat", + "source", + "digest" + ] + } + }, + { + "name": "seat-definition-removed", + "body": { + "type": "object", + "properties": { + "seat": { + "type": "string" + } + }, + "required": [ + "seat" + ] + } + } + ], + "states": [ + { + "name": "manifests", + "template": "state", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "module": { + "type": "string" + }, + "manifest": { + "type": "string", + "description": "the manifest, UTF-8 JSON text" + } + }, + "required": [ + "module", + "manifest" + ] + }, + "description": "a manifest by its digest, written once" + } + ], + "description": "every manifest the indexer judged", + "history": 1 + }, + { + "name": "modules", + "template": "state", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "fp": { + "type": "string", + "description": "the old-form fingerprint" + }, + "none": { + "type": "string" + }, + "fp2": { + "type": "string", + "description": "src2: fingerprint" + }, + "none2": { + "type": "string", + "description": "why it has none" + }, + "manifest": { + "type": "string", + "description": "manifest digest" + }, + "inputs2": { + "type": "object", + "properties": { + "tree": { + "type": "string" + }, + "bases": { + "type": "array", + "items": { + "type": "string" + } + }, + "builder": { + "type": "string" + }, + "contexts": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "locks": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "paths": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "packages": { + "type": "array", + "items": { + "type": "string" + } + }, + "imports": { + "type": "array", + "items": { + "type": "string" + } + }, + "import-form": { + "type": "string" + } + }, + "required": [ + "tree" + ] + }, + "src": { + "type": "integer", + "description": "the source revision" + }, + "fp-src": { + "type": "integer" + }, + "repository": { + "type": "string" + }, + "commit": { + "type": "string" + }, + "path": { + "type": "string" + }, + "toolchains": { + "type": "object", + "additionalProperties": { + "$ref": "#/defs/ToolchainUsed" + }, + "description": "by language: the toolchain FP2 was computed with" + }, + "paths": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "packages": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "verdict": { + "type": "string" + }, + "problems": { + "type": "array", + "items": { + "type": "string" + } + }, + "unknown": { + "type": "array", + "items": { + "type": "string" + } + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } + }, + "needs": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "repository", + "commit" + ], + "description": "one module as the trunk holds it" + }, + "description": "a module the trunk holds" + } + ], + "description": "the module catalog", + "history": 20 + }, + { + "name": "candidates", + "template": "state", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "group": { + "type": "string" + }, + "base": { + "type": "integer", + "description": "the source revision it is read on" + }, + "heads": { + "type": "string" + }, + "pulls": { + "type": "array", + "items": { + "type": "object", + "properties": { + "repository": { + "type": "string" + }, + "number": { + "type": "integer" + }, + "head": { + "type": "string" + }, + "modules": { + "type": "array", + "items": { + "type": "string" + } + }, + "fps": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "fp2": { + "type": "string" + }, + "none2": { + "type": "string" + }, + "manifest": { + "type": "string" + }, + "paths": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "packages": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + } + }, + "required": [ + "repository", + "number", + "head", + "modules" + ] + } + }, + "fpset": { + "type": "string" + } + }, + "required": [ + "group", + "base", + "heads", + "pulls" + ], + "description": "the modules a group of open pull requests would change" + }, + "description": "a group's candidate" + } + ], + "description": "the candidates of open groups", + "history": 10 + }, + { + "name": "verdicts", + "template": "state", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "module": { + "type": "string" + }, + "state": { + "type": "string" + }, + "problems": { + "type": "array", + "items": { + "type": "string" + } + }, + "unknown": { + "type": "array", + "items": { + "type": "string" + } + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } + }, + "judge": { + "type": "string" + }, + "at": { + "type": "string", + "description": "when (RFC 3339 time)", + "format": "date-time" + } + }, + "required": [ + "module", + "state" + ], + "description": "a manifest's verdict, by the judge named" + }, + "description": "a manifest's verdict by its digest" + } + ], + "description": "the manifest verdicts", + "history": 5 + }, + { + "name": "predefined", + "template": "state", + "key-forms": [ + { + "key": "list", + "value": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "commit": { + "type": "string" + }, + "at": { + "type": "string", + "description": "when (RFC 3339 time)", + "format": "date-time" + }, + "list": { + "description": "the foundation's predefined list as kept" + } + }, + "required": [ + "commit", + "list" + ] + }, + "description": "the one key" + } + ], + "description": "the kept predefined list", + "history": 5 + }, + { + "name": "config-candidates", + "template": "state", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "pull": { + "type": "string" + }, + "head": { + "type": "string" + }, + "declares": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "kind": { + "type": "string" + }, + "default": { + "description": "the default" + }, + "why": { + "type": "string" + }, + "type": { + "type": "string" + }, + "description": { + "type": "string" + }, + "enum": { + "description": "the allowed values" + }, + "minimum": { + "type": "number" + }, + "maximum": { + "type": "number" + }, + "pattern": { + "type": "string" + }, + "trusted": { + "type": "boolean" + }, + "predefined": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "runtime": { + "type": "boolean" + } + }, + "required": [ + "kind", + "default", + "why" + ] + } + }, + "description": "module to key to declaration" + }, + "at": { + "type": "string", + "description": "when (RFC 3339 time)", + "format": "date-time" + } + }, + "required": [ + "pull", + "head", + "declares" + ] + }, + "description": "the configuration a pull request declares" + } + ], + "description": "the configuration declared by open pull requests", + "history": 5 + }, + { + "name": "packages", + "template": "state", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "module": { + "type": "string", + "description": "the Go module path" + }, + "repository": { + "type": "string" + }, + "dir": { + "type": "string" + }, + "commit": { + "type": "string" + }, + "requires": { + "type": "string", + "description": "digest of go.mod and go.sum" + }, + "packages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "package": { + "type": "string" + }, + "digest": { + "type": "string" + } + }, + "required": [ + "package", + "digest" + ] + } + }, + "at": { + "type": "string", + "description": "when (RFC 3339 time)", + "format": "date-time" + } + }, + "required": [ + "module", + "repository", + "commit", + "packages" + ] + }, + "description": "a mesh Go module as its latest trunk merge published it" + }, + { + "key": ".", + "value": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "name": { + "type": "string" + }, + "package": { + "type": "string" + }, + "series": { + "type": "string" + }, + "seat": { + "type": "string" + }, + "go-module": { + "type": "string" + }, + "newest": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "commit": { + "type": "string" + }, + "digest": { + "type": "string" + }, + "fingerprint": { + "type": "string" + }, + "contents": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "module": { + "type": "string", + "description": "the Go module path" + }, + "repository": { + "type": "string" + }, + "dir": { + "type": "string" + }, + "commit": { + "type": "string" + }, + "requires": { + "type": "string", + "description": "digest of go.mod and go.sum" + }, + "packages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "package": { + "type": "string" + }, + "digest": { + "type": "string" + } + }, + "required": [ + "package", + "digest" + ] + } + }, + "at": { + "type": "string", + "description": "when (RFC 3339 time)", + "format": "date-time" + } + }, + "required": [ + "module", + "repository", + "commit", + "packages" + ] + } + }, + "required": [ + "version" + ] + }, + "numbered": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "commit": { + "type": "string" + }, + "digest": { + "type": "string" + }, + "fingerprint": { + "type": "string" + }, + "contents": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "module": { + "type": "string", + "description": "the Go module path" + }, + "repository": { + "type": "string" + }, + "dir": { + "type": "string" + }, + "commit": { + "type": "string" + }, + "requires": { + "type": "string", + "description": "digest of go.mod and go.sum" + }, + "packages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "package": { + "type": "string" + }, + "digest": { + "type": "string" + } + }, + "required": [ + "package", + "digest" + ] + } + }, + "at": { + "type": "string", + "description": "when (RFC 3339 time)", + "format": "date-time" + } + }, + "required": [ + "module", + "repository", + "commit", + "packages" + ] + } + }, + "required": [ + "version" + ] + }, + "at": { + "type": "string", + "description": "when (RFC 3339 time)", + "format": "date-time" + } + }, + "required": [ + "name", + "series" + ] + }, + "description": "a published package series, numbered and newest" + } + ], + "description": "the published packages", + "history": 5 + } + ], + "defs": { + "ToolchainUsed": { + "type": "object", + "description": "a toolchain a fingerprint was computed with (ADR 0405 clause 7)", + "properties": { + "language": { + "type": "string" + }, + "version": { + "type": "string", + "description": "the installed toolchain's version" + }, + "recipe": { + "type": "string", + "description": "the build module's recipe version" + } + }, + "required": [ + "language", + "version", + "recipe" + ] + } + } } diff --git a/go/seatlist/seatlisttest/seats/mesh-conditions.json b/go/seatlist/seatlisttest/seats/mesh-conditions.json index b48b098..76f464c 100644 --- a/go/seatlist/seatlisttest/seats/mesh-conditions.json +++ b/go/seatlist/seatlisttest/seats/mesh-conditions.json @@ -2,9 +2,10 @@ "format": 1, "seat": "mesh-conditions", "scope": "mesh", - "grain": "seat-list", + "grain": "whole", "domain": "Health and repair", - "decision": "novox/hq ADR 0564", + "decision": "novox/hq ADR 0564, ADR 0634, ADR 0635", + "description": "What is wrong with the mesh now (ADR 0564): the open conditions raised from the observers' findings and cleared when a finding goes.", "verbs": [ { "name": "conditions", @@ -41,6 +42,10 @@ } }, "type": "object" + }, + "answer": { + "type": "object", + "description": "the open conditions, or one whole, or the history" } }, { @@ -71,6 +76,10 @@ "why" ], "type": "object" + }, + "answer": { + "type": "object", + "description": "the condition now silenced" } }, { @@ -79,18 +88,560 @@ "input": { "properties": {}, "type": "object" + }, + "answer": { + "type": "object", + "description": "what was raised and cleared again" } } ], "events": [ { - "name": "condition-raised" + "name": "condition-raised", + "body": { + "$ref": "#/defs/ConditionEvent" + } }, { - "name": "condition-changed" + "name": "condition-changed", + "body": { + "$ref": "#/defs/ConditionEvent" + } }, { - "name": "condition-cleared" + "name": "condition-cleared", + "body": { + "$ref": "#/defs/ConditionEvent" + } } - ] + ], + "family": { + "name": "mesh-conditions.events", + "max-age-days": 7 + }, + "states": [ + { + "name": "conditions", + "template": "state", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "description": "one open condition", + "properties": { + "key": { + "type": "string", + "description": "scope.id.token" + }, + "kind": { + "type": "string" + }, + "subject": { + "$ref": "#/defs/Subject" + }, + "severity": { + "type": "string", + "enum": [ + "urgent", + "warning" + ] + }, + "summary": { + "type": "string", + "description": "one line" + }, + "headline": { + "type": "string" + }, + "explanation": { + "type": "string" + }, + "resolved": { + "type": "string" + }, + "needs": { + "type": "string" + }, + "actions": { + "type": "array", + "items": { + "$ref": "#/defs/Action" + } + }, + "evidence": { + "type": "array", + "description": "newest first, at most 10", + "items": { + "$ref": "#/defs/Evidence" + } + }, + "source": { + "type": "string", + "description": "the row, probe or event that saw it" + }, + "resolver": { + "type": "string", + "description": "self, operator, agent or healer:" + }, + "raised": { + "type": "string", + "format": "date-time" + }, + "last-observed": { + "type": "string", + "format": "date-time" + }, + "first": { + "type": "string", + "format": "date-time" + }, + "gaps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "cleared": { + "type": "string", + "format": "date-time" + }, + "reopened": { + "type": "string", + "format": "date-time" + } + } + } + }, + "observations": { + "type": "integer" + }, + "count": { + "type": "integer" + }, + "tried": { + "type": "array", + "items": { + "type": "object", + "properties": { + "at": { + "type": "string", + "format": "date-time" + }, + "what": { + "type": "string" + }, + "outcome": { + "type": "string" + }, + "by": { + "type": "string" + } + } + } + }, + "silenced": { + "type": "object", + "properties": { + "until": { + "type": "string", + "format": "date-time" + }, + "by": { + "type": "string" + }, + "why": { + "type": "string" + }, + "since": { + "type": "string", + "format": "date-time" + } + } + }, + "epoch": { + "type": "integer" + }, + "looks-unread": { + "type": "string" + } + }, + "required": [ + "key", + "kind", + "subject", + "severity", + "raised", + "last-observed" + ] + }, + "description": "one open condition, without its looks it keeps from its finding" + } + ], + "description": "the open conditions the keeper holds, one key each" + }, + { + "name": "findings", + "template": "state", + "writer": "caller", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "description": "one thing an observer sees wrong now", + "properties": { + "v": { + "type": "integer", + "description": "layout version, 1" + }, + "key": { + "type": "string", + "description": "scope.id.token" + }, + "kind": { + "type": "string" + }, + "subject": { + "$ref": "#/defs/Subject" + }, + "severity": { + "type": "string", + "enum": [ + "urgent", + "warning" + ] + }, + "summary": { + "type": "string", + "description": "one line" + }, + "headline": { + "type": "string" + }, + "explanation": { + "type": "string" + }, + "resolved": { + "type": "string" + }, + "needs": { + "type": "string" + }, + "actions": { + "type": "array", + "items": { + "$ref": "#/defs/Action" + } + }, + "evidence": { + "type": "array", + "description": "newest first, at most 10", + "items": { + "$ref": "#/defs/Evidence" + } + }, + "source": { + "type": "string", + "description": "the row, probe or event that saw it" + }, + "resolver": { + "type": "string", + "description": "self, operator, agent or healer:" + }, + "ref": { + "type": "string", + "description": "the key's last part when it is not the kind" + }, + "observer": { + "type": "string", + "description": "the module that wrote it" + }, + "run": { + "type": "string", + "description": "what of its own saw it last" + }, + "at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "v", + "key", + "kind", + "subject", + "severity", + "summary", + "source", + "observer", + "at" + ] + }, + "description": "one finding of the observer that wrote it" + } + ], + "description": "every observer's findings, one key per finding; a key written raises or changes the condition of the same key, a key deleted clears it" + } + ], + "defs": { + "Subject": { + "type": "object", + "description": "what the condition is about", + "properties": { + "scope": { + "type": "string", + "description": "machine, plan, call, build, merge, provider, seat, bus, core, probe, mesh, delivery or module" + }, + "id": { + "type": "string", + "description": "its id within the scope" + }, + "machine": { + "type": "string", + "description": "the machine it concerns, when there is one" + }, + "also": { + "type": "array", + "description": "the other machines it concerns", + "items": { + "type": "string" + } + } + }, + "required": [ + "scope", + "id" + ] + }, + "Evidence": { + "type": "object", + "description": "one observation, as it was said", + "properties": { + "at": { + "type": "string", + "format": "date-time" + }, + "said": { + "type": "string" + } + }, + "required": [ + "at", + "said" + ] + }, + "Action": { + "type": "object", + "description": "one answer the operator may give from a notification", + "properties": { + "label": { + "type": "string" + }, + "verb": { + "type": "string", + "description": "the seat verb the operator channel calls" + }, + "machine": { + "type": "string" + }, + "arguments": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "level": { + "type": "string", + "enum": [ + "acknowledge", + "approve" + ] + } + }, + "required": [ + "label", + "verb" + ] + }, + "ConditionEvent": { + "type": "object", + "description": "a condition and what changed", + "properties": { + "key": { + "type": "string", + "description": "scope.id.token" + }, + "kind": { + "type": "string" + }, + "subject": { + "$ref": "#/defs/Subject" + }, + "severity": { + "type": "string", + "enum": [ + "urgent", + "warning" + ] + }, + "summary": { + "type": "string", + "description": "one line" + }, + "headline": { + "type": "string" + }, + "explanation": { + "type": "string" + }, + "resolved": { + "type": "string" + }, + "needs": { + "type": "string" + }, + "actions": { + "type": "array", + "items": { + "$ref": "#/defs/Action" + } + }, + "evidence": { + "type": "array", + "description": "newest first, at most 10", + "items": { + "$ref": "#/defs/Evidence" + } + }, + "source": { + "type": "string", + "description": "the row, probe or event that saw it" + }, + "resolver": { + "type": "string", + "description": "self, operator, agent or healer:" + }, + "raised": { + "type": "string", + "format": "date-time" + }, + "last-observed": { + "type": "string", + "format": "date-time" + }, + "first": { + "type": "string", + "format": "date-time" + }, + "gaps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "cleared": { + "type": "string", + "format": "date-time" + }, + "reopened": { + "type": "string", + "format": "date-time" + } + } + } + }, + "observations": { + "type": "integer" + }, + "count": { + "type": "integer" + }, + "tried": { + "type": "array", + "items": { + "type": "object", + "properties": { + "at": { + "type": "string", + "format": "date-time" + }, + "what": { + "type": "string" + }, + "outcome": { + "type": "string" + }, + "by": { + "type": "string" + } + } + } + }, + "silenced": { + "type": "object", + "properties": { + "until": { + "type": "string", + "format": "date-time" + }, + "by": { + "type": "string" + }, + "why": { + "type": "string" + }, + "since": { + "type": "string", + "format": "date-time" + } + } + }, + "epoch": { + "type": "integer" + }, + "looks-unread": { + "type": "string" + }, + "event": { + "type": "string" + }, + "at": { + "type": "string", + "format": "date-time" + }, + "change": { + "type": "string", + "enum": [ + "raised", + "reopened", + "severity", + "resolver", + "silenced", + "silence-ended", + "cleared", + "words" + ] + }, + "was": { + "type": "string" + }, + "why": { + "type": "string" + }, + "cleared": { + "type": "string", + "format": "date-time" + }, + "show": { + "type": "string", + "description": "the tool call that shows it" + } + }, + "required": [ + "key", + "event", + "at", + "change" + ] + } + } } diff --git a/go/seatlist/seatlisttest/seats/mesh-config.json b/go/seatlist/seatlisttest/seats/mesh-config.json index 0a5e0e1..83c63f3 100644 --- a/go/seatlist/seatlisttest/seats/mesh-config.json +++ b/go/seatlist/seatlisttest/seats/mesh-config.json @@ -219,5 +219,54 @@ ] } } + ], + "states": [ + { + "name": "resolved", + "template": "state", + "key-forms": [ + { + "key": "..", + "value": { + "type": "object", + "description": "the value a module on a node takes for a config key, and where it came from", + "properties": { + "node": { + "type": "string" + }, + "module": { + "type": "string" + }, + "key": { + "type": "string" + }, + "value": {}, + "source": { + "type": "string" + }, + "predefined": { + "type": "string", + "description": "the predefined key the value falls back to" + }, + "required": { + "type": "boolean", + "description": "an unset value is the operator's own to set" + }, + "trusted": { + "type": "boolean" + } + }, + "required": [ + "node", + "module", + "key", + "source" + ] + }, + "description": "one resolved value" + } + ], + "description": "the value each module on each node takes for each config key, built from the sources alone" + } ] } diff --git a/go/seatlist/seatlisttest/seats/mesh-delivery-checker.json b/go/seatlist/seatlisttest/seats/mesh-delivery-checker.json index 808b3e9..0a9220d 100644 --- a/go/seatlist/seatlisttest/seats/mesh-delivery-checker.json +++ b/go/seatlist/seatlisttest/seats/mesh-delivery-checker.json @@ -242,5 +242,395 @@ "subject": "mesh.stream..event.preview.written", "family": "checks" } + ], + "states": [ + { + "name": "verdicts", + "template": "state", + "key-forms": [ + { + "key": ".", + "value": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "group": { + "type": "string" + }, + "heads": { + "type": "string", + "description": "12 hex digits" + }, + "trunk": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "repository to the trunk head merged onto" + }, + "state": { + "type": "string", + "description": "running, pass or fail" + }, + "failed": { + "type": "object", + "properties": { + "test": { + "type": "string" + }, + "machine": { + "type": "string" + }, + "words": { + "type": "string" + } + } + }, + "failures": { + "type": "array", + "items": { + "type": "object", + "properties": { + "test": { + "type": "string" + }, + "machine": { + "type": "string" + }, + "words": { + "type": "string" + } + } + } + }, + "levels": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "run": { + "type": "integer" + }, + "kept": { + "type": "integer" + }, + "skipped": { + "type": "integer" + } + }, + "required": [ + "run", + "kept" + ] + }, + "description": "by level, unit or integration" + }, + "took": { + "type": "object", + "properties": { + "queue": { + "type": "integer" + }, + "clone": { + "type": "integer" + }, + "unit": { + "type": "integer" + }, + "lint": { + "type": "integer" + }, + "integration-setup": { + "type": "integer" + }, + "integration-run": { + "type": "integer" + }, + "composition": { + "type": "integer" + } + }, + "description": "milliseconds" + }, + "machines": { + "type": "array", + "items": { + "type": "string" + } + }, + "sdk": { + "type": "string" + }, + "composition": { + "type": "object", + "properties": { + "state": { + "type": "string", + "description": "not-run, pass or fail" + }, + "words": { + "type": "string" + }, + "sdk": { + "type": "string" + }, + "machines": { + "type": "array", + "items": { + "type": "string" + } + }, + "fpset": { + "type": "string" + } + }, + "required": [ + "state" + ] + }, + "over-bound": { + "type": "array", + "items": { + "type": "string" + } + }, + "beside": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "carried-from": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "series": { + "type": "array", + "items": { + "type": "object", + "properties": { + "module": { + "type": "string" + }, + "artifact": { + "type": "string" + }, + "from": { + "type": "string" + }, + "to": { + "type": "string" + } + }, + "required": [ + "module", + "artifact", + "from", + "to" + ] + } + } + }, + "required": [ + "group", + "heads", + "state" + ], + "description": "one check of a group at one set of heads" + }, + "description": "one check of a group; the group is an escaped branch name, heads 12 hex digits" + } + ], + "description": "the checker's verdict of each group at each set of heads", + "history": 5 + }, + { + "name": "kept", + "template": "state", + "key-forms": [ + { + "key": ".", + "value": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "state": { + "type": "string", + "description": "pass: a failure is never kept" + }, + "repository": { + "type": "string" + }, + "package": { + "type": "string" + }, + "level": { + "type": "string" + }, + "tests": { + "type": "integer" + }, + "took": { + "type": "integer" + }, + "toolchain": { + "type": "string" + }, + "at": { + "type": "string", + "description": "when (RFC 3339 time)" + } + }, + "required": [ + "state", + "repository", + "package", + "level" + ], + "description": "one passing level of one package, kept so the next check does not run it again" + }, + "description": "a passing level of one package by its content digest" + } + ], + "description": "the kept passing levels", + "history": 1 + }, + { + "name": "owed", + "template": "state", + "key-forms": [ + { + "key": "...", + "value": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "act": { + "type": "string", + "description": "status=, view, note or comment." + }, + "context": { + "type": "string" + }, + "target": { + "type": "string" + }, + "state": { + "type": "string" + }, + "text": { + "type": "string" + }, + "ref": { + "type": "string" + } + }, + "required": [ + "act" + ], + "description": "one act owed to the forge, written by its owner and done by the forge bridge" + }, + "description": "the status mesh/check the checker owes the forge for a head (its act is status=mesh/check)" + } + ], + "description": "the status the checker owes the forge", + "history": 5 + }, + { + "name": "previews", + "template": "state", + "key-forms": [ + { + "key": "..", + "value": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "repository": { + "type": "string" + }, + "head": { + "type": "string" + }, + "trunk": { + "type": "string" + }, + "merge": { + "type": "string", + "description": "the merge commit" + }, + "conflicts": { + "type": "array", + "items": { + "type": "string" + }, + "description": "files in conflict" + }, + "modules": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "tree": { + "type": "string" + }, + "manifest": { + "type": "string", + "description": "manifest digest" + }, + "locks": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "paths": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "packages": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "tree", + "manifest" + ] + }, + "description": "by directory" + } + }, + "required": [ + "repository", + "head", + "trunk" + ], + "description": "the merge of a head onto a trunk commit, read before a pull request is checked" + }, + "description": "the merge of a head onto a trunk commit" + } + ], + "description": "the previews of a head merged onto a trunk", + "history": 5 + } ] } diff --git a/go/seatlist/seatlisttest/seats/mesh-delivery.json b/go/seatlist/seatlisttest/seats/mesh-delivery.json index 43ba193..8365118 100644 --- a/go/seatlist/seatlisttest/seats/mesh-delivery.json +++ b/go/seatlist/seatlisttest/seats/mesh-delivery.json @@ -2,7 +2,7 @@ "format": 1, "seat": "mesh-delivery", "scope": "mesh", - "grain": "seat-list", + "grain": "whole", "domain": "Delivery", "decision": "novox/hq ADR 0239", "verbs": [ @@ -33,6 +33,10 @@ } }, "type": "object" + }, + "answer": { + "type": "object", + "description": "what deliveries answers, as its description says" } }, { @@ -42,6 +46,10 @@ "input": { "properties": {}, "type": "object" + }, + "answer": { + "type": "object", + "description": "what times answers, as its description says" } }, { @@ -58,6 +66,10 @@ "id" ], "type": "object" + }, + "answer": { + "type": "object", + "description": "what show answers, as its description says" } }, { @@ -75,6 +87,10 @@ } }, "type": "object" + }, + "answer": { + "type": "object", + "description": "what groups answers, as its description says" } }, { @@ -84,6 +100,10 @@ "input": { "properties": {}, "type": "object" + }, + "answer": { + "type": "object", + "description": "what what-if answers, as its description says" } }, { @@ -109,6 +129,10 @@ "repository" ], "type": "object" + }, + "answer": { + "type": "object", + "description": "what checks answers, as its description says" } }, { @@ -117,6 +141,10 @@ "input": { "properties": {}, "type": "object" + }, + "answer": { + "type": "object", + "description": "what table answers, as its description says" } }, { @@ -125,6 +153,10 @@ "input": { "properties": {}, "type": "object" + }, + "answer": { + "type": "object", + "description": "what stalled answers, as its description says" } }, { @@ -146,6 +178,10 @@ "why" ], "type": "object" + }, + "answer": { + "type": "object", + "description": "what recheck answers, as its description says" } }, { @@ -155,6 +191,10 @@ "input": { "properties": {}, "type": "object" + }, + "answer": { + "type": "object", + "description": "what release answers, as its description says" } }, { @@ -164,6 +204,10 @@ "input": { "properties": {}, "type": "object" + }, + "answer": { + "type": "object", + "description": "what stop answers, as its description says" } }, { @@ -185,6 +229,10 @@ "why" ], "type": "object" + }, + "answer": { + "type": "object", + "description": "what close answers, as its description says" } }, { @@ -194,6 +242,10 @@ "input": { "properties": {}, "type": "object" + }, + "answer": { + "type": "object", + "description": "what retire-history answers, as its description says" } }, { @@ -221,7 +273,299 @@ "why" ], "type": "object" + }, + "answer": { + "type": "object", + "description": "what settle answers, as its description says" } } + ], + "description": "The delivery gate: reads and steers deliveries, merges and the checks that decide them; the merge is made on the operator's Approve once both checks passed.", + "states": [ + { + "name": "source", + "template": "state", + "key-forms": [ + { + "key": "main", + "value": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "group": { + "type": "string", + "description": "the group that moved it" + }, + "heads": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "repository to the merged head, a full commit" + } + }, + "required": [ + "group", + "heads" + ], + "description": "every repository's merged head; its revision is the mesh's source revision" + }, + "description": "the one key, written by the holder alone by compare-and-set" + } + ], + "description": "the mesh's source revision", + "history": 64 + }, + { + "name": "merges", + "template": "state", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "group": { + "type": "string" + }, + "ask": { + "type": "string", + "description": "the ask key" + }, + "approved": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "@ to the approved head" + }, + "pulls": { + "type": "array", + "items": { + "type": "object", + "properties": { + "repository": { + "type": "string" + }, + "number": { + "type": "integer" + }, + "head": { + "type": "string" + }, + "state": { + "type": "string", + "description": "merging, merged or refused" + }, + "commit": { + "type": "string" + }, + "words": { + "type": "string" + } + }, + "required": [ + "repository", + "number", + "head", + "state" + ] + } + } + }, + "required": [ + "group", + "ask", + "pulls" + ], + "description": "one group's merge and how far each pull request is" + }, + "description": "a group's merge" + } + ], + "description": "the merges", + "history": 10 + }, + { + "name": "asks", + "template": "state", + "key-forms": [ + { + "key": "merge..", + "value": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "group": { + "type": "string" + }, + "heads": { + "type": "array", + "items": { + "type": "object", + "properties": { + "repository": { + "type": "string", + "description": "/" + }, + "number": { + "type": "integer" + }, + "head": { + "type": "string", + "description": "the full commit" + } + }, + "required": [ + "repository", + "number", + "head" + ] + } + }, + "ask": { + "description": "the operator-channel ask as raised" + }, + "asking": { + "type": "integer", + "description": "the number of times it was asked" + } + }, + "required": [ + "group", + "heads", + "ask" + ] + }, + "description": "the ask of the operator for one group at one set of heads" + } + ], + "description": "the asks of the operator for a merge", + "history": 5 + }, + { + "name": "owed", + "template": "state", + "key-forms": [ + { + "key": "...", + "value": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "act": { + "type": "string", + "description": "status=, view, note or comment." + }, + "context": { + "type": "string" + }, + "target": { + "type": "string" + }, + "state": { + "type": "string" + }, + "text": { + "type": "string" + }, + "ref": { + "type": "string" + } + }, + "required": [ + "act" + ], + "description": "one act owed to the forge, written by its owner and done by the forge bridge" + }, + "description": "one act owed to the forge" + } + ], + "description": "the acts the gate owes the forge", + "history": 5 + }, + { + "name": "turns", + "template": "state", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "properties": { + "v": { + "type": "integer", + "description": "the layout version" + }, + "repository": { + "type": "string" + }, + "waiting": { + "type": "array", + "items": { + "type": "object", + "properties": { + "group": { + "type": "string" + }, + "heads": { + "type": "string" + }, + "ask": { + "type": "string" + }, + "approved": { + "type": "string", + "description": "when (RFC 3339 time)" + }, + "since": { + "type": "string", + "description": "when (RFC 3339 time)" + }, + "waits-for": { + "type": "string" + }, + "waits-since": { + "type": "string", + "description": "when (RFC 3339 time)" + }, + "step": { + "type": "string" + } + }, + "required": [ + "group", + "heads", + "approved", + "since" + ] + } + } + }, + "required": [ + "repository", + "waiting" + ], + "description": "the merges of a repository in the order they wait for their turn" + }, + "description": "the merges waiting on a repository" + } + ], + "description": "which merge waits for which", + "history": 5 + } ] } diff --git a/go/seatlist/seatlisttest/seats/mesh-hand-acts.json b/go/seatlist/seatlisttest/seats/mesh-hand-acts.json index 9d760ec..a075cfc 100644 --- a/go/seatlist/seatlisttest/seats/mesh-hand-acts.json +++ b/go/seatlist/seatlisttest/seats/mesh-hand-acts.json @@ -2,9 +2,10 @@ "format": 1, "seat": "mesh-hand-acts", "scope": "mesh", - "grain": "seat-list", + "grain": "whole", "domain": "Health and repair", - "decision": "novox/hq ADR 0565", + "decision": "novox/hq ADR 0565, ADR 0634, ADR 0635", + "description": "Acts done by hand, recorded with who, why and cause (ADR 0565): the log every module records through.", "verbs": [ { "name": "hand-act", @@ -37,6 +38,10 @@ "why" ], "type": "object" + }, + "answer": { + "type": "object", + "description": "the act recorded" } }, { @@ -66,6 +71,10 @@ } }, "type": "object" + }, + "answer": { + "type": "object", + "description": "the acts lately" } }, { @@ -87,12 +96,226 @@ "ask" ], "type": "object" + }, + "answer": { + "type": "object", + "description": "the warrant recorded" } } ], "events": [ { - "name": "act-recorded" + "name": "act-recorded", + "body": { + "$ref": "#/defs/Act" + } } - ] + ], + "family": { + "name": "mesh-hand-acts.events", + "max-age-days": 7 + }, + "states": [ + { + "name": "acts", + "template": "log", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "description": "one act done by hand", + "properties": { + "id": { + "type": "string" + }, + "at": { + "type": "string", + "format": "date-time" + }, + "by": { + "type": "string", + "description": "the caller the runtime proved" + }, + "verb": { + "type": "string" + }, + "arguments": { + "type": "array", + "items": { + "type": "string" + } + }, + "why": { + "type": "string" + }, + "cause": { + "type": "string" + }, + "condition": { + "type": "string" + }, + "kind": { + "type": "string", + "description": "what the act was read to be from what it did" + }, + "carried": { + "type": "array", + "items": { + "type": "string" + } + }, + "via": { + "type": "string" + }, + "ask": { + "type": "string" + }, + "proofs": { + "type": "array", + "items": { + "type": "string" + } + }, + "requested-by": { + "type": "string" + }, + "outcome": { + "type": "string" + }, + "log": { + "type": "string", + "description": "which log holds it, set when it is read" + } + }, + "required": [ + "id", + "at", + "by", + "verb", + "why", + "cause" + ] + }, + "description": "one act" + } + ], + "max-age-days": 400, + "description": "every act, kept 400 days" + }, + { + "name": "warrants", + "template": "state", + "key-forms": [ + { + "key": ".", + "value": { + "type": "object", + "description": "what a module did on the operator's warrant, made once per ask", + "properties": { + "ask": { + "type": "string" + }, + "asker": { + "type": "string" + }, + "option": { + "type": "string" + }, + "by": { + "type": "string" + }, + "at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "ask", + "asker" + ] + }, + "description": "one warrant" + } + ], + "description": "warrants recorded, one key per asker and ask" + } + ], + "defs": { + "Act": { + "type": "object", + "description": "one act done by hand", + "properties": { + "id": { + "type": "string" + }, + "at": { + "type": "string", + "format": "date-time" + }, + "by": { + "type": "string", + "description": "the caller the runtime proved" + }, + "verb": { + "type": "string" + }, + "arguments": { + "type": "array", + "items": { + "type": "string" + } + }, + "why": { + "type": "string" + }, + "cause": { + "type": "string" + }, + "condition": { + "type": "string" + }, + "kind": { + "type": "string", + "description": "what the act was read to be from what it did" + }, + "carried": { + "type": "array", + "items": { + "type": "string" + } + }, + "via": { + "type": "string" + }, + "ask": { + "type": "string" + }, + "proofs": { + "type": "array", + "items": { + "type": "string" + } + }, + "requested-by": { + "type": "string" + }, + "outcome": { + "type": "string" + }, + "log": { + "type": "string", + "description": "which log holds it, set when it is read" + } + }, + "required": [ + "id", + "at", + "by", + "verb", + "why", + "cause" + ] + } + } } diff --git a/go/seatlist/seatlisttest/seats/mesh-healers.json b/go/seatlist/seatlisttest/seats/mesh-healers.json index 5b67239..23772b0 100644 --- a/go/seatlist/seatlisttest/seats/mesh-healers.json +++ b/go/seatlist/seatlisttest/seats/mesh-healers.json @@ -2,9 +2,10 @@ "format": 1, "seat": "mesh-healers", "scope": "mesh", - "grain": "seat-list", + "grain": "whole", "domain": "Health and repair", - "decision": "novox/hq ADR 0564", + "decision": "novox/hq ADR 0564, ADR 0634, ADR 0635", + "description": "The healers (ADR 0564): each a registered response to one kind of condition, within a budget and the mesh-wide brake.", "verbs": [ { "name": "healers", @@ -17,6 +18,10 @@ } }, "type": "object" + }, + "answer": { + "type": "object", + "description": "the registered healers, acts lately and the brake" } }, { @@ -38,6 +43,10 @@ "why" ], "type": "object" + }, + "answer": { + "type": "object", + "description": "the act taken" } }, { @@ -63,12 +72,179 @@ "why" ], "type": "object" + }, + "answer": { + "type": "object", + "description": "the drill recorded" } } ], "events": [ { - "name": "healer-acted" + "name": "healer-acted", + "body": { + "type": "object", + "description": "a healer's act", + "properties": { + "event": { + "type": "string" + }, + "at": { + "type": "string", + "format": "date-time" + }, + "healer": { + "type": "string" + }, + "by": { + "type": "string" + }, + "condition": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "act": { + "type": "string" + }, + "outcome": { + "type": "string", + "enum": [ + "acting", + "acted", + "failed", + "escalated" + ] + }, + "said": { + "type": "string" + }, + "budget": { + "type": "string" + }, + "show": { + "type": "string" + } + }, + "required": [ + "event", + "at", + "healer", + "condition", + "outcome" + ] + } + } + ], + "family": { + "name": "mesh-healers.events", + "max-age-days": 7 + }, + "states": [ + { + "name": "attempts", + "template": "state", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "description": "one act of a healer, created before it is made and finished after, so a second instance cannot begin the same act", + "properties": { + "healer": { + "type": "string" + }, + "condition": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "budget-key": { + "type": "string", + "description": "what the budget counts by: the condition's key unless the healer says another" + }, + "act": { + "type": "string" + }, + "outcome": { + "type": "string", + "enum": [ + "acting", + "acted", + "failed", + "escalated" + ] + }, + "said": { + "type": "string" + }, + "begun": { + "type": "string", + "format": "date-time" + }, + "finished": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "healer", + "condition", + "kind", + "budget-key", + "act", + "outcome", + "begun" + ] + }, + "description": "one act, under the healers' own key" + } + ], + "description": "the acts begun, which the keeper reads to write a condition's tries" + }, + { + "name": "would", + "template": "state", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "description": "an act the healers would have taken while the controller's lease names a controller; it counts for no budget", + "properties": { + "healer": { + "type": "string" + }, + "condition": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "act": { + "type": "string" + }, + "at": { + "type": "string", + "format": "date-time" + }, + "why": { + "type": "string" + } + }, + "required": [ + "healer", + "condition", + "act", + "at" + ] + }, + "description": "one act not taken" + } + ], + "description": "the acts the healers would have taken" } ] } diff --git a/go/seatlist/seatlisttest/seats/mesh-module-assigner.json b/go/seatlist/seatlisttest/seats/mesh-module-assigner.json new file mode 100644 index 0000000..8e18a70 --- /dev/null +++ b/go/seatlist/seatlisttest/seats/mesh-module-assigner.json @@ -0,0 +1,942 @@ +{ + "format": 1, + "seat": "mesh-module-assigner", + "scope": "mesh", + "grain": "whole", + "domain": "Placement", + "decision": "novox/hq ADR 0567, ADR 0634, ADR 0635", + "description": "The seat that places modules on nodes (ADR 0567): the state of placement and the proposals that change it. Renamed from mesh-placement (ADR 0634 §9).", + "verbs": [ + { + "name": "placement", + "description": "Read placement: one node's key, the nodes one module is placed on, or every key, each with its revision, and how full the state is against its cap. With history, one node's or one module's changes in order: a revision the state keeps whole, an older one as the proposal's changes from its placement-changed event.", + "optional": true, + "input": { + "properties": { + "history": { + "description": "\"true\": the changes in order, from the stream and the state's revisions", + "enum": [ + "true", + "false" + ], + "type": "string" + }, + "module": { + "description": "one module: the nodes it is placed on", + "type": "string" + }, + "node": { + "description": "one node", + "type": "string" + }, + "seats": { + "description": "\"true\": who holds each seat, and on which node (the state seat-holders); with node, that node's", + "enum": [ + "true", + "false" + ], + "type": "string" + } + }, + "type": "object" + }, + "answer": { + "type": "object", + "description": "placement as read: nodes, keys with their revisions, history, seat holders, the state's fill" + } + }, + { + "name": "proposals", + "description": "Every open proposal of placement: its changes, the revision of each key it was laid over, its candidates, every node's answer, the differences computed and answered, its verdict and its ask. Narrowed to one by id.", + "optional": true, + "input": { + "properties": { + "id": { + "description": "one proposal's id", + "type": "string" + } + }, + "type": "object" + }, + "answer": { + "type": "object", + "description": "the open proposals" + } + }, + { + "name": "propose", + "description": "Propose a change of placement, with why: laid over the current keys and refused at once, naming every finding, when the result cannot be placed (two modules on one host port, a provider not placed, a seat held once held twice, two nodes with one address, two hubs, a module the indexer does not hold, a node with no record). Otherwise the nodes it touches compose it in shadow and answer, and on a passing verdict the operator is asked to approve it. Who proposes is the caller, as the runtime proves it. An add-node from mesh-enrolment, whose warrant names its node and address, is written with no ask of its own: its Approve is the enrolment's (ADR 0587 §7).", + "optional": true, + "input": { + "additionalProperties": false, + "properties": { + "changes": { + "description": "the changes, each an object with `op`: place (node, module, ports, bindings), take-off (node, module), pin (node, provision, provider {node, module}), unpin (node, provision), hold (node, module, claim, scope, kind, site), unhold (node, module, claim), decide (node, field, value: a decided field of the node, its address among them), config (node, module, mesh-wide, layer, mesh-wide-values, values), add-node (node, decided, keys: a joining node, mesh-enrolment's alone, the proposal's one change, with the enrolment's warrant, its tunnel key alone at the operator's yes; ADR 0587 §7), remove-node (node: a joining node never confirmed, taken off when its enrolment ends; mesh-enrolment's alone, on the same warrant)", + "items": { + "required": [ + "op", + "node" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "warrant": { + "description": "for an add-node alone: the enrolment's warrant {node, address, ask, warrant}, naming the change's node and address", + "required": [ + "node", + "address", + "ask", + "warrant" + ], + "type": "object" + }, + "why": { + "description": "why, shown to the operator with the proposal", + "type": "string" + } + }, + "required": [ + "why", + "changes" + ], + "type": "object" + }, + "answer": { + "type": "object", + "description": "the proposal's id and its verdict, or every finding refused at once" + } + }, + { + "name": "withdraw", + "description": "Withdraw an open proposal: its key and its candidates are deleted and placement-withdrawn carries it whole. With why.", + "optional": true, + "input": { + "properties": { + "id": { + "description": "the proposal's id", + "type": "string" + }, + "why": { + "description": "why, kept in the event", + "type": "string" + } + }, + "required": [ + "id", + "why" + ], + "type": "object" + }, + "answer": { + "type": "object", + "description": "the proposal withdrawn" + } + }, + { + "name": "again", + "description": "Judge an open proposal now with the answers that stand, and raise its ask again when its verdict passes and the operator's channel does not hold the ask. Nothing more: the recovery of a proposal whose judgement or ask was lost.", + "optional": true, + "input": { + "properties": { + "id": { + "description": "the proposal's id", + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + "answer": { + "type": "object", + "description": "what was judged or asked again" + } + } + ], + "events": [ + { + "name": "placement-proposed", + "body": { + "$ref": "#/defs/PlacementEvent" + } + }, + { + "name": "placement-withdrawn", + "body": { + "$ref": "#/defs/PlacementEvent" + } + }, + { + "name": "placement-judged", + "body": { + "$ref": "#/defs/PlacementEvent" + } + }, + { + "name": "placement-changed", + "body": { + "$ref": "#/defs/PlacementEvent" + } + } + ], + "family": { + "name": "mesh-module-assigner.events", + "max-age-days": 7 + }, + "states": [ + { + "name": "placement", + "template": "state", + "key-forms": [ + { + "key": ".node", + "value": { + "type": "object", + "description": "a node's key: what a person decided of it, its pins and holdings, and the modules placed on it; the commit of a write", + "properties": { + "v": { + "type": "integer" + }, + "node": { + "type": "string" + }, + "decided": { + "$ref": "#/defs/NodeDecided" + }, + "modules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "module": { + "type": "string" + }, + "digest": { + "type": "string", + "description": "the digest of the module key's value it stands for" + } + }, + "required": [ + "module", + "digest" + ] + }, + "description": "the modules placed, in the order the composition reads them" + }, + "pins": { + "type": "object", + "additionalProperties": { + "$ref": "#/defs/Provider" + } + }, + "holds": { + "type": "array", + "items": { + "$ref": "#/defs/Holding" + } + } + }, + "required": [ + "node", + "decided", + "modules" + ] + }, + "description": "a node's key" + }, + { + "key": ".", + "value": { + "type": "object", + "description": "a module's key: the module as placed", + "properties": { + "v": { + "type": "integer" + }, + "node": { + "type": "string" + }, + "placed": { + "$ref": "#/defs/PlacedModule" + }, + "previous": { + "$ref": "#/defs/PlacedModule", + "description": "the value it replaces while a write is under way" + } + }, + "required": [ + "node", + "placed" + ] + }, + "description": "one module on a node; no module is named node" + } + ], + "history": 64, + "description": "placements, one key per node and one per module placed on it, written only on the operator's Approve of a proposal by compare-and-set at the revision it was laid over" + }, + { + "name": "proposals", + "template": "state", + "key-forms": [ + { + "key": "", + "value": { + "$ref": "#/defs/Proposal" + }, + "description": "an open proposal, by its id" + } + ], + "history": 5, + "description": "open proposals; a key leaves when the proposal is applied, withdrawn, declined or expired" + }, + { + "name": "candidates", + "template": "state", + "key-forms": [ + { + "key": ".", + "value": { + "type": "object", + "description": "one node's candidate in one proposal", + "properties": { + "v": { + "type": "integer" + }, + "proposal": { + "type": "string" + }, + "reached": { + "type": "boolean", + "description": "the proposal changes this node's composition through another's key" + }, + "placement": { + "$ref": "#/defs/Placement", + "description": "the node's placement with the proposal laid over it; absent when the proposal takes its key away" + } + }, + "required": [ + "proposal" + ] + }, + "description": "a node's candidate" + } + ], + "history": 5, + "description": "the candidates a proposal needs answers on; a node watches its own" + }, + { + "name": "pulses", + "template": "state", + "key-forms": [ + { + "key": "answer..", + "value": { + "type": "object", + "description": "the bound on one node's answer", + "properties": { + "v": { + "type": "integer" + } + } + }, + "description": "the bound on a node's answer" + }, + { + "key": "ask.", + "value": { + "type": "object", + "description": "the bound on a raised ask", + "properties": { + "v": { + "type": "integer" + } + } + }, + "description": "the bound on a proposal's raised ask" + } + ], + "description": "bounds, each a key that lives for the bound; the server's delete marker when one ends is the event that fails a silent node or an unanswered ask" + }, + { + "name": "seat-holders", + "template": "state", + "key-forms": [ + { + "key": "", + "value": { + "$ref": "#/defs/SeatHolders" + }, + "description": "a seat held once for the mesh" + }, + { + "key": ".", + "value": { + "$ref": "#/defs/SeatHolders" + }, + "description": "a node seat on one node" + } + ], + "description": "who holds which seat on which node, composed from every node's placement" + } + ], + "defs": { + "Provider": { + "type": "object", + "description": "the module on a node that answers a requirement or holds a seat", + "properties": { + "node": { + "type": "string" + }, + "module": { + "type": "string" + } + }, + "required": [ + "node", + "module" + ] + }, + "PlacedModule": { + "type": "object", + "description": "one module placed on a node", + "properties": { + "module": { + "type": "string" + }, + "ports": { + "type": "object", + "additionalProperties": { + "type": "integer" + }, + "description": "the host port each declared port is published on, by the port the software listens on" + }, + "bindings": { + "type": "object", + "additionalProperties": { + "$ref": "#/defs/Provider" + }, + "description": "by provision, the provider each requirement that keeps data is bound to" + }, + "mesh-wide": { + "type": "object", + "description": "the whole mesh's settings layer of the module's placement" + }, + "has-mesh-wide": { + "type": "boolean" + }, + "layer": { + "type": "object", + "description": "this node's settings layer of the module's placement" + }, + "has-layer": { + "type": "boolean" + }, + "mesh-wide-values": { + "type": "object", + "description": "config values a person set in the whole mesh's layer, for a module in the settings form" + }, + "values": { + "type": "object", + "description": "config values a person set in this node's layer, for a module in the settings form" + } + }, + "required": [ + "module" + ] + }, + "Holding": { + "type": "object", + "description": "one seat a module placed on the node holds", + "properties": { + "claim": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "module": { + "type": "string" + }, + "site": { + "type": "string" + }, + "kind": { + "type": "string" + } + }, + "required": [ + "claim", + "module" + ] + }, + "NodeDecided": { + "type": "object", + "description": "what a person decides of a node; no secret and no key material", + "properties": { + "endpoint": { + "type": "string" + }, + "site": { + "type": "string" + }, + "hub": { + "type": "boolean" + }, + "address": { + "type": "string" + }, + "public-domain": { + "type": "string" + }, + "account": { + "type": "string" + }, + "account-home": { + "type": "string" + }, + "agent-account": { + "type": "string" + }, + "agent-account-home": { + "type": "string" + }, + "adopted": { + "type": "boolean" + }, + "taken": { + "type": "array", + "items": { + "type": "string" + } + }, + "tunnel": { + "type": "object", + "description": "the tunnel found on the node when it was adopted" + }, + "peer-names": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "on the hub, the name a person gave a peer of that tunnel, by its public key" + } + } + }, + "Placement": { + "type": "object", + "description": "one node's placement assembled", + "properties": { + "v": { + "type": "integer" + }, + "node": { + "type": "string" + }, + "revision": { + "type": "string" + }, + "decided": { + "$ref": "#/defs/NodeDecided" + }, + "modules": { + "type": "array", + "items": { + "$ref": "#/defs/PlacedModule" + } + }, + "pins": { + "type": "object", + "additionalProperties": { + "$ref": "#/defs/Provider" + }, + "description": "by provision, the provider chosen for the node" + }, + "holds": { + "type": "array", + "items": { + "$ref": "#/defs/Holding" + } + } + }, + "required": [ + "node", + "decided", + "modules" + ] + }, + "ProposalChange": { + "type": "object", + "description": "one change of a proposal", + "properties": { + "op": { + "type": "string", + "description": "place, take-off, pin, unpin, hold, unhold, decide, config, seed, add-node or remove-node" + }, + "node": { + "type": "string" + }, + "module": { + "type": "string" + }, + "ports": { + "type": "object", + "additionalProperties": { + "type": "integer" + } + }, + "bindings": { + "type": "object", + "additionalProperties": { + "$ref": "#/defs/Provider" + } + }, + "provision": { + "type": "string" + }, + "provider": { + "$ref": "#/defs/Provider" + }, + "claim": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "site": { + "type": "string" + }, + "field": { + "type": "string" + }, + "value": { + "description": "a decided field's value" + }, + "mesh-wide": { + "type": "object" + }, + "layer": { + "type": "object" + }, + "mesh-wide-values": { + "type": "object" + }, + "values": { + "type": "object" + }, + "whole": { + "$ref": "#/defs/Placement", + "description": "the node's whole placement, for a seed" + }, + "decided": { + "$ref": "#/defs/NodeDecided" + }, + "keys": { + "type": "object", + "properties": { + "identity": { + "type": "string" + }, + "overlay": { + "type": "string" + }, + "sealing": { + "type": "string" + }, + "serving": { + "type": "string" + } + }, + "required": [ + "identity", + "sealing" + ], + "description": "the public halves of a joining node's keys" + } + }, + "required": [ + "op", + "node" + ] + }, + "Warrant": { + "type": "object", + "description": "the enrolment's warrant an add-node carries", + "properties": { + "node": { + "type": "string" + }, + "address": { + "type": "string" + }, + "ask": { + "type": "string" + }, + "warrant": { + "type": "string" + } + }, + "required": [ + "node", + "address", + "ask", + "warrant" + ] + }, + "Verdict": { + "type": "object", + "description": "the holder's judgement of a proposal's answers", + "properties": { + "state": { + "type": "string", + "description": "waits, pass or fail" + }, + "at": { + "type": "string", + "format": "date-time" + }, + "fails": { + "type": "array", + "items": { + "type": "string" + } + }, + "silent": { + "type": "array", + "items": { + "type": "string" + } + }, + "until": { + "type": "string", + "format": "date-time" + }, + "intended": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "unintended": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "carried": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "answers": { + "type": "object", + "additionalProperties": { + "type": "object" + }, + "description": "every node's answer to its candidate, by node" + }, + "notes": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "state", + "at" + ] + }, + "Proposal": { + "type": "object", + "description": "an open proposal", + "properties": { + "v": { + "type": "integer" + }, + "id": { + "type": "string" + }, + "by": { + "type": "string" + }, + "why": { + "type": "string" + }, + "at": { + "type": "string", + "format": "date-time" + }, + "written": { + "type": "string", + "format": "date-time" + }, + "changes": { + "type": "array", + "items": { + "$ref": "#/defs/ProposalChange" + } + }, + "seed": { + "type": "boolean" + }, + "export-digest": { + "type": "string" + }, + "base": { + "type": "object", + "additionalProperties": { + "type": "integer" + }, + "description": "by node whose key it changes, the revision of the key it was laid over (0: none)" + }, + "must": { + "type": "array", + "items": { + "type": "string" + } + }, + "hand-back": { + "type": "boolean" + }, + "state": { + "type": "string", + "description": "answering, failed or asked" + }, + "verdict": { + "$ref": "#/defs/Verdict" + }, + "ask": { + "type": "string" + }, + "words": { + "type": "string" + }, + "again": { + "type": "string", + "format": "date-time" + }, + "warrant": { + "$ref": "#/defs/Warrant" + }, + "withdrawn": { + "type": "string" + } + }, + "required": [ + "id", + "by", + "why", + "at", + "changes", + "base", + "state" + ] + }, + "PlacementEvent": { + "type": "object", + "description": "what each placement event carries", + "properties": { + "proposal": { + "$ref": "#/defs/Proposal" + }, + "nodes": { + "type": "array", + "items": { + "type": "object" + }, + "description": "for placement-changed, by node the revisions before and after and the modules added, removed or changed" + }, + "warrant": { + "type": "object", + "description": "who approved it, through which channel, when", + "properties": { + "warrant": { + "type": "string" + }, + "words": { + "type": "string" + }, + "at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "warrant", + "words", + "at" + ] + }, + "ended": { + "type": "string", + "description": "for placement-withdrawn, why it ended" + } + }, + "required": [ + "proposal" + ] + }, + "SeatHolders": { + "type": "object", + "description": "one key of seat-holders", + "properties": { + "v": { + "type": "integer" + }, + "seat": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "node": { + "type": "string" + }, + "outside": { + "type": "boolean", + "description": "the claim names no seat of the seat list" + }, + "holders": { + "type": "array", + "items": { + "type": "object", + "properties": { + "module": { + "type": "string" + }, + "node": { + "type": "string" + } + }, + "required": [ + "module", + "node" + ] + } + } + }, + "required": [ + "seat", + "scope", + "holders" + ] + } + } +} diff --git a/go/seatlist/seatlisttest/seats/mesh-enrolment.json b/go/seatlist/seatlisttest/seats/mesh-node-enroller.json similarity index 51% rename from go/seatlist/seatlisttest/seats/mesh-enrolment.json rename to go/seatlist/seatlisttest/seats/mesh-node-enroller.json index 474653a..4d21cd0 100644 --- a/go/seatlist/seatlisttest/seats/mesh-enrolment.json +++ b/go/seatlist/seatlisttest/seats/mesh-node-enroller.json @@ -1,10 +1,11 @@ { "format": 1, - "seat": "mesh-enrolment", + "seat": "mesh-node-enroller", "scope": "mesh", - "grain": "seat-list", + "grain": "whole", "domain": "Identity and access", - "decision": "novox/hq ADR 0587", + "decision": "novox/hq ADR 0587, ADR 0634, ADR 0635", + "description": "The seat that enrols a new machine on the operator's Approve and a one-time token (ADR 0587). Renamed from mesh-enrolment (ADR 0634 §9).", "verbs": [ { "name": "issue", @@ -34,11 +35,15 @@ "overlay_key" ], "type": "object" + }, + "answer": { + "type": "object", + "description": "the enrolment's stage, address and ask, or every finding refused at once" } }, { "name": "redeem", - "description": "Spend a token: asked by a joining node-engine dialled as enrol.\u003cnode\u003e alone, with the public halves of its keys and its identity key's proof over them. Spends the token, asks the vault for the node's account and certificate, writes the node into placement, and answers the account sealed to the node, its certificate and the vault's public half, signed by the vault. Asked again with the same keys it finishes what is not done and answers the same; with other keys it is refused.", + "description": "Spend a token: asked by a joining node-engine dialled as enrol. alone, with the public halves of its keys and its identity key's proof over them. Spends the token, asks the vault for the node's account and certificate, writes the node into placement, and answers the account sealed to the node, its certificate and the vault's public half, signed by the vault. Asked again with the same keys it finishes what is not done and answers the same; with other keys it is refused.", "input": { "additionalProperties": false, "properties": { @@ -82,6 +87,43 @@ "proof" ], "type": "object" + }, + "answer": { + "type": "object", + "description": "the account sealed to the node, its certificate and the vault's public half, signed by the vault", + "properties": { + "node": { + "type": "string", + "description": "the node" + }, + "login": { + "type": "string", + "description": "the login it dials as next" + }, + "account": { + "type": "string", + "description": "its account sealed to the sealing key it presented, base64" + }, + "certificate": { + "type": "string", + "description": "its certificate, PEM" + }, + "signer": { + "type": "string", + "description": "the vault's public signing half, base64" + }, + "signature": { + "type": "string", + "description": "the vault's signature over the answer without it, under the context of the enrolment kind, base64" + } + }, + "required": [ + "node", + "login", + "account", + "certificate", + "signer" + ] } }, { @@ -90,6 +132,10 @@ "input": { "properties": {}, "type": "object" + }, + "answer": { + "type": "object", + "description": "the enrolment ended" } }, { @@ -111,6 +157,10 @@ "why" ], "type": "object" + }, + "answer": { + "type": "object", + "description": "the enrolment ended" } }, { @@ -132,6 +182,10 @@ } }, "type": "object" + }, + "answer": { + "type": "object", + "description": "every open enrolment, or one" } }, { @@ -148,24 +202,139 @@ "node" ], "type": "object" + }, + "answer": { + "type": "object", + "description": "the ask raised again" } } ], "events": [ { - "name": "enrolment-issued" + "name": "enrolment-issued", + "body": { + "$ref": "#/defs/Enrolment" + } }, { - "name": "node-enrolled" + "name": "node-enrolled", + "body": { + "$ref": "#/defs/Enrolment" + } }, { - "name": "enrolment-refused" + "name": "enrolment-refused", + "body": { + "$ref": "#/defs/Enrolment" + } }, { - "name": "enrolment-revoked" + "name": "enrolment-revoked", + "body": { + "$ref": "#/defs/Enrolment" + } }, { - "name": "enrolment-expired" + "name": "enrolment-expired", + "body": { + "$ref": "#/defs/Enrolment" + } } - ] + ], + "family": { + "name": "mesh-node-enroller.events", + "max-age-days": 7 + }, + "states": [ + { + "name": "enrolments", + "template": "state", + "key-forms": [ + { + "key": "node.", + "value": { + "$ref": "#/defs/Enrolment" + }, + "description": "a node's enrolment, living until the token expires while live" + }, + { + "key": "address.
", + "value": { + "type": "object", + "description": "the private-network address reserved at issue", + "properties": { + "v": { + "type": "integer" + }, + "node": { + "type": "string" + }, + "ask": { + "type": "string" + }, + "issued": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "node" + ] + }, + "description": "an address reserved for a node" + } + ], + "history": 5, + "description": "open enrolments and the addresses reserved for them" + } + ], + "defs": { + "Enrolment": { + "type": "object", + "description": "one open enrolment", + "properties": { + "v": { + "type": "integer" + }, + "node": { + "type": "string" + }, + "address": { + "type": "string" + }, + "why": { + "type": "string" + }, + "caller": { + "type": "string" + }, + "ask": { + "type": "string" + }, + "stage": { + "type": "string", + "description": "asked, live or redeeming" + }, + "issued": { + "type": "string", + "format": "date-time" + }, + "expires": { + "type": "string", + "format": "date-time" + }, + "keys-digest": { + "type": "string" + }, + "overlay-key": { + "type": "string" + } + }, + "required": [ + "node", + "address", + "stage" + ] + } + } } diff --git a/go/seatlist/seatlisttest/seats/mesh-placement.json b/go/seatlist/seatlisttest/seats/mesh-placement.json deleted file mode 100644 index c38d9f4..0000000 --- a/go/seatlist/seatlisttest/seats/mesh-placement.json +++ /dev/null @@ -1,138 +0,0 @@ -{ - "format": 1, - "seat": "mesh-placement", - "scope": "mesh", - "grain": "seat-list", - "domain": "Placement", - "decision": "novox/hq ADR 0567", - "verbs": [ - { - "name": "placement", - "description": "Read placement: one node's key, the nodes one module is placed on, or every key, each with its revision, and how full the state is against its cap. With history, one node's or one module's changes in order: a revision the state keeps whole, an older one as the proposal's changes from its placement-changed event.", - "optional": true, - "input": { - "properties": { - "history": { - "description": "\"true\": the changes in order, from the stream and the state's revisions", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "module": { - "description": "one module: the nodes it is placed on", - "type": "string" - }, - "node": { - "description": "one node", - "type": "string" - }, - "seats": { - "description": "\"true\": who holds each seat, and on which node (the state seat-holders); with node, that node's", - "enum": [ - "true", - "false" - ], - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "proposals", - "description": "Every open proposal of placement: its changes, the revision of each key it was laid over, its candidates, every node's answer, the differences computed and answered, its verdict and its ask. Narrowed to one by id.", - "optional": true, - "input": { - "properties": { - "id": { - "description": "one proposal's id", - "type": "string" - } - }, - "type": "object" - } - }, - { - "name": "propose", - "description": "Propose a change of placement, with why: laid over the current keys and refused at once, naming every finding, when the result cannot be placed (two modules on one host port, a provider not placed, a seat held once held twice, two nodes with one address, two hubs, a module the indexer does not hold, a node with no record). Otherwise the nodes it touches compose it in shadow and answer, and on a passing verdict the operator is asked to approve it. Who proposes is the caller, as the runtime proves it. An add-node from mesh-enrolment, whose warrant names its node and address, is written with no ask of its own: its Approve is the enrolment's (ADR 0587 §7).", - "optional": true, - "input": { - "additionalProperties": false, - "properties": { - "changes": { - "description": "the changes, each an object with `op`: place (node, module, ports, bindings), take-off (node, module), pin (node, provision, provider {node, module}), unpin (node, provision), hold (node, module, claim, scope, kind, site), unhold (node, module, claim), decide (node, field, value: a decided field of the node, its address among them), config (node, module, mesh-wide, layer, mesh-wide-values, values), add-node (node, decided, keys: a joining node, mesh-enrolment's alone, the proposal's one change, with the enrolment's warrant, its tunnel key alone at the operator's yes; ADR 0587 §7), remove-node (node: a joining node never confirmed, taken off when its enrolment ends; mesh-enrolment's alone, on the same warrant)", - "items": { - "required": [ - "op", - "node" - ], - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "warrant": { - "description": "for an add-node alone: the enrolment's warrant {node, address, ask, warrant}, naming the change's node and address", - "required": [ - "node", - "address", - "ask", - "warrant" - ], - "type": "object" - }, - "why": { - "description": "why, shown to the operator with the proposal", - "type": "string" - } - }, - "required": [ - "why", - "changes" - ], - "type": "object" - } - }, - { - "name": "withdraw", - "description": "Withdraw an open proposal: its key and its candidates are deleted and placement-withdrawn carries it whole. With why.", - "optional": true, - "input": { - "properties": { - "id": { - "description": "the proposal's id", - "type": "string" - }, - "why": { - "description": "why, kept in the event", - "type": "string" - } - }, - "required": [ - "id", - "why" - ], - "type": "object" - } - }, - { - "name": "again", - "description": "Judge an open proposal now with the answers that stand, and raise its ask again when its verdict passes and the operator's channel does not hold the ask. Nothing more: the recovery of a proposal whose judgement or ask was lost.", - "optional": true, - "input": { - "properties": { - "id": { - "description": "the proposal's id", - "type": "string" - } - }, - "required": [ - "id" - ], - "type": "object" - } - } - ] -} diff --git a/go/seatlist/seatlisttest/seats/mesh-provisioner.json b/go/seatlist/seatlisttest/seats/mesh-provisioner.json index 719758d..ad9b169 100644 --- a/go/seatlist/seatlisttest/seats/mesh-provisioner.json +++ b/go/seatlist/seatlisttest/seats/mesh-provisioner.json @@ -2,9 +2,10 @@ "format": 1, "seat": "mesh-provisioner", "scope": "mesh", - "grain": "seat-list", + "grain": "whole", "domain": "Provisioning", - "decision": "novox/hq ADR 0349", + "decision": "novox/hq ADR 0349, ADR 0634, ADR 0635", + "description": "The provisioner (ADR 0349): which domain answers which kind of placeholder, what each module needs, what is applied where.", "verbs": [ { "name": "provisioning", @@ -30,7 +31,389 @@ } }, "type": "object" + }, + "answer": { + "type": "object", + "description": "the registry, the needs, what is applied, or the collections' items" } } + ], + "states": [ + { + "name": "plans", + "template": "state", + "key-forms": [ + { + "key": ".", + "value": { + "type": "object", + "description": "what a module needs on a node", + "properties": { + "v": { + "type": "integer" + }, + "node": { + "type": "string" + }, + "module": { + "type": "string" + }, + "plan": { + "type": "object", + "description": "the module's plan" + } + }, + "required": [ + "node", + "module", + "plan" + ] + }, + "description": "one module's plan on one node" + } + ], + "description": "the needs recorded for each module on each node" + }, + { + "name": "provisions", + "template": "state", + "key-forms": [ + { + "key": "..", + "value": { + "type": "object", + "description": "one provision between a consumer and a provider", + "properties": { + "v": { + "type": "integer" + }, + "provision": { + "type": "string" + }, + "local": { + "type": "string" + }, + "consumer": { + "type": "object", + "properties": { + "module": { + "type": "string" + }, + "node": { + "type": "string" + } + } + }, + "provider": { + "type": "object", + "properties": { + "module": { + "type": "string" + }, + "node": { + "type": "string" + } + } + }, + "depends-on": { + "type": "array", + "items": { + "type": "string" + } + }, + "state": { + "type": "string", + "enum": [ + "asked", + "provided", + "waits", + "failed", + "retiring" + ] + }, + "why": { + "type": "string" + }, + "reference": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "generation": { + "type": "integer" + } + } + }, + "ask": { + "type": "string" + }, + "warrant": { + "type": "object", + "properties": { + "ask": { + "type": "string" + }, + "option": { + "type": "string" + }, + "by": { + "type": "string" + }, + "at": { + "type": "string", + "format": "date-time" + } + } + }, + "plan": { + "type": "object", + "description": "the plan's provision: the entry the key was composed from, at the consumer's wanted build or its newest (issue 1126)", + "properties": { + "name": { + "type": "string" + }, + "provision": { + "type": "string" + }, + "local": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "provider-module": { + "type": "string" + }, + "credential-form": { + "enum": [ + "pair", + "shared", + "issued", + "given", + "none" + ] + }, + "credential-tool": { + "type": "string" + }, + "path": { + "type": "string" + }, + "contributes": { + "type": "object" + }, + "at": { + "type": "string" + }, + "from": { + "type": "string" + }, + "as": { + "type": "string" + }, + "names": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "contributed": { + "type": "object", + "description": "the provider's own contract's bytes, carried unread" + } + }, + "required": [ + "provision", + "provider", + "provider-module" + ], + "additionalProperties": false + }, + "at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "provision", + "consumer", + "provider", + "state", + "at" + ] + }, + "description": "a provision with no local name" + }, + { + "key": "...", + "value": { + "type": "object", + "description": "one provision between a consumer and a provider", + "properties": { + "v": { + "type": "integer" + }, + "provision": { + "type": "string" + }, + "local": { + "type": "string" + }, + "consumer": { + "type": "object", + "properties": { + "module": { + "type": "string" + }, + "node": { + "type": "string" + } + } + }, + "provider": { + "type": "object", + "properties": { + "module": { + "type": "string" + }, + "node": { + "type": "string" + } + } + }, + "depends-on": { + "type": "array", + "items": { + "type": "string" + } + }, + "state": { + "type": "string", + "enum": [ + "asked", + "provided", + "waits", + "failed", + "retiring" + ] + }, + "why": { + "type": "string" + }, + "reference": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "generation": { + "type": "integer" + } + } + }, + "ask": { + "type": "string" + }, + "warrant": { + "type": "object", + "properties": { + "ask": { + "type": "string" + }, + "option": { + "type": "string" + }, + "by": { + "type": "string" + }, + "at": { + "type": "string", + "format": "date-time" + } + } + }, + "plan": { + "type": "object", + "description": "the plan's provision: the entry the key was composed from, at the consumer's wanted build or its newest (issue 1126)", + "properties": { + "name": { + "type": "string" + }, + "provision": { + "type": "string" + }, + "local": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "provider-module": { + "type": "string" + }, + "credential-form": { + "enum": [ + "pair", + "shared", + "issued", + "given", + "none" + ] + }, + "credential-tool": { + "type": "string" + }, + "path": { + "type": "string" + }, + "contributes": { + "type": "object" + }, + "at": { + "type": "string" + }, + "from": { + "type": "string" + }, + "as": { + "type": "string" + }, + "names": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "contributed": { + "type": "object", + "description": "the provider's own contract's bytes, carried unread" + } + }, + "required": [ + "provision", + "provider", + "provider-module" + ], + "additionalProperties": false + }, + "at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "provision", + "consumer", + "provider", + "state", + "at" + ] + }, + "description": "a provision of a local name" + } + ], + "description": "every provision between a consumer and a provider, and how it stands" + } ] } diff --git a/go/seatlist/seatlisttest/seats/mesh-self-check.json b/go/seatlist/seatlisttest/seats/mesh-self-check.json index 1029b9c..40025a8 100644 --- a/go/seatlist/seatlisttest/seats/mesh-self-check.json +++ b/go/seatlist/seatlisttest/seats/mesh-self-check.json @@ -2,9 +2,10 @@ "format": 1, "seat": "mesh-self-check", "scope": "mesh", - "grain": "seat-list", + "grain": "whole", "domain": "Health and repair", - "decision": "novox/hq ADR 0564", + "decision": "novox/hq ADR 0564, ADR 0634, ADR 0635", + "description": "The mesh checking itself (ADR 0564): probes run every five minutes, each failure a finding the seat's conditions raise.", "verbs": [ { "name": "doctor", @@ -25,6 +26,10 @@ } }, "type": "object" + }, + "answer": { + "type": "object", + "description": "the last run's verdict, or one probe's, or a run now" } }, { @@ -33,12 +38,128 @@ "input": { "properties": {}, "type": "object" + }, + "answer": { + "type": "object", + "description": "the registry of probes" } } ], "events": [ { - "name": "checked" + "name": "checked", + "body": { + "type": "object", + "description": "one run of the self-check", + "properties": { + "run": { + "type": "string" + }, + "at": { + "type": "string", + "format": "date-time" + }, + "started": { + "type": "string", + "format": "date-time" + }, + "took": { + "type": "string" + }, + "interval-seconds": { + "type": "integer" + }, + "counts": { + "type": "object", + "properties": { + "passed": { + "type": "integer" + }, + "failed": { + "type": "integer" + }, + "failed-to-run": { + "type": "integer" + }, + "deferred": { + "type": "integer" + } + } + }, + "probes": { + "type": "array", + "items": { + "$ref": "#/defs/ProbeVerdict" + } + }, + "controller": { + "type": "string" + }, + "why": { + "type": "string" + }, + "unsaid": { + "type": "integer" + } + }, + "required": [ + "run", + "at", + "probes" + ] + } } - ] + ], + "family": { + "name": "mesh-self-check.events", + "max-age-days": 7 + }, + "defs": { + "ProbeVerdict": { + "type": "object", + "description": "one probe's verdict", + "properties": { + "id": { + "type": "string" + }, + "verdict": { + "type": "string", + "enum": [ + "passed", + "failed", + "failed-to-run", + "deferred" + ] + }, + "found": { + "type": "array", + "items": { + "type": "string" + } + }, + "unconfirmed": { + "type": "array", + "items": { + "type": "string" + } + }, + "error": { + "type": "string" + }, + "took": { + "type": "string" + }, + "found-count": { + "type": "integer" + }, + "more": { + "type": "string" + } + }, + "required": [ + "id", + "verdict" + ] + } + } } diff --git a/go/seatlist/seatlisttest/seats/mesh-vault.json b/go/seatlist/seatlisttest/seats/mesh-vault.json index e83f02a..7c93f1b 100644 --- a/go/seatlist/seatlisttest/seats/mesh-vault.json +++ b/go/seatlist/seatlisttest/seats/mesh-vault.json @@ -4,7 +4,7 @@ "scope": "mesh", "grain": "whole", "domain": "Identity and access", - "decision": "novox/hq ADR 0161", + "decision": "novox/hq ADR 0161, ADR 0634, ADR 0635", "delivers": "secret", "verbs": [ { @@ -300,7 +300,7 @@ }, { "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.", + "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-node-enroller 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-node-enroller alone. Answers the certificate or the signature and the signer's fingerprint, and says signed; never a key.", "optional": true, "input": { "properties": { @@ -309,7 +309,7 @@ "type": "string" }, "node": { - "description": "the machine, for a node certificate: the caller's own, or the node mesh-enrolment enrols", + "description": "the machine, for a node certificate: the caller's own, or the node mesh-node-enroller enrols", "type": "string" }, "reply": { @@ -317,7 +317,7 @@ "type": "string" }, "serving_key": { - "description": "from mesh-enrolment: the serving key's public half the enrolling node presented", + "description": "from mesh-node-enroller: the serving key's public half the enrolling node presented", "type": "string" } }, @@ -368,7 +368,7 @@ }, { "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).", + "description": "Make a one-time token for a node mesh-node-enroller 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-node-enroller alone, only from the lease write. The token carries the first tunnel mesh-node-enroller built at issue (ADR 0587 §3, insight of 2026-11-02).", "optional": true, "input": { "properties": { @@ -398,7 +398,7 @@ }, "tunnel": { "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", + "description": "the node's first tunnel, built by mesh-node-enroller 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": { "address": { "description": "the joining node's tunnel address", @@ -472,7 +472,7 @@ }, { "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.", + "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-node-enroller alone. Never the value in the clear.", "optional": true, "input": { "properties": { @@ -758,5 +758,128 @@ ] } } + ], + "states": [ + { + "name": "signers", + "template": "state", + "key-forms": [ + { + "key": "key", + "value": { + "type": "object", + "description": "the vault's signing key's public half", + "properties": { + "v": { + "type": "integer" + }, + "key": { + "type": "string", + "description": "the signing key's public half, standard base64" + }, + "fingerprint": { + "type": "string" + }, + "made": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "key", + "fingerprint", + "made" + ] + }, + "description": "the signing key's public half" + }, + { + "key": "authority", + "value": { + "type": "object", + "description": "the vault's authority certificate", + "properties": { + "v": { + "type": "integer" + }, + "certificate": { + "type": "string", + "description": "the authority certificate, PEM" + }, + "fingerprint": { + "type": "string" + }, + "made": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "certificate", + "fingerprint", + "made" + ] + }, + "description": "the authority certificate" + } + ], + "history": 5, + "description": "the vault's public signing halves; written by the vault alone" + }, + { + "name": "node-logins", + "template": "state", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "description": "a node's two logins and the rotation's stage", + "properties": { + "v": { + "type": "integer" + }, + "node": { + "type": "string" + }, + "login": { + "type": "string", + "description": "the login in use" + }, + "rotating": { + "type": "string", + "description": "the login rotating to, empty when none" + }, + "stage": { + "type": "string", + "description": "begun, ready or confirmed; empty when no rotation" + }, + "since": { + "type": "string", + "format": "date-time" + }, + "list-revision": { + "type": "integer", + "description": "the user list's revision that held both logins, from ready on" + }, + "acts": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "node", + "login", + "since" + ] + }, + "description": "a node" + } + ], + "history": 5, + "description": "which login a node uses and the stage of its rotation; the key lives while the node does" + } ] } diff --git a/go/seatlist/seatlisttest/seats/mesh-wants.json b/go/seatlist/seatlisttest/seats/mesh-wants.json index 3de06f7..236ed60 100644 --- a/go/seatlist/seatlisttest/seats/mesh-wants.json +++ b/go/seatlist/seatlisttest/seats/mesh-wants.json @@ -2,9 +2,10 @@ "format": 1, "seat": "mesh-wants", "scope": "mesh", - "grain": "seat-list", + "grain": "whole", "domain": "Placement", - "decision": "novox/hq ADR 0565", + "decision": "novox/hq ADR 0565, ADR 0634, ADR 0635", + "description": "The seat that says what each node should run and whether each module's new builds wait for a person (ADR 0565, ADR 0539).", "verbs": [ { "name": "upgrade", @@ -33,6 +34,10 @@ } }, "type": "object" + }, + "answer": { + "type": "object", + "description": "each module's policy, or the one chosen, or the comparison" } }, { @@ -41,12 +46,235 @@ "input": { "properties": {}, "type": "object" + }, + "answer": { + "type": "object", + "description": "what a bus upgrade would do, read only" } } ], "events": [ { - "name": "policy-changed" + "name": "policy-changed", + "body": { + "type": "object" + } + } + ], + "family": { + "name": "mesh-wants.events", + "max-age-days": 7 + }, + "states": [ + { + "name": "wants", + "template": "state", + "key-forms": [ + { + "key": "want..", + "value": { + "type": "object", + "description": "a node's want for a module", + "properties": { + "v": { + "type": "integer" + }, + "fp": { + "type": "string" + }, + "composed": { + "type": "string" + }, + "removed": { + "type": "boolean" + }, + "newest": { + "type": "string" + }, + "rule": { + "type": "integer", + "description": "the rule that holds the node back, 1 to 10" + }, + "backup": { + "type": "string", + "description": "again or skip" + }, + "words": { + "type": "string" + }, + "put": { + "type": "boolean" + }, + "hold": { + "type": "boolean" + }, + "yes": { + "type": "string" + }, + "release": { + "type": "string" + }, + "self": { + "type": "boolean" + } + } + }, + "description": "a node's want for a module" + }, + { + "key": "want..node", + "value": { + "type": "object", + "description": "a node's own want", + "properties": { + "v": { + "type": "integer" + }, + "composed": { + "type": "string" + }, + "words": { + "type": "string" + }, + "self": { + "type": "boolean" + } + }, + "required": [ + "composed" + ] + }, + "description": "a node's own want, naming the digest of its own composition" + }, + { + "key": "held..", + "value": { + "type": "object", + "description": "a fingerprint the assigner holds", + "properties": { + "v": { + "type": "integer" + }, + "node": { + "type": "string" + }, + "words": { + "type": "string" + } + }, + "required": [ + "node" + ] + }, + "description": "a module's fingerprint the assigner holds" + } + ], + "history": 20, + "description": "what each node should run, and the fingerprints held back" + }, + { + "name": "differences", + "template": "state", + "key-forms": [ + { + "key": ".", + "value": { + "type": "object", + "description": "the difference between a want and what is sent", + "properties": { + "v": { + "type": "integer" + }, + "want": { + "type": "string" + }, + "want-removed": { + "type": "boolean" + }, + "sent": { + "type": "string" + }, + "sent-build": { + "type": "string" + }, + "rule": { + "type": "integer" + }, + "unexplained": { + "type": "boolean" + }, + "words": { + "type": "string" + } + } + }, + "description": "a module on a node" + } + ], + "history": 64, + "description": "differences between a want and what the node-engine is sent" + }, + { + "name": "compositions", + "template": "state", + "key-forms": [ + { + "key": ".", + "value": { + "type": "object", + "description": "the composition verdict of a group's check at its heads", + "properties": { + "v": { + "type": "integer" + }, + "group": { + "type": "string" + }, + "heads": { + "type": "string", + "description": "12 lowercase hex digits" + }, + "state": { + "type": "string", + "description": "pass, fail or waits" + }, + "words": { + "type": "string" + }, + "sdk": { + "type": "string" + }, + "machines": { + "type": "array", + "items": { + "type": "string" + } + }, + "machine": { + "type": "string" + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } + }, + "took": { + "type": "integer", + "description": "milliseconds" + } + }, + "required": [ + "group", + "heads", + "state" + ] + }, + "description": "a group's check at its heads" + } + ], + "history": 5, + "description": "composition verdicts of a group's check; the checker reads them" } ] } diff --git a/go/seatlist/seatlisttest/seats/mesh-watchdogs.json b/go/seatlist/seatlisttest/seats/mesh-watchdogs.json index 5d5a076..35a4341 100644 --- a/go/seatlist/seatlisttest/seats/mesh-watchdogs.json +++ b/go/seatlist/seatlisttest/seats/mesh-watchdogs.json @@ -2,9 +2,10 @@ "format": 1, "seat": "mesh-watchdogs", "scope": "mesh", - "grain": "seat-list", + "grain": "whole", "domain": "Health and repair", - "decision": "novox/hq ADR 0564", + "decision": "novox/hq ADR 0564, ADR 0634, ADR 0635", + "description": "The watchman's seat (ADR 0564, ADR 0433): signals that expire unless renewed, what stopped moving, who is present, the dead letters.", "verbs": [ { "name": "signals", @@ -17,6 +18,10 @@ } }, "type": "object" + }, + "answer": { + "type": "object", + "description": "the watchdog table" } }, { @@ -30,6 +35,10 @@ } }, "type": "object" + }, + "answer": { + "type": "object", + "description": "what stopped moving" } }, { @@ -43,6 +52,10 @@ } }, "type": "object" + }, + "answer": { + "type": "object", + "description": "who is connected and who is silent" } }, { @@ -55,7 +68,7 @@ "type": "string" }, "consumer": { - "description": "a consumer's name, or \u003cstream\u003e.\u003cconsumer\u003e: only what that one gave up on", + "description": "a consumer's name, or .: only what that one gave up on", "type": "string" }, "deliver": { @@ -80,6 +93,10 @@ } }, "type": "object" + }, + "answer": { + "type": "object", + "description": "the dead letters, one whole, or the act done" } }, { @@ -88,7 +105,139 @@ "input": { "properties": {}, "type": "object" + }, + "answer": { + "type": "object", + "description": "what was rebuilt" } } + ], + "states": [ + { + "name": "presence", + "template": "state", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "description": "whether a node is online", + "properties": { + "v": { + "type": "integer" + }, + "online": { + "type": "boolean" + } + }, + "required": [ + "online" + ] + }, + "description": "a node" + }, + { + "key": "part..", + "value": { + "type": "object", + "description": "whether a part's instance is online", + "properties": { + "v": { + "type": "integer" + }, + "online": { + "type": "boolean" + } + }, + "required": [ + "online" + ] + }, + "description": "a part's instance" + } + ], + "history": 20, + "description": "who is connected; the entry's own time says since when" + }, + { + "name": "pulse", + "template": "state", + "key-forms": [ + { + "key": ".", + "value": { + "type": "object", + "description": "a watched key mirrored, its revision, and who owes the next move", + "properties": { + "v": { + "type": "integer" + }, + "bucket": { + "type": "string" + }, + "key": { + "type": "string" + }, + "revision": { + "type": "integer" + }, + "owes": { + "type": "string" + } + }, + "required": [ + "bucket", + "key", + "revision" + ] + }, + "description": "the move watched at a key of a state" + } + ], + "description": "one key per watched move, living for the move's bound minus its age" + }, + { + "name": "stuck", + "template": "state", + "key-forms": [ + { + "key": ".", + "value": { + "type": "object", + "description": "what stopped moving", + "properties": { + "v": { + "type": "integer" + }, + "bucket": { + "type": "string" + }, + "key": { + "type": "string" + }, + "waited": { + "type": "string", + "description": "what it waited for, in words" + }, + "since": { + "type": "string", + "format": "date-time" + }, + "moved": { + "type": "boolean", + "description": "it has moved again" + } + }, + "required": [ + "bucket", + "key", + "since" + ] + }, + "description": "a key whose pulse expired" + } + ], + "description": "what stopped moving; no key is removed, one that moves again is written as moved" + } ] } diff --git a/go/seatlist/seatlisttest/seats/node-build-go.json b/go/seatlist/seatlisttest/seats/node-build-go.json index ccb5664..a810a6b 100644 --- a/go/seatlist/seatlisttest/seats/node-build-go.json +++ b/go/seatlist/seatlisttest/seats/node-build-go.json @@ -2,46 +2,87 @@ "format": 1, "seat": "node-build-go", "scope": "node", - "grain": "seat-list", + "grain": "whole", "domain": "Delivery", "decision": "novox/hq ADR 0525, ADR 0536, ADR 0553", "pulled-by-kind": true, "yields": true, - "verbs": [ + "accepts": [ { - "name": "current", - "description": "The Go asks this node runs now (each ask's id, kind, module and when it started), its build capacity, and whether it is paused, taking no new ask.", - "input": { - "properties": {}, - "type": "object" + "name": "delivery", + "body": { + "$ref": "#/defs/StepAsk" } }, { - "name": "pause", - "description": "Take no new Go ask on this node until resumed; an ask running here ends in its place. Kept across a restart of the build module (its pause key). A capacity of 0 is the lasting way to keep a node from building.", - "input": { - "properties": {}, - "type": "object" + "name": "check", + "body": { + "$ref": "#/defs/StepAsk" } }, { - "name": "resume", - "description": "Take Go asks again on this node.", - "input": { - "properties": {}, - "type": "object" + "name": "recheck", + "body": { + "$ref": "#/defs/StepAsk" } } ], - "accepts": [ + "description": "The Go build module on a node: it takes the asks of the builder and the checker, runs them as steps and answers.", + "includes": [ + "build-step" + ], + "states": [ { - "name": "delivery" + "name": "steps", + "template": "state", + "key-forms": [ + { + "key": ".", + "value": { + "$ref": "#/defs/Step" + }, + "description": "a step on a node" + } + ], + "description": "the steps this module runs", + "history": 5 }, { - "name": "check" + "name": "pauses", + "template": "state", + "key-forms": [ + { + "key": "paused.", + "value": { + "$ref": "#/defs/Pause" + }, + "description": "whether a node is paused" + }, + { + "key": "capacity.", + "value": { + "$ref": "#/defs/Capacity" + }, + "description": "a node's capacity" + } + ], + "description": "the pause and the capacity of each node", + "history": 1 }, { - "name": "recheck" + "name": "toolchains", + "template": "state", + "key-forms": [ + { + "key": ".", + "value": { + "$ref": "#/defs/Toolchain" + }, + "description": "the toolchain a node reports" + } + ], + "description": "the toolchains the nodes report", + "history": 1 } ] } diff --git a/go/seatlist/seatlisttest/seats/node-build-image.json b/go/seatlist/seatlisttest/seats/node-build-image.json index b275f85..037a890 100644 --- a/go/seatlist/seatlisttest/seats/node-build-image.json +++ b/go/seatlist/seatlisttest/seats/node-build-image.json @@ -2,46 +2,87 @@ "format": 1, "seat": "node-build-image", "scope": "node", - "grain": "seat-list", + "grain": "whole", "domain": "Delivery", "decision": "novox/hq ADR 0525, ADR 0536, ADR 0553", "pulled-by-kind": true, "yields": true, - "verbs": [ + "accepts": [ { - "name": "current", - "description": "The image asks this node runs now (each ask's id, kind, module and when it started), its build capacity, and whether it is paused, taking no new ask.", - "input": { - "properties": {}, - "type": "object" + "name": "delivery", + "body": { + "$ref": "#/defs/StepAsk" } }, { - "name": "pause", - "description": "Take no new image ask on this node until resumed; an ask running here ends in its place. Kept across a restart of the build module (its pause key). A capacity of 0 is the lasting way to keep a node from building.", - "input": { - "properties": {}, - "type": "object" + "name": "check", + "body": { + "$ref": "#/defs/StepAsk" } }, { - "name": "resume", - "description": "Take image asks again on this node.", - "input": { - "properties": {}, - "type": "object" + "name": "recheck", + "body": { + "$ref": "#/defs/StepAsk" } } ], - "accepts": [ + "description": "The image build module on a node: it takes the asks of the builder and the checker, runs them as steps and answers.", + "includes": [ + "build-step" + ], + "states": [ { - "name": "delivery" + "name": "steps", + "template": "state", + "key-forms": [ + { + "key": ".", + "value": { + "$ref": "#/defs/Step" + }, + "description": "a step on a node" + } + ], + "description": "the steps this module runs", + "history": 5 }, { - "name": "check" + "name": "pauses", + "template": "state", + "key-forms": [ + { + "key": "paused.", + "value": { + "$ref": "#/defs/Pause" + }, + "description": "whether a node is paused" + }, + { + "key": "capacity.", + "value": { + "$ref": "#/defs/Capacity" + }, + "description": "a node's capacity" + } + ], + "description": "the pause and the capacity of each node", + "history": 1 }, { - "name": "recheck" + "name": "toolchains", + "template": "state", + "key-forms": [ + { + "key": ".", + "value": { + "$ref": "#/defs/Toolchain" + }, + "description": "the toolchain a node reports" + } + ], + "description": "the toolchains the nodes report", + "history": 1 } ] } diff --git a/go/seatlist/seatlisttest/seats/node-build-node.json b/go/seatlist/seatlisttest/seats/node-build-node.json index aa7f182..6fce146 100644 --- a/go/seatlist/seatlisttest/seats/node-build-node.json +++ b/go/seatlist/seatlisttest/seats/node-build-node.json @@ -2,46 +2,87 @@ "format": 1, "seat": "node-build-node", "scope": "node", - "grain": "seat-list", + "grain": "whole", "domain": "Delivery", "decision": "novox/hq ADR 0525, ADR 0536, ADR 0553", "pulled-by-kind": true, "yields": true, - "verbs": [ + "accepts": [ { - "name": "current", - "description": "The Node asks this node runs now (each ask's id, kind, module and when it started), its build capacity, and whether it is paused, taking no new ask.", - "input": { - "properties": {}, - "type": "object" + "name": "delivery", + "body": { + "$ref": "#/defs/StepAsk" } }, { - "name": "pause", - "description": "Take no new Node ask on this node until resumed; an ask running here ends in its place. Kept across a restart of the build module (its pause key). A capacity of 0 is the lasting way to keep a node from building.", - "input": { - "properties": {}, - "type": "object" + "name": "check", + "body": { + "$ref": "#/defs/StepAsk" } }, { - "name": "resume", - "description": "Take Node asks again on this node.", - "input": { - "properties": {}, - "type": "object" + "name": "recheck", + "body": { + "$ref": "#/defs/StepAsk" } } ], - "accepts": [ + "description": "The Node build module on a node: it takes the asks of the builder and the checker, runs them as steps and answers.", + "includes": [ + "build-step" + ], + "states": [ { - "name": "delivery" + "name": "steps", + "template": "state", + "key-forms": [ + { + "key": ".", + "value": { + "$ref": "#/defs/Step" + }, + "description": "a step on a node" + } + ], + "description": "the steps this module runs", + "history": 5 }, { - "name": "check" + "name": "pauses", + "template": "state", + "key-forms": [ + { + "key": "paused.", + "value": { + "$ref": "#/defs/Pause" + }, + "description": "whether a node is paused" + }, + { + "key": "capacity.", + "value": { + "$ref": "#/defs/Capacity" + }, + "description": "a node's capacity" + } + ], + "description": "the pause and the capacity of each node", + "history": 1 }, { - "name": "recheck" + "name": "toolchains", + "template": "state", + "key-forms": [ + { + "key": ".", + "value": { + "$ref": "#/defs/Toolchain" + }, + "description": "the toolchain a node reports" + } + ], + "description": "the toolchains the nodes report", + "history": 1 } ] } diff --git a/go/seatlist/seatlisttest/seats/node-engine.json b/go/seatlist/seatlisttest/seats/node-engine.json index d09c320..237a651 100644 --- a/go/seatlist/seatlisttest/seats/node-engine.json +++ b/go/seatlist/seatlisttest/seats/node-engine.json @@ -674,6 +674,30 @@ ], "description": "what this machine's engine reads of a manifest (ADR 0618 §3)" } + }, + { + "name": "seat-list", + "description": "The seat list this engine holds and what it waits for (novox/hq ADR 0636 §7): each seat's source (trunk or bootstrap) and digest, the seats held from the bootstrap alone, the definitions refused, the last error and whether the seat mesh-catalog has answered. Read-only.", + "callers": "the operator and the healers", + "input": { + "type": "object", + "properties": {} + }, + "answer": { + "$ref": "#/defs/SeatList" + } + }, + { + "name": "seat-list-again", + "description": "Make this engine read the seat list from the seat mesh-catalog now (novox/hq ADR 0636 §7), the recovery of a list unread; it answers what it holds afterwards, and the error when the read failed. A read that changed nothing still ends the engine's wait.", + "callers": "the operator and the healers", + "input": { + "type": "object", + "properties": {} + }, + "answer": { + "$ref": "#/defs/SeatList" + } } ], "events": [ @@ -710,6 +734,550 @@ "name": "node-engine.events", "max-age-days": 7 }, + "states": [ + { + "name": "have", + "template": "state", + "key-forms": [ + { + "key": ".", + "value": { + "type": "object", + "description": "one module's state on a node: what the engine has built, composed and run", + "properties": { + "v": { + "type": "integer" + }, + "fp": { + "type": "string", + "description": "the fingerprint held" + }, + "composed": { + "type": "string", + "description": "the digest of the composition the node runs" + }, + "build": { + "type": "string" + }, + "phase": { + "type": "string", + "description": "how far it has come", + "enum": [ + "backing-up", + "applying", + "checking", + "running", + "failed", + "removed", + "held", + "waiting-yes" + ] + }, + "health": { + "type": "string", + "description": "what the module's own check says", + "enum": [ + "ok", + "failed", + "waits", + "unknown" + ] + }, + "detail": { + "type": "string" + }, + "user-list": { + "$ref": "#/defs/UserListWritten" + }, + "judgement": { + "type": "string", + "description": "in phase waiting-yes, the judgement of the move", + "enum": [ + "recreates", + "moves-data", + "cannot-judge" + ] + }, + "waits": { + "type": "string", + "description": "in phase waiting-yes, the digest of the composition that waits" + }, + "build-alone": { + "type": "boolean", + "description": "the move changes the build alone" + }, + "kept": { + "type": "array", + "items": { + "type": "string" + }, + "description": "what the node keeps of the module as it runs" + }, + "sealed": { + "type": "array", + "items": { + "type": "string" + }, + "description": "each value sealed to the node's key" + }, + "sealed-at": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "the sealed values by slot" + }, + "pull-wait": { + "$ref": "#/defs/PullWait" + }, + "handed": { + "$ref": "#/defs/AccountsHanded" + } + }, + "required": [ + "v", + "phase", + "health" + ] + }, + "description": "one module on a node" + }, + { + "key": ".node", + "value": { + "type": "object", + "description": "a node's own record", + "properties": { + "v": { + "type": "integer" + }, + "composed": { + "type": "string", + "description": "the digest of the node's own composition it runs" + }, + "windows": { + "type": "object" + }, + "busy": { + "type": "object" + }, + "strays": { + "type": "object" + }, + "filters": { + "type": "object" + }, + "profile": { + "type": "object" + }, + "record": { + "$ref": "#/defs/NodeRecord" + }, + "sealed": { + "type": "array", + "items": { + "type": "string" + } + }, + "sealed-at": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "candidates": { + "type": "object", + "additionalProperties": { + "$ref": "#/defs/CandidateAnswer" + }, + "description": "the node's answer to each placement candidate naming it, by proposal id" + }, + "composes": { + "type": "string", + "description": "own, or none (empty)" + }, + "composes-why": { + "type": "string" + } + }, + "required": [ + "v" + ] + }, + "description": "the node's own record" + } + ], + "history": 20, + "description": "what each node has of each module, written by its engine" + }, + { + "name": "composed", + "template": "state", + "key-forms": [ + { + "key": "..", + "value": { + "type": "object", + "description": "one composition of a module on a node under a fingerprint", + "properties": { + "v": { + "type": "integer" + }, + "node": { + "type": "string" + }, + "module": { + "type": "string" + }, + "fp": { + "type": "string" + }, + "data": { + "type": "boolean", + "description": "the module declares data" + }, + "resources": { + "type": "array", + "items": { + "type": "object" + }, + "description": "the declaration resources, each saying its module" + }, + "filled_files": { + "type": "array", + "items": { + "type": "string" + }, + "description": "the files the provisioner fills" + }, + "plan": { + "type": "object", + "description": "the module's whole provisioning plan, catalogue.Plan, kept whole: it is the manifest layer's type" + }, + "refused": { + "type": "string", + "description": "why the module cannot be composed at this fingerprint" + } + }, + "required": [ + "v", + "node", + "module", + "fp", + "resources" + ] + }, + "description": "one composition" + }, + { + "key": ".node", + "value": { + "type": "object", + "description": "the node's own composition: its resources and parts", + "properties": { + "v": { + "type": "integer" + }, + "node": { + "type": "string" + }, + "data": { + "type": "boolean", + "description": "the module declares data" + }, + "resources": { + "type": "array", + "items": { + "type": "object" + }, + "description": "the declaration resources, each saying its module" + }, + "filled_files": { + "type": "array", + "items": { + "type": "string" + }, + "description": "the files the provisioner fills" + }, + "own": { + "$ref": "#/defs/NodeOwn" + } + }, + "required": [ + "v", + "node", + "resources" + ] + }, + "description": "the node's own composition" + } + ], + "description": "the compositions of one node, kept in a bucket per node named composed-" + }, + { + "name": "data", + "template": "state", + "key-forms": [ + { + "key": ".", + "value": { + "type": "object", + "description": "the data items a module has on a node", + "properties": { + "v": { + "type": "integer" + }, + "node": { + "type": "string" + }, + "module": { + "type": "string" + }, + "digest": { + "type": "string" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/defs/PlacedDataItem" + } + } + }, + "required": [ + "v", + "node", + "module", + "digest", + "items" + ] + }, + "description": "a module's data on a node" + } + ], + "description": "the data items of each module on each node" + }, + { + "name": "packages", + "template": "state", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "description": "the packages a node declares", + "properties": { + "node": { + "type": "string" + }, + "packages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "package": { + "type": "string" + }, + "modules": { + "type": "array", + "items": { + "type": "string" + } + }, + "absent": { + "type": "boolean" + } + }, + "required": [ + "package", + "modules", + "absent" + ] + } + } + }, + "required": [ + "node", + "packages" + ] + }, + "description": "a node's declared packages" + } + ], + "description": "the packages each node declares" + }, + { + "name": "health", + "template": "state", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "description": "a node's health as its engine reports it", + "properties": { + "v": { + "type": "integer" + }, + "node": { + "type": "string" + }, + "network": { + "$ref": "#/defs/NetworkHealth" + }, + "peers": { + "type": "array", + "items": { + "$ref": "#/defs/PeerSeen" + } + }, + "units": { + "$ref": "#/defs/UnitsHealth" + }, + "relogin": { + "type": "array", + "items": { + "$ref": "#/defs/NodeAccount" + } + }, + "found": { + "type": "array", + "items": { + "$ref": "#/defs/NodeDirectory" + } + }, + "fallback": { + "$ref": "#/defs/LoginFallback" + }, + "root": { + "type": "array", + "items": { + "$ref": "#/defs/RootAccountVerdict" + } + }, + "contract": { + "type": "integer" + }, + "at": { + "type": "string", + "description": "when", + "format": "date-time" + } + }, + "required": [ + "v", + "node", + "at" + ] + }, + "description": "a node's health" + } + ], + "description": "each node's health as its engine reports it" + }, + { + "name": "findings", + "template": "state", + "writer": "holder", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "description": "one thing an observer sees wrong now", + "properties": { + "v": { + "type": "integer", + "description": "layout version, 1" + }, + "key": { + "type": "string", + "description": "scope.id.token" + }, + "kind": { + "type": "string" + }, + "subject": { + "$ref": "#/defs/FindingSubject" + }, + "severity": { + "type": "string", + "enum": [ + "urgent", + "warning" + ] + }, + "summary": { + "type": "string", + "description": "one line" + }, + "headline": { + "type": "string" + }, + "explanation": { + "type": "string" + }, + "resolved": { + "type": "string" + }, + "needs": { + "type": "string" + }, + "actions": { + "type": "array", + "items": { + "$ref": "#/defs/FindingAction" + } + }, + "evidence": { + "type": "array", + "description": "newest first, at most 10", + "items": { + "$ref": "#/defs/FindingEvidence" + } + }, + "source": { + "type": "string", + "description": "the row, probe or event that saw it" + }, + "resolver": { + "type": "string", + "description": "self, operator, agent or healer:" + }, + "ref": { + "type": "string", + "description": "the key's last part when it is not the kind" + }, + "observer": { + "type": "string", + "description": "the module that wrote it" + }, + "run": { + "type": "string", + "description": "what of its own saw it last" + }, + "at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "v", + "key", + "kind", + "subject", + "severity", + "summary", + "source", + "observer", + "at" + ] + }, + "description": "one finding of the observer that wrote it" + } + ], + "description": "the findings this node's engine raises about itself, one key per finding, in the layout of the seat mesh-conditions (its findings state): a key written raises the condition of the same key, a key deleted clears it (novox/hq ADR 0564 §2, ADR 0636 §1); today `machine..seat-list-unread`" + } + ], "defs": { "Act": { "type": "object", @@ -1051,6 +1619,1033 @@ "said" ], "description": "the answer of a read-only judgement" + }, + "SeatListHeld": { + "type": "object", + "properties": { + "seat": { + "type": "string" + }, + "source": { + "type": "string", + "enum": [ + "trunk", + "bootstrap" + ] + }, + "digest": { + "type": "string", + "description": "the digest of the definition held; empty for a bootstrapped one" + } + }, + "required": [ + "seat", + "source" + ] + }, + "SeatList": { + "type": "object", + "description": "what an engine holds of the seat list (novox/hq ADR 0636 §7)", + "properties": { + "status": { + "type": "object", + "properties": { + "heard": { + "type": "boolean", + "description": "the seat mesh-catalog has answered" + }, + "held": { + "type": "array", + "items": { + "$ref": "#/defs/SeatListHeld" + } + }, + "waiting": { + "type": "array", + "items": { + "type": "string" + }, + "description": "the seats held from the bootstrap alone" + }, + "refused": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "a trunk definition that does not parse, by seat: the one held stands" + }, + "last-error": { + "type": "string" + }, + "revision": { + "type": "integer" + } + }, + "required": [ + "heard", + "held" + ] + }, + "health": { + "type": "string", + "description": "the one line the engine says of it; empty when nothing waits" + }, + "error": { + "type": "string", + "description": "why the read failed, for seat-list-again" + } + }, + "required": [ + "status", + "health" + ] + }, + "FindingSubject": { + "type": "object", + "description": "what the condition is about", + "properties": { + "scope": { + "type": "string", + "description": "machine, plan, call, build, merge, provider, seat, bus, core, probe, mesh, delivery or module" + }, + "id": { + "type": "string", + "description": "its id within the scope" + }, + "machine": { + "type": "string", + "description": "the machine it concerns, when there is one" + }, + "also": { + "type": "array", + "description": "the other machines it concerns", + "items": { + "type": "string" + } + } + }, + "required": [ + "scope", + "id" + ] + }, + "FindingAction": { + "type": "object", + "description": "one answer the operator may give from a notification", + "properties": { + "label": { + "type": "string" + }, + "verb": { + "type": "string", + "description": "the seat verb the operator channel calls" + }, + "machine": { + "type": "string" + }, + "arguments": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "level": { + "type": "string", + "enum": [ + "acknowledge", + "approve" + ] + } + }, + "required": [ + "label", + "verb" + ] + }, + "FindingEvidence": { + "type": "object", + "description": "one observation, as it was said", + "properties": { + "at": { + "type": "string", + "format": "date-time" + }, + "said": { + "type": "string" + } + }, + "required": [ + "at", + "said" + ] + }, + "UserListWritten": { + "type": "object", + "description": "the bus's user list as the engine of the bus node wrote it to the bus module's file", + "properties": { + "source": { + "type": "string", + "description": "the key the list was taken from" + }, + "revision": { + "type": "integer" + }, + "wanted": { + "type": "string", + "description": "the digest of the list the source holds, sha256:" + }, + "digest": { + "type": "string", + "description": "the digest of the file on the machine after the write" + }, + "users": { + "type": "integer" + }, + "at": { + "type": "string", + "description": "when", + "format": "date-time" + }, + "error": { + "type": "string", + "description": "why a write failed" + }, + "follows": { + "type": "string", + "description": "which list the node follows now" + }, + "key": { + "type": "string", + "description": "the bus node's sealing key, its public half" + }, + "unwatched": { + "type": "string", + "description": "why the bus node does not watch the grants part's key now" + }, + "handed": { + "$ref": "#/defs/AccountsHanded" + } + }, + "required": [ + "source", + "revision", + "wanted", + "users", + "at" + ] + }, + "AccountsHanded": { + "type": "object", + "description": "the hashes of the users of the list, sealed to the vault", + "properties": { + "from": { + "type": "string" + }, + "to": { + "type": "string", + "description": "the fingerprint of the vault's key" + }, + "users": { + "type": "integer" + }, + "box": { + "$ref": "#/defs/SealedBox" + }, + "at": { + "type": "string", + "description": "when", + "format": "date-time" + } + }, + "required": [ + "from", + "to", + "users", + "box", + "at" + ] + }, + "SealedBox": { + "type": "object", + "description": "a value sealed to a key", + "properties": { + "v": { + "type": "integer" + }, + "eph": { + "type": "string" + }, + "iv": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "ct": { + "type": "string" + } + }, + "required": [ + "v", + "eph", + "iv", + "tag", + "ct" + ] + }, + "PullWait": { + "type": "object", + "description": "the pull from a store a failed apply waits on", + "properties": { + "image": { + "type": "string" + }, + "seat": { + "type": "string", + "description": "the seat the pull came through" + }, + "holder": { + "type": "string", + "description": "that seat's holder, whose settling the apply waits for" + }, + "began": { + "type": "string", + "description": "when the pull began", + "format": "date-time" + }, + "retried": { + "type": "string", + "description": "what triggered the last try again, in words" + }, + "retried-at": { + "type": "string", + "description": "when the apply was last tried again", + "format": "date-time" + } + }, + "required": [ + "image", + "holder", + "began" + ] + }, + "NodeRecord": { + "type": "object", + "description": "what the machine observes of itself, for every node and composing part to read", + "properties": { + "v": { + "type": "integer" + }, + "node": { + "type": "string" + }, + "tunnel-key": { + "type": "string" + }, + "capabilities": { + "type": "array", + "items": { + "type": "string" + } + }, + "sealing-key": { + "type": "string" + }, + "serving-key": { + "type": "string" + }, + "certificate": { + "type": "string" + }, + "outward-links": { + "type": "array", + "items": { + "type": "string" + } + }, + "networks": { + "type": "array", + "items": { + "$ref": "#/defs/AttachedNetwork" + } + }, + "networks-said": { + "type": "boolean" + }, + "contract": { + "type": "integer" + }, + "authority": { + "type": "string" + }, + "trusts": { + "type": "array", + "items": { + "type": "string" + } + }, + "manifest-reads": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "v", + "node" + ] + }, + "AttachedNetwork": { + "type": "object", + "description": "a network attached to a link", + "properties": { + "link": { + "type": "string" + }, + "network": { + "type": "string" + } + }, + "required": [ + "link", + "network" + ] + }, + "CandidateAnswer": { + "type": "object", + "description": "the node's answer to one placement candidate naming it", + "properties": { + "proposal": { + "type": "string" + }, + "revision": { + "type": "integer" + }, + "verdict": { + "type": "string", + "description": "same, differs or refused", + "enum": [ + "same", + "differs", + "refused" + ] + }, + "same": { + "type": "array", + "items": { + "type": "string" + }, + "description": "modules whose composition is the one running" + }, + "differs": { + "type": "array", + "items": { + "$ref": "#/defs/CandidateDiffers" + } + }, + "added": { + "type": "array", + "items": { + "$ref": "#/defs/CandidateAdded" + } + }, + "removed": { + "type": "array", + "items": { + "type": "string" + }, + "description": "modules the candidate removes" + }, + "refusals": { + "type": "array", + "items": { + "$ref": "#/defs/CandidateRefusal" + } + }, + "held": { + "type": "array", + "items": { + "$ref": "#/defs/CandidateRefusal" + } + }, + "refused": { + "type": "string", + "description": "why the node cannot compose with the candidate at all" + }, + "returns": { + "type": "string" + }, + "note": { + "type": "string" + }, + "values_differ": { + "type": "integer" + } + }, + "required": [ + "proposal", + "revision", + "verdict" + ] + }, + "CandidateDiffers": { + "type": "object", + "description": "a module whose composition with the candidate differs from the one running", + "properties": { + "module": { + "type": "string" + }, + "composed": { + "type": "string" + }, + "running": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "why": { + "type": "string" + }, + "bodies": { + "type": "array", + "items": { + "$ref": "#/defs/CandidateBodies" + } + } + }, + "required": [ + "module", + "why" + ] + }, + "CandidateBodies": { + "type": "object", + "description": "the bodies of one resource that differ", + "properties": { + "id": { + "type": "string" + }, + "why": { + "type": "string" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "composed": { + "type": "string" + }, + "running": { + "type": "string" + } + }, + "required": [ + "id", + "why" + ] + }, + "CandidateAdded": { + "type": "object", + "description": "a module the candidate adds", + "properties": { + "module": { + "type": "string" + }, + "composed": { + "type": "string" + }, + "unissued": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "module" + ] + }, + "CandidateRefusal": { + "type": "object", + "description": "a module refused or held with the candidate", + "properties": { + "module": { + "type": "string" + }, + "why": { + "type": "string" + }, + "today": { + "type": "string" + } + }, + "required": [ + "module", + "why" + ] + }, + "NodeOwn": { + "type": "object", + "description": "what a node's own composition carries beside its resources", + "properties": { + "adoption": { + "$ref": "#/defs/Adoption" + }, + "identities": { + "type": "array", + "items": { + "$ref": "#/defs/DeclaredIdentity" + } + }, + "groups": { + "type": "array", + "items": { + "$ref": "#/defs/DeclaredGroup" + } + }, + "libraries": { + "type": "array", + "items": { + "$ref": "#/defs/DeclaredLibrary" + } + }, + "provisioner_account": { + "type": "string", + "description": "the account the provisioner's client runs as on the node" + } + } + }, + "Adoption": { + "type": "object", + "description": "the node is adopted and which of its modules are taken", + "properties": { + "taken": { + "type": "array", + "items": { + "type": "string" + }, + "description": "the modules taken" + }, + "untaken": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "by module, the resources left untaken" + } + }, + "required": [ + "taken" + ] + }, + "DeclaredIdentity": { + "type": "object", + "description": "the identity of a module assigned to the node", + "properties": { + "module": { + "type": "string" + }, + "name": { + "type": "string" + }, + "uid": { + "type": "integer" + }, + "gid": { + "type": "integer" + }, + "groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "runner": { + "type": "string" + } + }, + "required": [ + "module", + "name", + "uid", + "gid" + ] + }, + "DeclaredGroup": { + "type": "object", + "description": "a group", + "properties": { + "name": { + "type": "string" + }, + "gid": { + "type": "integer" + } + }, + "required": [ + "name", + "gid" + ] + }, + "DeclaredLibrary": { + "type": "object", + "description": "a library the node is reached by", + "properties": { + "library": { + "type": "string" + }, + "root": { + "type": "string" + }, + "group": { + "type": "string" + }, + "gid": { + "type": "integer" + }, + "home": { + "type": "boolean" + }, + "taken": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "runner": { + "type": "string" + } + }, + "required": [ + "library", + "root", + "group", + "gid", + "home" + ] + }, + "PlacedDataItem": { + "type": "object", + "description": "one data item a module declares, as placed on a node", + "properties": { + "module": { + "type": "string" + }, + "item": { + "type": "string" + }, + "class": { + "type": "string" + }, + "path": { + "type": "string", + "description": "the item's place on the node" + }, + "covered_by": { + "type": "string", + "description": "the path whose snapshot keeps it, empty when it is not backed up" + }, + "protection": { + "type": "string", + "description": "backup, redundancy, both or none" + }, + "measure": { + "type": "string", + "description": "walk, dataset or shallow" + } + }, + "required": [ + "module", + "item", + "class", + "path" + ] + }, + "NetworkHealth": { + "type": "object", + "description": "the node's network", + "properties": { + "state": { + "type": "string", + "description": "running, degraded or unknown" + }, + "since": { + "type": "string", + "description": "since when", + "format": "date-time" + }, + "parts": { + "type": "array", + "items": { + "$ref": "#/defs/NetworkPart" + } + } + }, + "required": [ + "state", + "since", + "parts" + ] + }, + "NetworkPart": { + "type": "object", + "description": "one part of the node's network", + "properties": { + "part": { + "type": "string" + }, + "state": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "said": { + "type": "string" + }, + "writer": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "toward": { + "type": "array", + "items": { + "type": "string" + } + }, + "targets": { + "type": "array", + "items": { + "type": "string" + } + }, + "since": { + "type": "string", + "description": "since when", + "format": "date-time" + }, + "streak": { + "type": "integer" + } + }, + "required": [ + "part", + "state", + "since" + ] + }, + "UnitsHealth": { + "type": "object", + "description": "the node's units", + "properties": { + "state": { + "type": "string" + }, + "failed": { + "type": "array", + "items": { + "$ref": "#/defs/FailedUnit" + } + }, + "unread": { + "type": "array", + "items": { + "type": "string" + } + }, + "away": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "state", + "failed" + ] + }, + "FailedUnit": { + "type": "object", + "description": "a unit that failed", + "properties": { + "unit": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "load": { + "type": "string" + }, + "result": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "since": { + "type": "string", + "description": "since when", + "format": "date-time" + } + }, + "required": [ + "unit", + "scope", + "since" + ] + }, + "PeerSeen": { + "type": "object", + "description": "a tunnel peer and whether it is seen", + "properties": { + "key": { + "type": "string" + }, + "addresses": { + "type": "array", + "items": { + "type": "string" + } + }, + "seen": { + "type": "boolean" + } + }, + "required": [ + "key", + "seen" + ] + }, + "NodeAccount": { + "type": "object", + "description": "an account the node needs to log in again", + "properties": { + "account": { + "type": "string" + }, + "module": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "since": { + "type": "string", + "description": "since when", + "format": "date-time" + } + }, + "required": [ + "account", + "reason", + "since" + ] + }, + "NodeDirectory": { + "type": "object", + "description": "a directory found where the node expected none", + "properties": { + "module": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "path": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "since": { + "type": "string", + "description": "since when", + "format": "date-time" + } + }, + "required": [ + "module", + "resource", + "path", + "reason", + "since" + ] + }, + "LoginFallback": { + "type": "object", + "description": "a login that fell back to another node", + "properties": { + "node": { + "type": "string" + }, + "from": { + "type": "string" + }, + "why": { + "type": "string" + }, + "at": { + "type": "string", + "description": "when", + "format": "date-time" + } + }, + "required": [ + "node", + "from", + "why", + "at" + ] + }, + "RootAccountVerdict": { + "type": "object", + "description": "the engine's verdict of whether an account's resource is root-free, as the node's health lists it", + "properties": { + "account": { + "type": "string" + }, + "module": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "state": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "since": { + "type": "string", + "description": "since when", + "format": "date-time" + } + }, + "required": [ + "account", + "module", + "resource", + "state", + "since" + ] } } } diff --git a/go/seatlist/seatlisttest/seats/oci-artifact-registry.json b/go/seatlist/seatlisttest/seats/oci-artifact-registry.json index a9d969c..ee6f5bf 100644 --- a/go/seatlist/seatlisttest/seats/oci-artifact-registry.json +++ b/go/seatlist/seatlisttest/seats/oci-artifact-registry.json @@ -244,5 +244,140 @@ "held" ] } - } + }, + "states": [ + { + "name": "holds", + "template": "state", + "history": 1, + "description": "the bundles, archives and copied bases kept from retention's removal", + "key-forms": [ + { + "key": "..", + "value": { + "type": "object", + "description": "one hold: an untagged bundle, archive or copied base kept from retention's removal", + "properties": { + "seat": { + "type": "string" + }, + "name": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "digest": { + "type": "string" + }, + "by": { + "type": "string" + }, + "why": { + "type": "string" + }, + "since": { + "type": "string", + "description": "when (RFC 3339 time)" + } + }, + "required": [ + "seat", + "name", + "kind", + "digest", + "by", + "since" + ] + }, + "description": "one hold" + } + ] + }, + { + "name": "windows", + "template": "state", + "history": 1, + "description": "the direct-write windows, one per caller", + "key-forms": [ + { + "key": "", + "value": { + "type": "object", + "description": "the direct-write window of one caller: open while a node holds it open", + "properties": { + "name": { + "type": "string", + "description": "the caller whose window it is" + }, + "open": { + "type": "boolean" + }, + "holding": { + "type": "array", + "items": { + "type": "string" + }, + "description": "the nodes holding it open" + }, + "since": { + "type": "object", + "additionalProperties": { + "type": "string", + "description": "when (RFC 3339 time)" + }, + "description": "node to when it began to hold" + }, + "closed": { + "type": "string", + "description": "when it last closed (RFC 3339 time)" + }, + "last_holding": { + "type": "array", + "items": { + "type": "string" + } + }, + "dropped": { + "type": "array", + "items": { + "type": "object", + "properties": { + "node": { + "type": "string" + }, + "by": { + "type": "string" + }, + "why": { + "type": "string" + }, + "at": { + "type": "string", + "description": "when (RFC 3339 time)" + } + }, + "required": [ + "node" + ] + }, + "description": "retired nodes dropped by hand" + }, + "why": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "of each node holding the window, why it holds it" + } + }, + "required": [ + "name" + ] + }, + "description": "one caller's window" + } + ] + } + ] } diff --git a/go/seatlist/seatlisttest/seats/operator-channel.json b/go/seatlist/seatlisttest/seats/operator-channel.json index 0bd8c21..3f33532 100644 --- a/go/seatlist/seatlisttest/seats/operator-channel.json +++ b/go/seatlist/seatlisttest/seats/operator-channel.json @@ -182,7 +182,56 @@ "key": ".", "value": { "type": "object", - "description": "one ask the router holds and how it stands" + "description": "one ask the router holds and how it stands, as the router's tool states it", + "properties": { + "key": { + "type": "string", + "description": "." + }, + "asker": { + "type": "string", + "description": "its asker" + }, + "headline": { + "type": "string", + "description": "its headline" + }, + "state": { + "type": "string", + "description": "how it stands: open, chosen, expired, cancelled, replaced or refused" + }, + "opened": { + "type": "string", + "description": "when it was opened, RFC 3339" + }, + "expires": { + "type": "string", + "description": "when it ends unanswered, RFC 3339" + }, + "offered_on": { + "type": "array", + "items": { + "type": "string" + }, + "description": "the channels it was offered on" + }, + "asker_told": { + "type": "boolean", + "description": "its asker has been told how it ended" + }, + "answer": { + "type": "string", + "description": "how it ended, in words" + } + }, + "required": [ + "key", + "asker", + "headline", + "state", + "opened", + "expires" + ] }, "description": "one ask the router holds and how it stands" } @@ -563,6 +612,250 @@ "subject", "state" ] + }, + "Button": { + "type": "object", + "description": "one answer a channel offers: its label and the router's one-time ticket for it", + "properties": { + "label": { + "type": "string", + "description": "the button's label" + }, + "ticket": { + "type": "string", + "description": "the router's one-time ticket for it" + } + }, + "required": [ + "label", + "ticket" + ] + }, + "Message": { + "type": "object", + "description": "the work a channel takes on the bench channel: shown with buttons (show), shown again in place (edit) or said (send); the words are the router's, shown as given", + "properties": { + "handle": { + "type": "string", + "description": "the router's name for it, the same across a show and its edits" + }, + "title": { + "type": "string", + "description": "its title" + }, + "body": { + "type": "string", + "description": "its body" + }, + "buttons": { + "type": "array", + "items": { + "$ref": "#/defs/Button" + }, + "description": "the answers it offers (none for a send)" + }, + "note": { + "type": "string", + "description": "its secondary words after the body and the buttons: who asked and when, what each answer does, when it expires, the ask's details" + }, + "urgent": { + "type": "boolean", + "description": "urgent" + }, + "silent": { + "type": "boolean", + "description": "shown without notifying" + }, + "reply": { + "type": "string", + "description": "the choice or link request this answers, by its id: the channel shows it where that was made" + }, + "to": { + "type": "string", + "description": "the account linked as the operator on this kind, where the channel sends what is not a reply; empty when none is linked" + }, + "secret": { + "type": "boolean", + "description": "the words carry something shown once (a link's code): the channel keeps no copy" + } + }, + "required": [ + "handle", + "title", + "body" + ] + }, + "Sender": { + "type": "object", + "description": "who a channel's service says sent something; the router alone judges whether that is the operator", + "properties": { + "identity": { + "type": "string", + "description": "the account" + }, + "display": { + "type": "string", + "description": "the name it shows" + }, + "authenticated": { + "type": "boolean", + "description": "whether the service authenticated it" + } + }, + "required": [ + "identity", + "authenticated" + ] + }, + "Choice": { + "type": "object", + "description": "a button chosen on a channel, said on the bench intake as the event `choice`", + "properties": { + "id": { + "type": "string", + "description": "the channel's own for this arrival, unique, so the router acts on it once" + }, + "ticket": { + "type": "string", + "description": "the ticket of the button" + }, + "handle": { + "type": "string", + "description": "the message's handle" + }, + "sender": { + "$ref": "#/defs/Sender" + }, + "at": { + "type": "string", + "description": "when, RFC 3339", + "format": "date-time" + } + }, + "required": [ + "id", + "ticket", + "sender", + "at" + ] + }, + "LinkAsked": { + "type": "object", + "description": "somebody on a channel asking to be linked as the operator, said on the bench intake as the event `link`", + "properties": { + "id": { + "type": "string", + "description": "the channel's own for this arrival" + }, + "sender": { + "$ref": "#/defs/Sender" + }, + "at": { + "type": "string", + "description": "when, RFC 3339", + "format": "date-time" + } + }, + "required": [ + "id", + "sender", + "at" + ] + }, + "Failed": { + "type": "object", + "description": "a message a channel could not deliver, said on the bench intake as the event `failed`", + "properties": { + "handle": { + "type": "string", + "description": "the message's handle" + }, + "why": { + "type": "string", + "description": "why" + }, + "permanent": { + "type": "boolean", + "description": "trying again cannot help" + }, + "at": { + "type": "string", + "description": "when, RFC 3339", + "format": "date-time" + } + }, + "required": [ + "handle", + "why", + "at" + ] + }, + "Standing": { + "type": "object", + "description": "what a channel says of itself at least every five minutes and whenever it changes, on the bench intake as the event `standing`", + "properties": { + "ready": { + "type": "boolean", + "description": "it can send now" + }, + "why": { + "type": "string", + "description": "why not" + }, + "edits-silently": { + "type": "boolean", + "description": "its edits notify nobody" + }, + "at": { + "type": "string", + "description": "when, RFC 3339", + "format": "date-time" + } + }, + "required": [ + "ready", + "at" + ] + }, + "Code": { + "type": "object", + "description": "a code a person typed on a channel, asked as the proof `code` on the bench intake by request and reply: never in an event, never kept", + "properties": { + "sender": { + "$ref": "#/defs/Sender" + }, + "code": { + "type": "string", + "description": "the code" + }, + "purpose": { + "type": "string", + "description": "what it is for" + } + }, + "required": [ + "sender", + "code", + "purpose" + ] + }, + "ProofAnswer": { + "type": "object", + "description": "the router's answer to a proof", + "properties": { + "accepted": { + "type": "boolean", + "description": "whether it was taken" + }, + "words": { + "type": "string", + "description": "words to say to the person" + } + }, + "required": [ + "accepted", + "words" + ] } } } diff --git a/go/seatlist/seatlisttest/seats/the-private-network.json b/go/seatlist/seatlisttest/seats/the-private-network.json index 6cd6500..bb27936 100644 --- a/go/seatlist/seatlisttest/seats/the-private-network.json +++ b/go/seatlist/seatlisttest/seats/the-private-network.json @@ -2,7 +2,18 @@ "format": 1, "seat": "the-private-network", "scope": "node", - "grain": "seat-list", + "grain": "whole", "domain": "Connectivity", - "decision": "novox/hq ADR 0110" + "decision": "novox/hq ADR 0110, ADR 0634, ADR 0635", + "description": "The seat of the private network (ADR 0110): the one network the mesh runs over, which a machine can have only one of; the mesh provides its holder itself and the provision carries no fields.", + "verbs": [], + "provisions": [ + { + "name": "private-network", + "form": { + "type": "object", + "description": "the provision has no fields: it is the machine's reach to the others" + } + } + ] } diff --git a/go/testdata/seat-names.txt b/go/testdata/seat-names.txt index 4919957..c20b996 100644 --- a/go/testdata/seat-names.txt +++ b/go/testdata/seat-names.txt @@ -34,7 +34,6 @@ delivery/builder.go: container-image-registry delivery/builder.go: oci-artifact-registry delivery/checker.go: mesh-delivery-checker delivery/composed.go: mesh-provisioner -delivery/enrolment.go: mesh-enrolment delivery/held.go: mesh.seat.mesh-artifact-sweep.event. delivery/placement.go: mesh-self-check delivery/provided.go: the-private-network