node-engine: typed layouts for the states have (both forms), composed (both forms, wire names), data and health, with fixtures; Candidates typed as CandidateAnswer (ADR 0634 §1, §6; issue 1966)

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-05 14:50:04 +01:00
parent 99adc0eb98
commit f00aac3847
5 changed files with 2290 additions and 151 deletions
+361 -40
View File
@@ -112,6 +112,22 @@ export function parseComposedByNodeModuleFingerprintKey(key: string): { node: st
return { node, module, fingerprint };
}
/** The key of one value of the state composed under the form "<node>.node". */
export function composedByNodeNodeKey(node: string): string {
return node + ".node";
}
/** The parts of a key of the form "<node>.node", or undefined for a key of another form. */
export function parseComposedByNodeNodeKey(key: string): { node: string } | undefined {
let rest = key;
const at0 = rest.indexOf(".node");
if (at0 <= 0) return undefined;
const node = rest.slice(0, at0);
rest = rest.slice(at0 + 5);
if (rest !== "") return undefined;
return { node };
}
/** The key of one value of the state data under the form "<node>.<module>". */
export function dataByNodeModuleKey(node: string, module: string): string {
return node + "." + module;
@@ -176,6 +192,17 @@ export function parseFindingsByKeyKey(key: string): { key: string } | undefined
return { key };
}
/** the hashes of the users of the list, sealed to the vault */
export interface AccountsHanded {
/** when */
"at": string;
"box": SealedBox;
"from": string;
/** the fingerprint of the vault's key */
"to": string;
"users": number;
}
/** 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 */
@@ -242,6 +269,14 @@ export interface Acts {
"node": string;
}
/** the node is adopted and which of its modules are taken */
export interface Adoption {
/** the modules taken */
"taken": string[];
/** by module, the resources left untaken */
"untaken"?: Record<string, string[]>;
}
/** the answer of an act the operator is asked */
export interface Asked {
/** the ask's id: acts shows where it stands */
@@ -252,6 +287,105 @@ export interface Asked {
"says": string;
}
/** a network attached to a link */
export interface AttachedNetwork {
"link": string;
"network": string;
}
/** a module the candidate adds */
export interface CandidateAdded {
"composed"?: string;
"module": string;
"unissued"?: string[];
}
/** the node's answer to one placement candidate naming it */
export interface CandidateAnswer {
"added"?: CandidateAdded[];
"differs"?: CandidateDiffers[];
"held"?: CandidateRefusal[];
"note"?: string;
"proposal": string;
"refusals"?: CandidateRefusal[];
/** why the node cannot compose with the candidate at all */
"refused"?: string;
/** modules the candidate removes */
"removed"?: string[];
"returns"?: string;
"revision": number;
/** modules whose composition is the one running */
"same"?: string[];
"values_differ"?: number;
/** same, differs or refused */
"verdict": "same" | "differs" | "refused";
}
/** the bodies of one resource that differ */
export interface CandidateBodies {
"composed"?: string;
"id": string;
"paths"?: string[];
"running"?: string;
"why": string;
}
/** a module whose composition with the candidate differs from the one running */
export interface CandidateDiffers {
"bodies"?: CandidateBodies[];
"composed"?: string;
"module": string;
"resource"?: string;
"running"?: string;
"why": string;
}
/** a module refused or held with the candidate */
export interface CandidateRefusal {
"module": string;
"today"?: string;
"why": string;
}
/** a group */
export interface DeclaredGroup {
"gid": number;
"name": string;
}
/** the identity of a module assigned to the node */
export interface DeclaredIdentity {
"gid": number;
"groups"?: number[];
"module": string;
"name": string;
"runner"?: string;
"uid": number;
}
/** a library the node is reached by */
export interface DeclaredLibrary {
"gid": number;
"group": string;
"home": boolean;
"library": string;
"paths"?: string[];
"root": string;
"runner"?: string;
"taken"?: boolean;
}
/** a unit that failed */
export interface FailedUnit {
"load"?: string;
"resource"?: string;
"result"?: string;
"scope": string;
/** since when */
"since": string;
"unit": string;
}
/** one answer the operator may give from a notification */
export interface FindingAction {
"arguments"?: Record<string, string>;
@@ -280,6 +414,15 @@ export interface FindingSubject {
"scope": string;
}
/** a login that fell back to another node */
export interface LoginFallback {
/** when */
"at": string;
"from": string;
"node": string;
"why": 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 */
@@ -310,6 +453,115 @@ export interface Module {
"words"?: string;
}
/** the node's network */
export interface NetworkHealth {
"parts": NetworkPart[];
/** since when */
"since": string;
/** running, degraded or unknown */
"state": string;
}
/** one part of the node's network */
export interface NetworkPart {
"owner"?: string;
"part": string;
"reason"?: string;
"said"?: string;
/** since when */
"since": string;
"state": string;
"streak"?: number;
"targets"?: string[];
"toward"?: string[];
"writer"?: string;
}
/** an account the node needs to log in again */
export interface NodeAccount {
"account": string;
"module"?: string;
"reason": string;
"resource"?: string;
/** since when */
"since": string;
}
/** a directory found where the node expected none */
export interface NodeDirectory {
"module": string;
"path": string;
"reason": string;
"resource": string;
/** since when */
"since": string;
}
/** what a node's own composition carries beside its resources */
export interface NodeOwn {
"adoption"?: Adoption;
"groups"?: DeclaredGroup[];
"identities"?: DeclaredIdentity[];
"libraries"?: DeclaredLibrary[];
/** the account the provisioner's client runs as on the node */
"provisioner_account"?: string;
}
/** what the machine observes of itself, for every node and composing part to read */
export interface NodeRecord {
"authority"?: string;
"capabilities"?: string[];
"certificate"?: string;
"contract"?: number;
"manifest-reads"?: string[];
"networks"?: AttachedNetwork[];
"networks-said"?: boolean;
"node": string;
"outward-links"?: string[];
"sealing-key"?: string;
"serving-key"?: string;
"trusts"?: string[];
"tunnel-key"?: string;
"v": number;
}
/** a tunnel peer and whether it is seen */
export interface PeerSeen {
"addresses"?: string[];
"key": string;
"seen": boolean;
}
/** one data item a module declares, as placed on a node */
export interface PlacedDataItem {
"class": string;
/** the path whose snapshot keeps it, empty when it is not backed up */
"covered_by"?: string;
"item": string;
/** walk, dataset or shallow */
"measure"?: string;
"module": string;
/** the item's place on the node */
"path": string;
/** backup, redundancy, both or none */
"protection"?: string;
}
/** the pull from a store a failed apply waits on */
export interface PullWait {
/** when the pull began */
"began": string;
/** that seat's holder, whose settling the apply waits for */
"holder": string;
"image": string;
/** what triggered the last try again, in words */
"retried"?: string;
/** when the apply was last tried again */
"retried-at"?: string;
/** the seat the pull came through */
"seat"?: string;
}
/** one revision of a composition key */
export interface Revision {
/** its digest */
@@ -326,6 +578,17 @@ export interface Revision {
"revision"?: number;
}
/** the engine's verdict of whether an account's resource is root-free, as the node's health lists it */
export interface RootAccountVerdict {
"account": string;
"module": string;
"reason"?: string;
"resource": string;
/** since when */
"since": string;
"state": string;
}
/** the engine's root-free verdict (ADR 0563 §6) */
export interface RootVerdict {
/** the machine is root-free */
@@ -346,6 +609,15 @@ export interface Said {
"said": string;
}
/** a value sealed to a key */
export interface SealedBox {
"ct": string;
"eph": string;
"iv": string;
"tag": string;
"v": number;
}
export interface SeatListStatus {
/** the seat mesh-catalog has answered */
"heard": boolean;
@@ -374,6 +646,37 @@ export interface SeatListHeld {
"source": "trunk" | "bootstrap";
}
/** the node's units */
export interface UnitsHealth {
"away"?: string[];
"failed": FailedUnit[];
"state": string;
"unread"?: string[];
}
/** the bus's user list as the engine of the bus node wrote it to the bus module's file */
export interface UserListWritten {
/** when */
"at": string;
/** the digest of the file on the machine after the write */
"digest"?: string;
/** why a write failed */
"error"?: string;
/** which list the node follows now */
"follows"?: string;
"handed"?: AccountsHanded;
/** the bus node's sealing key, its public half */
"key"?: string;
"revision": number;
/** the key the list was taken from */
"source": string;
/** why the bus node does not watch the grants part's key now */
"unwatched"?: string;
"users": number;
/** the digest of the list the source holds, sha256:<hex> */
"wanted": string;
}
/** is what hand-over takes. */
export interface HandOverInput {
/** the directory's absolute path, as the engine states it */
@@ -711,45 +1014,48 @@ export type ManifestReadsChangedEvent = ManifestReadsChanged;
/** is one value of the state have under the key <node>.<module>. */
export interface HaveByNodeModuleValue {
"build"?: string;
/** the move changes the build alone */
"build-alone"?: boolean;
/** the composition's digest */
/** the digest of the composition the node runs */
"composed"?: string;
"detail"?: string;
/** the fingerprint held */
"fp"?: string;
/** the accounts handed */
"handed"?: Record<string, unknown>;
/** how it is */
"health": string;
"judgement"?: string;
"handed"?: AccountsHanded;
/** what the module's own check says */
"health": "ok" | "failed" | "waits" | "unknown";
/** in phase waiting-yes, the judgement of the move */
"judgement"?: "recreates" | "moves-data" | "cannot-judge";
/** what the node keeps of the module as it runs */
"kept"?: string[];
/** how far it has come */
"phase": string;
/** a pull the engine waits for */
"pull-wait"?: Record<string, unknown>;
"phase": "backing-up" | "applying" | "checking" | "running" | "failed" | "removed" | "held" | "waiting-yes";
"pull-wait"?: PullWait;
/** each value sealed to the node's key */
"sealed"?: string[];
"sealed-at"?: Record<string, unknown>;
/** the user list written */
"user-list"?: Record<string, unknown>;
/** the sealed values by slot */
"sealed-at"?: Record<string, string>;
"user-list"?: UserListWritten;
"v": number;
/** what it waits for */
/** in phase waiting-yes, the digest of the composition that waits */
"waits"?: string;
}
/** is one value of the state have under the key <node>.node. */
export interface HaveByNodeNodeValue {
"busy"?: Record<string, unknown>;
"candidates"?: Record<string, Record<string, unknown>>;
/** the node's answer to each placement candidate naming it, by proposal id */
"candidates"?: Record<string, CandidateAnswer>;
/** the digest of the node's own composition it runs */
"composed"?: string;
/** own, or the controller's */
/** own, or none (empty) */
"composes"?: string;
"composes-why"?: string;
"filters"?: Record<string, unknown>;
"profile"?: Record<string, unknown>;
/** the node's record: its tunnel and certificate */
"record"?: Record<string, unknown>;
"record"?: NodeRecord;
"sealed"?: string[];
"sealed-at"?: Record<string, unknown>;
"sealed-at"?: Record<string, string>;
"strays"?: Record<string, unknown>;
"v": number;
"windows"?: Record<string, unknown>;
@@ -757,28 +1063,42 @@ export interface HaveByNodeNodeValue {
/** is one value of the state composed under the key <node>.<module>.<fingerprint>. */
export interface ComposedByNodeModuleFingerprintValue {
"Data"?: boolean;
"FP": string;
"Filled"?: string[];
"Module": string;
"Node": string;
/** the node's own adoption, identities, groups, libraries and provisioner account */
"Own"?: Record<string, unknown>;
/** the module's plan */
"Plan"?: Record<string, unknown>;
/** why the composition was refused */
"Refused"?: string;
"Resources"?: Record<string, unknown>[];
"V"?: number;
/** the module declares data */
"data"?: boolean;
/** the files the provisioner fills */
"filled_files"?: string[];
"fp": string;
"module": string;
"node": string;
/** the module's whole provisioning plan, catalogue.Plan, kept whole: it is the manifest layer's type */
"plan"?: Record<string, unknown>;
/** why the module cannot be composed at this fingerprint */
"refused"?: string;
/** the declaration resources, each saying its module */
"resources": Record<string, unknown>[];
"v": number;
}
/** is one value of the state composed under the key <node>.node. */
export interface ComposedByNodeNodeValue {
/** the module declares data */
"data"?: boolean;
/** the files the provisioner fills */
"filled_files"?: string[];
"node": string;
"own"?: NodeOwn;
/** the declaration resources, each saying its module */
"resources": Record<string, unknown>[];
"v": number;
}
/** is one value of the state data under the key <node>.<module>. */
export interface DataByNodeModuleValue {
"digest": string;
"items": Record<string, unknown>[];
"items": PlacedDataItem[];
"module": string;
"node": string;
"v"?: number;
"v": number;
}
export interface PackagesByNodeValuePackagesItem {
@@ -795,16 +1115,17 @@ export interface PackagesByNodeValue {
/** is one value of the state health under the key <node>. */
export interface HealthByNodeValue {
/** when */
"at": string;
"contract"?: number;
"fallback"?: Record<string, unknown>;
"found"?: Record<string, unknown>[];
"network"?: Record<string, unknown>;
"fallback"?: LoginFallback;
"found"?: NodeDirectory[];
"network"?: NetworkHealth;
"node": string;
"peers"?: Record<string, unknown>[];
"relogin"?: Record<string, unknown>[];
"root"?: Record<string, unknown>[];
"units"?: Record<string, unknown>;
"peers"?: PeerSeen[];
"relogin"?: NodeAccount[];
"root"?: RootAccountVerdict[];
"units"?: UnitsHealth;
"v": number;
}