The node-engine seat whole: every verb's answer, its events' bodies, and ADR 0618's manifest-reads and manifest-reads-changed

The seat's area changes in this milestone, so it moves whole into its contracts (ADR 0613 §3): the eleven verbs it serves
get their answers as the engine writes them, asked, done and refused their body, and the seat serves the optional verb
manifest-reads (ADR 0618 §3) and says manifest-reads-changed (§4). Each event is published per node, the node's name its
last token (by-caller), and kept with the seat's events for seven days. compose-candidate's input is a boolean, as the
engine and the checker read it. Fixtures for every event, compose-candidate's input and three answers.

Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
This commit is contained in:
2026-11-04 12:39:56 +01:00
parent f3a9cd17d6
commit 7498d689e2
9 changed files with 3058 additions and 40 deletions
+554
View File
@@ -0,0 +1,554 @@
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
// Package nodeengine is the contract of the seat "node-engine", defined whole (seats/node-engine/seat.json): The node seat every node's engine holds (novox/hq ADR 0563, ADR 0618 §3, §4): its hand acts on the operator's warrant, its read-only judgements, its compositions and what it reads of a manifest. Each verb is answered on this node's subject, mesh.seat.node-engine.tool.<verb>.<node>, and each event is published on mesh.seat.node-engine.event.<event>.<node>, the node's name its last token.
package nodeengine
// Seat is the seat's name.
const Seat = "node-engine"
const (
VerbHandOver = "hand-over"
VerbAccessAct = "access-act"
VerbJudgeDirectories = "judge-directories"
VerbSetuidSearch = "setuid-search"
VerbRootFree = "root-free"
VerbActs = "acts"
VerbPackages = "packages"
VerbComposition = "composition"
VerbCompose = "compose"
VerbComposeCandidate = "compose-candidate"
VerbPullWaits = "pull-waits"
VerbManifestReads = "manifest-reads"
)
const (
EventAsked = "asked"
EventDone = "done"
EventRefused = "refused"
EventManifestReadsChanged = "manifest-reads-changed"
)
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
func Verbs() []string {
return []string{VerbHandOver, VerbAccessAct, VerbJudgeDirectories, VerbSetuidSearch, VerbRootFree, VerbActs, VerbPackages, VerbComposition, VerbCompose, VerbComposeCandidate, VerbPullWaits, VerbManifestReads}
}
// Events is every event published on the seat's subject: a new copy each call.
func Events() []string {
return []string{EventAsked, EventDone, EventRefused, EventManifestReadsChanged}
}
// Family is where the seat's events are kept.
const (
Family = "node-engine.events"
FamilyMaxAgeDays = 7
)
// SubjectAsked is where asked is published for one caller.
func SubjectAsked(caller string) string { return "mesh.seat.node-engine.event.asked." + caller }
// SubjectDone is where done is published for one caller.
func SubjectDone(caller string) string { return "mesh.seat.node-engine.event.done." + caller }
// SubjectRefused is where refused is published for one caller.
func SubjectRefused(caller string) string { return "mesh.seat.node-engine.event.refused." + caller }
// SubjectManifestReadsChanged is where manifest-reads-changed is published for one caller.
func SubjectManifestReadsChanged(caller string) string {
return "mesh.seat.node-engine.event.manifest-reads-changed." + caller
}
// Act is one act asked through the seat, as acts reads it (ADR 0563 §9).
type Act struct {
// the act: hand-over, access-act, judge-directories or setuid-search
Act string `json:"act"`
// the operator's ask of a hand-over or an access act
Ask string `json:"ask,omitempty"`
// when it was asked, RFC 3339
At string `json:"at"`
// the engine asked it itself, on finding a library's root holding data outside its group (ADR 0563 §4)
Auto bool `json:"auto,omitempty"`
// who answered, as the operator's warrant names them
By string `json:"by,omitempty"`
// the caller as the runtime proved it
Caller string `json:"caller"`
// what the engine read on disk when it judged
Disk string `json:"disk,omitempty"`
// when it ended, RFC 3339
Ended string `json:"ended,omitempty"`
// when the ask ends unanswered, RFC 3339
Expires string `json:"expires,omitempty"`
// the act's id
ID string `json:"id"`
// the directory the act changes
Path string `json:"path,omitempty"`
// the warrant's proofs
Proofs []string `json:"proofs,omitempty"`
// what the engine said of it: its outcome or why it was refused
Said string `json:"said,omitempty"`
// the owner, group and mode it sets, in words
Sets string `json:"sets,omitempty"`
// where it stands
State string `json:"state"`
// the path of a hand-over, or the access of an access act
Subject string `json:"subject,omitempty"`
// the channel the warrant came on
Via string `json:"via,omitempty"`
// why it was asked, in words
Why string `json:"why,omitempty"`
}
// ActSaid is what the seat says of an act: the act, the path or access, the caller as the runtime proved it and the ask (ADR 0563 §1).
type ActSaid struct {
// the act
Act string `json:"act"`
// the operator's ask
Ask string `json:"ask,omitempty"`
// the caller as the runtime proved it
Caller string `json:"caller"`
// the node whose engine says it
Node string `json:"node"`
// what the engine said of it
Said string `json:"said,omitempty"`
// where the act stands
State string `json:"state"`
// the path or the access
Subject string `json:"subject,omitempty"`
}
// Acts is the acts kept.
type Acts struct {
// the acts, newest first
Acts []Act `json:"acts"`
// the node
Node string `json:"node"`
}
// Asked is the answer of an act the operator is asked.
type Asked struct {
// the ask's id: acts shows where it stands
Asked string `json:"asked"`
// the node
Node string `json:"node"`
// what happens next, in words
Says string `json:"says"`
}
// ManifestReadsChanged is what a node-engine says when what it reads changed or its node returned (ADR 0618 §4).
type ManifestReadsChanged struct {
// when it says it, RFC 3339
At string `json:"at"`
// what its engine states it reads now
ManifestReads []string `json:"manifest-reads,omitempty"`
// the node
Node string `json:"node"`
// the layout's version
V int64 `json:"v"`
// started: the engine started or what it states changed; returned: its node returned after being away
Why string `json:"why"`
}
// Module is one module's composition with the candidate.
type Module struct {
// the candidate changes it from the one composed without it
Changed bool `json:"changed,omitempty"`
// the composition's digest when composed
Digest string `json:"digest,omitempty"`
// the fingerprint it was composed at
Fp string `json:"fp,omitempty"`
// the module, node for the node's own composition
Module string `json:"module"`
// composed (digest set), refused by the candidate, or held without it too (words set)
State string `json:"state"`
// why, for a refused or held one
Words string `json:"words,omitempty"`
}
// Revision is one revision of a composition key.
type Revision struct {
// its digest
Digest string `json:"digest,omitempty"`
// the composition's key
Key string `json:"key"`
// the latest revision
Latest bool `json:"latest,omitempty"`
// why it cannot be read or trusted
Refused string `json:"refused,omitempty"`
// how many resources it names
Resources int64 `json:"resources,omitempty"`
// its revision
Revision int64 `json:"revision,omitempty"`
}
// RootVerdict is the engine's root-free verdict (ADR 0563 §6).
type RootVerdict struct {
// the machine is root-free
Free bool `json:"free"`
// when the engine judged it, RFC 3339
Judged string `json:"judged"`
// the machine
Machine string `json:"machine"`
// why, in words
Why string `json:"why"`
}
// Said is the answer of a read-only judgement.
type Said struct {
// the node
Node string `json:"node"`
// the engine's judgement or search, one line per directory or program
Said string `json:"said"`
}
// HandOverInput is what hand-over takes.
type HandOverInput struct {
// the directory's absolute path, as the engine states it
Path string `json:"path"`
// why it is asked, in words the operator reads
Why string `json:"why,omitempty"`
}
// HandOverAnswer is what hand-over answers.
type HandOverAnswer = Asked
// AccessActInput is what access-act takes.
type AccessActInput struct {
// the access, library:<name>
Access string `json:"access"`
// the act: take
Act string `json:"act"`
// why it is asked, in words the operator reads
Why string `json:"why,omitempty"`
}
// AccessActAnswer is what access-act answers.
type AccessActAnswer = Asked
// JudgeDirectoriesInput is what judge-directories takes.
type JudgeDirectoriesInput struct {
}
// JudgeDirectoriesAnswer is what judge-directories answers.
type JudgeDirectoriesAnswer = Said
// SetuidSearchInput is what setuid-search takes.
type SetuidSearchInput struct {
// why it is asked, in words
Why string `json:"why,omitempty"`
}
// SetuidSearchAnswer is what setuid-search answers.
type SetuidSearchAnswer = Said
// RootFreeInput is what root-free takes.
type RootFreeInput struct {
}
// RootFreeAnswer is what root-free answers.
type RootFreeAnswer struct {
// this machine's verdict
Machines []RootVerdict `json:"machines"`
}
// ActsInput is what acts takes.
type ActsInput struct {
// one act, by its id
ID string `json:"id,omitempty"`
// at most this many, newest
Limit string `json:"limit,omitempty"`
}
// ActsAnswer is what acts answers.
type ActsAnswer = Acts
// PackagesInput is what packages takes.
type PackagesInput struct {
// "true": write this machine's key again from what the engine composed
Write string `json:"write,omitempty"`
}
// PackagesAnswerKeyPackagesItem is a type of the contract.
type PackagesAnswerKeyPackagesItem struct {
// declared absent
Absent bool `json:"absent"`
// the modules that declare it
Modules []string `json:"modules"`
// the package
Package string `json:"package"`
}
// PackagesAnswerKey is the node's key as the bus holds it; absent when the engine wrote none.
type PackagesAnswerKey struct {
// the node
Node string `json:"node"`
// its packages sorted by name, absent after present
Packages []PackagesAnswerKeyPackagesItem `json:"packages"`
}
// PackagesAnswer is what packages answers.
type PackagesAnswer struct {
// the node's key as the bus holds it; absent when the engine wrote none
Key *PackagesAnswerKey `json:"key,omitempty"`
// why the key could not be read
KeyError string `json:"key_error,omitempty"`
// the node
Node string `json:"node"`
// a note, such as that no key is written yet
Note string `json:"note,omitempty"`
// where the key is read: the engine's state packages
Source string `json:"source"`
// with write: the key was written again (false: it already held what the engine composed)
Wrote bool `json:"wrote,omitempty"`
}
// CompositionInput is what composition takes.
type CompositionInput struct {
// "true": list the resources that differ from what runs
Diff string `json:"diff,omitempty"`
// a composition's digest (sha256:…), to read that revision rather than the latest
Digest string `json:"digest,omitempty"`
// the module, as placed on this machine; node for the node's own
Module string `json:"module"`
}
// CompositionAnswerAttempt is the module's last apply that did not end running; absent when none.
type CompositionAnswerAttempt struct {
// the want's revision the hold took
AnsweredRevision int64 `json:"answered_revision,omitempty"`
// the digest tried
Composed string `json:"composed,omitempty"`
// the composition's revision it was tried at
CompositionRevision int64 `json:"composition_revision"`
// the fingerprint tried
Fp string `json:"fp"`
// held for a failed backup
Held bool `json:"held,omitempty"`
// it failed because the bus did not answer for a moment
Transient bool `json:"transient,omitempty"`
// the want's revision it was tried at
WantRevision int64 `json:"want_revision"`
}
// CompositionAnswerHave is what the module's have key says now; absent when it says nothing.
type CompositionAnswerHave struct {
// digest
Composed string `json:"composed,omitempty"`
// detail
Detail string `json:"detail,omitempty"`
// fingerprint
Fp string `json:"fp,omitempty"`
// judgement
Judgement string `json:"judgement,omitempty"`
// running, failed, held or waiting-yes
Phase string `json:"phase"`
// the digest that waits for a yes
Waits string `json:"waits,omitempty"`
}
// CompositionAnswerJudged is the move from what runs to the composition read.
type CompositionAnswerJudged struct {
// the judgement
Judgement string `json:"judgement"`
// the agent would wait for a person's yes before applying it
WaitsForYes bool `json:"waits_for_yes"`
// its words
Words string `json:"words"`
}
// CompositionAnswerOwn is of the node's own composition, its own fields beside what runs names.
type CompositionAnswerOwn struct {
// the revision's adoption; absent for a converged node
Adoption map[string]any `json:"adoption,omitempty"`
// groups
Groups int64 `json:"groups,omitempty"`
// each module the node's composer held in its last pass, with why
Held map[string]string `json:"held,omitempty"`
// identities the revision names
Identities int64 `json:"identities,omitempty"`
// libraries
Libraries int64 `json:"libraries,omitempty"`
// the account the provisioner's client runs as, as the revision names it
ProvisionerAccount string `json:"provisioner_account"`
// as what runs names it
RunsProvisionerAccount string `json:"runs_provisioner_account,omitempty"`
}
// CompositionAnswerRuns is what runs of the module; absent when nothing runs.
type CompositionAnswerRuns struct {
// digest
Composed string `json:"composed,omitempty"`
// fingerprint
Fp string `json:"fp,omitempty"`
// applied, have or kept
From string `json:"from"`
// phase
Phase string `json:"phase,omitempty"`
// how many resources run
Resources int64 `json:"resources"`
}
// CompositionAnswerWant is the module's want as the agent holds it; absent when it holds none.
type CompositionAnswerWant struct {
// the operator's answer to a failed backup, again or skip
Backup string `json:"backup,omitempty"`
// the composition's digest it names
Composed string `json:"composed,omitempty"`
// its build's fingerprint
Fp string `json:"fp"`
// the module is removed
Removed bool `json:"removed,omitempty"`
// the revision of the want's key read last
Revision int64 `json:"revision"`
// the node composes the module itself (ADR 0547 §2)
Self bool `json:"self,omitempty"`
// its words
Words string `json:"words,omitempty"`
// the digest a person said yes to
Yes string `json:"yes,omitempty"`
}
// CompositionAnswer is what composition answers.
type CompositionAnswer struct {
// the module's last apply that did not end running; absent when none
Attempt *CompositionAnswerAttempt `json:"attempt,omitempty"`
Composition Revision `json:"composition"`
// every resource that differs from what runs, when asked
Diff []map[string]any `json:"diff,omitempty"`
// what the module's have key says now; absent when it says nothing
Have *CompositionAnswerHave `json:"have,omitempty"`
// the move from what runs to the composition read
Judged CompositionAnswerJudged `json:"judged"`
// the module
Module string `json:"module"`
// the node
Node string `json:"node"`
// of the node's own composition, its own fields beside what runs names
Own *CompositionAnswerOwn `json:"own,omitempty"`
// the revisions of its key the bucket keeps, oldest first
Revisions []Revision `json:"revisions,omitempty"`
// what runs of the module; absent when nothing runs
Runs *CompositionAnswerRuns `json:"runs,omitempty"`
// the agent has looked at the module once since it started
Started bool `json:"started"`
// the module's want as the agent holds it; absent when it holds none
Want *CompositionAnswerWant `json:"want,omitempty"`
}
// ComposeInput is what compose takes.
type ComposeInput struct {
// a module placed on this machine whose apply is also tried again (optional)
Module string `json:"module,omitempty"`
}
// ComposeAnswerPass is how the pass went.
type ComposeAnswerPass struct {
// when the node composes again on its own, RFC 3339
Again string `json:"again,omitempty"`
// when the pass ended, RFC 3339
At string `json:"at"`
// the answer is the last pass, made moments ago
Earlier bool `json:"earlier,omitempty"`
// how many compositions the pass handed the agent that differ from before
Handed int64 `json:"handed"`
// each module refused alone, with why
Held map[string]string `json:"held,omitempty"`
// why the node composed none of its modules
Refused string `json:"refused,omitempty"`
}
// ComposeAnswer is what compose answers.
type ComposeAnswer struct {
// how the pass went
Pass ComposeAnswerPass `json:"pass"`
// the module whose apply was tried again
Retried string `json:"retried,omitempty"`
}
// ComposeCandidateInput is what compose-candidate takes.
type ComposeCandidateInput struct {
// the key of the indexer's candidate, <group>.<heads hash>
Candidate string `json:"candidate"`
// also answer the composition input the candidate was composed from, for the sandbox path
Input bool `json:"input,omitempty"`
}
// ComposeCandidateAnswer is what compose-candidate answers.
type ComposeCandidateAnswer struct {
// the candidate's key
Candidate string `json:"candidate"`
// when asked: the composition input it composed from
Input map[string]any `json:"input,omitempty"`
// every module placed on the node, sorted by name
Modules []Module `json:"modules"`
// the node
Node string `json:"node"`
Own Module `json:"own"`
// why the node could not compose with the candidate at all
Refused string `json:"refused,omitempty"`
// how long the engine took, in milliseconds
Took int64 `json:"took"`
// the layout's version
V int64 `json:"v"`
// the node's verdict (ADR 0593 §7, §9)
Verdict string `json:"verdict"`
}
// PullWaitsInput is what pull-waits takes.
type PullWaitsInput struct {
}
// PullWaitsAnswerWaitsItem is a type of the contract.
type PullWaitsAnswerWaitsItem struct {
// that seat's holder, whose settled the retry waits on
Holder string `json:"holder,omitempty"`
// the image whose pull failed
Image string `json:"image"`
// the module whose apply failed
Module string `json:"module"`
// when the failed pull began, RFC 3339
PullBegan string `json:"pull-began,omitempty"`
// when it was last tried again, RFC 3339
RetryLast string `json:"retry-last,omitempty"`
// what started that retry: settled, or the engine's start
RetryTrigger string `json:"retry-trigger,omitempty"`
// the seat the image came through
Seat string `json:"seat,omitempty"`
// the settled events of the holder heard since the engine started
SettledCount int64 `json:"settled-count,omitempty"`
// when the last was heard, RFC 3339
SettledLast string `json:"settled-last,omitempty"`
}
// PullWaitsAnswer is what pull-waits answers.
type PullWaitsAnswer struct {
// every failed pull waiting on a holder
Waits []PullWaitsAnswerWaitsItem `json:"waits"`
}
// ManifestReadsInput is what manifest-reads takes.
type ManifestReadsInput struct {
}
// ManifestReadsAnswer is what manifest-reads answers.
type ManifestReadsAnswer struct {
// the reader capabilities its engine states: the manifest words, forms and seat names it reads; empty for an engine built before it said them, read as the baseline
ManifestReads []string `json:"manifest-reads,omitempty"`
// the node
Node string `json:"node"`
// when the engine last stated them, RFC 3339
StatedAt string `json:"stated-at"`
// the layout's version
V int64 `json:"v"`
}
// AskedEvent is the body of the event asked.
type AskedEvent = ActSaid
// DoneEvent is the body of the event done.
type DoneEvent = ActSaid
// RefusedEvent is the body of the event refused.
type RefusedEvent = ActSaid
// ManifestReadsChangedEvent is the body of the event manifest-reads-changed.
type ManifestReadsChangedEvent = ManifestReadsChanged
+74
View File
@@ -0,0 +1,74 @@
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
package nodeengine
import (
"encoding/json"
"reflect"
"regexp"
"testing"
)
// TestFixtures reads every fixture of seats/node-engine/fixtures: each decodes into its type, a field the type does not
// know is dropped (ADR 0608 §6), and it reads back as its wire says; each subject named is the contract's.
func TestFixtures(t *testing.T) {
cases := []struct {
name, subject, given, wire string
decode func([]byte) (any, error)
subjectIs *regexp.Regexp
}{
{name: "asked on the node's subject", subject: "mesh.seat.node-engine.event.asked.ace", given: "{\"act\":\"hand-over\",\"ask\":\"hand-over-1\",\"caller\":\"mesh-tools\",\"node\":\"ace\",\"state\":\"asked\",\"subject\":\"/srv/media\"}", wire: "{\"act\":\"hand-over\",\"ask\":\"hand-over-1\",\"caller\":\"mesh-tools\",\"node\":\"ace\",\"state\":\"asked\",\"subject\":\"/srv/media\"}",
decode: func(b []byte) (any, error) { var v AskedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.asked\\.[A-Za-z0-9_-]+$")},
{name: "done on the node's subject", subject: "mesh.seat.node-engine.event.done.ace", given: "{\"act\":\"hand-over\",\"ask\":\"hand-over-1\",\"caller\":\"mesh-tools\",\"node\":\"ace\",\"said\":\"handed over: owner media, group media, mode 2775\",\"state\":\"done\",\"subject\":\"/srv/media\"}", wire: "{\"act\":\"hand-over\",\"ask\":\"hand-over-1\",\"caller\":\"mesh-tools\",\"node\":\"ace\",\"said\":\"handed over: owner media, group media, mode 2775\",\"state\":\"done\",\"subject\":\"/srv/media\"}",
decode: func(b []byte) (any, error) { var v DoneEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.done\\.[A-Za-z0-9_-]+$")},
{name: "refused on the node's subject", subject: "mesh.seat.node-engine.event.refused.ace", given: "{\"act\":\"access-act\",\"ask\":\"access-act-1\",\"caller\":\"mesh-tools\",\"node\":\"ace\",\"said\":\"the operator said no\",\"state\":\"refused\",\"subject\":\"library:music\"}", wire: "{\"act\":\"access-act\",\"ask\":\"access-act-1\",\"caller\":\"mesh-tools\",\"node\":\"ace\",\"said\":\"the operator said no\",\"state\":\"refused\",\"subject\":\"library:music\"}",
decode: func(b []byte) (any, error) { var v RefusedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.refused\\.[A-Za-z0-9_-]+$")},
{name: "manifest-reads-changed when the engine started", subject: "mesh.seat.node-engine.event.manifest-reads-changed.ace", given: "{\"at\":\"2026-11-04T10:00:00Z\",\"manifest-reads\":[\"form:seat-reference\",\"word:series\"],\"node\":\"ace\",\"v\":1,\"why\":\"started\"}", wire: "{\"at\":\"2026-11-04T10:00:00Z\",\"manifest-reads\":[\"form:seat-reference\",\"word:series\"],\"node\":\"ace\",\"v\":1,\"why\":\"started\"}",
decode: func(b []byte) (any, error) {
var v ManifestReadsChangedEvent
err := json.Unmarshal(b, &v)
return v, err
}, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.manifest-reads-changed\\.[A-Za-z0-9_-]+$")},
{name: "manifest-reads-changed when the node returned, from an engine that states none", subject: "mesh.seat.node-engine.event.manifest-reads-changed.g14", given: "{\"at\":\"2026-11-04T10:05:00Z\",\"node\":\"g14\",\"v\":1,\"why\":\"returned\"}", wire: "{\"at\":\"2026-11-04T10:05:00Z\",\"node\":\"g14\",\"v\":1,\"why\":\"returned\"}",
decode: func(b []byte) (any, error) {
var v ManifestReadsChangedEvent
err := json.Unmarshal(b, &v)
return v, err
}, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.manifest-reads-changed\\.[A-Za-z0-9_-]+$")},
{name: "manifest-reads answers what the engine reads and when it stated it", subject: "", given: "{\"manifest-reads\":[\"form:seat-reference\",\"word:series\"],\"node\":\"ace\",\"stated-at\":\"2026-11-04T10:00:00Z\",\"v\":1}", wire: "{\"manifest-reads\":[\"form:seat-reference\",\"word:series\"],\"node\":\"ace\",\"stated-at\":\"2026-11-04T10:00:00Z\",\"v\":1}",
decode: func(b []byte) (any, error) { var v ManifestReadsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
{name: "manifest-reads of an engine built before it said them is the baseline", subject: "", given: "{\"node\":\"g14\",\"stated-at\":\"2026-11-04T10:05:00Z\",\"v\":1}", wire: "{\"node\":\"g14\",\"stated-at\":\"2026-11-04T10:05:00Z\",\"v\":1}",
decode: func(b []byte) (any, error) { var v ManifestReadsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
{name: "compose-candidate asks the candidate and, as a boolean, its composition input", subject: "", given: "{\"candidate\":\"mesh-host.0123abcd\",\"input\":true}", wire: "{\"candidate\":\"mesh-host.0123abcd\",\"input\":true}",
decode: func(b []byte) (any, error) { var v ComposeCandidateInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
{name: "compose-candidate answers per module and the node's own", subject: "", given: "{\"candidate\":\"mesh-host.0123abcd\",\"modules\":[{\"changed\":true,\"digest\":\"sha256:aa\",\"fp\":\"fp-1\",\"module\":\"plex\",\"state\":\"composed\"}],\"node\":\"ace\",\"own\":{\"digest\":\"sha256:bb\",\"module\":\"node\",\"state\":\"composed\"},\"took\":1200,\"v\":1,\"verdict\":\"composed\"}", wire: "{\"candidate\":\"mesh-host.0123abcd\",\"modules\":[{\"changed\":true,\"digest\":\"sha256:aa\",\"fp\":\"fp-1\",\"module\":\"plex\",\"state\":\"composed\"}],\"node\":\"ace\",\"own\":{\"digest\":\"sha256:bb\",\"module\":\"node\",\"state\":\"composed\"},\"took\":1200,\"v\":1,\"verdict\":\"composed\"}",
decode: func(b []byte) (any, error) { var v ComposeCandidateAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
{name: "acts answers each act kept", subject: "", given: "{\"acts\":[{\"act\":\"hand-over\",\"ask\":\"hand-over-1\",\"at\":\"2026-11-04T09:00:00Z\",\"caller\":\"mesh-tools\",\"expires\":\"2026-11-05T09:00:00Z\",\"id\":\"hand-over-1\",\"state\":\"asked\",\"subject\":\"/srv/media\"}],\"node\":\"ace\"}", wire: "{\"acts\":[{\"act\":\"hand-over\",\"ask\":\"hand-over-1\",\"at\":\"2026-11-04T09:00:00Z\",\"caller\":\"mesh-tools\",\"expires\":\"2026-11-05T09:00:00Z\",\"id\":\"hand-over-1\",\"state\":\"asked\",\"subject\":\"/srv/media\"}],\"node\":\"ace\"}",
decode: func(b []byte) (any, error) { var v ActsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
}
for _, c := range cases {
v, err := c.decode([]byte(c.given))
if err != nil {
t.Errorf("%s: does not decode: %v", c.name, err)
continue
}
back, err := json.Marshal(v)
if err != nil {
t.Errorf("%s: does not encode: %v", c.name, err)
continue
}
var got, want any
if err := json.Unmarshal(back, &got); err != nil {
t.Fatal(err)
}
if err := json.Unmarshal([]byte(c.wire), &want); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(got, want) {
t.Errorf("%s: reads back as %s, want %s", c.name, back, c.wire)
}
if c.subjectIs != nil && !c.subjectIs.MatchString(c.subject) {
t.Errorf("%s: subject %q is not the contract's %s", c.name, c.subject, c.subjectIs)
}
}
}
+24 -11
View File
File diff suppressed because one or more lines are too long
+69
View File
@@ -0,0 +1,69 @@
[
{
"name": "asked on the node's subject",
"of": "event:asked",
"subject": "mesh.seat.node-engine.event.asked.ace",
"given": {
"act": "hand-over",
"subject": "/srv/media",
"caller": "mesh-tools",
"ask": "hand-over-1",
"state": "asked",
"node": "ace"
}
},
{
"name": "done on the node's subject",
"of": "event:done",
"subject": "mesh.seat.node-engine.event.done.ace",
"given": {
"act": "hand-over",
"subject": "/srv/media",
"caller": "mesh-tools",
"ask": "hand-over-1",
"state": "done",
"said": "handed over: owner media, group media, mode 2775",
"node": "ace"
}
},
{
"name": "refused on the node's subject",
"of": "event:refused",
"subject": "mesh.seat.node-engine.event.refused.ace",
"given": {
"act": "access-act",
"subject": "library:music",
"caller": "mesh-tools",
"ask": "access-act-1",
"state": "refused",
"said": "the operator said no",
"node": "ace"
}
},
{
"name": "manifest-reads-changed when the engine started",
"of": "event:manifest-reads-changed",
"subject": "mesh.seat.node-engine.event.manifest-reads-changed.ace",
"given": {
"v": 1,
"node": "ace",
"manifest-reads": [
"form:seat-reference",
"word:series"
],
"at": "2026-11-04T10:00:00Z",
"why": "started"
}
},
{
"name": "manifest-reads-changed when the node returned, from an engine that states none",
"of": "event:manifest-reads-changed",
"subject": "mesh.seat.node-engine.event.manifest-reads-changed.g14",
"given": {
"v": 1,
"node": "g14",
"at": "2026-11-04T10:05:00Z",
"why": "returned"
}
}
]
+76
View File
@@ -0,0 +1,76 @@
[
{
"name": "manifest-reads answers what the engine reads and when it stated it",
"of": "verb:manifest-reads.answer",
"given": {
"v": 1,
"node": "ace",
"manifest-reads": [
"form:seat-reference",
"word:series"
],
"stated-at": "2026-11-04T10:00:00Z"
}
},
{
"name": "manifest-reads of an engine built before it said them is the baseline",
"of": "verb:manifest-reads.answer",
"given": {
"v": 1,
"node": "g14",
"stated-at": "2026-11-04T10:05:00Z"
}
},
{
"name": "compose-candidate asks the candidate and, as a boolean, its composition input",
"of": "verb:compose-candidate.input",
"given": {
"candidate": "mesh-host.0123abcd",
"input": true
}
},
{
"name": "compose-candidate answers per module and the node's own",
"of": "verb:compose-candidate.answer",
"given": {
"v": 1,
"node": "ace",
"candidate": "mesh-host.0123abcd",
"verdict": "composed",
"modules": [
{
"module": "plex",
"fp": "fp-1",
"state": "composed",
"digest": "sha256:aa",
"changed": true
}
],
"own": {
"module": "node",
"state": "composed",
"digest": "sha256:bb"
},
"took": 1200
}
},
{
"name": "acts answers each act kept",
"of": "verb:acts.answer",
"given": {
"node": "ace",
"acts": [
{
"id": "hand-over-1",
"act": "hand-over",
"subject": "/srv/media",
"caller": "mesh-tools",
"at": "2026-11-04T09:00:00Z",
"state": "asked",
"ask": "hand-over-1",
"expires": "2026-11-05T09:00:00Z"
}
]
}
}
]
+872 -11
View File
@@ -2,8 +2,9 @@
"format": 1,
"seat": "node-engine",
"scope": "node",
"grain": "seat-list",
"grain": "whole",
"decision": "novox/hq ADR 0563",
"description": "The node seat every node's engine holds (novox/hq ADR 0563, ADR 0618 §3, §4): its hand acts on the operator's warrant, its read-only judgements, its compositions and what it reads of a manifest. Each verb is answered on this node's subject, mesh.seat.node-engine.tool.<verb>.<node>, and each event is published on mesh.seat.node-engine.event.<event>.<node>, the node's name its last token.",
"held-by-the-engine": true,
"verbs": [
{
@@ -24,6 +25,9 @@
"path"
],
"type": "object"
},
"answer": {
"$ref": "#/defs/Asked"
}
},
{
@@ -49,6 +53,9 @@
"access"
],
"type": "object"
},
"answer": {
"$ref": "#/defs/Asked"
}
},
{
@@ -57,6 +64,9 @@
"input": {
"properties": {},
"type": "object"
},
"answer": {
"$ref": "#/defs/Said"
}
},
{
@@ -70,6 +80,9 @@
}
},
"type": "object"
},
"answer": {
"$ref": "#/defs/Said"
}
},
{
@@ -78,6 +91,21 @@
"input": {
"properties": {},
"type": "object"
},
"answer": {
"type": "object",
"properties": {
"machines": {
"type": "array",
"items": {
"$ref": "#/defs/RootVerdict"
},
"description": "this machine's verdict"
}
},
"required": [
"machines"
]
}
},
{
@@ -95,6 +123,9 @@
}
},
"type": "object"
},
"answer": {
"$ref": "#/defs/Acts"
}
},
{
@@ -113,6 +144,78 @@
}
},
"type": "object"
},
"answer": {
"type": "object",
"properties": {
"node": {
"type": "string",
"description": "the node"
},
"source": {
"type": "string",
"description": "where the key is read: the engine's state packages"
},
"key": {
"type": "object",
"properties": {
"node": {
"type": "string",
"description": "the node"
},
"packages": {
"type": "array",
"items": {
"type": "object",
"properties": {
"package": {
"type": "string",
"description": "the package"
},
"modules": {
"type": "array",
"items": {
"type": "string"
},
"description": "the modules that declare it"
},
"absent": {
"type": "boolean",
"description": "declared absent"
}
},
"required": [
"package",
"modules",
"absent"
]
},
"description": "its packages sorted by name, absent after present"
}
},
"required": [
"node",
"packages"
],
"description": "the node's key as the bus holds it; absent when the engine wrote none"
},
"key_error": {
"type": "string",
"description": "why the key could not be read"
},
"wrote": {
"type": "boolean",
"description": "with write: the key was written again (false: it already held what the engine composed)"
},
"note": {
"type": "string",
"description": "a note, such as that no key is written yet"
}
},
"required": [
"node",
"source"
]
}
},
{
@@ -138,6 +241,255 @@
"module"
],
"type": "object"
},
"answer": {
"type": "object",
"properties": {
"node": {
"type": "string",
"description": "the node"
},
"module": {
"type": "string",
"description": "the module"
},
"want": {
"type": "object",
"properties": {
"fp": {
"type": "string",
"description": "its build's fingerprint"
},
"composed": {
"type": "string",
"description": "the composition's digest it names"
},
"yes": {
"type": "string",
"description": "the digest a person said yes to"
},
"removed": {
"type": "boolean",
"description": "the module is removed"
},
"self": {
"type": "boolean",
"description": "the node composes the module itself (ADR 0547 §2)"
},
"backup": {
"type": "string",
"description": "the operator's answer to a failed backup, again or skip"
},
"words": {
"type": "string",
"description": "its words"
},
"revision": {
"type": "integer",
"description": "the revision of the want's key read last"
}
},
"required": [
"fp",
"revision"
],
"description": "the module's want as the agent holds it; absent when it holds none"
},
"attempt": {
"type": "object",
"properties": {
"fp": {
"type": "string",
"description": "the fingerprint tried"
},
"composed": {
"type": "string",
"description": "the digest tried"
},
"want_revision": {
"type": "integer",
"description": "the want's revision it was tried at"
},
"composition_revision": {
"type": "integer",
"description": "the composition's revision it was tried at"
},
"held": {
"type": "boolean",
"description": "held for a failed backup"
},
"answered_revision": {
"type": "integer",
"description": "the want's revision the hold took"
},
"transient": {
"type": "boolean",
"description": "it failed because the bus did not answer for a moment"
}
},
"required": [
"fp",
"want_revision",
"composition_revision"
],
"description": "the module's last apply that did not end running; absent when none"
},
"started": {
"type": "boolean",
"description": "the agent has looked at the module once since it started"
},
"have": {
"type": "object",
"properties": {
"fp": {
"type": "string",
"description": "fingerprint"
},
"composed": {
"type": "string",
"description": "digest"
},
"phase": {
"type": "string",
"description": "running, failed, held or waiting-yes"
},
"detail": {
"type": "string",
"description": "detail"
},
"judgement": {
"type": "string",
"description": "judgement"
},
"waits": {
"type": "string",
"description": "the digest that waits for a yes"
}
},
"required": [
"phase"
],
"description": "what the module's have key says now; absent when it says nothing"
},
"composition": {
"$ref": "#/defs/Revision"
},
"revisions": {
"type": "array",
"items": {
"$ref": "#/defs/Revision"
},
"description": "the revisions of its key the bucket keeps, oldest first"
},
"runs": {
"type": "object",
"properties": {
"fp": {
"type": "string",
"description": "fingerprint"
},
"composed": {
"type": "string",
"description": "digest"
},
"phase": {
"type": "string",
"description": "phase"
},
"from": {
"type": "string",
"description": "applied, have or kept"
},
"resources": {
"type": "integer",
"description": "how many resources run"
}
},
"required": [
"from",
"resources"
],
"description": "what runs of the module; absent when nothing runs"
},
"judged": {
"type": "object",
"properties": {
"judgement": {
"type": "string",
"description": "the judgement"
},
"words": {
"type": "string",
"description": "its words"
},
"waits_for_yes": {
"type": "boolean",
"description": "the agent would wait for a person's yes before applying it"
}
},
"required": [
"judgement",
"words",
"waits_for_yes"
],
"description": "the move from what runs to the composition read"
},
"diff": {
"type": "array",
"items": {
"type": "object",
"description": "one resource added, removed or changed, as the engine's judge says it"
},
"description": "every resource that differs from what runs, when asked"
},
"own": {
"type": "object",
"properties": {
"provisioner_account": {
"type": "string",
"description": "the account the provisioner's client runs as, as the revision names it"
},
"runs_provisioner_account": {
"type": "string",
"description": "as what runs names it"
},
"adoption": {
"type": "object",
"description": "the revision's adoption; absent for a converged node"
},
"identities": {
"type": "integer",
"description": "identities the revision names"
},
"groups": {
"type": "integer",
"description": "groups"
},
"libraries": {
"type": "integer",
"description": "libraries"
},
"held": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "each module the node's composer held in its last pass, with why"
}
},
"required": [
"provisioner_account"
],
"description": "of the node's own composition, its own fields beside what runs names"
}
},
"required": [
"node",
"module",
"started",
"composition",
"judged"
]
}
},
{
@@ -152,6 +504,55 @@
}
},
"type": "object"
},
"answer": {
"type": "object",
"properties": {
"retried": {
"type": "string",
"description": "the module whose apply was tried again"
},
"pass": {
"type": "object",
"properties": {
"at": {
"type": "string",
"description": "when the pass ended, RFC 3339"
},
"refused": {
"type": "string",
"description": "why the node composed none of its modules"
},
"held": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "each module refused alone, with why"
},
"handed": {
"type": "integer",
"description": "how many compositions the pass handed the agent that differ from before"
},
"again": {
"type": "string",
"description": "when the node composes again on its own, RFC 3339"
},
"earlier": {
"type": "boolean",
"description": "the answer is the last pass, made moments ago"
}
},
"required": [
"at",
"handed"
],
"description": "how the pass went"
}
},
"required": [
"pass"
]
}
},
{
@@ -165,18 +566,72 @@
"type": "string"
},
"input": {
"description": "\"true\": also answer the composition input the candidate was composed from, for the sandbox path",
"enum": [
"true",
"false"
],
"type": "string"
"description": "also answer the composition input the candidate was composed from, for the sandbox path",
"type": "boolean"
}
},
"required": [
"candidate"
],
"type": "object"
},
"answer": {
"type": "object",
"properties": {
"v": {
"type": "integer",
"description": "the layout's version"
},
"node": {
"type": "string",
"description": "the node"
},
"candidate": {
"type": "string",
"description": "the candidate's key"
},
"verdict": {
"type": "string",
"enum": [
"composed",
"refused",
"unready",
"not-judged"
],
"description": "the node's verdict (ADR 0593 §7, §9)"
},
"modules": {
"type": "array",
"items": {
"$ref": "#/defs/Module"
},
"description": "every module placed on the node, sorted by name"
},
"own": {
"$ref": "#/defs/Module"
},
"refused": {
"type": "string",
"description": "why the node could not compose with the candidate at all"
},
"took": {
"type": "integer",
"description": "how long the engine took, in milliseconds"
},
"input": {
"type": "object",
"description": "when asked: the composition input it composed from"
}
},
"required": [
"v",
"node",
"candidate",
"verdict",
"modules",
"own",
"took"
]
}
},
{
@@ -244,17 +699,423 @@
}
}
}
},
{
"name": "manifest-reads",
"optional": true,
"description": "The reader capabilities this machine's engine states (manifest words, forms and seat names it reads, novox/hq ADR 0618 §3) and when it last stated them. An engine built before it said them states none, which a reader takes as the baseline. Read-only. Answered by this machine's engine alone.",
"input": {
"type": "object",
"properties": {}
},
"answer": {
"type": "object",
"properties": {
"v": {
"type": "integer",
"description": "the layout's version"
},
"node": {
"type": "string",
"description": "the node"
},
"manifest-reads": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"description": "the reader capabilities its engine states: the manifest words, forms and seat names it reads; empty for an engine built before it said them, read as the baseline"
},
"stated-at": {
"type": "string",
"description": "when the engine last stated them, RFC 3339"
}
},
"required": [
"v",
"node",
"stated-at"
],
"description": "what this machine's engine reads of a manifest (ADR 0618 §3)"
}
}
],
"events": [
{
"name": "asked"
"name": "asked",
"by-caller": true,
"body": {
"$ref": "#/defs/ActSaid"
}
},
{
"name": "done"
"name": "done",
"by-caller": true,
"body": {
"$ref": "#/defs/ActSaid"
}
},
{
"name": "refused"
"name": "refused",
"by-caller": true,
"body": {
"$ref": "#/defs/ActSaid"
}
},
{
"name": "manifest-reads-changed",
"by-caller": true,
"body": {
"$ref": "#/defs/ManifestReadsChanged"
}
}
]
],
"family": {
"name": "node-engine.events",
"max-age-days": 7
},
"defs": {
"Act": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "the act's id"
},
"act": {
"type": "string",
"description": "the act: hand-over, access-act, judge-directories or setuid-search"
},
"subject": {
"type": "string",
"description": "the path of a hand-over, or the access of an access act"
},
"caller": {
"type": "string",
"description": "the caller as the runtime proved it"
},
"why": {
"type": "string",
"description": "why it was asked, in words"
},
"at": {
"type": "string",
"description": "when it was asked, RFC 3339"
},
"state": {
"type": "string",
"enum": [
"asked",
"done",
"refused",
"answered"
],
"description": "where it stands"
},
"said": {
"type": "string",
"description": "what the engine said of it: its outcome or why it was refused"
},
"ended": {
"type": "string",
"description": "when it ended, RFC 3339"
},
"ask": {
"type": "string",
"description": "the operator's ask of a hand-over or an access act"
},
"sets": {
"type": "string",
"description": "the owner, group and mode it sets, in words"
},
"disk": {
"type": "string",
"description": "what the engine read on disk when it judged"
},
"path": {
"type": "string",
"description": "the directory the act changes"
},
"expires": {
"type": "string",
"description": "when the ask ends unanswered, RFC 3339"
},
"by": {
"type": "string",
"description": "who answered, as the operator's warrant names them"
},
"via": {
"type": "string",
"description": "the channel the warrant came on"
},
"proofs": {
"type": "array",
"items": {
"type": "string"
},
"description": "the warrant's proofs"
},
"auto": {
"type": "boolean",
"description": "the engine asked it itself, on finding a library's root holding data outside its group (ADR 0563 §4)"
}
},
"required": [
"id",
"act",
"caller",
"at",
"state"
],
"description": "one act asked through the seat, as acts reads it (ADR 0563 §9)"
},
"ActSaid": {
"type": "object",
"properties": {
"act": {
"type": "string",
"description": "the act"
},
"subject": {
"type": "string",
"description": "the path or the access"
},
"caller": {
"type": "string",
"description": "the caller as the runtime proved it"
},
"ask": {
"type": "string",
"description": "the operator's ask"
},
"state": {
"type": "string",
"enum": [
"asked",
"done",
"refused"
],
"description": "where the act stands"
},
"said": {
"type": "string",
"description": "what the engine said of it"
},
"node": {
"type": "string",
"description": "the node whose engine says it"
}
},
"required": [
"act",
"caller",
"state",
"node"
],
"description": "what the seat says of an act: the act, the path or access, the caller as the runtime proved it and the ask (ADR 0563 §1)"
},
"Acts": {
"type": "object",
"properties": {
"node": {
"type": "string",
"description": "the node"
},
"acts": {
"type": "array",
"items": {
"$ref": "#/defs/Act"
},
"description": "the acts, newest first"
}
},
"required": [
"node",
"acts"
],
"description": "the acts kept"
},
"Asked": {
"type": "object",
"properties": {
"asked": {
"type": "string",
"description": "the ask's id: acts shows where it stands"
},
"node": {
"type": "string",
"description": "the node"
},
"says": {
"type": "string",
"description": "what happens next, in words"
}
},
"required": [
"asked",
"node",
"says"
],
"description": "the answer of an act the operator is asked"
},
"ManifestReadsChanged": {
"type": "object",
"properties": {
"v": {
"type": "integer",
"description": "the layout's version"
},
"node": {
"type": "string",
"description": "the node"
},
"manifest-reads": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"description": "what its engine states it reads now"
},
"at": {
"type": "string",
"description": "when it says it, RFC 3339"
},
"why": {
"type": "string",
"enum": [
"started",
"returned"
],
"description": "started: the engine started or what it states changed; returned: its node returned after being away"
}
},
"required": [
"v",
"node",
"at",
"why"
],
"description": "what a node-engine says when what it reads changed or its node returned (ADR 0618 §4)"
},
"Module": {
"type": "object",
"properties": {
"module": {
"type": "string",
"description": "the module, node for the node's own composition"
},
"fp": {
"type": "string",
"description": "the fingerprint it was composed at"
},
"state": {
"type": "string",
"enum": [
"composed",
"refused",
"held"
],
"description": "composed (digest set), refused by the candidate, or held without it too (words set)"
},
"digest": {
"type": "string",
"description": "the composition's digest when composed"
},
"changed": {
"type": "boolean",
"description": "the candidate changes it from the one composed without it"
},
"words": {
"type": "string",
"description": "why, for a refused or held one"
}
},
"required": [
"module",
"state"
],
"description": "one module's composition with the candidate"
},
"Revision": {
"type": "object",
"properties": {
"key": {
"type": "string",
"description": "the composition's key"
},
"revision": {
"type": "integer",
"description": "its revision"
},
"digest": {
"type": "string",
"description": "its digest"
},
"resources": {
"type": "integer",
"description": "how many resources it names"
},
"latest": {
"type": "boolean",
"description": "the latest revision"
},
"refused": {
"type": "string",
"description": "why it cannot be read or trusted"
}
},
"required": [
"key"
],
"description": "one revision of a composition key"
},
"RootVerdict": {
"type": "object",
"properties": {
"machine": {
"type": "string",
"description": "the machine"
},
"free": {
"type": "boolean",
"description": "the machine is root-free"
},
"why": {
"type": "string",
"description": "why, in words"
},
"judged": {
"type": "string",
"description": "when the engine judged it, RFC 3339"
}
},
"required": [
"machine",
"free",
"why",
"judged"
],
"description": "the engine's root-free verdict (ADR 0563 §6)"
},
"Said": {
"type": "object",
"properties": {
"node": {
"type": "string",
"description": "the node"
},
"said": {
"type": "string",
"description": "the engine's judgement or search, one line per directory or program"
}
},
"required": [
"node",
"said"
],
"description": "the answer of a read-only judgement"
}
}
}
+1
View File
@@ -10,3 +10,4 @@ export * as npmPackageRegistry from "./npm-package-registry.js";
export * as goPackageRegistry from "./go-package-registry.js";
export * as containerImageRegistry from "./container-image-registry.js";
export * as ociArtifactRegistry from "./oci-artifact-registry.js";
export * as nodeEngine from "./node-engine.js";
+539
View File
@@ -0,0 +1,539 @@
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
// The contract of the seat "node-engine", defined whole (seats/node-engine/seat.json): The node seat every node's engine holds (novox/hq ADR 0563, ADR 0618 §3, §4): its hand acts on the operator's warrant, its read-only judgements, its compositions and what it reads of a manifest. Each verb is answered on this node's subject, mesh.seat.node-engine.tool.<verb>.<node>, and each event is published on mesh.seat.node-engine.event.<event>.<node>, the node's name its last token.
export const seat = "node-engine";
export const verbs = {
handOver: "hand-over",
accessAct: "access-act",
judgeDirectories: "judge-directories",
setuidSearch: "setuid-search",
rootFree: "root-free",
acts: "acts",
packages: "packages",
composition: "composition",
compose: "compose",
composeCandidate: "compose-candidate",
pullWaits: "pull-waits",
manifestReads: "manifest-reads",
} as const;
export const events = {
asked: "asked",
done: "done",
refused: "refused",
manifestReadsChanged: "manifest-reads-changed",
} as const;
export const family = { name: "node-engine.events", maxAgeDays: 7 } as const;
export function askedSubject(caller: string): string {
return "mesh.seat.node-engine.event.asked." + caller;
}
export function doneSubject(caller: string): string {
return "mesh.seat.node-engine.event.done." + caller;
}
export function refusedSubject(caller: string): string {
return "mesh.seat.node-engine.event.refused." + caller;
}
export function manifestReadsChangedSubject(caller: string): string {
return "mesh.seat.node-engine.event.manifest-reads-changed." + caller;
}
/** one act asked through the seat, as acts reads it (ADR 0563 §9) */
export interface Act {
/** the act: hand-over, access-act, judge-directories or setuid-search */
"act": string;
/** the operator's ask of a hand-over or an access act */
"ask"?: string;
/** when it was asked, RFC 3339 */
"at": string;
/** the engine asked it itself, on finding a library's root holding data outside its group (ADR 0563 §4) */
"auto"?: boolean;
/** who answered, as the operator's warrant names them */
"by"?: string;
/** the caller as the runtime proved it */
"caller": string;
/** what the engine read on disk when it judged */
"disk"?: string;
/** when it ended, RFC 3339 */
"ended"?: string;
/** when the ask ends unanswered, RFC 3339 */
"expires"?: string;
/** the act's id */
"id": string;
/** the directory the act changes */
"path"?: string;
/** the warrant's proofs */
"proofs"?: string[];
/** what the engine said of it: its outcome or why it was refused */
"said"?: string;
/** the owner, group and mode it sets, in words */
"sets"?: string;
/** where it stands */
"state": "asked" | "done" | "refused" | "answered";
/** the path of a hand-over, or the access of an access act */
"subject"?: string;
/** the channel the warrant came on */
"via"?: string;
/** why it was asked, in words */
"why"?: string;
}
/** what the seat says of an act: the act, the path or access, the caller as the runtime proved it and the ask (ADR 0563 §1) */
export interface ActSaid {
/** the act */
"act": string;
/** the operator's ask */
"ask"?: string;
/** the caller as the runtime proved it */
"caller": string;
/** the node whose engine says it */
"node": string;
/** what the engine said of it */
"said"?: string;
/** where the act stands */
"state": "asked" | "done" | "refused";
/** the path or the access */
"subject"?: string;
}
/** the acts kept */
export interface Acts {
/** the acts, newest first */
"acts": Act[];
/** the node */
"node": string;
}
/** the answer of an act the operator is asked */
export interface Asked {
/** the ask's id: acts shows where it stands */
"asked": string;
/** the node */
"node": string;
/** what happens next, in words */
"says": string;
}
/** what a node-engine says when what it reads changed or its node returned (ADR 0618 §4) */
export interface ManifestReadsChanged {
/** when it says it, RFC 3339 */
"at": string;
/** what its engine states it reads now */
"manifest-reads"?: string[];
/** the node */
"node": string;
/** the layout's version */
"v": number;
/** started: the engine started or what it states changed; returned: its node returned after being away */
"why": "started" | "returned";
}
/** one module's composition with the candidate */
export interface Module {
/** the candidate changes it from the one composed without it */
"changed"?: boolean;
/** the composition's digest when composed */
"digest"?: string;
/** the fingerprint it was composed at */
"fp"?: string;
/** the module, node for the node's own composition */
"module": string;
/** composed (digest set), refused by the candidate, or held without it too (words set) */
"state": "composed" | "refused" | "held";
/** why, for a refused or held one */
"words"?: string;
}
/** one revision of a composition key */
export interface Revision {
/** its digest */
"digest"?: string;
/** the composition's key */
"key": string;
/** the latest revision */
"latest"?: boolean;
/** why it cannot be read or trusted */
"refused"?: string;
/** how many resources it names */
"resources"?: number;
/** its revision */
"revision"?: number;
}
/** the engine's root-free verdict (ADR 0563 §6) */
export interface RootVerdict {
/** the machine is root-free */
"free": boolean;
/** when the engine judged it, RFC 3339 */
"judged": string;
/** the machine */
"machine": string;
/** why, in words */
"why": string;
}
/** the answer of a read-only judgement */
export interface Said {
/** the node */
"node": string;
/** the engine's judgement or search, one line per directory or program */
"said": string;
}
/** is what hand-over takes. */
export interface HandOverInput {
/** the directory's absolute path, as the engine states it */
"path": string;
/** why it is asked, in words the operator reads */
"why"?: string;
}
/** is what hand-over answers. */
export type HandOverAnswer = Asked;
/** is what access-act takes. */
export interface AccessActInput {
/** the access, library:<name> */
"access": string;
/** the act: take */
"act": string;
/** why it is asked, in words the operator reads */
"why"?: string;
}
/** is what access-act answers. */
export type AccessActAnswer = Asked;
/** is what judge-directories takes. */
export interface JudgeDirectoriesInput {
}
/** is what judge-directories answers. */
export type JudgeDirectoriesAnswer = Said;
/** is what setuid-search takes. */
export interface SetuidSearchInput {
/** why it is asked, in words */
"why"?: string;
}
/** is what setuid-search answers. */
export type SetuidSearchAnswer = Said;
/** is what root-free takes. */
export interface RootFreeInput {
}
/** is what root-free answers. */
export interface RootFreeAnswer {
/** this machine's verdict */
"machines": RootVerdict[];
}
/** is what acts takes. */
export interface ActsInput {
/** one act, by its id */
"id"?: string;
/** at most this many, newest */
"limit"?: string;
}
/** is what acts answers. */
export type ActsAnswer = Acts;
/** is what packages takes. */
export interface PackagesInput {
/** "true": write this machine's key again from what the engine composed */
"write"?: "true" | "false";
}
export interface PackagesAnswerKeyPackagesItem {
/** declared absent */
"absent": boolean;
/** the modules that declare it */
"modules": string[];
/** the package */
"package": string;
}
/** the node's key as the bus holds it; absent when the engine wrote none */
export interface PackagesAnswerKey {
/** the node */
"node": string;
/** its packages sorted by name, absent after present */
"packages": PackagesAnswerKeyPackagesItem[];
}
/** is what packages answers. */
export interface PackagesAnswer {
/** the node's key as the bus holds it; absent when the engine wrote none */
"key"?: PackagesAnswerKey;
/** why the key could not be read */
"key_error"?: string;
/** the node */
"node": string;
/** a note, such as that no key is written yet */
"note"?: string;
/** where the key is read: the engine's state packages */
"source": string;
/** with write: the key was written again (false: it already held what the engine composed) */
"wrote"?: boolean;
}
/** is what composition takes. */
export interface CompositionInput {
/** "true": list the resources that differ from what runs */
"diff"?: string;
/** a composition's digest (sha256:…), to read that revision rather than the latest */
"digest"?: string;
/** the module, as placed on this machine; node for the node's own */
"module": string;
}
/** the module's last apply that did not end running; absent when none */
export interface CompositionAnswerAttempt {
/** the want's revision the hold took */
"answered_revision"?: number;
/** the digest tried */
"composed"?: string;
/** the composition's revision it was tried at */
"composition_revision": number;
/** the fingerprint tried */
"fp": string;
/** held for a failed backup */
"held"?: boolean;
/** it failed because the bus did not answer for a moment */
"transient"?: boolean;
/** the want's revision it was tried at */
"want_revision": number;
}
/** what the module's have key says now; absent when it says nothing */
export interface CompositionAnswerHave {
/** digest */
"composed"?: string;
/** detail */
"detail"?: string;
/** fingerprint */
"fp"?: string;
/** judgement */
"judgement"?: string;
/** running, failed, held or waiting-yes */
"phase": string;
/** the digest that waits for a yes */
"waits"?: string;
}
/** the move from what runs to the composition read */
export interface CompositionAnswerJudged {
/** the judgement */
"judgement": string;
/** the agent would wait for a person's yes before applying it */
"waits_for_yes": boolean;
/** its words */
"words": string;
}
/** of the node's own composition, its own fields beside what runs names */
export interface CompositionAnswerOwn {
/** the revision's adoption; absent for a converged node */
"adoption"?: Record<string, unknown>;
/** groups */
"groups"?: number;
/** each module the node's composer held in its last pass, with why */
"held"?: Record<string, string>;
/** identities the revision names */
"identities"?: number;
/** libraries */
"libraries"?: number;
/** the account the provisioner's client runs as, as the revision names it */
"provisioner_account": string;
/** as what runs names it */
"runs_provisioner_account"?: string;
}
/** what runs of the module; absent when nothing runs */
export interface CompositionAnswerRuns {
/** digest */
"composed"?: string;
/** fingerprint */
"fp"?: string;
/** applied, have or kept */
"from": string;
/** phase */
"phase"?: string;
/** how many resources run */
"resources": number;
}
/** the module's want as the agent holds it; absent when it holds none */
export interface CompositionAnswerWant {
/** the operator's answer to a failed backup, again or skip */
"backup"?: string;
/** the composition's digest it names */
"composed"?: string;
/** its build's fingerprint */
"fp": string;
/** the module is removed */
"removed"?: boolean;
/** the revision of the want's key read last */
"revision": number;
/** the node composes the module itself (ADR 0547 §2) */
"self"?: boolean;
/** its words */
"words"?: string;
/** the digest a person said yes to */
"yes"?: string;
}
/** is what composition answers. */
export interface CompositionAnswer {
/** the module's last apply that did not end running; absent when none */
"attempt"?: CompositionAnswerAttempt;
"composition": Revision;
/** every resource that differs from what runs, when asked */
"diff"?: Record<string, unknown>[];
/** what the module's have key says now; absent when it says nothing */
"have"?: CompositionAnswerHave;
/** the move from what runs to the composition read */
"judged": CompositionAnswerJudged;
/** the module */
"module": string;
/** the node */
"node": string;
/** of the node's own composition, its own fields beside what runs names */
"own"?: CompositionAnswerOwn;
/** the revisions of its key the bucket keeps, oldest first */
"revisions"?: Revision[];
/** what runs of the module; absent when nothing runs */
"runs"?: CompositionAnswerRuns;
/** the agent has looked at the module once since it started */
"started": boolean;
/** the module's want as the agent holds it; absent when it holds none */
"want"?: CompositionAnswerWant;
}
/** is what compose takes. */
export interface ComposeInput {
/** a module placed on this machine whose apply is also tried again (optional) */
"module"?: string;
}
/** how the pass went */
export interface ComposeAnswerPass {
/** when the node composes again on its own, RFC 3339 */
"again"?: string;
/** when the pass ended, RFC 3339 */
"at": string;
/** the answer is the last pass, made moments ago */
"earlier"?: boolean;
/** how many compositions the pass handed the agent that differ from before */
"handed": number;
/** each module refused alone, with why */
"held"?: Record<string, string>;
/** why the node composed none of its modules */
"refused"?: string;
}
/** is what compose answers. */
export interface ComposeAnswer {
/** how the pass went */
"pass": ComposeAnswerPass;
/** the module whose apply was tried again */
"retried"?: string;
}
/** is what compose-candidate takes. */
export interface ComposeCandidateInput {
/** the key of the indexer's candidate, <group>.<heads hash> */
"candidate": string;
/** also answer the composition input the candidate was composed from, for the sandbox path */
"input"?: boolean;
}
/** is what compose-candidate answers. */
export interface ComposeCandidateAnswer {
/** the candidate's key */
"candidate": string;
/** when asked: the composition input it composed from */
"input"?: Record<string, unknown>;
/** every module placed on the node, sorted by name */
"modules": Module[];
/** the node */
"node": string;
"own": Module;
/** why the node could not compose with the candidate at all */
"refused"?: string;
/** how long the engine took, in milliseconds */
"took": number;
/** the layout's version */
"v": number;
/** the node's verdict (ADR 0593 §7, §9) */
"verdict": "composed" | "refused" | "unready" | "not-judged";
}
/** is what pull-waits takes. */
export interface PullWaitsInput {
}
export interface PullWaitsAnswerWaitsItem {
/** that seat's holder, whose settled the retry waits on */
"holder"?: string;
/** the image whose pull failed */
"image": string;
/** the module whose apply failed */
"module": string;
/** when the failed pull began, RFC 3339 */
"pull-began"?: string;
/** when it was last tried again, RFC 3339 */
"retry-last"?: string;
/** what started that retry: settled, or the engine's start */
"retry-trigger"?: string;
/** the seat the image came through */
"seat"?: string;
/** the settled events of the holder heard since the engine started */
"settled-count"?: number;
/** when the last was heard, RFC 3339 */
"settled-last"?: string;
}
/** is what pull-waits answers. */
export interface PullWaitsAnswer {
/** every failed pull waiting on a holder */
"waits": PullWaitsAnswerWaitsItem[];
}
/** is what manifest-reads takes. */
export interface ManifestReadsInput {
}
/** is what manifest-reads answers. */
export interface ManifestReadsAnswer {
/** the reader capabilities its engine states: the manifest words, forms and seat names it reads; empty for an engine built before it said them, read as the baseline */
"manifest-reads"?: string[];
/** the node */
"node": string;
/** when the engine last stated them, RFC 3339 */
"stated-at": string;
/** the layout's version */
"v": number;
}
/** is the body of the event asked. */
export type AskedEvent = ActSaid;
/** is the body of the event done. */
export type DoneEvent = ActSaid;
/** is the body of the event refused. */
export type RefusedEvent = ActSaid;
/** is the body of the event manifest-reads-changed. */
export type ManifestReadsChangedEvent = ManifestReadsChanged;
+849 -18
View File
@@ -6432,9 +6432,10 @@ export const list: readonly Seat[] = [
"emits": [
"asked",
"done",
"refused"
"refused",
"manifest-reads-changed"
],
"grain": "seat-list",
"grain": "whole",
"heldByTheEngine": true,
"name": "node-engine",
"scope": "node",
@@ -6457,7 +6458,30 @@ export const list: readonly Seat[] = [
],
"type": "object"
},
"name": "hand-over"
"name": "hand-over",
"output": {
"description": "the answer of an act the operator is asked",
"properties": {
"asked": {
"description": "the ask's id: acts shows where it stands",
"type": "string"
},
"node": {
"description": "the node",
"type": "string"
},
"says": {
"description": "what happens next, in words",
"type": "string"
}
},
"required": [
"asked",
"node",
"says"
],
"type": "object"
}
},
{
"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.",
@@ -6482,7 +6506,30 @@ export const list: readonly Seat[] = [
],
"type": "object"
},
"name": "access-act"
"name": "access-act",
"output": {
"description": "the answer of an act the operator is asked",
"properties": {
"asked": {
"description": "the ask's id: acts shows where it stands",
"type": "string"
},
"node": {
"description": "the node",
"type": "string"
},
"says": {
"description": "what happens next, in words",
"type": "string"
}
},
"required": [
"asked",
"node",
"says"
],
"type": "object"
}
},
{
"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.",
@@ -6490,7 +6537,25 @@ export const list: readonly Seat[] = [
"properties": {},
"type": "object"
},
"name": "judge-directories"
"name": "judge-directories",
"output": {
"description": "the answer of a read-only judgement",
"properties": {
"node": {
"description": "the node",
"type": "string"
},
"said": {
"description": "the engine's judgement or search, one line per directory or program",
"type": "string"
}
},
"required": [
"node",
"said"
],
"type": "object"
}
},
{
"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.",
@@ -6503,7 +6568,25 @@ export const list: readonly Seat[] = [
},
"type": "object"
},
"name": "setuid-search"
"name": "setuid-search",
"output": {
"description": "the answer of a read-only judgement",
"properties": {
"node": {
"description": "the node",
"type": "string"
},
"said": {
"description": "the engine's judgement or search, one line per directory or program",
"type": "string"
}
},
"required": [
"node",
"said"
],
"type": "object"
}
},
{
"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.",
@@ -6511,7 +6594,47 @@ export const list: readonly Seat[] = [
"properties": {},
"type": "object"
},
"name": "root-free"
"name": "root-free",
"output": {
"properties": {
"machines": {
"description": "this machine's verdict",
"items": {
"description": "the engine's root-free verdict (ADR 0563 §6)",
"properties": {
"free": {
"description": "the machine is root-free",
"type": "boolean"
},
"judged": {
"description": "when the engine judged it, RFC 3339",
"type": "string"
},
"machine": {
"description": "the machine",
"type": "string"
},
"why": {
"description": "why, in words",
"type": "string"
}
},
"required": [
"machine",
"free",
"why",
"judged"
],
"type": "object"
},
"type": "array"
}
},
"required": [
"machines"
],
"type": "object"
}
},
{
"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.",
@@ -6528,7 +6651,119 @@ export const list: readonly Seat[] = [
},
"type": "object"
},
"name": "acts"
"name": "acts",
"output": {
"description": "the acts kept",
"properties": {
"acts": {
"description": "the acts, newest first",
"items": {
"description": "one act asked through the seat, as acts reads it (ADR 0563 §9)",
"properties": {
"act": {
"description": "the act: hand-over, access-act, judge-directories or setuid-search",
"type": "string"
},
"ask": {
"description": "the operator's ask of a hand-over or an access act",
"type": "string"
},
"at": {
"description": "when it was asked, RFC 3339",
"type": "string"
},
"auto": {
"description": "the engine asked it itself, on finding a library's root holding data outside its group (ADR 0563 §4)",
"type": "boolean"
},
"by": {
"description": "who answered, as the operator's warrant names them",
"type": "string"
},
"caller": {
"description": "the caller as the runtime proved it",
"type": "string"
},
"disk": {
"description": "what the engine read on disk when it judged",
"type": "string"
},
"ended": {
"description": "when it ended, RFC 3339",
"type": "string"
},
"expires": {
"description": "when the ask ends unanswered, RFC 3339",
"type": "string"
},
"id": {
"description": "the act's id",
"type": "string"
},
"path": {
"description": "the directory the act changes",
"type": "string"
},
"proofs": {
"description": "the warrant's proofs",
"items": {
"type": "string"
},
"type": "array"
},
"said": {
"description": "what the engine said of it: its outcome or why it was refused",
"type": "string"
},
"sets": {
"description": "the owner, group and mode it sets, in words",
"type": "string"
},
"state": {
"description": "where it stands",
"enum": [
"asked",
"done",
"refused",
"answered"
],
"type": "string"
},
"subject": {
"description": "the path of a hand-over, or the access of an access act",
"type": "string"
},
"via": {
"description": "the channel the warrant came on",
"type": "string"
},
"why": {
"description": "why it was asked, in words",
"type": "string"
}
},
"required": [
"id",
"act",
"caller",
"at",
"state"
],
"type": "object"
},
"type": "array"
},
"node": {
"description": "the node",
"type": "string"
}
},
"required": [
"node",
"acts"
],
"type": "object"
}
},
{
"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.",
@@ -6546,7 +6781,79 @@ export const list: readonly Seat[] = [
"type": "object"
},
"name": "packages",
"optional": true
"optional": true,
"output": {
"properties": {
"key": {
"description": "the node's key as the bus holds it; absent when the engine wrote none",
"properties": {
"node": {
"description": "the node",
"type": "string"
},
"packages": {
"description": "its packages sorted by name, absent after present",
"items": {
"properties": {
"absent": {
"description": "declared absent",
"type": "boolean"
},
"modules": {
"description": "the modules that declare it",
"items": {
"type": "string"
},
"type": "array"
},
"package": {
"description": "the package",
"type": "string"
}
},
"required": [
"package",
"modules",
"absent"
],
"type": "object"
},
"type": "array"
}
},
"required": [
"node",
"packages"
],
"type": "object"
},
"key_error": {
"description": "why the key could not be read",
"type": "string"
},
"node": {
"description": "the node",
"type": "string"
},
"note": {
"description": "a note, such as that no key is written yet",
"type": "string"
},
"source": {
"description": "where the key is read: the engine's state packages",
"type": "string"
},
"wrote": {
"description": "with write: the key was written again (false: it already held what the engine composed)",
"type": "boolean"
}
},
"required": [
"node",
"source"
],
"type": "object"
}
},
{
"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-<node> (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.",
@@ -6571,7 +6878,316 @@ export const list: readonly Seat[] = [
"type": "object"
},
"name": "composition",
"optional": true
"optional": true,
"output": {
"properties": {
"attempt": {
"description": "the module's last apply that did not end running; absent when none",
"properties": {
"answered_revision": {
"description": "the want's revision the hold took",
"type": "integer"
},
"composed": {
"description": "the digest tried",
"type": "string"
},
"composition_revision": {
"description": "the composition's revision it was tried at",
"type": "integer"
},
"fp": {
"description": "the fingerprint tried",
"type": "string"
},
"held": {
"description": "held for a failed backup",
"type": "boolean"
},
"transient": {
"description": "it failed because the bus did not answer for a moment",
"type": "boolean"
},
"want_revision": {
"description": "the want's revision it was tried at",
"type": "integer"
}
},
"required": [
"fp",
"want_revision",
"composition_revision"
],
"type": "object"
},
"composition": {
"description": "one revision of a composition key",
"properties": {
"digest": {
"description": "its digest",
"type": "string"
},
"key": {
"description": "the composition's key",
"type": "string"
},
"latest": {
"description": "the latest revision",
"type": "boolean"
},
"refused": {
"description": "why it cannot be read or trusted",
"type": "string"
},
"resources": {
"description": "how many resources it names",
"type": "integer"
},
"revision": {
"description": "its revision",
"type": "integer"
}
},
"required": [
"key"
],
"type": "object"
},
"diff": {
"description": "every resource that differs from what runs, when asked",
"items": {
"description": "one resource added, removed or changed, as the engine's judge says it",
"type": "object"
},
"type": "array"
},
"have": {
"description": "what the module's have key says now; absent when it says nothing",
"properties": {
"composed": {
"description": "digest",
"type": "string"
},
"detail": {
"description": "detail",
"type": "string"
},
"fp": {
"description": "fingerprint",
"type": "string"
},
"judgement": {
"description": "judgement",
"type": "string"
},
"phase": {
"description": "running, failed, held or waiting-yes",
"type": "string"
},
"waits": {
"description": "the digest that waits for a yes",
"type": "string"
}
},
"required": [
"phase"
],
"type": "object"
},
"judged": {
"description": "the move from what runs to the composition read",
"properties": {
"judgement": {
"description": "the judgement",
"type": "string"
},
"waits_for_yes": {
"description": "the agent would wait for a person's yes before applying it",
"type": "boolean"
},
"words": {
"description": "its words",
"type": "string"
}
},
"required": [
"judgement",
"words",
"waits_for_yes"
],
"type": "object"
},
"module": {
"description": "the module",
"type": "string"
},
"node": {
"description": "the node",
"type": "string"
},
"own": {
"description": "of the node's own composition, its own fields beside what runs names",
"properties": {
"adoption": {
"description": "the revision's adoption; absent for a converged node",
"type": "object"
},
"groups": {
"description": "groups",
"type": "integer"
},
"held": {
"additionalProperties": {
"type": "string"
},
"description": "each module the node's composer held in its last pass, with why",
"type": "object"
},
"identities": {
"description": "identities the revision names",
"type": "integer"
},
"libraries": {
"description": "libraries",
"type": "integer"
},
"provisioner_account": {
"description": "the account the provisioner's client runs as, as the revision names it",
"type": "string"
},
"runs_provisioner_account": {
"description": "as what runs names it",
"type": "string"
}
},
"required": [
"provisioner_account"
],
"type": "object"
},
"revisions": {
"description": "the revisions of its key the bucket keeps, oldest first",
"items": {
"description": "one revision of a composition key",
"properties": {
"digest": {
"description": "its digest",
"type": "string"
},
"key": {
"description": "the composition's key",
"type": "string"
},
"latest": {
"description": "the latest revision",
"type": "boolean"
},
"refused": {
"description": "why it cannot be read or trusted",
"type": "string"
},
"resources": {
"description": "how many resources it names",
"type": "integer"
},
"revision": {
"description": "its revision",
"type": "integer"
}
},
"required": [
"key"
],
"type": "object"
},
"type": "array"
},
"runs": {
"description": "what runs of the module; absent when nothing runs",
"properties": {
"composed": {
"description": "digest",
"type": "string"
},
"fp": {
"description": "fingerprint",
"type": "string"
},
"from": {
"description": "applied, have or kept",
"type": "string"
},
"phase": {
"description": "phase",
"type": "string"
},
"resources": {
"description": "how many resources run",
"type": "integer"
}
},
"required": [
"from",
"resources"
],
"type": "object"
},
"started": {
"description": "the agent has looked at the module once since it started",
"type": "boolean"
},
"want": {
"description": "the module's want as the agent holds it; absent when it holds none",
"properties": {
"backup": {
"description": "the operator's answer to a failed backup, again or skip",
"type": "string"
},
"composed": {
"description": "the composition's digest it names",
"type": "string"
},
"fp": {
"description": "its build's fingerprint",
"type": "string"
},
"removed": {
"description": "the module is removed",
"type": "boolean"
},
"revision": {
"description": "the revision of the want's key read last",
"type": "integer"
},
"self": {
"description": "the node composes the module itself (ADR 0547 §2)",
"type": "boolean"
},
"words": {
"description": "its words",
"type": "string"
},
"yes": {
"description": "the digest a person said yes to",
"type": "string"
}
},
"required": [
"fp",
"revision"
],
"type": "object"
}
},
"required": [
"node",
"module",
"started",
"composition",
"judged"
],
"type": "object"
}
},
{
"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.",
@@ -6585,7 +7201,56 @@ export const list: readonly Seat[] = [
"type": "object"
},
"name": "compose",
"optional": true
"optional": true,
"output": {
"properties": {
"pass": {
"description": "how the pass went",
"properties": {
"again": {
"description": "when the node composes again on its own, RFC 3339",
"type": "string"
},
"at": {
"description": "when the pass ended, RFC 3339",
"type": "string"
},
"earlier": {
"description": "the answer is the last pass, made moments ago",
"type": "boolean"
},
"handed": {
"description": "how many compositions the pass handed the agent that differ from before",
"type": "integer"
},
"held": {
"additionalProperties": {
"type": "string"
},
"description": "each module refused alone, with why",
"type": "object"
},
"refused": {
"description": "why the node composed none of its modules",
"type": "string"
}
},
"required": [
"at",
"handed"
],
"type": "object"
},
"retried": {
"description": "the module whose apply was tried again",
"type": "string"
}
},
"required": [
"pass"
],
"type": "object"
}
},
{
"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.",
@@ -6596,12 +7261,8 @@ export const list: readonly Seat[] = [
"type": "string"
},
"input": {
"description": "\"true\": also answer the composition input the candidate was composed from, for the sandbox path",
"enum": [
"true",
"false"
],
"type": "string"
"description": "also answer the composition input the candidate was composed from, for the sandbox path",
"type": "boolean"
}
},
"required": [
@@ -6610,7 +7271,137 @@ export const list: readonly Seat[] = [
"type": "object"
},
"name": "compose-candidate",
"optional": true
"optional": true,
"output": {
"properties": {
"candidate": {
"description": "the candidate's key",
"type": "string"
},
"input": {
"description": "when asked: the composition input it composed from",
"type": "object"
},
"modules": {
"description": "every module placed on the node, sorted by name",
"items": {
"description": "one module's composition with the candidate",
"properties": {
"changed": {
"description": "the candidate changes it from the one composed without it",
"type": "boolean"
},
"digest": {
"description": "the composition's digest when composed",
"type": "string"
},
"fp": {
"description": "the fingerprint it was composed at",
"type": "string"
},
"module": {
"description": "the module, node for the node's own composition",
"type": "string"
},
"state": {
"description": "composed (digest set), refused by the candidate, or held without it too (words set)",
"enum": [
"composed",
"refused",
"held"
],
"type": "string"
},
"words": {
"description": "why, for a refused or held one",
"type": "string"
}
},
"required": [
"module",
"state"
],
"type": "object"
},
"type": "array"
},
"node": {
"description": "the node",
"type": "string"
},
"own": {
"description": "one module's composition with the candidate",
"properties": {
"changed": {
"description": "the candidate changes it from the one composed without it",
"type": "boolean"
},
"digest": {
"description": "the composition's digest when composed",
"type": "string"
},
"fp": {
"description": "the fingerprint it was composed at",
"type": "string"
},
"module": {
"description": "the module, node for the node's own composition",
"type": "string"
},
"state": {
"description": "composed (digest set), refused by the candidate, or held without it too (words set)",
"enum": [
"composed",
"refused",
"held"
],
"type": "string"
},
"words": {
"description": "why, for a refused or held one",
"type": "string"
}
},
"required": [
"module",
"state"
],
"type": "object"
},
"refused": {
"description": "why the node could not compose with the candidate at all",
"type": "string"
},
"took": {
"description": "how long the engine took, in milliseconds",
"type": "integer"
},
"v": {
"description": "the layout's version",
"type": "integer"
},
"verdict": {
"description": "the node's verdict (ADR 0593 §7, §9)",
"enum": [
"composed",
"refused",
"unready",
"not-judged"
],
"type": "string"
}
},
"required": [
"v",
"node",
"candidate",
"verdict",
"modules",
"own",
"took"
],
"type": "object"
}
},
{
"description": "The failed pulls this machine's engine waits on a store's holder for (design 51 *What a pull meets in the swap*): per module, the image, the seat it came through and that seat's holder, when the pull began, how many settled events of that holder it heard and the last, and its last retry and what started it. Read-only. Answered by this machine's engine alone.",
@@ -6677,6 +7468,46 @@ export const list: readonly Seat[] = [
],
"type": "object"
}
},
{
"description": "The reader capabilities this machine's engine states (manifest words, forms and seat names it reads, novox/hq ADR 0618 §3) and when it last stated them. An engine built before it said them states none, which a reader takes as the baseline. Read-only. Answered by this machine's engine alone.",
"input": {
"properties": {},
"type": "object"
},
"name": "manifest-reads",
"optional": true,
"output": {
"description": "what this machine's engine reads of a manifest (ADR 0618 §3)",
"properties": {
"manifest-reads": {
"description": "the reader capabilities its engine states: the manifest words, forms and seat names it reads; empty for an engine built before it said them, read as the baseline",
"items": {
"minLength": 1,
"type": "string"
},
"type": "array"
},
"node": {
"description": "the node",
"type": "string"
},
"stated-at": {
"description": "when the engine last stated them, RFC 3339",
"type": "string"
},
"v": {
"description": "the layout's version",
"type": "integer"
}
},
"required": [
"v",
"node",
"stated-at"
],
"type": "object"
}
}
]
},