diff --git a/go/seats/nodeengine/contract.go b/go/seats/nodeengine/contract.go index 2fa471a..f8267e8 100644 --- a/go/seats/nodeengine/contract.go +++ b/go/seats/nodeengine/contract.go @@ -185,6 +185,37 @@ func DecodeComposedByNodeModuleFingerprintValue(raw []byte) (ComposedByNodeModul return v, nil } +// ComposedByNodeNodeKey is the key of one value of the state composed under the form ".node". +func ComposedByNodeNodeKey(node string) string { return node + ".node" } + +// ParseComposedByNodeNodeKey reads the parts of a key of the form ".node", in the order of the form; the last result is false for a key of another form. +func ParseComposedByNodeNodeKey(key string) (string, bool) { + rest := key + v0, after0, found0 := strings.Cut(rest, ".node") + if !found0 || v0 == "" { + return "", false + } + rest = after0 + if rest != "" { + return "", false + } + return v0, true +} + +// DecodeComposedByNodeNodeValue reads one value of the state composed under the form ".node", refusing a field ComposedByNodeNodeValue does not know and any text after the value. +func DecodeComposedByNodeNodeValue(raw []byte) (ComposedByNodeNodeValue, error) { + var v ComposedByNodeNodeValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + // DataByNodeModuleKey is the key of one value of the state data under the form ".". func DataByNodeModuleKey(node, module string) string { return node + "." + module } @@ -314,6 +345,17 @@ func DecodeFindingsByKeyValue(raw []byte) (FindingsByKeyValue, error) { return v, nil } +// AccountsHanded is the hashes of the users of the list, sealed to the vault. +type AccountsHanded struct { + // when + At string `json:"at"` + Box SealedBox `json:"box"` + From string `json:"from"` + // the fingerprint of the vault's key + To string `json:"to"` + Users int64 `json:"users"` +} + // 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 @@ -395,6 +437,14 @@ type Acts struct { Node string `json:"node"` } +// Adoption is the node is adopted and which of its modules are taken. +type Adoption struct { + // the modules taken + Taken []string `json:"taken"` + // by module, the resources left untaken + Untaken map[string][]string `json:"untaken,omitempty"` +} + // Asked is the answer of an act the operator is asked. type Asked struct { // the ask's id: acts shows where it stands @@ -405,6 +455,112 @@ type Asked struct { Says string `json:"says"` } +// AttachedNetwork is a network attached to a link. +type AttachedNetwork struct { + Link string `json:"link"` + Network string `json:"network"` +} + +// CandidateAdded is a module the candidate adds. +type CandidateAdded struct { + Composed string `json:"composed,omitempty"` + Module string `json:"module"` + Unissued []string `json:"unissued,omitempty"` +} + +// CandidateAnswer is the node's answer to one placement candidate naming it. +type CandidateAnswer struct { + Added []CandidateAdded `json:"added,omitempty"` + Differs []CandidateDiffers `json:"differs,omitempty"` + Held []CandidateRefusal `json:"held,omitempty"` + Note string `json:"note,omitempty"` + Proposal string `json:"proposal"` + Refusals []CandidateRefusal `json:"refusals,omitempty"` + // why the node cannot compose with the candidate at all + Refused string `json:"refused,omitempty"` + // modules the candidate removes + Removed []string `json:"removed,omitempty"` + Returns string `json:"returns,omitempty"` + Revision int64 `json:"revision"` + // modules whose composition is the one running + Same []string `json:"same,omitempty"` + ValuesDiffer int64 `json:"values_differ,omitempty"` + // same, differs or refused + Verdict string `json:"verdict"` +} + +// The values of CandidateAnswer's verdict. +const ( + CandidateAnswerVerdictSame = "same" + CandidateAnswerVerdictDiffers = "differs" + CandidateAnswerVerdictRefused = "refused" +) + +// CandidateBodies is the bodies of one resource that differ. +type CandidateBodies struct { + Composed string `json:"composed,omitempty"` + ID string `json:"id"` + Paths []string `json:"paths,omitempty"` + Running string `json:"running,omitempty"` + Why string `json:"why"` +} + +// CandidateDiffers is a module whose composition with the candidate differs from the one running. +type CandidateDiffers struct { + Bodies []CandidateBodies `json:"bodies,omitempty"` + Composed string `json:"composed,omitempty"` + Module string `json:"module"` + Resource string `json:"resource,omitempty"` + Running string `json:"running,omitempty"` + Why string `json:"why"` +} + +// CandidateRefusal is a module refused or held with the candidate. +type CandidateRefusal struct { + Module string `json:"module"` + Today string `json:"today,omitempty"` + Why string `json:"why"` +} + +// DeclaredGroup is a group. +type DeclaredGroup struct { + Gid int64 `json:"gid"` + Name string `json:"name"` +} + +// DeclaredIdentity is the identity of a module assigned to the node. +type DeclaredIdentity struct { + Gid int64 `json:"gid"` + Groups []int64 `json:"groups,omitempty"` + Module string `json:"module"` + Name string `json:"name"` + Runner string `json:"runner,omitempty"` + Uid int64 `json:"uid"` +} + +// DeclaredLibrary is a library the node is reached by. +type DeclaredLibrary struct { + Gid int64 `json:"gid"` + Group string `json:"group"` + Home bool `json:"home"` + Library string `json:"library"` + Paths []string `json:"paths,omitempty"` + Root string `json:"root"` + Runner string `json:"runner,omitempty"` + Taken bool `json:"taken,omitempty"` +} + +// FailedUnit is a unit that failed. +type FailedUnit struct { + Load string `json:"load,omitempty"` + Resource string `json:"resource,omitempty"` + Result string `json:"result,omitempty"` + Scope string `json:"scope"` + // since when + Since string `json:"since"` + Unit string `json:"unit"` +} + // FindingAction is one answer the operator may give from a notification. type FindingAction struct { Arguments map[string]string `json:"arguments,omitempty"` @@ -439,6 +595,15 @@ type FindingSubject struct { Scope string `json:"scope"` } +// LoginFallback is a login that fell back to another node. +type LoginFallback struct { + // when + At string `json:"at"` + From string `json:"from"` + Node string `json:"node"` + Why string `json:"why"` +} + // 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 @@ -482,6 +647,115 @@ const ( ModuleStateHeld = "held" ) +// NetworkHealth is the node's network. +type NetworkHealth struct { + Parts []NetworkPart `json:"parts"` + // since when + Since string `json:"since"` + // running, degraded or unknown + State string `json:"state"` +} + +// NetworkPart is one part of the node's network. +type NetworkPart struct { + Owner string `json:"owner,omitempty"` + Part string `json:"part"` + Reason string `json:"reason,omitempty"` + Said string `json:"said,omitempty"` + // since when + Since string `json:"since"` + State string `json:"state"` + Streak int64 `json:"streak,omitempty"` + Targets []string `json:"targets,omitempty"` + Toward []string `json:"toward,omitempty"` + Writer string `json:"writer,omitempty"` +} + +// NodeAccount is an account the node needs to log in again. +type NodeAccount struct { + Account string `json:"account"` + Module string `json:"module,omitempty"` + Reason string `json:"reason"` + Resource string `json:"resource,omitempty"` + // since when + Since string `json:"since"` +} + +// NodeDirectory is a directory found where the node expected none. +type NodeDirectory struct { + Module string `json:"module"` + Path string `json:"path"` + Reason string `json:"reason"` + Resource string `json:"resource"` + // since when + Since string `json:"since"` +} + +// NodeOwn is what a node's own composition carries beside its resources. +type NodeOwn struct { + Adoption *Adoption `json:"adoption,omitempty"` + Groups []DeclaredGroup `json:"groups,omitempty"` + Identities []DeclaredIdentity `json:"identities,omitempty"` + Libraries []DeclaredLibrary `json:"libraries,omitempty"` + // the account the provisioner's client runs as on the node + ProvisionerAccount string `json:"provisioner_account,omitempty"` +} + +// NodeRecord is what the machine observes of itself, for every node and composing part to read. +type NodeRecord struct { + Authority string `json:"authority,omitempty"` + Capabilities []string `json:"capabilities,omitempty"` + Certificate string `json:"certificate,omitempty"` + Contract int64 `json:"contract,omitempty"` + ManifestReads []string `json:"manifest-reads,omitempty"` + Networks []AttachedNetwork `json:"networks,omitempty"` + NetworksSaid bool `json:"networks-said,omitempty"` + Node string `json:"node"` + OutwardLinks []string `json:"outward-links,omitempty"` + SealingKey string `json:"sealing-key,omitempty"` + ServingKey string `json:"serving-key,omitempty"` + Trusts []string `json:"trusts,omitempty"` + TunnelKey string `json:"tunnel-key,omitempty"` + V int64 `json:"v"` +} + +// PeerSeen is a tunnel peer and whether it is seen. +type PeerSeen struct { + Addresses []string `json:"addresses,omitempty"` + Key string `json:"key"` + Seen bool `json:"seen"` +} + +// PlacedDataItem is one data item a module declares, as placed on a node. +type PlacedDataItem struct { + Class string `json:"class"` + // the path whose snapshot keeps it, empty when it is not backed up + CoveredBy string `json:"covered_by,omitempty"` + Item string `json:"item"` + // walk, dataset or shallow + Measure string `json:"measure,omitempty"` + Module string `json:"module"` + // the item's place on the node + Path string `json:"path"` + // backup, redundancy, both or none + Protection string `json:"protection,omitempty"` +} + +// PullWait is the pull from a store a failed apply waits on. +type PullWait struct { + // when the pull began + Began string `json:"began"` + // that seat's holder, whose settling the apply waits for + Holder string `json:"holder"` + Image string `json:"image"` + // what triggered the last try again, in words + Retried string `json:"retried,omitempty"` + // when the apply was last tried again + RetriedAt string `json:"retried-at,omitempty"` + // the seat the pull came through + Seat string `json:"seat,omitempty"` +} + // Revision is one revision of a composition key. type Revision struct { // its digest @@ -498,6 +772,17 @@ type Revision struct { Revision int64 `json:"revision,omitempty"` } +// RootAccountVerdict is the engine's verdict of whether an account's resource is root-free, as the node's health lists it. +type RootAccountVerdict struct { + Account string `json:"account"` + Module string `json:"module"` + Reason string `json:"reason,omitempty"` + Resource string `json:"resource"` + // since when + Since string `json:"since"` + State string `json:"state"` +} + // RootVerdict is the engine's root-free verdict (ADR 0563 §6). type RootVerdict struct { // the machine is root-free @@ -518,6 +803,15 @@ type Said struct { Said string `json:"said"` } +// SealedBox is a value sealed to a key. +type SealedBox struct { + Ct string `json:"ct"` + Eph string `json:"eph"` + Iv string `json:"iv"` + Tag string `json:"tag"` + V int64 `json:"v"` +} + // SeatListStatus is a type of the contract. type SeatListStatus struct { // the seat mesh-catalog has answered @@ -554,6 +848,37 @@ const ( SeatListHeldSourceBootstrap = "bootstrap" ) +// UnitsHealth is the node's units. +type UnitsHealth struct { + Away []string `json:"away,omitempty"` + Failed []FailedUnit `json:"failed"` + State string `json:"state"` + Unread []string `json:"unread,omitempty"` +} + +// UserListWritten is the bus's user list as the engine of the bus node wrote it to the bus module's file. +type UserListWritten struct { + // when + At string `json:"at"` + // the digest of the file on the machine after the write + Digest string `json:"digest,omitempty"` + // why a write failed + Error string `json:"error,omitempty"` + // which list the node follows now + Follows string `json:"follows,omitempty"` + Handed *AccountsHanded `json:"handed,omitempty"` + // the bus node's sealing key, its public half + Key string `json:"key,omitempty"` + Revision int64 `json:"revision"` + // the key the list was taken from + Source string `json:"source"` + // why the bus node does not watch the grants part's key now + Unwatched string `json:"unwatched,omitempty"` + Users int64 `json:"users"` + // the digest of the list the source holds, sha256: + Wanted string `json:"wanted"` +} + // HandOverInput is what hand-over takes. type HandOverInput struct { // the directory's absolute path, as the engine states it @@ -905,75 +1230,119 @@ type ManifestReadsChangedEvent = ManifestReadsChanged // HaveByNodeModuleValue is one value of the state have under the key .. type HaveByNodeModuleValue struct { - Build string `json:"build,omitempty"` - BuildAlone bool `json:"build-alone,omitempty"` - // the composition's digest + Build string `json:"build,omitempty"` + // the move changes the build alone + BuildAlone bool `json:"build-alone,omitempty"` + // the digest of the composition the node runs Composed string `json:"composed,omitempty"` Detail string `json:"detail,omitempty"` // the fingerprint held - Fp string `json:"fp,omitempty"` - // the accounts handed - Handed map[string]any `json:"handed,omitempty"` - // how it is - Health string `json:"health"` - Judgement string `json:"judgement,omitempty"` - Kept []string `json:"kept,omitempty"` + Fp string `json:"fp,omitempty"` + Handed *AccountsHanded `json:"handed,omitempty"` + // what the module's own check says + Health string `json:"health"` + // in phase waiting-yes, the judgement of the move + Judgement string `json:"judgement,omitempty"` + // what the node keeps of the module as it runs + Kept []string `json:"kept,omitempty"` // how far it has come - Phase string `json:"phase"` - // a pull the engine waits for - PullWait map[string]any `json:"pull-wait,omitempty"` - Sealed []string `json:"sealed,omitempty"` - SealedAt map[string]any `json:"sealed-at,omitempty"` - // the user list written - UserList map[string]any `json:"user-list,omitempty"` - V int64 `json:"v"` - // what it waits for + Phase string `json:"phase"` + PullWait *PullWait `json:"pull-wait,omitempty"` + // each value sealed to the node's key + Sealed []string `json:"sealed,omitempty"` + // the sealed values by slot + SealedAt map[string]string `json:"sealed-at,omitempty"` + UserList *UserListWritten `json:"user-list,omitempty"` + V int64 `json:"v"` + // in phase waiting-yes, the digest of the composition that waits Waits string `json:"waits,omitempty"` } +// The values of HaveByNodeModuleValue's health. +const ( + HaveByNodeModuleValueHealthOk = "ok" + HaveByNodeModuleValueHealthFailed = "failed" + HaveByNodeModuleValueHealthWaits = "waits" + HaveByNodeModuleValueHealthUnknown = "unknown" +) + +// The values of HaveByNodeModuleValue's judgement. +const ( + HaveByNodeModuleValueJudgementRecreates = "recreates" + HaveByNodeModuleValueJudgementMovesData = "moves-data" + HaveByNodeModuleValueJudgementCannotJudge = "cannot-judge" +) + +// The values of HaveByNodeModuleValue's phase. +const ( + HaveByNodeModuleValuePhaseBackingUp = "backing-up" + HaveByNodeModuleValuePhaseApplying = "applying" + HaveByNodeModuleValuePhaseChecking = "checking" + HaveByNodeModuleValuePhaseRunning = "running" + HaveByNodeModuleValuePhaseFailed = "failed" + HaveByNodeModuleValuePhaseRemoved = "removed" + HaveByNodeModuleValuePhaseHeld = "held" + HaveByNodeModuleValuePhaseWaitingYes = "waiting-yes" +) + // HaveByNodeNodeValue is one value of the state have under the key .node. type HaveByNodeNodeValue struct { - Busy map[string]any `json:"busy,omitempty"` - Candidates map[string]map[string]any `json:"candidates,omitempty"` - Composed string `json:"composed,omitempty"` - // own, or the controller's - Composes string `json:"composes,omitempty"` - ComposesWhy string `json:"composes-why,omitempty"` - Filters map[string]any `json:"filters,omitempty"` - Profile map[string]any `json:"profile,omitempty"` - // the node's record: its tunnel and certificate - Record map[string]any `json:"record,omitempty"` - Sealed []string `json:"sealed,omitempty"` - SealedAt map[string]any `json:"sealed-at,omitempty"` - Strays map[string]any `json:"strays,omitempty"` - V int64 `json:"v"` - Windows map[string]any `json:"windows,omitempty"` + Busy map[string]any `json:"busy,omitempty"` + // the node's answer to each placement candidate naming it, by proposal id + Candidates map[string]CandidateAnswer `json:"candidates,omitempty"` + // the digest of the node's own composition it runs + Composed string `json:"composed,omitempty"` + // own, or none (empty) + Composes string `json:"composes,omitempty"` + ComposesWhy string `json:"composes-why,omitempty"` + Filters map[string]any `json:"filters,omitempty"` + Profile map[string]any `json:"profile,omitempty"` + Record *NodeRecord `json:"record,omitempty"` + Sealed []string `json:"sealed,omitempty"` + SealedAt map[string]string `json:"sealed-at,omitempty"` + Strays map[string]any `json:"strays,omitempty"` + V int64 `json:"v"` + Windows map[string]any `json:"windows,omitempty"` } // ComposedByNodeModuleFingerprintValue is one value of the state composed under the key ... type ComposedByNodeModuleFingerprintValue struct { - Data bool `json:"Data,omitempty"` - FP string `json:"FP"` - Filled []string `json:"Filled,omitempty"` - Module string `json:"Module"` - Node string `json:"Node"` - // the node's own adoption, identities, groups, libraries and provisioner account - Own map[string]any `json:"Own,omitempty"` - // the module's plan - Plan map[string]any `json:"Plan,omitempty"` - // why the composition was refused - Refused string `json:"Refused,omitempty"` - Resources []map[string]any `json:"Resources,omitempty"` - V int64 `json:"V,omitempty"` + // the module declares data + Data bool `json:"data,omitempty"` + // the files the provisioner fills + FilledFiles []string `json:"filled_files,omitempty"` + Fp string `json:"fp"` + Module string `json:"module"` + Node string `json:"node"` + // the module's whole provisioning plan, catalogue.Plan, kept whole: it is the manifest layer's type + Plan map[string]any `json:"plan,omitempty"` + // why the module cannot be composed at this fingerprint + Refused string `json:"refused,omitempty"` + // the declaration resources, each saying its module + Resources []map[string]any `json:"resources"` + V int64 `json:"v"` +} + +// ComposedByNodeNodeValue is one value of the state composed under the key .node. +type ComposedByNodeNodeValue struct { + // the module declares data + Data bool `json:"data,omitempty"` + // the files the provisioner fills + FilledFiles []string `json:"filled_files,omitempty"` + Node string `json:"node"` + Own *NodeOwn `json:"own,omitempty"` + // the declaration resources, each saying its module + Resources []map[string]any `json:"resources"` + V int64 `json:"v"` } // DataByNodeModuleValue is one value of the state data under the key .. type DataByNodeModuleValue struct { Digest string `json:"digest"` - Items []map[string]any `json:"items"` + Items []PlacedDataItem `json:"items"` Module string `json:"module"` Node string `json:"node"` - V int64 `json:"v,omitempty"` + V int64 `json:"v"` } // PackagesByNodeValuePackagesItem is a type of the contract. @@ -991,17 +1360,18 @@ type PackagesByNodeValue struct { // HealthByNodeValue is one value of the state health under the key . type HealthByNodeValue struct { - At string `json:"at"` - Contract int64 `json:"contract,omitempty"` - Fallback map[string]any `json:"fallback,omitempty"` - Found []map[string]any `json:"found,omitempty"` - Network map[string]any `json:"network,omitempty"` - Node string `json:"node"` - Peers []map[string]any `json:"peers,omitempty"` - Relogin []map[string]any `json:"relogin,omitempty"` - Root []map[string]any `json:"root,omitempty"` - Units map[string]any `json:"units,omitempty"` - V int64 `json:"v"` + // when + At string `json:"at"` + Contract int64 `json:"contract,omitempty"` + Fallback *LoginFallback `json:"fallback,omitempty"` + Found []NodeDirectory `json:"found,omitempty"` + Network *NetworkHealth `json:"network,omitempty"` + Node string `json:"node"` + Peers []PeerSeen `json:"peers,omitempty"` + Relogin []NodeAccount `json:"relogin,omitempty"` + Root []RootAccountVerdict `json:"root,omitempty"` + Units *UnitsHealth `json:"units,omitempty"` + V int64 `json:"v"` } // FindingsByKeyValue is one value of the state findings under the key . diff --git a/go/seats/nodeengine/contract_test.go b/go/seats/nodeengine/contract_test.go index aa467b2..5ca811b 100644 --- a/go/seats/nodeengine/contract_test.go +++ b/go/seats/nodeengine/contract_test.go @@ -39,6 +39,44 @@ func TestFixtures(t *testing.T) { decode: func(b []byte) (any, error) { var v FindingsByKeyValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil}, {name: "a node's packages, present and absent (captured from ace, shortened)", subject: "", given: "{\"node\":\"ace\",\"packages\":[{\"absent\":false,\"modules\":[\"avahi\"],\"package\":\"avahi\"},{\"absent\":false,\"modules\":[\"mesh-build-go\",\"mesh-build-node\"],\"package\":\"bubblewrap\"},{\"absent\":true,\"modules\":[\"time-sync\"],\"package\":\"ntp\"}]}", wire: "{\"node\":\"ace\",\"packages\":[{\"absent\":false,\"modules\":[\"avahi\"],\"package\":\"avahi\"},{\"absent\":false,\"modules\":[\"mesh-build-go\",\"mesh-build-node\"],\"package\":\"bubblewrap\"},{\"absent\":true,\"modules\":[\"time-sync\"],\"package\":\"ntp\"}]}", decode: func(b []byte) (any, error) { var v PackagesByNodeValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil}, + {name: "buildah composed on ace (resources captured from the declaration of 2026-10-24; the fingerprint stands in for the indexer's)", subject: "", given: "{\"fp\":\"src2:62756c6c6462756c6c6462756c6c6462756c6c6462756c6c6462756c6c646275\",\"module\":\"buildah\",\"node\":\"ace\",\"resources\":[{\"id\":\"buildah.buildah\",\"module\":\"buildah\",\"package\":\"buildah\",\"type\":\"package\"}],\"v\":1}", wire: "{\"fp\":\"src2:62756c6c6462756c6c6462756c6c6462756c6c6462756c6c6462756c6c646275\",\"module\":\"buildah\",\"node\":\"ace\",\"resources\":[{\"id\":\"buildah.buildah\",\"module\":\"buildah\",\"package\":\"buildah\",\"type\":\"package\"}],\"v\":1}", + decode: func(b []byte) (any, error) { + var v ComposedByNodeModuleFingerprintValue + err := json.Unmarshal(b, &v) + return v, err + }, subjectIs: nil}, + {name: "node-tools composed with a filled file (captured; the fingerprint stands in)", subject: "", given: "{\"fp\":\"src2:6e6f64652d6e6f64652d6e6f64652d6e6f64652d6e6f64652d6e6f64652d6e6f\",\"module\":\"node-tools\",\"node\":\"ace\",\"resources\":[{\"id\":\"node-tools.mesh-state\",\"mode\":\"0755\",\"module\":\"node-tools\",\"path\":\"/var/lib/mesh/node-tools\",\"type\":\"directory\"},{\"id\":\"node-tools.interpreter\",\"module\":\"node-tools\",\"package\":\"nodejs\",\"type\":\"package\"},{\"digest\":\"sha256:ef790876783d817903b337b8418b1eb21fb49d34128a390bbf7c3d08a4b2be8a\",\"env\":{\"MESH_AGENT_ACCOUNT\":\"ace\",\"MESH_AGENT_HOME\":\"/home/ace\",\"MESH_BROKER_FILE\":\"/var/lib/mesh/node-tools/broker\",\"MESH_OPERATOR_ACCOUNT\":\"ace\",\"MESH_OPERATOR_HOME\":\"/home/ace\",\"MESH_TOOL_ENV\":\"{\\\"baserow\\\":{\\\"MESH_BASEROW_CONFIG_FILE\\\":\\\"/var/lib/mesh/baserow/config.json\\\",\\\"MESH_BASEROW_URL\\\":\\\"http://127.0.0.1:20011\\\"},\\\"bazarr\\\":{\\\"MESH_BAZARR_CONFIG_DIR\\\":\\\"/var/lib/bazarr/config\\\",\\\"MESH_BAZARR_CONFIG_FILE\\\":\\\"/var/lib/bazarr/config.json\\\",\\\"MESH_BAZARR_CONTAINER\\\":\\\"bazarr\\\",\\\"MESH_BAZARR_URL\\\":\\\"http://127.0.0.1:20020\\\"},\\\"bookshelf\\\":{\\\"MESH_BOOKSHELF_CONFIG_DIR\\\":\\\"/var/lib/bookshelf/config\\\",\\\"MESH_BOOKSHELF_URL\\\":\\\"http://127.0.0.1:20021\\\"},\\\"claude-code\\\":{\\\"MESH_CLAUDE_CODE_FACTS\\\":\\\"/var/lib/claude-code/facts.json\\\",\\\"MESH_CLAUDE_CODE_SETTINGS\\\":\\\"/var/lib/claude-code/settings.json\\\",\\\"MESH_CLAUDE_CODE_STATE\\\":\\\"/var/lib/claude-code\\\"},\\\"grafana\\\":{\\\"MESH_GRAFANA_CONFIG_FILE\\\":\\\"/var/lib/mesh/grafana/config.json\\\",\\\"MESH_GRAFANA_URL\\\":\\\"http://127.0.0.1:20012\\\"},\\\"home-assistant\\\":{\\\"MESH_HOMEASSISTANT_CONFIG_FILE\\\":\\\"/var/lib/mesh/home-assistant/config.json\\\",\\\"MESH_HOMEASSISTANT_TOKEN_FILE\\\":\\\"/var/lib/mesh/home-assistant/token\\\",\\\"MESH_HOMEASSISTANT_URL\\\":\\\"http://127.0.0.1:8123\\\"},\\\"icecast\\\":{\\\"MESH_ICECAST_ADMIN_PASSWORD_FILE\\\":\\\"/var/lib/icecast/admin.secret\\\",\\\"MESH_ICECAST_CONFIG_FILE\\\":\\\"/var/lib/mesh/icecast/config.json\\\",\\\"MESH_ICECAST_URL\\\":\\\"http://127.0.0.1:20005\\\"},\\\"influxdb\\\":{\\\"MESH_INFLUXDB_CONFIG_FILE\\\":\\\"/var/lib/mesh/influxdb/config.json\\\",\\\"MESH_INFLUXDB_TOKEN_FILE\\\":\\\"/var/lib/influxdb/admin-token.secret\\\",\\\"MESH_INFLUXDB_URL\\\":\\\"http://127.0.0.1:8086\\\",\\\"MESH_RECEIVES\\\":\\\"/var/lib/influxdb/grants/mesh.json\\\"},\\\"jackett\\\":{\\\"MESH_JACKETT_API_KEY_FILE\\\":\\\"/var/lib/mesh/jackett/api-key\\\",\\\"MESH_JACKETT_CONFIG_DIR\\\":\\\"/var/lib/jackett/config\\\",\\\"MESH_JACKETT_CONFIG_FILE\\\":\\\"/var/lib/jackett/config.json\\\",\\\"MESH_JACKETT_URL\\\":\\\"http://127.0.0.1:20004\\\"},\\\"kometa\\\":{\\\"MESH_KOMETA_CONFIG_DIR\\\":\\\"/var/lib/kometa/config\\\",\\\"MESH_KOMETA_CONFIG_FILE\\\":\\\"/var/lib/kometa/config.yml\\\"},\\\"letta\\\":{\\\"MESH_LETTA_CONFIG_FILE\\\":\\\"/var/lib/mesh/letta/config.json\\\",\\\"MESH_LETTA_URL\\\":\\\"http://127.0.0.1:20002\\\"},\\\"lidarr\\\":{\\\"MESH_LIDARR_API_KEY_FILE\\\":\\\"/var/lib/mesh/lidarr/api-key\\\",\\\"MESH_LIDARR_CONFIG_DIR\\\":\\\"/var/lib/lidarr/config\\\",\\\"MESH_LIDARR_RECYCLE_BIN\\\":\\\"/recycle/\\\",\\\"MESH_LIDARR_RECYCLE_DAYS\\\":\\\"7\\\",\\\"MESH_LIDARR_URL\\\":\\\"http://127.0.0.1:20022\\\",\\\"MESH_PLEX_BIND_FILE\\\":\\\"/var/lib/lidarr/plex-api.json\\\",\\\"MESH_PLEX_TOKEN_FILE\\\":\\\"/var/lib/lidarr/plex-api.secret\\\"},\\\"media-importer\\\":{\\\"MESH_MEDIA_IMPORTER_SETTINGS\\\":\\\"/var/lib/media-importer/settings.json\\\",\\\"MESH_MEDIA_IMPORTER_STATE\\\":\\\"/var/lib/media-importer\\\",\\\"MESH_MEDIA_LIBRARY\\\":\\\"/storage/media/poc\\\"},\\\"mesh-files\\\":{\\\"MESH_FILES_STATE\\\":\\\"/var/lib/mesh-files\\\",\\\"MESH_OWNED_PATHS\\\":\\\"{\\\\\\\"/etc/NetworkManager/conf.d/50-mesh.conf\\\\\\\":\\\\\\\"networkmanager (its own tools)\\\\\\\",\\\\\\\"/etc/claude-code\\\\\\\":\\\\\\\"claude-code (its own tools)\\\\\\\",\\\\\\\"/etc/claude-code/agent-account\\\\\\\":\\\\\\\"claude-code (its own tools)\\\\\\\",\\\\\\\"/etc/dnsmasq.conf\\\\\\\":\\\\\\\"dnsmasq (its own tools)\\\\\\\",\\\\\\\"/etc/docker/daemon.json\\\\\\\":\\\\\\\"docker (its own tools)\\\\\\\",\\\\\\\"/etc/fail2ban/action.d\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/etc/fail2ban/action.d/iptables-allports-dualchain.conf\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/etc/fail2ban/filter.d\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/etc/fail2ban/jail.d\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/etc/fail2ban/jail.d/recidive.conf\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/etc/fail2ban/jail.d/sshd.conf\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/etc/fail2ban/jail.local\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/etc/fail2ban/mesh-own-addresses.json\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/etc/hostname\\\\\\\":\\\\\\\"hostname (node-hostname)\\\\\\\",\\\\\\\"/etc/hosts\\\\\\\":\\\\\\\"hostname (node-hostname)\\\\\\\",\\\\\\\"/etc/locale.conf\\\\\\\":\\\\\\\"localization (its own tools)\\\\\\\",\\\\\\\"/etc/logrotate.conf\\\\\\\":\\\\\\\"logrotate (its own tools)\\\\\\\",\\\\\\\"/etc/logrotate.d/fail2ban\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-power\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-power/moments\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-power/moments/after-boot\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-power/moments/after-wake\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-power/moments/before-shutdown\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-power/moments/before-sleep\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-power/moments/on-battery\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-power/moments/on-mains\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-resolver/nodes.conf\\\\\\\":\\\\\\\"dnsmasq (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-resolver/zones.conf\\\\\\\":\\\\\\\"dnsmasq (its own tools)\\\\\\\",\\\\\\\"/etc/nfs-server\\\\\\\":\\\\\\\"nfs-server (its own tools)\\\\\\\",\\\\\\\"/etc/nfs-server/machines\\\\\\\":\\\\\\\"nfs-server (its own tools)\\\\\\\",\\\\\\\"/etc/nfs-server/shares.conf\\\\\\\":\\\\\\\"nfs-server (its own tools)\\\\\\\",\\\\\\\"/etc/nfs.conf.d/50-mesh.conf\\\\\\\":\\\\\\\"nfs-server (its own tools)\\\\\\\",\\\\\\\"/etc/pacman.conf\\\\\\\":\\\\\\\"pacman (its own tools)\\\\\\\",\\\\\\\"/etc/pacman.d/hooks/90-mesh-kernel-boot-entries.hook\\\\\\\":\\\\\\\"kernel (its own tools)\\\\\\\",\\\\\\\"/etc/resolv.conf\\\\\\\":\\\\\\\"networkmanager (node-uplink)\\\\\\\",\\\\\\\"/etc/ssh/sshd_config.d/10-mesh.conf\\\\\\\":\\\\\\\"sshd (its own tools)\\\\\\\",\\\\\\\"/etc/sudoers.d/10-mesh-operator\\\\\\\":\\\\\\\"sudo (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/logind.conf.d\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/logind.conf.d/power.conf\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/dnsmasq.service.d\\\\\\\":\\\\\\\"dnsmasq (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/dnsmasq.service.d/mesh.conf\\\\\\\":\\\\\\\"dnsmasq (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/docker-prune.service\\\\\\\":\\\\\\\"docker (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/docker-prune.timer\\\\\\\":\\\\\\\"docker (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/hibernate.target.d\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/hibernate.target.d/mesh-power.conf\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/hybrid-sleep.target.d\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/hybrid-sleep.target.d/mesh-power.conf\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/mesh-ca-trust.service\\\\\\\":\\\\\\\"ca-trust (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/mesh-filter.service\\\\\\\":\\\\\\\"nftables (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/mesh-kernel-boot-entries.service\\\\\\\":\\\\\\\"kernel (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/mesh-kernel-huge-pages.service\\\\\\\":\\\\\\\"kernel (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/mesh-power-after-boot.service\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/mesh-power-after-wake.service\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/mesh-power-before-shutdown.service\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/mesh-power-before-sleep.service\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/mesh-power-supply.service\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/nftables.service.d/mesh.conf\\\\\\\":\\\\\\\"nftables (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/sleep.target.d\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/sleep.target.d/mesh-power.conf\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/suspend-then-hibernate.target.d\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/suspend-then-hibernate.target.d/mesh-power.conf\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/suspend.target.d\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/suspend.target.d/mesh-power.conf\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/timesyncd.conf.d/50-mesh.conf\\\\\\\":\\\\\\\"time-sync (its own tools)\\\\\\\",\\\\\\\"/etc/udev/rules.d/90-mesh-power.rules\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/vconsole.conf\\\\\\\":\\\\\\\"localization (its own tools)\\\\\\\",\\\\\\\"/etc/xdg/reflector/reflector.conf\\\\\\\":\\\\\\\"pacman (its own tools)\\\\\\\",\\\\\\\"/run/mesh-power\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/run/nfs-server\\\\\\\":\\\\\\\"nfs-server (its own tools)\\\\\\\",\\\\\\\"/usr/lib/nox-mesh-host/launch\\\\\\\":\\\\\\\"mesh-host (its own tools)\\\\\\\",\\\\\\\"/usr/lib/nox-mesh-host/versions/4e121b6e88f1\\\\\\\":\\\\\\\"mesh-host (its own tools)\\\\\\\",\\\\\\\"/usr/local/bin\\\\\\\":\\\\\\\"mesh-cli (its own tools)\\\\\\\",\\\\\\\"/usr/local/bin/claude-agent\\\\\\\":\\\\\\\"claude-code (its own tools)\\\\\\\",\\\\\\\"/usr/local/lib/mesh-power\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/var/lib/dnsmasq-watch\\\\\\\":\\\\\\\"dnsmasq (its own tools)\\\\\\\",\\\\\\\"/var/lib/mesh-fail2ban\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/var/lib/mesh-fail2ban/own-addresses\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/var/lib/mesh-store\\\\\\\":\\\\\\\"postgres (its own tools)\\\\\\\",\\\\\\\"/var/lib/nfs-server\\\\\\\":\\\\\\\"nfs-server (its own tools)\\\\\\\",\\\\\\\"/var/lib/nfs-server/from-bus\\\\\\\":\\\\\\\"nfs-server (its own tools)\\\\\\\",\\\\\\\"/var/lib/postgres\\\\\\\":\\\\\\\"postgres (its own tools)\\\\\\\",\\\\\\\"/var/lib/route-proxy/acme\\\\\\\":\\\\\\\"route-proxy (its own tools)\\\\\\\",\\\\\\\"/var/lib/route-proxy/ca\\\\\\\":\\\\\\\"route-proxy (its own tools)\\\\\\\",\\\\\\\"/var/lib/route-proxy/routes\\\\\\\":\\\\\\\"route-proxy (its own tools)\\\\\\\",\\\\\\\"/var/lib/systemd\\\\\\\":\\\\\\\"systemd (its own tools)\\\\\\\",\\\\\\\"/var/log/fail2ban.log\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/var/run/fail2ban\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\"}\\\",\\\"MESH_SECRET_PLACES\\\":\\\"[\\\\\\\"/var/lib/baserow\\\\\\\",\\\\\\\"/var/lib/bazarr/radarr-api.secret\\\\\\\",\\\\\\\"/var/lib/bazarr/sonarr-api.secret\\\\\\\",\\\\\\\"/var/lib/bookshelf/jackett-api.secret\\\\\\\",\\\\\\\"/var/lib/bookshelf/nzbget-api.secret\\\\\\\",\\\\\\\"/var/lib/bookshelf/qbittorrent-api.secret\\\\\\\",\\\\\\\"/var/lib/car-hunter\\\\\\\",\\\\\\\"/var/lib/claude-code/gitea-read.secret\\\\\\\",\\\\\\\"/var/lib/home-assistant/lidarr-api.secret\\\\\\\",\\\\\\\"/var/lib/home-assistant/mqtt-topic.secret\\\\\\\",\\\\\\\"/var/lib/home-assistant/radarr-api.secret\\\\\\\",\\\\\\\"/var/lib/home-assistant/sonarr-api.secret\\\\\\\",\\\\\\\"/var/lib/icecast/admin.secret\\\\\\\",\\\\\\\"/var/lib/icecast/relay.secret\\\\\\\",\\\\\\\"/var/lib/icecast/source.secret\\\\\\\",\\\\\\\"/var/lib/influxdb\\\\\\\",\\\\\\\"/var/lib/kometa\\\\\\\",\\\\\\\"/var/lib/letta\\\\\\\",\\\\\\\"/var/lib/lidarr/jackett-api.secret\\\\\\\",\\\\\\\"/var/lib/lidarr/nzbget-api.secret\\\\\\\",\\\\\\\"/var/lib/lidarr/plex-api.secret\\\\\\\",\\\\\\\"/var/lib/lidarr/qbittorrent-api.secret\\\\\\\",\\\\\\\"/var/lib/mesh-delivery-assigner\\\\\\\",\\\\\\\"/var/lib/mesh-provisioner-client\\\\\\\",\\\\\\\"/var/lib/mesh-reads\\\\\\\",\\\\\\\"/var/lib/mesh/dnsmasq\\\\\\\",\\\\\\\"/var/lib/mesh/grafana\\\\\\\",\\\\\\\"/var/lib/mesh/home-assistant\\\\\\\",\\\\\\\"/var/lib/mesh/jackett\\\\\\\",\\\\\\\"/var/lib/mesh/lidarr\\\\\\\",\\\\\\\"/var/lib/mesh/mosquitto\\\\\\\",\\\\\\\"/var/lib/mesh/node-tools\\\\\\\",\\\\\\\"/var/lib/mesh/nodered\\\\\\\",\\\\\\\"/var/lib/mesh/nzbget\\\\\\\",\\\\\\\"/var/lib/mesh/ombi\\\\\\\",\\\\\\\"/var/lib/mesh/pacman\\\\\\\",\\\\\\\"/var/lib/mesh/postgres\\\\\\\",\\\\\\\"/var/lib/mesh/qbittorrent\\\\\\\",\\\\\\\"/var/lib/mesh/radarr\\\\\\\",\\\\\\\"/var/lib/mesh/route-proxy\\\\\\\",\\\\\\\"/var/lib/mesh/searxng\\\\\\\",\\\\\\\"/var/lib/mesh/sonarr\\\\\\\",\\\\\\\"/var/lib/mesh/unifi\\\\\\\",\\\\\\\"/var/lib/mosquitto/grants\\\\\\\",\\\\\\\"/var/lib/mssql\\\\\\\",\\\\\\\"/var/lib/n8n/database.secret\\\\\\\",\\\\\\\"/var/lib/nodered/mqtt-topic.secret\\\\\\\",\\\\\\\"/var/lib/nzbgeek\\\\\\\",\\\\\\\"/var/lib/ombi/lidarr-api.secret\\\\\\\",\\\\\\\"/var/lib/ombi/plex-api.secret\\\\\\\",\\\\\\\"/var/lib/ombi/radarr-api.secret\\\\\\\",\\\\\\\"/var/lib/ombi/sonarr-api.secret\\\\\\\",\\\\\\\"/var/lib/radarr/jackett-api.secret\\\\\\\",\\\\\\\"/var/lib/radarr/nzbget-api.secret\\\\\\\",\\\\\\\"/var/lib/radarr/plex-api.secret\\\\\\\",\\\\\\\"/var/lib/radarr/qbittorrent-api.secret\\\\\\\",\\\\\\\"/var/lib/redis/default.secret\\\\\\\",\\\\\\\"/var/lib/redis/grants\\\\\\\",\\\\\\\"/var/lib/restic\\\\\\\",\\\\\\\"/var/lib/sonarr/jackett-api.secret\\\\\\\",\\\\\\\"/var/lib/sonarr/nzbget-api.secret\\\\\\\",\\\\\\\"/var/lib/sonarr/plex-api.secret\\\\\\\",\\\\\\\"/var/lib/sonarr/qbittorrent-api.secret\\\\\\\",\\\\\\\"/var/lib/supabase\\\\\\\",\\\\\\\"/var/lib/tautulli/plex-api.secret\\\\\\\",\\\\\\\"/var/lib/txt-game/database.secret\\\\\\\"]\\\"},\\\"mosquitto\\\":{\\\"MESH_MQTT_CTRL_CONTAINER\\\":\\\"mosquitto\\\",\\\"MESH_PROVISION_ADMIN_USER\\\":\\\"mesh-admin\\\",\\\"MESH_PROVISION_MQTT\\\":\\\"127.0.0.1:1883\\\",\\\"MESH_PROVISION_PASSWORD_FILE\\\":\\\"/var/lib/mesh/mosquitto/admin\\\",\\\"MESH_RECEIVES\\\":\\\"/var/lib/mosquitto/grants/mesh.json\\\"},\\\"mssql\\\":{\\\"MESH_MSSQL_READER_PASSWORD_FILE\\\":\\\"/var/lib/mssql/reader.secret\\\",\\\"MESH_PROVISION_MSSQL\\\":\\\"mssql://sa@127.0.0.1:4848/master\\\",\\\"MESH_PROVISION_PASSWORD_FILE\\\":\\\"/var/lib/mssql/sa.secret\\\",\\\"MESH_RECEIVES\\\":\\\"/var/lib/mssql/grants/mesh.json\\\"},\\\"nodered\\\":{\\\"MESH_NODERED_CONFIG_FILE\\\":\\\"/var/lib/mesh/nodered/config.json\\\",\\\"MESH_NODERED_URL\\\":\\\"http://127.0.0.1:20006\\\"},\\\"nzbgeek\\\":{\\\"MESH_NZBGEEK_KEY_FILE\\\":\\\"/var/lib/nzbgeek/api-key.secret\\\",\\\"MESH_NZBGEEK_SETTINGS\\\":\\\"/var/lib/nzbgeek/settings.json\\\"},\\\"nzbget\\\":{\\\"MESH_NZBGET_CONFIG_DIR\\\":\\\"/var/lib/nzbget/config\\\",\\\"MESH_NZBGET_CONFIG_FILE\\\":\\\"/var/lib/nzbget/config.json\\\",\\\"MESH_NZBGET_PASSWORD_FILE\\\":\\\"/var/lib/mesh/nzbget/password\\\",\\\"MESH_NZBGET_SECRETS_DIR\\\":\\\"/var/lib/mesh/nzbget\\\",\\\"MESH_NZBGET_STATE\\\":\\\"/var/lib/nzbget\\\",\\\"MESH_NZBGET_URL\\\":\\\"http://127.0.0.1:20017\\\"},\\\"ombi\\\":{\\\"MESH_OMBI_API_KEY_FILE\\\":\\\"/var/lib/mesh/ombi/api-key\\\",\\\"MESH_OMBI_CONFIG_FILE\\\":\\\"/var/lib/mesh/ombi/config.json\\\",\\\"MESH_OMBI_URL\\\":\\\"http://127.0.0.1:20024\\\"},\\\"pacman\\\":{\\\"PACMAN_KEEPER_CONFIG\\\":\\\"/var/lib/mesh/pacman/keeper.json\\\"},\\\"plex\\\":{\\\"MESH_PLEX_CONFIG_DIR\\\":\\\"/mnt/plex/config\\\",\\\"MESH_PLEX_URL\\\":\\\"http://127.0.0.1:32400\\\"},\\\"postgres\\\":{\\\"MESH_CONSUMERS_FILE\\\":\\\"/var/lib/mesh/postgres/consumers.json\\\",\\\"MESH_POSTGRES_READER_PASSWORD_FILE\\\":\\\"/var/lib/mesh/postgres/reader.secret\\\",\\\"MESH_PROVISION_PASSWORD_FILE\\\":\\\"/var/lib/mesh/postgres/superuser.secret\\\",\\\"MESH_PROVISION_POSTGRES\\\":\\\"postgres://postgres@127.0.0.1:5434/postgres?sslmode=disable\\\",\\\"MESH_RECEIVES\\\":\\\"/var/lib/mesh/postgres/grants/mesh.json\\\"},\\\"qbittorrent\\\":{\\\"MESH_QBITTORRENT_CONFIG_DIR\\\":\\\"/var/lib/qbittorrent/config\\\",\\\"MESH_QBITTORRENT_CONFIG_FILE\\\":\\\"/var/lib/qbittorrent/config.json\\\",\\\"MESH_QBITTORRENT_PASSWORD_FILE\\\":\\\"/var/lib/mesh/qbittorrent/password\\\",\\\"MESH_QBITTORRENT_URL\\\":\\\"http://127.0.0.1:8112\\\"},\\\"radarr\\\":{\\\"MESH_PLEX_BIND_FILE\\\":\\\"/var/lib/radarr/plex-api.json\\\",\\\"MESH_PLEX_TOKEN_FILE\\\":\\\"/var/lib/radarr/plex-api.secret\\\",\\\"MESH_RADARR_API_KEY_FILE\\\":\\\"/var/lib/mesh/radarr/api-key\\\",\\\"MESH_RADARR_CONFIG_DIR\\\":\\\"/var/lib/radarr/config\\\",\\\"MESH_RADARR_URL\\\":\\\"http://127.0.0.1:20019\\\"},\\\"redis\\\":{\\\"MESH_PROVISION_PASSWORD_FILE\\\":\\\"/var/lib/redis/default.secret\\\",\\\"MESH_PROVISION_REDIS\\\":\\\"127.0.0.1:20003\\\",\\\"MESH_RECEIVES\\\":\\\"/var/lib/redis/grants/mesh.json\\\"},\\\"restic\\\":{\\\"MESH_BACKUP_DATA\\\":\\\"/var/lib/restic/data.conf\\\",\\\"MESH_BACKUP_DECLARED\\\":\\\"/var/lib/restic/backups.conf\\\",\\\"MESH_BACKUP_PASSWORD_FILE\\\":\\\"/var/lib/restic/repository.secret\\\",\\\"MESH_BACKUP_REPOSITORY\\\":\\\"/storage/restic\\\",\\\"MESH_BACKUP_STATE\\\":\\\"/var/lib/restic\\\"},\\\"searxng\\\":{\\\"MESH_SEARXNG_CONFIG_FILE\\\":\\\"/var/lib/mesh/searxng/config.json\\\",\\\"MESH_SEARXNG_URL\\\":\\\"http://127.0.0.1:20000\\\"},\\\"sonarr\\\":{\\\"MESH_PLEX_BIND_FILE\\\":\\\"/var/lib/sonarr/plex-api.json\\\",\\\"MESH_PLEX_TOKEN_FILE\\\":\\\"/var/lib/sonarr/plex-api.secret\\\",\\\"MESH_SONARR_API_KEY_FILE\\\":\\\"/var/lib/mesh/sonarr/api-key\\\",\\\"MESH_SONARR_CONFIG_DIR\\\":\\\"/var/lib/sonarr/config\\\",\\\"MESH_SONARR_RECYCLE_DIR\\\":\\\"/var/lib/sonarr/recycle-bin\\\",\\\"MESH_SONARR_URL\\\":\\\"http://127.0.0.1:20018\\\"},\\\"systemd\\\":{\\\"MESH_SYSTEMD_STATE\\\":\\\"/var/lib/mesh/systemd\\\"},\\\"tautulli\\\":{\\\"MESH_TAUTULLI_CONFIG_DIR\\\":\\\"/var/lib/tautulli/config\\\",\\\"MESH_TAUTULLI_CONFIG_FILE\\\":\\\"/var/lib/mesh/tautulli/config.json\\\",\\\"MESH_TAUTULLI_CONTAINER\\\":\\\"tautulli\\\",\\\"MESH_TAUTULLI_URL\\\":\\\"http://127.0.0.1:20023\\\"},\\\"unifi\\\":{\\\"MESH_UNIFI_CONFIG_FILE\\\":\\\"/var/lib/mesh/unifi/config.json\\\",\\\"MESH_UNIFI_URL\\\":\\\"https://127.0.0.1:8443\\\"},\\\"youtube\\\":{\\\"MESH_YOUTUBE_DOWNLOADS\\\":\\\"/storage/downloads/youtube\\\",\\\"MESH_YOUTUBE_SETTINGS\\\":\\\"/var/lib/youtube/settings.json\\\"},\\\"zsh\\\":{\\\"ZSH_TOOLS_CONF\\\":\\\"/var/lib/zsh/tools.conf\\\"}}\",\"MESH_TOOL_MODULES\":\"avahi=/var/lib/mesh/bundles/avahi/tools/avahi-tools,baserow=/var/lib/mesh/bundles/baserow/tools/tools/index.serve.mjs,bazarr=/var/lib/mesh/bundles/bazarr/code/index.serve.mjs,bazarr=/var/lib/mesh/bundles/bazarr/code/tools/index.serve.mjs,bookshelf=/var/lib/mesh/bundles/bookshelf/code/index.serve.mjs,bookshelf=/var/lib/mesh/bundles/bookshelf/code/tools/index.serve.mjs,claude-code=/var/lib/mesh/bundles/claude-code/tools/claude-code,dbus=/var/lib/mesh/bundles/dbus/tools-go/dbus-tools,disk-load=/var/lib/mesh/bundles/disk-load/tools-go/disk-load,dnsmasq=/var/lib/mesh/bundles/dnsmasq/tools/dnsmasq-tools,docker=/var/lib/mesh/bundles/docker/tools/docker-tools,fail2ban=/var/lib/mesh/bundles/fail2ban/tools/fail2ban-tools,grafana=/var/lib/mesh/bundles/grafana/code/index.serve.mjs,grafana=/var/lib/mesh/bundles/grafana/code/tools/index.serve.mjs,home-assistant=/var/lib/mesh/bundles/home-assistant/code/index.serve.mjs,home-assistant=/var/lib/mesh/bundles/home-assistant/code/tools/index.serve.mjs,hostname=/var/lib/mesh/bundles/hostname/tools/hostname-tools,icecast=/var/lib/mesh/bundles/icecast/code/index.serve.mjs,icecast=/var/lib/mesh/bundles/icecast/code/tools/index.serve.mjs,influxdb=/var/lib/mesh/bundles/influxdb/code/provisioner/index.serve.mjs,influxdb=/var/lib/mesh/bundles/influxdb/code/tools/index.serve.mjs,jackett=/var/lib/mesh/bundles/jackett/code/tools/index.serve.mjs,kernel=/var/lib/mesh/bundles/kernel/tools/kernel-tools,kometa=/var/lib/mesh/bundles/kometa/code/tools/index.serve.mjs,letta=/var/lib/mesh/bundles/letta/tools/tools/index.serve.mjs,lidarr=/var/lib/mesh/bundles/lidarr/tools-go/lidarr,localization=/var/lib/mesh/bundles/localization/tools/localization-tools,logrotate=/var/lib/mesh/bundles/logrotate/tools/logrotate-tools,media-importer=/var/lib/mesh/bundles/media-importer/tools/media-importer,mesh-files=/var/lib/mesh/bundles/mesh-files/tools/mesh-files,mosquitto=/var/lib/mesh/bundles/mosquitto/code/index.serve.mjs,mosquitto=/var/lib/mesh/bundles/mosquitto/code/provisioner/index.serve.mjs,mosquitto=/var/lib/mesh/bundles/mosquitto/code/tools/index.serve.mjs,mssql=/var/lib/mesh/bundles/mssql/code/index.serve.mjs,mssql=/var/lib/mesh/bundles/mssql/code/provisioner/index.serve.mjs,mssql=/var/lib/mesh/bundles/mssql/code/tools/index.serve.mjs,netcheck=/var/lib/mesh/bundles/netcheck/tools-go/netcheck,netcheck=/var/lib/mesh/bundles/netcheck/tools-typescript/tools/index.serve.mjs,networkmanager=/var/lib/mesh/bundles/networkmanager/tools/uplink-tools,nfs-server=/var/lib/mesh/bundles/nfs-server/tools/nfs-server,nftables=/var/lib/mesh/bundles/nftables/tools/tools/index.serve.mjs,nodered=/var/lib/mesh/bundles/nodered/code/tools/index.serve.mjs,nzbgeek=/var/lib/mesh/bundles/nzbgeek/tools/nzbgeek,nzbget=/var/lib/mesh/bundles/nzbget/code/nzbget,ombi=/var/lib/mesh/bundles/ombi/code/index.serve.mjs,ombi=/var/lib/mesh/bundles/ombi/code/tools/index.serve.mjs,pacman=/var/lib/mesh/bundles/pacman/tools/pacman-tools,plex=/var/lib/mesh/bundles/plex/code/plex,postgres=/var/lib/mesh/bundles/postgres/code/postgres-provider,power=/var/lib/mesh/bundles/power/tools-go/power,qbittorrent=/var/lib/mesh/bundles/qbittorrent/code/qbittorrent,radarr=/var/lib/mesh/bundles/radarr/tools-go/radarr,redis=/var/lib/mesh/bundles/redis/code/index.serve.mjs,redis=/var/lib/mesh/bundles/redis/code/provisioner/index.serve.mjs,redis=/var/lib/mesh/bundles/redis/code/tools/index.serve.mjs,restic=/var/lib/mesh/bundles/restic/tools/restic-backups,route-proxy=/var/lib/mesh/bundles/route-proxy/provider/route-provider,searxng=/var/lib/mesh/bundles/searxng/tools/tools/index.serve.mjs,sonarr=/var/lib/mesh/bundles/sonarr/code/index.serve.mjs,sonarr=/var/lib/mesh/bundles/sonarr/code/tools/index.serve.mjs,ssh-client=/var/lib/mesh/bundles/ssh-client/tools/ssh-client-tools,sudo=/var/lib/mesh/bundles/sudo/tools/sudo-tools,systemd=/var/lib/mesh/bundles/systemd/tools/systemd-tools,tautulli=/var/lib/mesh/bundles/tautulli/code/index.serve.mjs,tautulli=/var/lib/mesh/bundles/tautulli/code/tools/index.serve.mjs,time-sync=/var/lib/mesh/bundles/time-sync/tools/time-sync-tools,unifi=/var/lib/mesh/bundles/unifi/tools/tools/index.serve.mjs,youtube=/var/lib/mesh/bundles/youtube/tools/youtube,zsh=/var/lib/mesh/bundles/zsh/tools/tools/index.serve.mjs\"},\"id\":\"node-tools.runtime\",\"module\":\"node-tools\",\"name\":\"node-tools\",\"restart-on\":[\"avahi.bundle-tools\",\"baserow.bundle-tools\",\"bazarr.bundle-code\",\"bookshelf.bundle-code\",\"claude-code.bundle-tools\",\"dbus.bundle-tools-go\",\"disk-load.bundle-tools-go\",\"dnsmasq.bundle-tools\",\"docker.bundle-tools\",\"fail2ban.bundle-tools\",\"grafana.bundle-code\",\"home-assistant.bundle-code\",\"hostname.bundle-tools\",\"icecast.bundle-code\",\"influxdb.bundle-code\",\"jackett.bundle-code\",\"kernel.bundle-tools\",\"kometa.bundle-code\",\"letta.bundle-tools\",\"lidarr.bundle-tools-go\",\"localization.bundle-tools\",\"logrotate.bundle-tools\",\"media-importer.bundle-tools\",\"mesh-files.bundle-tools\",\"mosquitto.bundle-code\",\"mssql.bundle-code\",\"netcheck.bundle-tools-go\",\"netcheck.bundle-tools-typescript\",\"networkmanager.bundle-tools\",\"nfs-server.bundle-tools\",\"nftables.bundle-tools\",\"node-tools.needs-broker\",\"nodered.bundle-code\",\"nzbgeek.bundle-tools\",\"nzbget.bundle-code\",\"ombi.bundle-code\",\"pacman.bundle-tools\",\"plex.bundle-code\",\"postgres.bundle-code\",\"power.bundle-tools-go\",\"qbittorrent.bundle-code\",\"radarr.bundle-tools-go\",\"redis.bundle-code\",\"restic.bundle-tools\",\"route-proxy.bundle-provider\",\"searxng.bundle-tools\",\"sonarr.bundle-code\",\"ssh-client.bundle-tools\",\"sudo.bundle-tools\",\"systemd.bundle-tools\",\"tautulli.bundle-code\",\"time-sync.bundle-tools\",\"unifi.bundle-tools\",\"youtube.bundle-tools\",\"zsh.bundle-tools\",\"baserow.runtime-config\",\"bazarr.runtime-config\",\"claude-code.facts\",\"claude-code.settings\",\"grafana.runtime-config\",\"home-assistant.needs-token\",\"home-assistant.runtime-config\",\"icecast.runtime-config\",\"icecast.secret-admin\",\"influxdb.needs-admin-token\",\"influxdb.received-influxdb-api\",\"influxdb.runtime-config\",\"jackett.needs-api-key\",\"jackett.runtime-config\",\"kometa.settings\",\"letta.runtime-config\",\"lidarr.bound-plex-api\",\"lidarr.needs-api-key\",\"lidarr.secret-plex-api\",\"media-importer.settings\",\"mosquitto.needs-admin\",\"mosquitto.received-mqtt-topic\",\"mssql.needs-reader\",\"mssql.needs-sa\",\"mssql.received-mssql-database\",\"nodered.runtime-config\",\"nzbgeek.needs-api-key\",\"nzbgeek.settings\",\"nzbget.needs-password\",\"nzbget.runtime-config\",\"ombi.needs-api-key\",\"ombi.runtime-config\",\"pacman.keeper-config\",\"postgres.consumers\",\"postgres.needs-reader\",\"postgres.needs-superuser\",\"postgres.received-postgres-database\",\"qbittorrent.needs-password\",\"qbittorrent.runtime-config\",\"radarr.bound-plex-api\",\"radarr.needs-api-key\",\"radarr.secret-plex-api\",\"redis.received-redis-cache\",\"redis.secret-secret\",\"restic.data-declared\",\"restic.declared\",\"restic.needs-repository\",\"searxng.runtime-config\",\"sonarr.bound-plex-api\",\"sonarr.needs-api-key\",\"sonarr.secret-plex-api\",\"tautulli.runtime-config\",\"unifi.runtime-config\",\"youtube.settings\",\"zsh.tools-conf\"],\"run\":[\"./node-tools\"],\"source\":\"http://novox.internal:5100/v2/node-tools/runtime/blobs/sha256:ef790876783d817903b337b8418b1eb21fb49d34128a390bbf7c3d08a4b2be8a\",\"type\":\"process\",\"user\":\"ace\"}],\"v\":1}", wire: "{\"fp\":\"src2:6e6f64652d6e6f64652d6e6f64652d6e6f64652d6e6f64652d6e6f64652d6e6f\",\"module\":\"node-tools\",\"node\":\"ace\",\"resources\":[{\"id\":\"node-tools.mesh-state\",\"mode\":\"0755\",\"module\":\"node-tools\",\"path\":\"/var/lib/mesh/node-tools\",\"type\":\"directory\"},{\"id\":\"node-tools.interpreter\",\"module\":\"node-tools\",\"package\":\"nodejs\",\"type\":\"package\"},{\"digest\":\"sha256:ef790876783d817903b337b8418b1eb21fb49d34128a390bbf7c3d08a4b2be8a\",\"env\":{\"MESH_AGENT_ACCOUNT\":\"ace\",\"MESH_AGENT_HOME\":\"/home/ace\",\"MESH_BROKER_FILE\":\"/var/lib/mesh/node-tools/broker\",\"MESH_OPERATOR_ACCOUNT\":\"ace\",\"MESH_OPERATOR_HOME\":\"/home/ace\",\"MESH_TOOL_ENV\":\"{\\\"baserow\\\":{\\\"MESH_BASEROW_CONFIG_FILE\\\":\\\"/var/lib/mesh/baserow/config.json\\\",\\\"MESH_BASEROW_URL\\\":\\\"http://127.0.0.1:20011\\\"},\\\"bazarr\\\":{\\\"MESH_BAZARR_CONFIG_DIR\\\":\\\"/var/lib/bazarr/config\\\",\\\"MESH_BAZARR_CONFIG_FILE\\\":\\\"/var/lib/bazarr/config.json\\\",\\\"MESH_BAZARR_CONTAINER\\\":\\\"bazarr\\\",\\\"MESH_BAZARR_URL\\\":\\\"http://127.0.0.1:20020\\\"},\\\"bookshelf\\\":{\\\"MESH_BOOKSHELF_CONFIG_DIR\\\":\\\"/var/lib/bookshelf/config\\\",\\\"MESH_BOOKSHELF_URL\\\":\\\"http://127.0.0.1:20021\\\"},\\\"claude-code\\\":{\\\"MESH_CLAUDE_CODE_FACTS\\\":\\\"/var/lib/claude-code/facts.json\\\",\\\"MESH_CLAUDE_CODE_SETTINGS\\\":\\\"/var/lib/claude-code/settings.json\\\",\\\"MESH_CLAUDE_CODE_STATE\\\":\\\"/var/lib/claude-code\\\"},\\\"grafana\\\":{\\\"MESH_GRAFANA_CONFIG_FILE\\\":\\\"/var/lib/mesh/grafana/config.json\\\",\\\"MESH_GRAFANA_URL\\\":\\\"http://127.0.0.1:20012\\\"},\\\"home-assistant\\\":{\\\"MESH_HOMEASSISTANT_CONFIG_FILE\\\":\\\"/var/lib/mesh/home-assistant/config.json\\\",\\\"MESH_HOMEASSISTANT_TOKEN_FILE\\\":\\\"/var/lib/mesh/home-assistant/token\\\",\\\"MESH_HOMEASSISTANT_URL\\\":\\\"http://127.0.0.1:8123\\\"},\\\"icecast\\\":{\\\"MESH_ICECAST_ADMIN_PASSWORD_FILE\\\":\\\"/var/lib/icecast/admin.secret\\\",\\\"MESH_ICECAST_CONFIG_FILE\\\":\\\"/var/lib/mesh/icecast/config.json\\\",\\\"MESH_ICECAST_URL\\\":\\\"http://127.0.0.1:20005\\\"},\\\"influxdb\\\":{\\\"MESH_INFLUXDB_CONFIG_FILE\\\":\\\"/var/lib/mesh/influxdb/config.json\\\",\\\"MESH_INFLUXDB_TOKEN_FILE\\\":\\\"/var/lib/influxdb/admin-token.secret\\\",\\\"MESH_INFLUXDB_URL\\\":\\\"http://127.0.0.1:8086\\\",\\\"MESH_RECEIVES\\\":\\\"/var/lib/influxdb/grants/mesh.json\\\"},\\\"jackett\\\":{\\\"MESH_JACKETT_API_KEY_FILE\\\":\\\"/var/lib/mesh/jackett/api-key\\\",\\\"MESH_JACKETT_CONFIG_DIR\\\":\\\"/var/lib/jackett/config\\\",\\\"MESH_JACKETT_CONFIG_FILE\\\":\\\"/var/lib/jackett/config.json\\\",\\\"MESH_JACKETT_URL\\\":\\\"http://127.0.0.1:20004\\\"},\\\"kometa\\\":{\\\"MESH_KOMETA_CONFIG_DIR\\\":\\\"/var/lib/kometa/config\\\",\\\"MESH_KOMETA_CONFIG_FILE\\\":\\\"/var/lib/kometa/config.yml\\\"},\\\"letta\\\":{\\\"MESH_LETTA_CONFIG_FILE\\\":\\\"/var/lib/mesh/letta/config.json\\\",\\\"MESH_LETTA_URL\\\":\\\"http://127.0.0.1:20002\\\"},\\\"lidarr\\\":{\\\"MESH_LIDARR_API_KEY_FILE\\\":\\\"/var/lib/mesh/lidarr/api-key\\\",\\\"MESH_LIDARR_CONFIG_DIR\\\":\\\"/var/lib/lidarr/config\\\",\\\"MESH_LIDARR_RECYCLE_BIN\\\":\\\"/recycle/\\\",\\\"MESH_LIDARR_RECYCLE_DAYS\\\":\\\"7\\\",\\\"MESH_LIDARR_URL\\\":\\\"http://127.0.0.1:20022\\\",\\\"MESH_PLEX_BIND_FILE\\\":\\\"/var/lib/lidarr/plex-api.json\\\",\\\"MESH_PLEX_TOKEN_FILE\\\":\\\"/var/lib/lidarr/plex-api.secret\\\"},\\\"media-importer\\\":{\\\"MESH_MEDIA_IMPORTER_SETTINGS\\\":\\\"/var/lib/media-importer/settings.json\\\",\\\"MESH_MEDIA_IMPORTER_STATE\\\":\\\"/var/lib/media-importer\\\",\\\"MESH_MEDIA_LIBRARY\\\":\\\"/storage/media/poc\\\"},\\\"mesh-files\\\":{\\\"MESH_FILES_STATE\\\":\\\"/var/lib/mesh-files\\\",\\\"MESH_OWNED_PATHS\\\":\\\"{\\\\\\\"/etc/NetworkManager/conf.d/50-mesh.conf\\\\\\\":\\\\\\\"networkmanager (its own tools)\\\\\\\",\\\\\\\"/etc/claude-code\\\\\\\":\\\\\\\"claude-code (its own tools)\\\\\\\",\\\\\\\"/etc/claude-code/agent-account\\\\\\\":\\\\\\\"claude-code (its own tools)\\\\\\\",\\\\\\\"/etc/dnsmasq.conf\\\\\\\":\\\\\\\"dnsmasq (its own tools)\\\\\\\",\\\\\\\"/etc/docker/daemon.json\\\\\\\":\\\\\\\"docker (its own tools)\\\\\\\",\\\\\\\"/etc/fail2ban/action.d\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/etc/fail2ban/action.d/iptables-allports-dualchain.conf\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/etc/fail2ban/filter.d\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/etc/fail2ban/jail.d\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/etc/fail2ban/jail.d/recidive.conf\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/etc/fail2ban/jail.d/sshd.conf\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/etc/fail2ban/jail.local\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/etc/fail2ban/mesh-own-addresses.json\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/etc/hostname\\\\\\\":\\\\\\\"hostname (node-hostname)\\\\\\\",\\\\\\\"/etc/hosts\\\\\\\":\\\\\\\"hostname (node-hostname)\\\\\\\",\\\\\\\"/etc/locale.conf\\\\\\\":\\\\\\\"localization (its own tools)\\\\\\\",\\\\\\\"/etc/logrotate.conf\\\\\\\":\\\\\\\"logrotate (its own tools)\\\\\\\",\\\\\\\"/etc/logrotate.d/fail2ban\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-power\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-power/moments\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-power/moments/after-boot\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-power/moments/after-wake\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-power/moments/before-shutdown\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-power/moments/before-sleep\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-power/moments/on-battery\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-power/moments/on-mains\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-resolver/nodes.conf\\\\\\\":\\\\\\\"dnsmasq (its own tools)\\\\\\\",\\\\\\\"/etc/mesh-resolver/zones.conf\\\\\\\":\\\\\\\"dnsmasq (its own tools)\\\\\\\",\\\\\\\"/etc/nfs-server\\\\\\\":\\\\\\\"nfs-server (its own tools)\\\\\\\",\\\\\\\"/etc/nfs-server/machines\\\\\\\":\\\\\\\"nfs-server (its own tools)\\\\\\\",\\\\\\\"/etc/nfs-server/shares.conf\\\\\\\":\\\\\\\"nfs-server (its own tools)\\\\\\\",\\\\\\\"/etc/nfs.conf.d/50-mesh.conf\\\\\\\":\\\\\\\"nfs-server (its own tools)\\\\\\\",\\\\\\\"/etc/pacman.conf\\\\\\\":\\\\\\\"pacman (its own tools)\\\\\\\",\\\\\\\"/etc/pacman.d/hooks/90-mesh-kernel-boot-entries.hook\\\\\\\":\\\\\\\"kernel (its own tools)\\\\\\\",\\\\\\\"/etc/resolv.conf\\\\\\\":\\\\\\\"networkmanager (node-uplink)\\\\\\\",\\\\\\\"/etc/ssh/sshd_config.d/10-mesh.conf\\\\\\\":\\\\\\\"sshd (its own tools)\\\\\\\",\\\\\\\"/etc/sudoers.d/10-mesh-operator\\\\\\\":\\\\\\\"sudo (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/logind.conf.d\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/logind.conf.d/power.conf\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/dnsmasq.service.d\\\\\\\":\\\\\\\"dnsmasq (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/dnsmasq.service.d/mesh.conf\\\\\\\":\\\\\\\"dnsmasq (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/docker-prune.service\\\\\\\":\\\\\\\"docker (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/docker-prune.timer\\\\\\\":\\\\\\\"docker (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/hibernate.target.d\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/hibernate.target.d/mesh-power.conf\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/hybrid-sleep.target.d\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/hybrid-sleep.target.d/mesh-power.conf\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/mesh-ca-trust.service\\\\\\\":\\\\\\\"ca-trust (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/mesh-filter.service\\\\\\\":\\\\\\\"nftables (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/mesh-kernel-boot-entries.service\\\\\\\":\\\\\\\"kernel (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/mesh-kernel-huge-pages.service\\\\\\\":\\\\\\\"kernel (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/mesh-power-after-boot.service\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/mesh-power-after-wake.service\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/mesh-power-before-shutdown.service\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/mesh-power-before-sleep.service\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/mesh-power-supply.service\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/nftables.service.d/mesh.conf\\\\\\\":\\\\\\\"nftables (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/sleep.target.d\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/sleep.target.d/mesh-power.conf\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/suspend-then-hibernate.target.d\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/suspend-then-hibernate.target.d/mesh-power.conf\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/suspend.target.d\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/system/suspend.target.d/mesh-power.conf\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/systemd/timesyncd.conf.d/50-mesh.conf\\\\\\\":\\\\\\\"time-sync (its own tools)\\\\\\\",\\\\\\\"/etc/udev/rules.d/90-mesh-power.rules\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/etc/vconsole.conf\\\\\\\":\\\\\\\"localization (its own tools)\\\\\\\",\\\\\\\"/etc/xdg/reflector/reflector.conf\\\\\\\":\\\\\\\"pacman (its own tools)\\\\\\\",\\\\\\\"/run/mesh-power\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/run/nfs-server\\\\\\\":\\\\\\\"nfs-server (its own tools)\\\\\\\",\\\\\\\"/usr/lib/nox-mesh-host/launch\\\\\\\":\\\\\\\"mesh-host (its own tools)\\\\\\\",\\\\\\\"/usr/lib/nox-mesh-host/versions/4e121b6e88f1\\\\\\\":\\\\\\\"mesh-host (its own tools)\\\\\\\",\\\\\\\"/usr/local/bin\\\\\\\":\\\\\\\"mesh-cli (its own tools)\\\\\\\",\\\\\\\"/usr/local/bin/claude-agent\\\\\\\":\\\\\\\"claude-code (its own tools)\\\\\\\",\\\\\\\"/usr/local/lib/mesh-power\\\\\\\":\\\\\\\"power (its own tools)\\\\\\\",\\\\\\\"/var/lib/dnsmasq-watch\\\\\\\":\\\\\\\"dnsmasq (its own tools)\\\\\\\",\\\\\\\"/var/lib/mesh-fail2ban\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/var/lib/mesh-fail2ban/own-addresses\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/var/lib/mesh-store\\\\\\\":\\\\\\\"postgres (its own tools)\\\\\\\",\\\\\\\"/var/lib/nfs-server\\\\\\\":\\\\\\\"nfs-server (its own tools)\\\\\\\",\\\\\\\"/var/lib/nfs-server/from-bus\\\\\\\":\\\\\\\"nfs-server (its own tools)\\\\\\\",\\\\\\\"/var/lib/postgres\\\\\\\":\\\\\\\"postgres (its own tools)\\\\\\\",\\\\\\\"/var/lib/route-proxy/acme\\\\\\\":\\\\\\\"route-proxy (its own tools)\\\\\\\",\\\\\\\"/var/lib/route-proxy/ca\\\\\\\":\\\\\\\"route-proxy (its own tools)\\\\\\\",\\\\\\\"/var/lib/route-proxy/routes\\\\\\\":\\\\\\\"route-proxy (its own tools)\\\\\\\",\\\\\\\"/var/lib/systemd\\\\\\\":\\\\\\\"systemd (its own tools)\\\\\\\",\\\\\\\"/var/log/fail2ban.log\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\",\\\\\\\"/var/run/fail2ban\\\\\\\":\\\\\\\"fail2ban (its own tools)\\\\\\\"}\\\",\\\"MESH_SECRET_PLACES\\\":\\\"[\\\\\\\"/var/lib/baserow\\\\\\\",\\\\\\\"/var/lib/bazarr/radarr-api.secret\\\\\\\",\\\\\\\"/var/lib/bazarr/sonarr-api.secret\\\\\\\",\\\\\\\"/var/lib/bookshelf/jackett-api.secret\\\\\\\",\\\\\\\"/var/lib/bookshelf/nzbget-api.secret\\\\\\\",\\\\\\\"/var/lib/bookshelf/qbittorrent-api.secret\\\\\\\",\\\\\\\"/var/lib/car-hunter\\\\\\\",\\\\\\\"/var/lib/claude-code/gitea-read.secret\\\\\\\",\\\\\\\"/var/lib/home-assistant/lidarr-api.secret\\\\\\\",\\\\\\\"/var/lib/home-assistant/mqtt-topic.secret\\\\\\\",\\\\\\\"/var/lib/home-assistant/radarr-api.secret\\\\\\\",\\\\\\\"/var/lib/home-assistant/sonarr-api.secret\\\\\\\",\\\\\\\"/var/lib/icecast/admin.secret\\\\\\\",\\\\\\\"/var/lib/icecast/relay.secret\\\\\\\",\\\\\\\"/var/lib/icecast/source.secret\\\\\\\",\\\\\\\"/var/lib/influxdb\\\\\\\",\\\\\\\"/var/lib/kometa\\\\\\\",\\\\\\\"/var/lib/letta\\\\\\\",\\\\\\\"/var/lib/lidarr/jackett-api.secret\\\\\\\",\\\\\\\"/var/lib/lidarr/nzbget-api.secret\\\\\\\",\\\\\\\"/var/lib/lidarr/plex-api.secret\\\\\\\",\\\\\\\"/var/lib/lidarr/qbittorrent-api.secret\\\\\\\",\\\\\\\"/var/lib/mesh-delivery-assigner\\\\\\\",\\\\\\\"/var/lib/mesh-provisioner-client\\\\\\\",\\\\\\\"/var/lib/mesh-reads\\\\\\\",\\\\\\\"/var/lib/mesh/dnsmasq\\\\\\\",\\\\\\\"/var/lib/mesh/grafana\\\\\\\",\\\\\\\"/var/lib/mesh/home-assistant\\\\\\\",\\\\\\\"/var/lib/mesh/jackett\\\\\\\",\\\\\\\"/var/lib/mesh/lidarr\\\\\\\",\\\\\\\"/var/lib/mesh/mosquitto\\\\\\\",\\\\\\\"/var/lib/mesh/node-tools\\\\\\\",\\\\\\\"/var/lib/mesh/nodered\\\\\\\",\\\\\\\"/var/lib/mesh/nzbget\\\\\\\",\\\\\\\"/var/lib/mesh/ombi\\\\\\\",\\\\\\\"/var/lib/mesh/pacman\\\\\\\",\\\\\\\"/var/lib/mesh/postgres\\\\\\\",\\\\\\\"/var/lib/mesh/qbittorrent\\\\\\\",\\\\\\\"/var/lib/mesh/radarr\\\\\\\",\\\\\\\"/var/lib/mesh/route-proxy\\\\\\\",\\\\\\\"/var/lib/mesh/searxng\\\\\\\",\\\\\\\"/var/lib/mesh/sonarr\\\\\\\",\\\\\\\"/var/lib/mesh/unifi\\\\\\\",\\\\\\\"/var/lib/mosquitto/grants\\\\\\\",\\\\\\\"/var/lib/mssql\\\\\\\",\\\\\\\"/var/lib/n8n/database.secret\\\\\\\",\\\\\\\"/var/lib/nodered/mqtt-topic.secret\\\\\\\",\\\\\\\"/var/lib/nzbgeek\\\\\\\",\\\\\\\"/var/lib/ombi/lidarr-api.secret\\\\\\\",\\\\\\\"/var/lib/ombi/plex-api.secret\\\\\\\",\\\\\\\"/var/lib/ombi/radarr-api.secret\\\\\\\",\\\\\\\"/var/lib/ombi/sonarr-api.secret\\\\\\\",\\\\\\\"/var/lib/radarr/jackett-api.secret\\\\\\\",\\\\\\\"/var/lib/radarr/nzbget-api.secret\\\\\\\",\\\\\\\"/var/lib/radarr/plex-api.secret\\\\\\\",\\\\\\\"/var/lib/radarr/qbittorrent-api.secret\\\\\\\",\\\\\\\"/var/lib/redis/default.secret\\\\\\\",\\\\\\\"/var/lib/redis/grants\\\\\\\",\\\\\\\"/var/lib/restic\\\\\\\",\\\\\\\"/var/lib/sonarr/jackett-api.secret\\\\\\\",\\\\\\\"/var/lib/sonarr/nzbget-api.secret\\\\\\\",\\\\\\\"/var/lib/sonarr/plex-api.secret\\\\\\\",\\\\\\\"/var/lib/sonarr/qbittorrent-api.secret\\\\\\\",\\\\\\\"/var/lib/supabase\\\\\\\",\\\\\\\"/var/lib/tautulli/plex-api.secret\\\\\\\",\\\\\\\"/var/lib/txt-game/database.secret\\\\\\\"]\\\"},\\\"mosquitto\\\":{\\\"MESH_MQTT_CTRL_CONTAINER\\\":\\\"mosquitto\\\",\\\"MESH_PROVISION_ADMIN_USER\\\":\\\"mesh-admin\\\",\\\"MESH_PROVISION_MQTT\\\":\\\"127.0.0.1:1883\\\",\\\"MESH_PROVISION_PASSWORD_FILE\\\":\\\"/var/lib/mesh/mosquitto/admin\\\",\\\"MESH_RECEIVES\\\":\\\"/var/lib/mosquitto/grants/mesh.json\\\"},\\\"mssql\\\":{\\\"MESH_MSSQL_READER_PASSWORD_FILE\\\":\\\"/var/lib/mssql/reader.secret\\\",\\\"MESH_PROVISION_MSSQL\\\":\\\"mssql://sa@127.0.0.1:4848/master\\\",\\\"MESH_PROVISION_PASSWORD_FILE\\\":\\\"/var/lib/mssql/sa.secret\\\",\\\"MESH_RECEIVES\\\":\\\"/var/lib/mssql/grants/mesh.json\\\"},\\\"nodered\\\":{\\\"MESH_NODERED_CONFIG_FILE\\\":\\\"/var/lib/mesh/nodered/config.json\\\",\\\"MESH_NODERED_URL\\\":\\\"http://127.0.0.1:20006\\\"},\\\"nzbgeek\\\":{\\\"MESH_NZBGEEK_KEY_FILE\\\":\\\"/var/lib/nzbgeek/api-key.secret\\\",\\\"MESH_NZBGEEK_SETTINGS\\\":\\\"/var/lib/nzbgeek/settings.json\\\"},\\\"nzbget\\\":{\\\"MESH_NZBGET_CONFIG_DIR\\\":\\\"/var/lib/nzbget/config\\\",\\\"MESH_NZBGET_CONFIG_FILE\\\":\\\"/var/lib/nzbget/config.json\\\",\\\"MESH_NZBGET_PASSWORD_FILE\\\":\\\"/var/lib/mesh/nzbget/password\\\",\\\"MESH_NZBGET_SECRETS_DIR\\\":\\\"/var/lib/mesh/nzbget\\\",\\\"MESH_NZBGET_STATE\\\":\\\"/var/lib/nzbget\\\",\\\"MESH_NZBGET_URL\\\":\\\"http://127.0.0.1:20017\\\"},\\\"ombi\\\":{\\\"MESH_OMBI_API_KEY_FILE\\\":\\\"/var/lib/mesh/ombi/api-key\\\",\\\"MESH_OMBI_CONFIG_FILE\\\":\\\"/var/lib/mesh/ombi/config.json\\\",\\\"MESH_OMBI_URL\\\":\\\"http://127.0.0.1:20024\\\"},\\\"pacman\\\":{\\\"PACMAN_KEEPER_CONFIG\\\":\\\"/var/lib/mesh/pacman/keeper.json\\\"},\\\"plex\\\":{\\\"MESH_PLEX_CONFIG_DIR\\\":\\\"/mnt/plex/config\\\",\\\"MESH_PLEX_URL\\\":\\\"http://127.0.0.1:32400\\\"},\\\"postgres\\\":{\\\"MESH_CONSUMERS_FILE\\\":\\\"/var/lib/mesh/postgres/consumers.json\\\",\\\"MESH_POSTGRES_READER_PASSWORD_FILE\\\":\\\"/var/lib/mesh/postgres/reader.secret\\\",\\\"MESH_PROVISION_PASSWORD_FILE\\\":\\\"/var/lib/mesh/postgres/superuser.secret\\\",\\\"MESH_PROVISION_POSTGRES\\\":\\\"postgres://postgres@127.0.0.1:5434/postgres?sslmode=disable\\\",\\\"MESH_RECEIVES\\\":\\\"/var/lib/mesh/postgres/grants/mesh.json\\\"},\\\"qbittorrent\\\":{\\\"MESH_QBITTORRENT_CONFIG_DIR\\\":\\\"/var/lib/qbittorrent/config\\\",\\\"MESH_QBITTORRENT_CONFIG_FILE\\\":\\\"/var/lib/qbittorrent/config.json\\\",\\\"MESH_QBITTORRENT_PASSWORD_FILE\\\":\\\"/var/lib/mesh/qbittorrent/password\\\",\\\"MESH_QBITTORRENT_URL\\\":\\\"http://127.0.0.1:8112\\\"},\\\"radarr\\\":{\\\"MESH_PLEX_BIND_FILE\\\":\\\"/var/lib/radarr/plex-api.json\\\",\\\"MESH_PLEX_TOKEN_FILE\\\":\\\"/var/lib/radarr/plex-api.secret\\\",\\\"MESH_RADARR_API_KEY_FILE\\\":\\\"/var/lib/mesh/radarr/api-key\\\",\\\"MESH_RADARR_CONFIG_DIR\\\":\\\"/var/lib/radarr/config\\\",\\\"MESH_RADARR_URL\\\":\\\"http://127.0.0.1:20019\\\"},\\\"redis\\\":{\\\"MESH_PROVISION_PASSWORD_FILE\\\":\\\"/var/lib/redis/default.secret\\\",\\\"MESH_PROVISION_REDIS\\\":\\\"127.0.0.1:20003\\\",\\\"MESH_RECEIVES\\\":\\\"/var/lib/redis/grants/mesh.json\\\"},\\\"restic\\\":{\\\"MESH_BACKUP_DATA\\\":\\\"/var/lib/restic/data.conf\\\",\\\"MESH_BACKUP_DECLARED\\\":\\\"/var/lib/restic/backups.conf\\\",\\\"MESH_BACKUP_PASSWORD_FILE\\\":\\\"/var/lib/restic/repository.secret\\\",\\\"MESH_BACKUP_REPOSITORY\\\":\\\"/storage/restic\\\",\\\"MESH_BACKUP_STATE\\\":\\\"/var/lib/restic\\\"},\\\"searxng\\\":{\\\"MESH_SEARXNG_CONFIG_FILE\\\":\\\"/var/lib/mesh/searxng/config.json\\\",\\\"MESH_SEARXNG_URL\\\":\\\"http://127.0.0.1:20000\\\"},\\\"sonarr\\\":{\\\"MESH_PLEX_BIND_FILE\\\":\\\"/var/lib/sonarr/plex-api.json\\\",\\\"MESH_PLEX_TOKEN_FILE\\\":\\\"/var/lib/sonarr/plex-api.secret\\\",\\\"MESH_SONARR_API_KEY_FILE\\\":\\\"/var/lib/mesh/sonarr/api-key\\\",\\\"MESH_SONARR_CONFIG_DIR\\\":\\\"/var/lib/sonarr/config\\\",\\\"MESH_SONARR_RECYCLE_DIR\\\":\\\"/var/lib/sonarr/recycle-bin\\\",\\\"MESH_SONARR_URL\\\":\\\"http://127.0.0.1:20018\\\"},\\\"systemd\\\":{\\\"MESH_SYSTEMD_STATE\\\":\\\"/var/lib/mesh/systemd\\\"},\\\"tautulli\\\":{\\\"MESH_TAUTULLI_CONFIG_DIR\\\":\\\"/var/lib/tautulli/config\\\",\\\"MESH_TAUTULLI_CONFIG_FILE\\\":\\\"/var/lib/mesh/tautulli/config.json\\\",\\\"MESH_TAUTULLI_CONTAINER\\\":\\\"tautulli\\\",\\\"MESH_TAUTULLI_URL\\\":\\\"http://127.0.0.1:20023\\\"},\\\"unifi\\\":{\\\"MESH_UNIFI_CONFIG_FILE\\\":\\\"/var/lib/mesh/unifi/config.json\\\",\\\"MESH_UNIFI_URL\\\":\\\"https://127.0.0.1:8443\\\"},\\\"youtube\\\":{\\\"MESH_YOUTUBE_DOWNLOADS\\\":\\\"/storage/downloads/youtube\\\",\\\"MESH_YOUTUBE_SETTINGS\\\":\\\"/var/lib/youtube/settings.json\\\"},\\\"zsh\\\":{\\\"ZSH_TOOLS_CONF\\\":\\\"/var/lib/zsh/tools.conf\\\"}}\",\"MESH_TOOL_MODULES\":\"avahi=/var/lib/mesh/bundles/avahi/tools/avahi-tools,baserow=/var/lib/mesh/bundles/baserow/tools/tools/index.serve.mjs,bazarr=/var/lib/mesh/bundles/bazarr/code/index.serve.mjs,bazarr=/var/lib/mesh/bundles/bazarr/code/tools/index.serve.mjs,bookshelf=/var/lib/mesh/bundles/bookshelf/code/index.serve.mjs,bookshelf=/var/lib/mesh/bundles/bookshelf/code/tools/index.serve.mjs,claude-code=/var/lib/mesh/bundles/claude-code/tools/claude-code,dbus=/var/lib/mesh/bundles/dbus/tools-go/dbus-tools,disk-load=/var/lib/mesh/bundles/disk-load/tools-go/disk-load,dnsmasq=/var/lib/mesh/bundles/dnsmasq/tools/dnsmasq-tools,docker=/var/lib/mesh/bundles/docker/tools/docker-tools,fail2ban=/var/lib/mesh/bundles/fail2ban/tools/fail2ban-tools,grafana=/var/lib/mesh/bundles/grafana/code/index.serve.mjs,grafana=/var/lib/mesh/bundles/grafana/code/tools/index.serve.mjs,home-assistant=/var/lib/mesh/bundles/home-assistant/code/index.serve.mjs,home-assistant=/var/lib/mesh/bundles/home-assistant/code/tools/index.serve.mjs,hostname=/var/lib/mesh/bundles/hostname/tools/hostname-tools,icecast=/var/lib/mesh/bundles/icecast/code/index.serve.mjs,icecast=/var/lib/mesh/bundles/icecast/code/tools/index.serve.mjs,influxdb=/var/lib/mesh/bundles/influxdb/code/provisioner/index.serve.mjs,influxdb=/var/lib/mesh/bundles/influxdb/code/tools/index.serve.mjs,jackett=/var/lib/mesh/bundles/jackett/code/tools/index.serve.mjs,kernel=/var/lib/mesh/bundles/kernel/tools/kernel-tools,kometa=/var/lib/mesh/bundles/kometa/code/tools/index.serve.mjs,letta=/var/lib/mesh/bundles/letta/tools/tools/index.serve.mjs,lidarr=/var/lib/mesh/bundles/lidarr/tools-go/lidarr,localization=/var/lib/mesh/bundles/localization/tools/localization-tools,logrotate=/var/lib/mesh/bundles/logrotate/tools/logrotate-tools,media-importer=/var/lib/mesh/bundles/media-importer/tools/media-importer,mesh-files=/var/lib/mesh/bundles/mesh-files/tools/mesh-files,mosquitto=/var/lib/mesh/bundles/mosquitto/code/index.serve.mjs,mosquitto=/var/lib/mesh/bundles/mosquitto/code/provisioner/index.serve.mjs,mosquitto=/var/lib/mesh/bundles/mosquitto/code/tools/index.serve.mjs,mssql=/var/lib/mesh/bundles/mssql/code/index.serve.mjs,mssql=/var/lib/mesh/bundles/mssql/code/provisioner/index.serve.mjs,mssql=/var/lib/mesh/bundles/mssql/code/tools/index.serve.mjs,netcheck=/var/lib/mesh/bundles/netcheck/tools-go/netcheck,netcheck=/var/lib/mesh/bundles/netcheck/tools-typescript/tools/index.serve.mjs,networkmanager=/var/lib/mesh/bundles/networkmanager/tools/uplink-tools,nfs-server=/var/lib/mesh/bundles/nfs-server/tools/nfs-server,nftables=/var/lib/mesh/bundles/nftables/tools/tools/index.serve.mjs,nodered=/var/lib/mesh/bundles/nodered/code/tools/index.serve.mjs,nzbgeek=/var/lib/mesh/bundles/nzbgeek/tools/nzbgeek,nzbget=/var/lib/mesh/bundles/nzbget/code/nzbget,ombi=/var/lib/mesh/bundles/ombi/code/index.serve.mjs,ombi=/var/lib/mesh/bundles/ombi/code/tools/index.serve.mjs,pacman=/var/lib/mesh/bundles/pacman/tools/pacman-tools,plex=/var/lib/mesh/bundles/plex/code/plex,postgres=/var/lib/mesh/bundles/postgres/code/postgres-provider,power=/var/lib/mesh/bundles/power/tools-go/power,qbittorrent=/var/lib/mesh/bundles/qbittorrent/code/qbittorrent,radarr=/var/lib/mesh/bundles/radarr/tools-go/radarr,redis=/var/lib/mesh/bundles/redis/code/index.serve.mjs,redis=/var/lib/mesh/bundles/redis/code/provisioner/index.serve.mjs,redis=/var/lib/mesh/bundles/redis/code/tools/index.serve.mjs,restic=/var/lib/mesh/bundles/restic/tools/restic-backups,route-proxy=/var/lib/mesh/bundles/route-proxy/provider/route-provider,searxng=/var/lib/mesh/bundles/searxng/tools/tools/index.serve.mjs,sonarr=/var/lib/mesh/bundles/sonarr/code/index.serve.mjs,sonarr=/var/lib/mesh/bundles/sonarr/code/tools/index.serve.mjs,ssh-client=/var/lib/mesh/bundles/ssh-client/tools/ssh-client-tools,sudo=/var/lib/mesh/bundles/sudo/tools/sudo-tools,systemd=/var/lib/mesh/bundles/systemd/tools/systemd-tools,tautulli=/var/lib/mesh/bundles/tautulli/code/index.serve.mjs,tautulli=/var/lib/mesh/bundles/tautulli/code/tools/index.serve.mjs,time-sync=/var/lib/mesh/bundles/time-sync/tools/time-sync-tools,unifi=/var/lib/mesh/bundles/unifi/tools/tools/index.serve.mjs,youtube=/var/lib/mesh/bundles/youtube/tools/youtube,zsh=/var/lib/mesh/bundles/zsh/tools/tools/index.serve.mjs\"},\"id\":\"node-tools.runtime\",\"module\":\"node-tools\",\"name\":\"node-tools\",\"restart-on\":[\"avahi.bundle-tools\",\"baserow.bundle-tools\",\"bazarr.bundle-code\",\"bookshelf.bundle-code\",\"claude-code.bundle-tools\",\"dbus.bundle-tools-go\",\"disk-load.bundle-tools-go\",\"dnsmasq.bundle-tools\",\"docker.bundle-tools\",\"fail2ban.bundle-tools\",\"grafana.bundle-code\",\"home-assistant.bundle-code\",\"hostname.bundle-tools\",\"icecast.bundle-code\",\"influxdb.bundle-code\",\"jackett.bundle-code\",\"kernel.bundle-tools\",\"kometa.bundle-code\",\"letta.bundle-tools\",\"lidarr.bundle-tools-go\",\"localization.bundle-tools\",\"logrotate.bundle-tools\",\"media-importer.bundle-tools\",\"mesh-files.bundle-tools\",\"mosquitto.bundle-code\",\"mssql.bundle-code\",\"netcheck.bundle-tools-go\",\"netcheck.bundle-tools-typescript\",\"networkmanager.bundle-tools\",\"nfs-server.bundle-tools\",\"nftables.bundle-tools\",\"node-tools.needs-broker\",\"nodered.bundle-code\",\"nzbgeek.bundle-tools\",\"nzbget.bundle-code\",\"ombi.bundle-code\",\"pacman.bundle-tools\",\"plex.bundle-code\",\"postgres.bundle-code\",\"power.bundle-tools-go\",\"qbittorrent.bundle-code\",\"radarr.bundle-tools-go\",\"redis.bundle-code\",\"restic.bundle-tools\",\"route-proxy.bundle-provider\",\"searxng.bundle-tools\",\"sonarr.bundle-code\",\"ssh-client.bundle-tools\",\"sudo.bundle-tools\",\"systemd.bundle-tools\",\"tautulli.bundle-code\",\"time-sync.bundle-tools\",\"unifi.bundle-tools\",\"youtube.bundle-tools\",\"zsh.bundle-tools\",\"baserow.runtime-config\",\"bazarr.runtime-config\",\"claude-code.facts\",\"claude-code.settings\",\"grafana.runtime-config\",\"home-assistant.needs-token\",\"home-assistant.runtime-config\",\"icecast.runtime-config\",\"icecast.secret-admin\",\"influxdb.needs-admin-token\",\"influxdb.received-influxdb-api\",\"influxdb.runtime-config\",\"jackett.needs-api-key\",\"jackett.runtime-config\",\"kometa.settings\",\"letta.runtime-config\",\"lidarr.bound-plex-api\",\"lidarr.needs-api-key\",\"lidarr.secret-plex-api\",\"media-importer.settings\",\"mosquitto.needs-admin\",\"mosquitto.received-mqtt-topic\",\"mssql.needs-reader\",\"mssql.needs-sa\",\"mssql.received-mssql-database\",\"nodered.runtime-config\",\"nzbgeek.needs-api-key\",\"nzbgeek.settings\",\"nzbget.needs-password\",\"nzbget.runtime-config\",\"ombi.needs-api-key\",\"ombi.runtime-config\",\"pacman.keeper-config\",\"postgres.consumers\",\"postgres.needs-reader\",\"postgres.needs-superuser\",\"postgres.received-postgres-database\",\"qbittorrent.needs-password\",\"qbittorrent.runtime-config\",\"radarr.bound-plex-api\",\"radarr.needs-api-key\",\"radarr.secret-plex-api\",\"redis.received-redis-cache\",\"redis.secret-secret\",\"restic.data-declared\",\"restic.declared\",\"restic.needs-repository\",\"searxng.runtime-config\",\"sonarr.bound-plex-api\",\"sonarr.needs-api-key\",\"sonarr.secret-plex-api\",\"tautulli.runtime-config\",\"unifi.runtime-config\",\"youtube.settings\",\"zsh.tools-conf\"],\"run\":[\"./node-tools\"],\"source\":\"http://novox.internal:5100/v2/node-tools/runtime/blobs/sha256:ef790876783d817903b337b8418b1eb21fb49d34128a390bbf7c3d08a4b2be8a\",\"type\":\"process\",\"user\":\"ace\"}],\"v\":1}", + decode: func(b []byte) (any, error) { + var v ComposedByNodeModuleFingerprintValue + err := json.Unmarshal(b, &v) + return v, err + }, subjectIs: nil}, + {name: "a module the engine could not compose", subject: "", given: "{\"fp\":\"src2:62756c6c6462756c6c6462756c6c6462756c6c6462756c6c6462756c6c646275\",\"module\":\"nzbget\",\"node\":\"ace\",\"refused\":\"a secret the vault does not hold\",\"resources\":[],\"v\":1}", wire: "{\"fp\":\"src2:62756c6c6462756c6c6462756c6c6462756c6c6462756c6c6462756c6c646275\",\"module\":\"nzbget\",\"node\":\"ace\",\"refused\":\"a secret the vault does not hold\",\"resources\":[],\"v\":1}", + decode: func(b []byte) (any, error) { + var v ComposedByNodeModuleFingerprintValue + err := json.Unmarshal(b, &v) + return v, err + }, subjectIs: nil}, + {name: "the node's own composition", subject: "", given: "{\"node\":\"ace\",\"own\":{\"adoption\":{\"taken\":[\"avahi\"],\"untaken\":{\"plex\":[\"plex.bundle-code\"]}},\"groups\":[{\"gid\":1002,\"name\":\"mesh-media\"}],\"identities\":[{\"gid\":1001,\"groups\":[1002],\"module\":\"plex\",\"name\":\"plex\",\"runner\":\"plex\",\"uid\":1001}],\"libraries\":[{\"gid\":1002,\"group\":\"mesh-media\",\"home\":false,\"library\":\"movies\",\"paths\":[\"/srv/media/movies\"],\"root\":\"/srv/media/movies\",\"taken\":true}],\"provisioner_account\":\"mesh-provisioner-client\"},\"resources\":[],\"v\":1}", wire: "{\"node\":\"ace\",\"own\":{\"adoption\":{\"taken\":[\"avahi\"],\"untaken\":{\"plex\":[\"plex.bundle-code\"]}},\"groups\":[{\"gid\":1002,\"name\":\"mesh-media\"}],\"identities\":[{\"gid\":1001,\"groups\":[1002],\"module\":\"plex\",\"name\":\"plex\",\"runner\":\"plex\",\"uid\":1001}],\"libraries\":[{\"gid\":1002,\"group\":\"mesh-media\",\"home\":false,\"library\":\"movies\",\"paths\":[\"/srv/media/movies\"],\"root\":\"/srv/media/movies\",\"taken\":true}],\"provisioner_account\":\"mesh-provisioner-client\"},\"resources\":[],\"v\":1}", + decode: func(b []byte) (any, error) { + var v ComposedByNodeNodeValue + err := json.Unmarshal(b, &v) + return v, err + }, subjectIs: nil}, + {name: "a module running (typed from the engine's own values; no read tool answers a have key)", subject: "", given: "{\"build\":\"b1\",\"composed\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fp\":\"src2:62756c6c6462756c6c6462756c6c6462756c6c6462756c6c6462756c6c646275\",\"health\":\"ok\",\"phase\":\"running\",\"sealed\":[\"web.secret\"],\"sealed-at\":{\"web.secret#sealed\":\"box1\"},\"v\":1}", wire: "{\"build\":\"b1\",\"composed\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fp\":\"src2:62756c6c6462756c6c6462756c6c6462756c6c6462756c6c6462756c6c646275\",\"health\":\"ok\",\"phase\":\"running\",\"sealed\":[\"web.secret\"],\"sealed-at\":{\"web.secret#sealed\":\"box1\"},\"v\":1}", + decode: func(b []byte) (any, error) { var v HaveByNodeModuleValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil}, + {name: "a failed apply waiting on a store's holder (typed from the engine's values)", subject: "", given: "{\"composed\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"detail\":\"the pull met a store that is not settled\",\"fp\":\"src2:62756c6c6462756c6c6462756c6c6462756c6c6462756c6c6462756c6c646275\",\"health\":\"failed\",\"phase\":\"failed\",\"pull-wait\":{\"began\":\"2026-11-05T12:00:00Z\",\"holder\":\"gitea\",\"image\":\"registry.example/web:1\",\"retried\":\"the holder settled\",\"retried-at\":\"2026-11-05T12:05:00Z\",\"seat\":\"container-image-registry\"},\"v\":1}", wire: "{\"composed\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"detail\":\"the pull met a store that is not settled\",\"fp\":\"src2:62756c6c6462756c6c6462756c6c6462756c6c6462756c6c6462756c6c646275\",\"health\":\"failed\",\"phase\":\"failed\",\"pull-wait\":{\"began\":\"2026-11-05T12:00:00Z\",\"holder\":\"gitea\",\"image\":\"registry.example/web:1\",\"retried\":\"the holder settled\",\"retried-at\":\"2026-11-05T12:05:00Z\",\"seat\":\"container-image-registry\"},\"v\":1}", + decode: func(b []byte) (any, error) { var v HaveByNodeModuleValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil}, + {name: "a move waiting for a person's yes", subject: "", given: "{\"build-alone\":true,\"composed\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fp\":\"src2:62756c6c6462756c6c6462756c6c6462756c6c6462756c6c6462756c6c646275\",\"health\":\"ok\",\"judgement\":\"recreates\",\"kept\":[\"tool runner not declared\"],\"phase\":\"waiting-yes\",\"v\":1,\"waits\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"}", wire: "{\"build-alone\":true,\"composed\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fp\":\"src2:62756c6c6462756c6c6462756c6c6462756c6c6462756c6c6462756c6c646275\",\"health\":\"ok\",\"judgement\":\"recreates\",\"kept\":[\"tool runner not declared\"],\"phase\":\"waiting-yes\",\"v\":1,\"waits\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"}", + decode: func(b []byte) (any, error) { var v HaveByNodeModuleValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil}, + {name: "the bus module's have carries the user list written (typed from the engine's values)", subject: "", given: "{\"health\":\"ok\",\"phase\":\"running\",\"user-list\":{\"at\":\"2026-11-05T12:00:00Z\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"follows\":\"grants\",\"handed\":{\"at\":\"2026-11-05T12:00:00Z\",\"box\":{\"ct\":\"c\",\"eph\":\"e\",\"iv\":\"i\",\"tag\":\"t\",\"v\":1},\"from\":\"ace\",\"to\":\"sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"users\":3},\"key\":\"-----BEGIN PUBLIC KEY-----\",\"revision\":7,\"source\":\"user-list\",\"users\":3,\"wanted\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"},\"v\":1}", wire: "{\"health\":\"ok\",\"phase\":\"running\",\"user-list\":{\"at\":\"2026-11-05T12:00:00Z\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"follows\":\"grants\",\"handed\":{\"at\":\"2026-11-05T12:00:00Z\",\"box\":{\"ct\":\"c\",\"eph\":\"e\",\"iv\":\"i\",\"tag\":\"t\",\"v\":1},\"from\":\"ace\",\"to\":\"sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"users\":3},\"key\":\"-----BEGIN PUBLIC KEY-----\",\"revision\":7,\"source\":\"user-list\",\"users\":3,\"wanted\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"},\"v\":1}", + decode: func(b []byte) (any, error) { var v HaveByNodeModuleValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil}, + {name: "a node's own facts with its record and an answer to a candidate (typed from the engine's values)", subject: "", given: "{\"candidates\":{\"p1\":{\"added\":[{\"module\":\"db\",\"unissued\":[\"db.secret\"]}],\"differs\":[{\"bodies\":[{\"id\":\"api.cfg\",\"paths\":[\"/etc/api.conf\"],\"why\":\"content\"}],\"composed\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"module\":\"api\",\"running\":\"sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\",\"why\":\"a file differs\"}],\"proposal\":\"p1\",\"refusals\":[{\"module\":\"mail\",\"today\":\"runs\",\"why\":\"a value is missing\"}],\"removed\":[\"old\"],\"revision\":4,\"same\":[\"web\"],\"verdict\":\"differs\"}},\"composed\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"composes\":\"own\",\"record\":{\"capabilities\":[\"docker\"],\"contract\":3,\"networks\":[{\"link\":\"eth0\",\"network\":\"192.0.2.0/24\"}],\"networks-said\":true,\"node\":\"ace\",\"tunnel-key\":\"k\",\"v\":1},\"v\":1}", wire: "{\"candidates\":{\"p1\":{\"added\":[{\"module\":\"db\",\"unissued\":[\"db.secret\"]}],\"differs\":[{\"bodies\":[{\"id\":\"api.cfg\",\"paths\":[\"/etc/api.conf\"],\"why\":\"content\"}],\"composed\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"module\":\"api\",\"running\":\"sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\",\"why\":\"a file differs\"}],\"proposal\":\"p1\",\"refusals\":[{\"module\":\"mail\",\"today\":\"runs\",\"why\":\"a value is missing\"}],\"removed\":[\"old\"],\"revision\":4,\"same\":[\"web\"],\"verdict\":\"differs\"}},\"composed\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"composes\":\"own\",\"record\":{\"capabilities\":[\"docker\"],\"contract\":3,\"networks\":[{\"link\":\"eth0\",\"network\":\"192.0.2.0/24\"}],\"networks-said\":true,\"node\":\"ace\",\"tunnel-key\":\"k\",\"v\":1},\"v\":1}", + decode: func(b []byte) (any, error) { var v HaveByNodeNodeValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil}, + {name: "a module's data items on a node (typed from the engine's values)", subject: "", given: "{\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"items\":[{\"class\":\"database\",\"covered_by\":\"/var/lib/postgres\",\"item\":\"cluster\",\"measure\":\"dataset\",\"module\":\"postgres\",\"path\":\"/var/lib/postgres\",\"protection\":\"backup\"}],\"module\":\"postgres\",\"node\":\"ace\",\"v\":1}", wire: "{\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"items\":[{\"class\":\"database\",\"covered_by\":\"/var/lib/postgres\",\"item\":\"cluster\",\"measure\":\"dataset\",\"module\":\"postgres\",\"path\":\"/var/lib/postgres\",\"protection\":\"backup\"}],\"module\":\"postgres\",\"node\":\"ace\",\"v\":1}", + decode: func(b []byte) (any, error) { var v DataByNodeModuleValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil}, + {name: "a node's health (typed from the engine's values)", subject: "", given: "{\"at\":\"2026-11-05T12:00:00Z\",\"contract\":3,\"network\":{\"parts\":[{\"part\":\"resolver\",\"since\":\"2026-11-05T10:00:00Z\",\"state\":\"running\"}],\"since\":\"2026-11-05T10:00:00Z\",\"state\":\"running\"},\"node\":\"ace\",\"peers\":[{\"addresses\":[\"198.51.100.1\"],\"key\":\"k\",\"seen\":true}],\"relogin\":[{\"account\":\"web\",\"reason\":\"password changed\",\"since\":\"2026-11-05T11:00:00Z\"}],\"units\":{\"failed\":[{\"result\":\"exit-code\",\"scope\":\"system\",\"since\":\"2026-11-05T11:00:00Z\",\"unit\":\"x.service\"}],\"state\":\"degraded\"},\"v\":1}", wire: "{\"at\":\"2026-11-05T12:00:00Z\",\"contract\":3,\"network\":{\"parts\":[{\"part\":\"resolver\",\"since\":\"2026-11-05T10:00:00Z\",\"state\":\"running\"}],\"since\":\"2026-11-05T10:00:00Z\",\"state\":\"running\"},\"node\":\"ace\",\"peers\":[{\"addresses\":[\"198.51.100.1\"],\"key\":\"k\",\"seen\":true}],\"relogin\":[{\"account\":\"web\",\"reason\":\"password changed\",\"since\":\"2026-11-05T11:00:00Z\"}],\"units\":{\"failed\":[{\"result\":\"exit-code\",\"scope\":\"system\",\"since\":\"2026-11-05T11:00:00Z\",\"unit\":\"x.service\"}],\"state\":\"degraded\"},\"v\":1}", + decode: func(b []byte) (any, error) { var v HealthByNodeValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil}, {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}", diff --git a/seats/node-engine/fixtures/states.json b/seats/node-engine/fixtures/states.json index aff5a04..35631e7 100644 --- a/seats/node-engine/fixtures/states.json +++ b/seats/node-engine/fixtures/states.json @@ -48,5 +48,472 @@ } ] } + }, + { + "name": "buildah composed on ace (resources captured from the declaration of 2026-10-24; the fingerprint stands in for the indexer's)", + "of": "state:composed/..", + "given": { + "v": 1, + "node": "ace", + "module": "buildah", + "fp": "src2:62756c6c6462756c6c6462756c6c6462756c6c6462756c6c6462756c6c646275", + "resources": [ + { + "id": "buildah.buildah", + "module": "buildah", + "package": "buildah", + "type": "package" + } + ] + } + }, + { + "name": "node-tools composed with a filled file (captured; the fingerprint stands in)", + "of": "state:composed/..", + "given": { + "v": 1, + "node": "ace", + "module": "node-tools", + "fp": "src2:6e6f64652d6e6f64652d6e6f64652d6e6f64652d6e6f64652d6e6f64652d6e6f", + "resources": [ + { + "id": "node-tools.mesh-state", + "mode": "0755", + "module": "node-tools", + "path": "/var/lib/mesh/node-tools", + "type": "directory" + }, + { + "id": "node-tools.interpreter", + "module": "node-tools", + "package": "nodejs", + "type": "package" + }, + { + "digest": "sha256:ef790876783d817903b337b8418b1eb21fb49d34128a390bbf7c3d08a4b2be8a", + "env": { + "MESH_AGENT_ACCOUNT": "ace", + "MESH_AGENT_HOME": "/home/ace", + "MESH_BROKER_FILE": "/var/lib/mesh/node-tools/broker", + "MESH_OPERATOR_ACCOUNT": "ace", + "MESH_OPERATOR_HOME": "/home/ace", + "MESH_TOOL_ENV": "{\"baserow\":{\"MESH_BASEROW_CONFIG_FILE\":\"/var/lib/mesh/baserow/config.json\",\"MESH_BASEROW_URL\":\"http://127.0.0.1:20011\"},\"bazarr\":{\"MESH_BAZARR_CONFIG_DIR\":\"/var/lib/bazarr/config\",\"MESH_BAZARR_CONFIG_FILE\":\"/var/lib/bazarr/config.json\",\"MESH_BAZARR_CONTAINER\":\"bazarr\",\"MESH_BAZARR_URL\":\"http://127.0.0.1:20020\"},\"bookshelf\":{\"MESH_BOOKSHELF_CONFIG_DIR\":\"/var/lib/bookshelf/config\",\"MESH_BOOKSHELF_URL\":\"http://127.0.0.1:20021\"},\"claude-code\":{\"MESH_CLAUDE_CODE_FACTS\":\"/var/lib/claude-code/facts.json\",\"MESH_CLAUDE_CODE_SETTINGS\":\"/var/lib/claude-code/settings.json\",\"MESH_CLAUDE_CODE_STATE\":\"/var/lib/claude-code\"},\"grafana\":{\"MESH_GRAFANA_CONFIG_FILE\":\"/var/lib/mesh/grafana/config.json\",\"MESH_GRAFANA_URL\":\"http://127.0.0.1:20012\"},\"home-assistant\":{\"MESH_HOMEASSISTANT_CONFIG_FILE\":\"/var/lib/mesh/home-assistant/config.json\",\"MESH_HOMEASSISTANT_TOKEN_FILE\":\"/var/lib/mesh/home-assistant/token\",\"MESH_HOMEASSISTANT_URL\":\"http://127.0.0.1:8123\"},\"icecast\":{\"MESH_ICECAST_ADMIN_PASSWORD_FILE\":\"/var/lib/icecast/admin.secret\",\"MESH_ICECAST_CONFIG_FILE\":\"/var/lib/mesh/icecast/config.json\",\"MESH_ICECAST_URL\":\"http://127.0.0.1:20005\"},\"influxdb\":{\"MESH_INFLUXDB_CONFIG_FILE\":\"/var/lib/mesh/influxdb/config.json\",\"MESH_INFLUXDB_TOKEN_FILE\":\"/var/lib/influxdb/admin-token.secret\",\"MESH_INFLUXDB_URL\":\"http://127.0.0.1:8086\",\"MESH_RECEIVES\":\"/var/lib/influxdb/grants/mesh.json\"},\"jackett\":{\"MESH_JACKETT_API_KEY_FILE\":\"/var/lib/mesh/jackett/api-key\",\"MESH_JACKETT_CONFIG_DIR\":\"/var/lib/jackett/config\",\"MESH_JACKETT_CONFIG_FILE\":\"/var/lib/jackett/config.json\",\"MESH_JACKETT_URL\":\"http://127.0.0.1:20004\"},\"kometa\":{\"MESH_KOMETA_CONFIG_DIR\":\"/var/lib/kometa/config\",\"MESH_KOMETA_CONFIG_FILE\":\"/var/lib/kometa/config.yml\"},\"letta\":{\"MESH_LETTA_CONFIG_FILE\":\"/var/lib/mesh/letta/config.json\",\"MESH_LETTA_URL\":\"http://127.0.0.1:20002\"},\"lidarr\":{\"MESH_LIDARR_API_KEY_FILE\":\"/var/lib/mesh/lidarr/api-key\",\"MESH_LIDARR_CONFIG_DIR\":\"/var/lib/lidarr/config\",\"MESH_LIDARR_RECYCLE_BIN\":\"/recycle/\",\"MESH_LIDARR_RECYCLE_DAYS\":\"7\",\"MESH_LIDARR_URL\":\"http://127.0.0.1:20022\",\"MESH_PLEX_BIND_FILE\":\"/var/lib/lidarr/plex-api.json\",\"MESH_PLEX_TOKEN_FILE\":\"/var/lib/lidarr/plex-api.secret\"},\"media-importer\":{\"MESH_MEDIA_IMPORTER_SETTINGS\":\"/var/lib/media-importer/settings.json\",\"MESH_MEDIA_IMPORTER_STATE\":\"/var/lib/media-importer\",\"MESH_MEDIA_LIBRARY\":\"/storage/media/poc\"},\"mesh-files\":{\"MESH_FILES_STATE\":\"/var/lib/mesh-files\",\"MESH_OWNED_PATHS\":\"{\\\"/etc/NetworkManager/conf.d/50-mesh.conf\\\":\\\"networkmanager (its own tools)\\\",\\\"/etc/claude-code\\\":\\\"claude-code (its own tools)\\\",\\\"/etc/claude-code/agent-account\\\":\\\"claude-code (its own tools)\\\",\\\"/etc/dnsmasq.conf\\\":\\\"dnsmasq (its own tools)\\\",\\\"/etc/docker/daemon.json\\\":\\\"docker (its own tools)\\\",\\\"/etc/fail2ban/action.d\\\":\\\"fail2ban (its own tools)\\\",\\\"/etc/fail2ban/action.d/iptables-allports-dualchain.conf\\\":\\\"fail2ban (its own tools)\\\",\\\"/etc/fail2ban/filter.d\\\":\\\"fail2ban (its own tools)\\\",\\\"/etc/fail2ban/jail.d\\\":\\\"fail2ban (its own tools)\\\",\\\"/etc/fail2ban/jail.d/recidive.conf\\\":\\\"fail2ban (its own tools)\\\",\\\"/etc/fail2ban/jail.d/sshd.conf\\\":\\\"fail2ban (its own tools)\\\",\\\"/etc/fail2ban/jail.local\\\":\\\"fail2ban (its own tools)\\\",\\\"/etc/fail2ban/mesh-own-addresses.json\\\":\\\"fail2ban (its own tools)\\\",\\\"/etc/hostname\\\":\\\"hostname (node-hostname)\\\",\\\"/etc/hosts\\\":\\\"hostname (node-hostname)\\\",\\\"/etc/locale.conf\\\":\\\"localization (its own tools)\\\",\\\"/etc/logrotate.conf\\\":\\\"logrotate (its own tools)\\\",\\\"/etc/logrotate.d/fail2ban\\\":\\\"fail2ban (its own tools)\\\",\\\"/etc/mesh-power\\\":\\\"power (its own tools)\\\",\\\"/etc/mesh-power/moments\\\":\\\"power (its own tools)\\\",\\\"/etc/mesh-power/moments/after-boot\\\":\\\"power (its own tools)\\\",\\\"/etc/mesh-power/moments/after-wake\\\":\\\"power (its own tools)\\\",\\\"/etc/mesh-power/moments/before-shutdown\\\":\\\"power (its own tools)\\\",\\\"/etc/mesh-power/moments/before-sleep\\\":\\\"power (its own tools)\\\",\\\"/etc/mesh-power/moments/on-battery\\\":\\\"power (its own tools)\\\",\\\"/etc/mesh-power/moments/on-mains\\\":\\\"power (its own tools)\\\",\\\"/etc/mesh-resolver/nodes.conf\\\":\\\"dnsmasq (its own tools)\\\",\\\"/etc/mesh-resolver/zones.conf\\\":\\\"dnsmasq (its own tools)\\\",\\\"/etc/nfs-server\\\":\\\"nfs-server (its own tools)\\\",\\\"/etc/nfs-server/machines\\\":\\\"nfs-server (its own tools)\\\",\\\"/etc/nfs-server/shares.conf\\\":\\\"nfs-server (its own tools)\\\",\\\"/etc/nfs.conf.d/50-mesh.conf\\\":\\\"nfs-server (its own tools)\\\",\\\"/etc/pacman.conf\\\":\\\"pacman (its own tools)\\\",\\\"/etc/pacman.d/hooks/90-mesh-kernel-boot-entries.hook\\\":\\\"kernel (its own tools)\\\",\\\"/etc/resolv.conf\\\":\\\"networkmanager (node-uplink)\\\",\\\"/etc/ssh/sshd_config.d/10-mesh.conf\\\":\\\"sshd (its own tools)\\\",\\\"/etc/sudoers.d/10-mesh-operator\\\":\\\"sudo (its own tools)\\\",\\\"/etc/systemd/logind.conf.d\\\":\\\"power (its own tools)\\\",\\\"/etc/systemd/logind.conf.d/power.conf\\\":\\\"power (its own tools)\\\",\\\"/etc/systemd/system/dnsmasq.service.d\\\":\\\"dnsmasq (its own tools)\\\",\\\"/etc/systemd/system/dnsmasq.service.d/mesh.conf\\\":\\\"dnsmasq (its own tools)\\\",\\\"/etc/systemd/system/docker-prune.service\\\":\\\"docker (its own tools)\\\",\\\"/etc/systemd/system/docker-prune.timer\\\":\\\"docker (its own tools)\\\",\\\"/etc/systemd/system/hibernate.target.d\\\":\\\"power (its own tools)\\\",\\\"/etc/systemd/system/hibernate.target.d/mesh-power.conf\\\":\\\"power (its own tools)\\\",\\\"/etc/systemd/system/hybrid-sleep.target.d\\\":\\\"power (its own tools)\\\",\\\"/etc/systemd/system/hybrid-sleep.target.d/mesh-power.conf\\\":\\\"power (its own tools)\\\",\\\"/etc/systemd/system/mesh-ca-trust.service\\\":\\\"ca-trust (its own tools)\\\",\\\"/etc/systemd/system/mesh-filter.service\\\":\\\"nftables (its own tools)\\\",\\\"/etc/systemd/system/mesh-kernel-boot-entries.service\\\":\\\"kernel (its own tools)\\\",\\\"/etc/systemd/system/mesh-kernel-huge-pages.service\\\":\\\"kernel (its own tools)\\\",\\\"/etc/systemd/system/mesh-power-after-boot.service\\\":\\\"power (its own tools)\\\",\\\"/etc/systemd/system/mesh-power-after-wake.service\\\":\\\"power (its own tools)\\\",\\\"/etc/systemd/system/mesh-power-before-shutdown.service\\\":\\\"power (its own tools)\\\",\\\"/etc/systemd/system/mesh-power-before-sleep.service\\\":\\\"power (its own tools)\\\",\\\"/etc/systemd/system/mesh-power-supply.service\\\":\\\"power (its own tools)\\\",\\\"/etc/systemd/system/nftables.service.d/mesh.conf\\\":\\\"nftables (its own tools)\\\",\\\"/etc/systemd/system/sleep.target.d\\\":\\\"power (its own tools)\\\",\\\"/etc/systemd/system/sleep.target.d/mesh-power.conf\\\":\\\"power (its own tools)\\\",\\\"/etc/systemd/system/suspend-then-hibernate.target.d\\\":\\\"power (its own tools)\\\",\\\"/etc/systemd/system/suspend-then-hibernate.target.d/mesh-power.conf\\\":\\\"power (its own tools)\\\",\\\"/etc/systemd/system/suspend.target.d\\\":\\\"power (its own tools)\\\",\\\"/etc/systemd/system/suspend.target.d/mesh-power.conf\\\":\\\"power (its own tools)\\\",\\\"/etc/systemd/timesyncd.conf.d/50-mesh.conf\\\":\\\"time-sync (its own tools)\\\",\\\"/etc/udev/rules.d/90-mesh-power.rules\\\":\\\"power (its own tools)\\\",\\\"/etc/vconsole.conf\\\":\\\"localization (its own tools)\\\",\\\"/etc/xdg/reflector/reflector.conf\\\":\\\"pacman (its own tools)\\\",\\\"/run/mesh-power\\\":\\\"power (its own tools)\\\",\\\"/run/nfs-server\\\":\\\"nfs-server (its own tools)\\\",\\\"/usr/lib/nox-mesh-host/launch\\\":\\\"mesh-host (its own tools)\\\",\\\"/usr/lib/nox-mesh-host/versions/4e121b6e88f1\\\":\\\"mesh-host (its own tools)\\\",\\\"/usr/local/bin\\\":\\\"mesh-cli (its own tools)\\\",\\\"/usr/local/bin/claude-agent\\\":\\\"claude-code (its own tools)\\\",\\\"/usr/local/lib/mesh-power\\\":\\\"power (its own tools)\\\",\\\"/var/lib/dnsmasq-watch\\\":\\\"dnsmasq (its own tools)\\\",\\\"/var/lib/mesh-fail2ban\\\":\\\"fail2ban (its own tools)\\\",\\\"/var/lib/mesh-fail2ban/own-addresses\\\":\\\"fail2ban (its own tools)\\\",\\\"/var/lib/mesh-store\\\":\\\"postgres (its own tools)\\\",\\\"/var/lib/nfs-server\\\":\\\"nfs-server (its own tools)\\\",\\\"/var/lib/nfs-server/from-bus\\\":\\\"nfs-server (its own tools)\\\",\\\"/var/lib/postgres\\\":\\\"postgres (its own tools)\\\",\\\"/var/lib/route-proxy/acme\\\":\\\"route-proxy (its own tools)\\\",\\\"/var/lib/route-proxy/ca\\\":\\\"route-proxy (its own tools)\\\",\\\"/var/lib/route-proxy/routes\\\":\\\"route-proxy (its own tools)\\\",\\\"/var/lib/systemd\\\":\\\"systemd (its own tools)\\\",\\\"/var/log/fail2ban.log\\\":\\\"fail2ban (its own tools)\\\",\\\"/var/run/fail2ban\\\":\\\"fail2ban (its own tools)\\\"}\",\"MESH_SECRET_PLACES\":\"[\\\"/var/lib/baserow\\\",\\\"/var/lib/bazarr/radarr-api.secret\\\",\\\"/var/lib/bazarr/sonarr-api.secret\\\",\\\"/var/lib/bookshelf/jackett-api.secret\\\",\\\"/var/lib/bookshelf/nzbget-api.secret\\\",\\\"/var/lib/bookshelf/qbittorrent-api.secret\\\",\\\"/var/lib/car-hunter\\\",\\\"/var/lib/claude-code/gitea-read.secret\\\",\\\"/var/lib/home-assistant/lidarr-api.secret\\\",\\\"/var/lib/home-assistant/mqtt-topic.secret\\\",\\\"/var/lib/home-assistant/radarr-api.secret\\\",\\\"/var/lib/home-assistant/sonarr-api.secret\\\",\\\"/var/lib/icecast/admin.secret\\\",\\\"/var/lib/icecast/relay.secret\\\",\\\"/var/lib/icecast/source.secret\\\",\\\"/var/lib/influxdb\\\",\\\"/var/lib/kometa\\\",\\\"/var/lib/letta\\\",\\\"/var/lib/lidarr/jackett-api.secret\\\",\\\"/var/lib/lidarr/nzbget-api.secret\\\",\\\"/var/lib/lidarr/plex-api.secret\\\",\\\"/var/lib/lidarr/qbittorrent-api.secret\\\",\\\"/var/lib/mesh-delivery-assigner\\\",\\\"/var/lib/mesh-provisioner-client\\\",\\\"/var/lib/mesh-reads\\\",\\\"/var/lib/mesh/dnsmasq\\\",\\\"/var/lib/mesh/grafana\\\",\\\"/var/lib/mesh/home-assistant\\\",\\\"/var/lib/mesh/jackett\\\",\\\"/var/lib/mesh/lidarr\\\",\\\"/var/lib/mesh/mosquitto\\\",\\\"/var/lib/mesh/node-tools\\\",\\\"/var/lib/mesh/nodered\\\",\\\"/var/lib/mesh/nzbget\\\",\\\"/var/lib/mesh/ombi\\\",\\\"/var/lib/mesh/pacman\\\",\\\"/var/lib/mesh/postgres\\\",\\\"/var/lib/mesh/qbittorrent\\\",\\\"/var/lib/mesh/radarr\\\",\\\"/var/lib/mesh/route-proxy\\\",\\\"/var/lib/mesh/searxng\\\",\\\"/var/lib/mesh/sonarr\\\",\\\"/var/lib/mesh/unifi\\\",\\\"/var/lib/mosquitto/grants\\\",\\\"/var/lib/mssql\\\",\\\"/var/lib/n8n/database.secret\\\",\\\"/var/lib/nodered/mqtt-topic.secret\\\",\\\"/var/lib/nzbgeek\\\",\\\"/var/lib/ombi/lidarr-api.secret\\\",\\\"/var/lib/ombi/plex-api.secret\\\",\\\"/var/lib/ombi/radarr-api.secret\\\",\\\"/var/lib/ombi/sonarr-api.secret\\\",\\\"/var/lib/radarr/jackett-api.secret\\\",\\\"/var/lib/radarr/nzbget-api.secret\\\",\\\"/var/lib/radarr/plex-api.secret\\\",\\\"/var/lib/radarr/qbittorrent-api.secret\\\",\\\"/var/lib/redis/default.secret\\\",\\\"/var/lib/redis/grants\\\",\\\"/var/lib/restic\\\",\\\"/var/lib/sonarr/jackett-api.secret\\\",\\\"/var/lib/sonarr/nzbget-api.secret\\\",\\\"/var/lib/sonarr/plex-api.secret\\\",\\\"/var/lib/sonarr/qbittorrent-api.secret\\\",\\\"/var/lib/supabase\\\",\\\"/var/lib/tautulli/plex-api.secret\\\",\\\"/var/lib/txt-game/database.secret\\\"]\"},\"mosquitto\":{\"MESH_MQTT_CTRL_CONTAINER\":\"mosquitto\",\"MESH_PROVISION_ADMIN_USER\":\"mesh-admin\",\"MESH_PROVISION_MQTT\":\"127.0.0.1:1883\",\"MESH_PROVISION_PASSWORD_FILE\":\"/var/lib/mesh/mosquitto/admin\",\"MESH_RECEIVES\":\"/var/lib/mosquitto/grants/mesh.json\"},\"mssql\":{\"MESH_MSSQL_READER_PASSWORD_FILE\":\"/var/lib/mssql/reader.secret\",\"MESH_PROVISION_MSSQL\":\"mssql://sa@127.0.0.1:4848/master\",\"MESH_PROVISION_PASSWORD_FILE\":\"/var/lib/mssql/sa.secret\",\"MESH_RECEIVES\":\"/var/lib/mssql/grants/mesh.json\"},\"nodered\":{\"MESH_NODERED_CONFIG_FILE\":\"/var/lib/mesh/nodered/config.json\",\"MESH_NODERED_URL\":\"http://127.0.0.1:20006\"},\"nzbgeek\":{\"MESH_NZBGEEK_KEY_FILE\":\"/var/lib/nzbgeek/api-key.secret\",\"MESH_NZBGEEK_SETTINGS\":\"/var/lib/nzbgeek/settings.json\"},\"nzbget\":{\"MESH_NZBGET_CONFIG_DIR\":\"/var/lib/nzbget/config\",\"MESH_NZBGET_CONFIG_FILE\":\"/var/lib/nzbget/config.json\",\"MESH_NZBGET_PASSWORD_FILE\":\"/var/lib/mesh/nzbget/password\",\"MESH_NZBGET_SECRETS_DIR\":\"/var/lib/mesh/nzbget\",\"MESH_NZBGET_STATE\":\"/var/lib/nzbget\",\"MESH_NZBGET_URL\":\"http://127.0.0.1:20017\"},\"ombi\":{\"MESH_OMBI_API_KEY_FILE\":\"/var/lib/mesh/ombi/api-key\",\"MESH_OMBI_CONFIG_FILE\":\"/var/lib/mesh/ombi/config.json\",\"MESH_OMBI_URL\":\"http://127.0.0.1:20024\"},\"pacman\":{\"PACMAN_KEEPER_CONFIG\":\"/var/lib/mesh/pacman/keeper.json\"},\"plex\":{\"MESH_PLEX_CONFIG_DIR\":\"/mnt/plex/config\",\"MESH_PLEX_URL\":\"http://127.0.0.1:32400\"},\"postgres\":{\"MESH_CONSUMERS_FILE\":\"/var/lib/mesh/postgres/consumers.json\",\"MESH_POSTGRES_READER_PASSWORD_FILE\":\"/var/lib/mesh/postgres/reader.secret\",\"MESH_PROVISION_PASSWORD_FILE\":\"/var/lib/mesh/postgres/superuser.secret\",\"MESH_PROVISION_POSTGRES\":\"postgres://postgres@127.0.0.1:5434/postgres?sslmode=disable\",\"MESH_RECEIVES\":\"/var/lib/mesh/postgres/grants/mesh.json\"},\"qbittorrent\":{\"MESH_QBITTORRENT_CONFIG_DIR\":\"/var/lib/qbittorrent/config\",\"MESH_QBITTORRENT_CONFIG_FILE\":\"/var/lib/qbittorrent/config.json\",\"MESH_QBITTORRENT_PASSWORD_FILE\":\"/var/lib/mesh/qbittorrent/password\",\"MESH_QBITTORRENT_URL\":\"http://127.0.0.1:8112\"},\"radarr\":{\"MESH_PLEX_BIND_FILE\":\"/var/lib/radarr/plex-api.json\",\"MESH_PLEX_TOKEN_FILE\":\"/var/lib/radarr/plex-api.secret\",\"MESH_RADARR_API_KEY_FILE\":\"/var/lib/mesh/radarr/api-key\",\"MESH_RADARR_CONFIG_DIR\":\"/var/lib/radarr/config\",\"MESH_RADARR_URL\":\"http://127.0.0.1:20019\"},\"redis\":{\"MESH_PROVISION_PASSWORD_FILE\":\"/var/lib/redis/default.secret\",\"MESH_PROVISION_REDIS\":\"127.0.0.1:20003\",\"MESH_RECEIVES\":\"/var/lib/redis/grants/mesh.json\"},\"restic\":{\"MESH_BACKUP_DATA\":\"/var/lib/restic/data.conf\",\"MESH_BACKUP_DECLARED\":\"/var/lib/restic/backups.conf\",\"MESH_BACKUP_PASSWORD_FILE\":\"/var/lib/restic/repository.secret\",\"MESH_BACKUP_REPOSITORY\":\"/storage/restic\",\"MESH_BACKUP_STATE\":\"/var/lib/restic\"},\"searxng\":{\"MESH_SEARXNG_CONFIG_FILE\":\"/var/lib/mesh/searxng/config.json\",\"MESH_SEARXNG_URL\":\"http://127.0.0.1:20000\"},\"sonarr\":{\"MESH_PLEX_BIND_FILE\":\"/var/lib/sonarr/plex-api.json\",\"MESH_PLEX_TOKEN_FILE\":\"/var/lib/sonarr/plex-api.secret\",\"MESH_SONARR_API_KEY_FILE\":\"/var/lib/mesh/sonarr/api-key\",\"MESH_SONARR_CONFIG_DIR\":\"/var/lib/sonarr/config\",\"MESH_SONARR_RECYCLE_DIR\":\"/var/lib/sonarr/recycle-bin\",\"MESH_SONARR_URL\":\"http://127.0.0.1:20018\"},\"systemd\":{\"MESH_SYSTEMD_STATE\":\"/var/lib/mesh/systemd\"},\"tautulli\":{\"MESH_TAUTULLI_CONFIG_DIR\":\"/var/lib/tautulli/config\",\"MESH_TAUTULLI_CONFIG_FILE\":\"/var/lib/mesh/tautulli/config.json\",\"MESH_TAUTULLI_CONTAINER\":\"tautulli\",\"MESH_TAUTULLI_URL\":\"http://127.0.0.1:20023\"},\"unifi\":{\"MESH_UNIFI_CONFIG_FILE\":\"/var/lib/mesh/unifi/config.json\",\"MESH_UNIFI_URL\":\"https://127.0.0.1:8443\"},\"youtube\":{\"MESH_YOUTUBE_DOWNLOADS\":\"/storage/downloads/youtube\",\"MESH_YOUTUBE_SETTINGS\":\"/var/lib/youtube/settings.json\"},\"zsh\":{\"ZSH_TOOLS_CONF\":\"/var/lib/zsh/tools.conf\"}}", + "MESH_TOOL_MODULES": "avahi=/var/lib/mesh/bundles/avahi/tools/avahi-tools,baserow=/var/lib/mesh/bundles/baserow/tools/tools/index.serve.mjs,bazarr=/var/lib/mesh/bundles/bazarr/code/index.serve.mjs,bazarr=/var/lib/mesh/bundles/bazarr/code/tools/index.serve.mjs,bookshelf=/var/lib/mesh/bundles/bookshelf/code/index.serve.mjs,bookshelf=/var/lib/mesh/bundles/bookshelf/code/tools/index.serve.mjs,claude-code=/var/lib/mesh/bundles/claude-code/tools/claude-code,dbus=/var/lib/mesh/bundles/dbus/tools-go/dbus-tools,disk-load=/var/lib/mesh/bundles/disk-load/tools-go/disk-load,dnsmasq=/var/lib/mesh/bundles/dnsmasq/tools/dnsmasq-tools,docker=/var/lib/mesh/bundles/docker/tools/docker-tools,fail2ban=/var/lib/mesh/bundles/fail2ban/tools/fail2ban-tools,grafana=/var/lib/mesh/bundles/grafana/code/index.serve.mjs,grafana=/var/lib/mesh/bundles/grafana/code/tools/index.serve.mjs,home-assistant=/var/lib/mesh/bundles/home-assistant/code/index.serve.mjs,home-assistant=/var/lib/mesh/bundles/home-assistant/code/tools/index.serve.mjs,hostname=/var/lib/mesh/bundles/hostname/tools/hostname-tools,icecast=/var/lib/mesh/bundles/icecast/code/index.serve.mjs,icecast=/var/lib/mesh/bundles/icecast/code/tools/index.serve.mjs,influxdb=/var/lib/mesh/bundles/influxdb/code/provisioner/index.serve.mjs,influxdb=/var/lib/mesh/bundles/influxdb/code/tools/index.serve.mjs,jackett=/var/lib/mesh/bundles/jackett/code/tools/index.serve.mjs,kernel=/var/lib/mesh/bundles/kernel/tools/kernel-tools,kometa=/var/lib/mesh/bundles/kometa/code/tools/index.serve.mjs,letta=/var/lib/mesh/bundles/letta/tools/tools/index.serve.mjs,lidarr=/var/lib/mesh/bundles/lidarr/tools-go/lidarr,localization=/var/lib/mesh/bundles/localization/tools/localization-tools,logrotate=/var/lib/mesh/bundles/logrotate/tools/logrotate-tools,media-importer=/var/lib/mesh/bundles/media-importer/tools/media-importer,mesh-files=/var/lib/mesh/bundles/mesh-files/tools/mesh-files,mosquitto=/var/lib/mesh/bundles/mosquitto/code/index.serve.mjs,mosquitto=/var/lib/mesh/bundles/mosquitto/code/provisioner/index.serve.mjs,mosquitto=/var/lib/mesh/bundles/mosquitto/code/tools/index.serve.mjs,mssql=/var/lib/mesh/bundles/mssql/code/index.serve.mjs,mssql=/var/lib/mesh/bundles/mssql/code/provisioner/index.serve.mjs,mssql=/var/lib/mesh/bundles/mssql/code/tools/index.serve.mjs,netcheck=/var/lib/mesh/bundles/netcheck/tools-go/netcheck,netcheck=/var/lib/mesh/bundles/netcheck/tools-typescript/tools/index.serve.mjs,networkmanager=/var/lib/mesh/bundles/networkmanager/tools/uplink-tools,nfs-server=/var/lib/mesh/bundles/nfs-server/tools/nfs-server,nftables=/var/lib/mesh/bundles/nftables/tools/tools/index.serve.mjs,nodered=/var/lib/mesh/bundles/nodered/code/tools/index.serve.mjs,nzbgeek=/var/lib/mesh/bundles/nzbgeek/tools/nzbgeek,nzbget=/var/lib/mesh/bundles/nzbget/code/nzbget,ombi=/var/lib/mesh/bundles/ombi/code/index.serve.mjs,ombi=/var/lib/mesh/bundles/ombi/code/tools/index.serve.mjs,pacman=/var/lib/mesh/bundles/pacman/tools/pacman-tools,plex=/var/lib/mesh/bundles/plex/code/plex,postgres=/var/lib/mesh/bundles/postgres/code/postgres-provider,power=/var/lib/mesh/bundles/power/tools-go/power,qbittorrent=/var/lib/mesh/bundles/qbittorrent/code/qbittorrent,radarr=/var/lib/mesh/bundles/radarr/tools-go/radarr,redis=/var/lib/mesh/bundles/redis/code/index.serve.mjs,redis=/var/lib/mesh/bundles/redis/code/provisioner/index.serve.mjs,redis=/var/lib/mesh/bundles/redis/code/tools/index.serve.mjs,restic=/var/lib/mesh/bundles/restic/tools/restic-backups,route-proxy=/var/lib/mesh/bundles/route-proxy/provider/route-provider,searxng=/var/lib/mesh/bundles/searxng/tools/tools/index.serve.mjs,sonarr=/var/lib/mesh/bundles/sonarr/code/index.serve.mjs,sonarr=/var/lib/mesh/bundles/sonarr/code/tools/index.serve.mjs,ssh-client=/var/lib/mesh/bundles/ssh-client/tools/ssh-client-tools,sudo=/var/lib/mesh/bundles/sudo/tools/sudo-tools,systemd=/var/lib/mesh/bundles/systemd/tools/systemd-tools,tautulli=/var/lib/mesh/bundles/tautulli/code/index.serve.mjs,tautulli=/var/lib/mesh/bundles/tautulli/code/tools/index.serve.mjs,time-sync=/var/lib/mesh/bundles/time-sync/tools/time-sync-tools,unifi=/var/lib/mesh/bundles/unifi/tools/tools/index.serve.mjs,youtube=/var/lib/mesh/bundles/youtube/tools/youtube,zsh=/var/lib/mesh/bundles/zsh/tools/tools/index.serve.mjs" + }, + "id": "node-tools.runtime", + "module": "node-tools", + "name": "node-tools", + "restart-on": [ + "avahi.bundle-tools", + "baserow.bundle-tools", + "bazarr.bundle-code", + "bookshelf.bundle-code", + "claude-code.bundle-tools", + "dbus.bundle-tools-go", + "disk-load.bundle-tools-go", + "dnsmasq.bundle-tools", + "docker.bundle-tools", + "fail2ban.bundle-tools", + "grafana.bundle-code", + "home-assistant.bundle-code", + "hostname.bundle-tools", + "icecast.bundle-code", + "influxdb.bundle-code", + "jackett.bundle-code", + "kernel.bundle-tools", + "kometa.bundle-code", + "letta.bundle-tools", + "lidarr.bundle-tools-go", + "localization.bundle-tools", + "logrotate.bundle-tools", + "media-importer.bundle-tools", + "mesh-files.bundle-tools", + "mosquitto.bundle-code", + "mssql.bundle-code", + "netcheck.bundle-tools-go", + "netcheck.bundle-tools-typescript", + "networkmanager.bundle-tools", + "nfs-server.bundle-tools", + "nftables.bundle-tools", + "node-tools.needs-broker", + "nodered.bundle-code", + "nzbgeek.bundle-tools", + "nzbget.bundle-code", + "ombi.bundle-code", + "pacman.bundle-tools", + "plex.bundle-code", + "postgres.bundle-code", + "power.bundle-tools-go", + "qbittorrent.bundle-code", + "radarr.bundle-tools-go", + "redis.bundle-code", + "restic.bundle-tools", + "route-proxy.bundle-provider", + "searxng.bundle-tools", + "sonarr.bundle-code", + "ssh-client.bundle-tools", + "sudo.bundle-tools", + "systemd.bundle-tools", + "tautulli.bundle-code", + "time-sync.bundle-tools", + "unifi.bundle-tools", + "youtube.bundle-tools", + "zsh.bundle-tools", + "baserow.runtime-config", + "bazarr.runtime-config", + "claude-code.facts", + "claude-code.settings", + "grafana.runtime-config", + "home-assistant.needs-token", + "home-assistant.runtime-config", + "icecast.runtime-config", + "icecast.secret-admin", + "influxdb.needs-admin-token", + "influxdb.received-influxdb-api", + "influxdb.runtime-config", + "jackett.needs-api-key", + "jackett.runtime-config", + "kometa.settings", + "letta.runtime-config", + "lidarr.bound-plex-api", + "lidarr.needs-api-key", + "lidarr.secret-plex-api", + "media-importer.settings", + "mosquitto.needs-admin", + "mosquitto.received-mqtt-topic", + "mssql.needs-reader", + "mssql.needs-sa", + "mssql.received-mssql-database", + "nodered.runtime-config", + "nzbgeek.needs-api-key", + "nzbgeek.settings", + "nzbget.needs-password", + "nzbget.runtime-config", + "ombi.needs-api-key", + "ombi.runtime-config", + "pacman.keeper-config", + "postgres.consumers", + "postgres.needs-reader", + "postgres.needs-superuser", + "postgres.received-postgres-database", + "qbittorrent.needs-password", + "qbittorrent.runtime-config", + "radarr.bound-plex-api", + "radarr.needs-api-key", + "radarr.secret-plex-api", + "redis.received-redis-cache", + "redis.secret-secret", + "restic.data-declared", + "restic.declared", + "restic.needs-repository", + "searxng.runtime-config", + "sonarr.bound-plex-api", + "sonarr.needs-api-key", + "sonarr.secret-plex-api", + "tautulli.runtime-config", + "unifi.runtime-config", + "youtube.settings", + "zsh.tools-conf" + ], + "run": [ + "./node-tools" + ], + "source": "http://novox.internal:5100/v2/node-tools/runtime/blobs/sha256:ef790876783d817903b337b8418b1eb21fb49d34128a390bbf7c3d08a4b2be8a", + "type": "process", + "user": "ace" + } + ] + } + }, + { + "name": "a module the engine could not compose", + "of": "state:composed/..", + "given": { + "v": 1, + "node": "ace", + "module": "nzbget", + "fp": "src2:62756c6c6462756c6c6462756c6c6462756c6c6462756c6c6462756c6c646275", + "resources": [], + "refused": "a secret the vault does not hold" + } + }, + { + "name": "the node's own composition", + "of": "state:composed/.node", + "given": { + "v": 1, + "node": "ace", + "resources": [], + "own": { + "adoption": { + "taken": [ + "avahi" + ], + "untaken": { + "plex": [ + "plex.bundle-code" + ] + } + }, + "identities": [ + { + "module": "plex", + "name": "plex", + "uid": 1001, + "gid": 1001, + "groups": [ + 1002 + ], + "runner": "plex" + } + ], + "groups": [ + { + "name": "mesh-media", + "gid": 1002 + } + ], + "libraries": [ + { + "library": "movies", + "root": "/srv/media/movies", + "group": "mesh-media", + "gid": 1002, + "home": false, + "taken": true, + "paths": [ + "/srv/media/movies" + ] + } + ], + "provisioner_account": "mesh-provisioner-client" + } + } + }, + { + "name": "a module running (typed from the engine's own values; no read tool answers a have key)", + "of": "state:have/.", + "given": { + "v": 1, + "fp": "src2:62756c6c6462756c6c6462756c6c6462756c6c6462756c6c6462756c6c646275", + "composed": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "build": "b1", + "phase": "running", + "health": "ok", + "sealed": [ + "web.secret" + ], + "sealed-at": { + "web.secret#sealed": "box1" + } + } + }, + { + "name": "a failed apply waiting on a store's holder (typed from the engine's values)", + "of": "state:have/.", + "given": { + "v": 1, + "fp": "src2:62756c6c6462756c6c6462756c6c6462756c6c6462756c6c6462756c6c646275", + "composed": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "phase": "failed", + "health": "failed", + "detail": "the pull met a store that is not settled", + "pull-wait": { + "image": "registry.example/web:1", + "seat": "container-image-registry", + "holder": "gitea", + "began": "2026-11-05T12:00:00Z", + "retried": "the holder settled", + "retried-at": "2026-11-05T12:05:00Z" + } + } + }, + { + "name": "a move waiting for a person's yes", + "of": "state:have/.", + "given": { + "v": 1, + "fp": "src2:62756c6c6462756c6c6462756c6c6462756c6c6462756c6c6462756c6c646275", + "composed": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "phase": "waiting-yes", + "health": "ok", + "judgement": "recreates", + "waits": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "build-alone": true, + "kept": [ + "tool runner not declared" + ] + } + }, + { + "name": "the bus module's have carries the user list written (typed from the engine's values)", + "of": "state:have/.", + "given": { + "v": 1, + "phase": "running", + "health": "ok", + "user-list": { + "source": "user-list", + "revision": 7, + "wanted": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "users": 3, + "at": "2026-11-05T12:00:00Z", + "follows": "grants", + "key": "-----BEGIN PUBLIC KEY-----", + "handed": { + "from": "ace", + "to": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "users": 3, + "box": { + "v": 1, + "eph": "e", + "iv": "i", + "tag": "t", + "ct": "c" + }, + "at": "2026-11-05T12:00:00Z" + } + } + } + }, + { + "name": "a node's own facts with its record and an answer to a candidate (typed from the engine's values)", + "of": "state:have/.node", + "given": { + "v": 1, + "composed": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "record": { + "v": 1, + "node": "ace", + "tunnel-key": "k", + "capabilities": [ + "docker" + ], + "networks": [ + { + "link": "eth0", + "network": "192.0.2.0/24" + } + ], + "networks-said": true, + "contract": 3 + }, + "composes": "own", + "candidates": { + "p1": { + "proposal": "p1", + "revision": 4, + "verdict": "differs", + "same": [ + "web" + ], + "differs": [ + { + "module": "api", + "composed": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "running": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "why": "a file differs", + "bodies": [ + { + "id": "api.cfg", + "why": "content", + "paths": [ + "/etc/api.conf" + ] + } + ] + } + ], + "added": [ + { + "module": "db", + "unissued": [ + "db.secret" + ] + } + ], + "removed": [ + "old" + ], + "refusals": [ + { + "module": "mail", + "why": "a value is missing", + "today": "runs" + } + ] + } + } + } + }, + { + "name": "a module's data items on a node (typed from the engine's values)", + "of": "state:data/.", + "given": { + "v": 1, + "node": "ace", + "module": "postgres", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "items": [ + { + "module": "postgres", + "item": "cluster", + "class": "database", + "path": "/var/lib/postgres", + "covered_by": "/var/lib/postgres", + "protection": "backup", + "measure": "dataset" + } + ] + } + }, + { + "name": "a node's health (typed from the engine's values)", + "of": "state:health/", + "given": { + "v": 1, + "node": "ace", + "network": { + "state": "running", + "since": "2026-11-05T10:00:00Z", + "parts": [ + { + "part": "resolver", + "state": "running", + "since": "2026-11-05T10:00:00Z" + } + ] + }, + "peers": [ + { + "key": "k", + "addresses": [ + "198.51.100.1" + ], + "seen": true + } + ], + "units": { + "state": "degraded", + "failed": [ + { + "unit": "x.service", + "scope": "system", + "result": "exit-code", + "since": "2026-11-05T11:00:00Z" + } + ] + }, + "relogin": [ + { + "account": "web", + "reason": "password changed", + "since": "2026-11-05T11:00:00Z" + } + ], + "contract": 3, + "at": "2026-11-05T12:00:00Z" + } } ] diff --git a/seats/node-engine/seat.json b/seats/node-engine/seat.json index fec0fbb..237a651 100644 --- a/seats/node-engine/seat.json +++ b/seats/node-engine/seat.json @@ -754,58 +754,84 @@ }, "composed": { "type": "string", - "description": "the composition's digest" + "description": "the digest of the composition the node runs" }, "build": { "type": "string" }, "phase": { "type": "string", - "description": "how far it has come" + "description": "how far it has come", + "enum": [ + "backing-up", + "applying", + "checking", + "running", + "failed", + "removed", + "held", + "waiting-yes" + ] }, "health": { "type": "string", - "description": "how it is" + "description": "what the module's own check says", + "enum": [ + "ok", + "failed", + "waits", + "unknown" + ] }, "detail": { "type": "string" }, "user-list": { - "type": "object", - "description": "the user list written" + "$ref": "#/defs/UserListWritten" }, "judgement": { - "type": "string" + "type": "string", + "description": "in phase waiting-yes, the judgement of the move", + "enum": [ + "recreates", + "moves-data", + "cannot-judge" + ] }, "waits": { "type": "string", - "description": "what it waits for" + "description": "in phase waiting-yes, the digest of the composition that waits" }, "build-alone": { - "type": "boolean" + "type": "boolean", + "description": "the move changes the build alone" }, "kept": { "type": "array", "items": { "type": "string" - } + }, + "description": "what the node keeps of the module as it runs" }, "sealed": { "type": "array", "items": { "type": "string" - } + }, + "description": "each value sealed to the node's key" }, "sealed-at": { - "type": "object" + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "the sealed values by slot" }, "pull-wait": { - "type": "object", - "description": "a pull the engine waits for" + "$ref": "#/defs/PullWait" }, "handed": { - "type": "object", - "description": "the accounts handed" + "$ref": "#/defs/AccountsHanded" } }, "required": [ @@ -826,7 +852,8 @@ "type": "integer" }, "composed": { - "type": "string" + "type": "string", + "description": "the digest of the node's own composition it runs" }, "windows": { "type": "object" @@ -844,8 +871,7 @@ "type": "object" }, "record": { - "type": "object", - "description": "the node's record: its tunnel and certificate" + "$ref": "#/defs/NodeRecord" }, "sealed": { "type": "array", @@ -854,17 +880,21 @@ } }, "sealed-at": { - "type": "object" + "type": "object", + "additionalProperties": { + "type": "string" + } }, "candidates": { "type": "object", "additionalProperties": { - "type": "object" - } + "$ref": "#/defs/CandidateAnswer" + }, + "description": "the node's answer to each placement candidate naming it, by proposal id" }, "composes": { "type": "string", - "description": "own, or the controller's" + "description": "own, or none (empty)" }, "composes-why": { "type": "string" @@ -890,53 +920,96 @@ "type": "object", "description": "one composition of a module on a node under a fingerprint", "properties": { - "V": { + "v": { "type": "integer" }, - "Node": { + "node": { "type": "string" }, - "Module": { + "module": { "type": "string" }, - "FP": { + "fp": { "type": "string" }, - "Data": { - "type": "boolean" + "data": { + "type": "boolean", + "description": "the module declares data" }, - "Resources": { + "resources": { "type": "array", "items": { "type": "object" - } + }, + "description": "the declaration resources, each saying its module" }, - "Filled": { + "filled_files": { "type": "array", "items": { "type": "string" - } + }, + "description": "the files the provisioner fills" }, - "Own": { + "plan": { "type": "object", - "description": "the node's own adoption, identities, groups, libraries and provisioner account" + "description": "the module's whole provisioning plan, catalogue.Plan, kept whole: it is the manifest layer's type" }, - "Plan": { - "type": "object", - "description": "the module's plan" - }, - "Refused": { + "refused": { "type": "string", - "description": "why the composition was refused" + "description": "why the module cannot be composed at this fingerprint" } }, "required": [ - "Node", - "Module", - "FP" + "v", + "node", + "module", + "fp", + "resources" ] }, "description": "one composition" + }, + { + "key": ".node", + "value": { + "type": "object", + "description": "the node's own composition: its resources and parts", + "properties": { + "v": { + "type": "integer" + }, + "node": { + "type": "string" + }, + "data": { + "type": "boolean", + "description": "the module declares data" + }, + "resources": { + "type": "array", + "items": { + "type": "object" + }, + "description": "the declaration resources, each saying its module" + }, + "filled_files": { + "type": "array", + "items": { + "type": "string" + }, + "description": "the files the provisioner fills" + }, + "own": { + "$ref": "#/defs/NodeOwn" + } + }, + "required": [ + "v", + "node", + "resources" + ] + }, + "description": "the node's own composition" } ], "description": "the compositions of one node, kept in a bucket per node named composed-" @@ -966,11 +1039,12 @@ "items": { "type": "array", "items": { - "type": "object" + "$ref": "#/defs/PlacedDataItem" } } }, "required": [ + "v", "node", "module", "digest", @@ -1048,36 +1122,36 @@ "type": "string" }, "network": { - "type": "object" + "$ref": "#/defs/NetworkHealth" }, "peers": { "type": "array", "items": { - "type": "object" + "$ref": "#/defs/PeerSeen" } }, "units": { - "type": "object" + "$ref": "#/defs/UnitsHealth" }, "relogin": { "type": "array", "items": { - "type": "object" + "$ref": "#/defs/NodeAccount" } }, "found": { "type": "array", "items": { - "type": "object" + "$ref": "#/defs/NodeDirectory" } }, "fallback": { - "type": "object" + "$ref": "#/defs/LoginFallback" }, "root": { "type": "array", "items": { - "type": "object" + "$ref": "#/defs/RootAccountVerdict" } }, "contract": { @@ -1085,6 +1159,7 @@ }, "at": { "type": "string", + "description": "when", "format": "date-time" } }, @@ -1703,6 +1778,874 @@ "at", "said" ] + }, + "UserListWritten": { + "type": "object", + "description": "the bus's user list as the engine of the bus node wrote it to the bus module's file", + "properties": { + "source": { + "type": "string", + "description": "the key the list was taken from" + }, + "revision": { + "type": "integer" + }, + "wanted": { + "type": "string", + "description": "the digest of the list the source holds, sha256:" + }, + "digest": { + "type": "string", + "description": "the digest of the file on the machine after the write" + }, + "users": { + "type": "integer" + }, + "at": { + "type": "string", + "description": "when", + "format": "date-time" + }, + "error": { + "type": "string", + "description": "why a write failed" + }, + "follows": { + "type": "string", + "description": "which list the node follows now" + }, + "key": { + "type": "string", + "description": "the bus node's sealing key, its public half" + }, + "unwatched": { + "type": "string", + "description": "why the bus node does not watch the grants part's key now" + }, + "handed": { + "$ref": "#/defs/AccountsHanded" + } + }, + "required": [ + "source", + "revision", + "wanted", + "users", + "at" + ] + }, + "AccountsHanded": { + "type": "object", + "description": "the hashes of the users of the list, sealed to the vault", + "properties": { + "from": { + "type": "string" + }, + "to": { + "type": "string", + "description": "the fingerprint of the vault's key" + }, + "users": { + "type": "integer" + }, + "box": { + "$ref": "#/defs/SealedBox" + }, + "at": { + "type": "string", + "description": "when", + "format": "date-time" + } + }, + "required": [ + "from", + "to", + "users", + "box", + "at" + ] + }, + "SealedBox": { + "type": "object", + "description": "a value sealed to a key", + "properties": { + "v": { + "type": "integer" + }, + "eph": { + "type": "string" + }, + "iv": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "ct": { + "type": "string" + } + }, + "required": [ + "v", + "eph", + "iv", + "tag", + "ct" + ] + }, + "PullWait": { + "type": "object", + "description": "the pull from a store a failed apply waits on", + "properties": { + "image": { + "type": "string" + }, + "seat": { + "type": "string", + "description": "the seat the pull came through" + }, + "holder": { + "type": "string", + "description": "that seat's holder, whose settling the apply waits for" + }, + "began": { + "type": "string", + "description": "when the pull began", + "format": "date-time" + }, + "retried": { + "type": "string", + "description": "what triggered the last try again, in words" + }, + "retried-at": { + "type": "string", + "description": "when the apply was last tried again", + "format": "date-time" + } + }, + "required": [ + "image", + "holder", + "began" + ] + }, + "NodeRecord": { + "type": "object", + "description": "what the machine observes of itself, for every node and composing part to read", + "properties": { + "v": { + "type": "integer" + }, + "node": { + "type": "string" + }, + "tunnel-key": { + "type": "string" + }, + "capabilities": { + "type": "array", + "items": { + "type": "string" + } + }, + "sealing-key": { + "type": "string" + }, + "serving-key": { + "type": "string" + }, + "certificate": { + "type": "string" + }, + "outward-links": { + "type": "array", + "items": { + "type": "string" + } + }, + "networks": { + "type": "array", + "items": { + "$ref": "#/defs/AttachedNetwork" + } + }, + "networks-said": { + "type": "boolean" + }, + "contract": { + "type": "integer" + }, + "authority": { + "type": "string" + }, + "trusts": { + "type": "array", + "items": { + "type": "string" + } + }, + "manifest-reads": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "v", + "node" + ] + }, + "AttachedNetwork": { + "type": "object", + "description": "a network attached to a link", + "properties": { + "link": { + "type": "string" + }, + "network": { + "type": "string" + } + }, + "required": [ + "link", + "network" + ] + }, + "CandidateAnswer": { + "type": "object", + "description": "the node's answer to one placement candidate naming it", + "properties": { + "proposal": { + "type": "string" + }, + "revision": { + "type": "integer" + }, + "verdict": { + "type": "string", + "description": "same, differs or refused", + "enum": [ + "same", + "differs", + "refused" + ] + }, + "same": { + "type": "array", + "items": { + "type": "string" + }, + "description": "modules whose composition is the one running" + }, + "differs": { + "type": "array", + "items": { + "$ref": "#/defs/CandidateDiffers" + } + }, + "added": { + "type": "array", + "items": { + "$ref": "#/defs/CandidateAdded" + } + }, + "removed": { + "type": "array", + "items": { + "type": "string" + }, + "description": "modules the candidate removes" + }, + "refusals": { + "type": "array", + "items": { + "$ref": "#/defs/CandidateRefusal" + } + }, + "held": { + "type": "array", + "items": { + "$ref": "#/defs/CandidateRefusal" + } + }, + "refused": { + "type": "string", + "description": "why the node cannot compose with the candidate at all" + }, + "returns": { + "type": "string" + }, + "note": { + "type": "string" + }, + "values_differ": { + "type": "integer" + } + }, + "required": [ + "proposal", + "revision", + "verdict" + ] + }, + "CandidateDiffers": { + "type": "object", + "description": "a module whose composition with the candidate differs from the one running", + "properties": { + "module": { + "type": "string" + }, + "composed": { + "type": "string" + }, + "running": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "why": { + "type": "string" + }, + "bodies": { + "type": "array", + "items": { + "$ref": "#/defs/CandidateBodies" + } + } + }, + "required": [ + "module", + "why" + ] + }, + "CandidateBodies": { + "type": "object", + "description": "the bodies of one resource that differ", + "properties": { + "id": { + "type": "string" + }, + "why": { + "type": "string" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "composed": { + "type": "string" + }, + "running": { + "type": "string" + } + }, + "required": [ + "id", + "why" + ] + }, + "CandidateAdded": { + "type": "object", + "description": "a module the candidate adds", + "properties": { + "module": { + "type": "string" + }, + "composed": { + "type": "string" + }, + "unissued": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "module" + ] + }, + "CandidateRefusal": { + "type": "object", + "description": "a module refused or held with the candidate", + "properties": { + "module": { + "type": "string" + }, + "why": { + "type": "string" + }, + "today": { + "type": "string" + } + }, + "required": [ + "module", + "why" + ] + }, + "NodeOwn": { + "type": "object", + "description": "what a node's own composition carries beside its resources", + "properties": { + "adoption": { + "$ref": "#/defs/Adoption" + }, + "identities": { + "type": "array", + "items": { + "$ref": "#/defs/DeclaredIdentity" + } + }, + "groups": { + "type": "array", + "items": { + "$ref": "#/defs/DeclaredGroup" + } + }, + "libraries": { + "type": "array", + "items": { + "$ref": "#/defs/DeclaredLibrary" + } + }, + "provisioner_account": { + "type": "string", + "description": "the account the provisioner's client runs as on the node" + } + } + }, + "Adoption": { + "type": "object", + "description": "the node is adopted and which of its modules are taken", + "properties": { + "taken": { + "type": "array", + "items": { + "type": "string" + }, + "description": "the modules taken" + }, + "untaken": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "by module, the resources left untaken" + } + }, + "required": [ + "taken" + ] + }, + "DeclaredIdentity": { + "type": "object", + "description": "the identity of a module assigned to the node", + "properties": { + "module": { + "type": "string" + }, + "name": { + "type": "string" + }, + "uid": { + "type": "integer" + }, + "gid": { + "type": "integer" + }, + "groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "runner": { + "type": "string" + } + }, + "required": [ + "module", + "name", + "uid", + "gid" + ] + }, + "DeclaredGroup": { + "type": "object", + "description": "a group", + "properties": { + "name": { + "type": "string" + }, + "gid": { + "type": "integer" + } + }, + "required": [ + "name", + "gid" + ] + }, + "DeclaredLibrary": { + "type": "object", + "description": "a library the node is reached by", + "properties": { + "library": { + "type": "string" + }, + "root": { + "type": "string" + }, + "group": { + "type": "string" + }, + "gid": { + "type": "integer" + }, + "home": { + "type": "boolean" + }, + "taken": { + "type": "boolean" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + }, + "runner": { + "type": "string" + } + }, + "required": [ + "library", + "root", + "group", + "gid", + "home" + ] + }, + "PlacedDataItem": { + "type": "object", + "description": "one data item a module declares, as placed on a node", + "properties": { + "module": { + "type": "string" + }, + "item": { + "type": "string" + }, + "class": { + "type": "string" + }, + "path": { + "type": "string", + "description": "the item's place on the node" + }, + "covered_by": { + "type": "string", + "description": "the path whose snapshot keeps it, empty when it is not backed up" + }, + "protection": { + "type": "string", + "description": "backup, redundancy, both or none" + }, + "measure": { + "type": "string", + "description": "walk, dataset or shallow" + } + }, + "required": [ + "module", + "item", + "class", + "path" + ] + }, + "NetworkHealth": { + "type": "object", + "description": "the node's network", + "properties": { + "state": { + "type": "string", + "description": "running, degraded or unknown" + }, + "since": { + "type": "string", + "description": "since when", + "format": "date-time" + }, + "parts": { + "type": "array", + "items": { + "$ref": "#/defs/NetworkPart" + } + } + }, + "required": [ + "state", + "since", + "parts" + ] + }, + "NetworkPart": { + "type": "object", + "description": "one part of the node's network", + "properties": { + "part": { + "type": "string" + }, + "state": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "said": { + "type": "string" + }, + "writer": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "toward": { + "type": "array", + "items": { + "type": "string" + } + }, + "targets": { + "type": "array", + "items": { + "type": "string" + } + }, + "since": { + "type": "string", + "description": "since when", + "format": "date-time" + }, + "streak": { + "type": "integer" + } + }, + "required": [ + "part", + "state", + "since" + ] + }, + "UnitsHealth": { + "type": "object", + "description": "the node's units", + "properties": { + "state": { + "type": "string" + }, + "failed": { + "type": "array", + "items": { + "$ref": "#/defs/FailedUnit" + } + }, + "unread": { + "type": "array", + "items": { + "type": "string" + } + }, + "away": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "state", + "failed" + ] + }, + "FailedUnit": { + "type": "object", + "description": "a unit that failed", + "properties": { + "unit": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "load": { + "type": "string" + }, + "result": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "since": { + "type": "string", + "description": "since when", + "format": "date-time" + } + }, + "required": [ + "unit", + "scope", + "since" + ] + }, + "PeerSeen": { + "type": "object", + "description": "a tunnel peer and whether it is seen", + "properties": { + "key": { + "type": "string" + }, + "addresses": { + "type": "array", + "items": { + "type": "string" + } + }, + "seen": { + "type": "boolean" + } + }, + "required": [ + "key", + "seen" + ] + }, + "NodeAccount": { + "type": "object", + "description": "an account the node needs to log in again", + "properties": { + "account": { + "type": "string" + }, + "module": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "since": { + "type": "string", + "description": "since when", + "format": "date-time" + } + }, + "required": [ + "account", + "reason", + "since" + ] + }, + "NodeDirectory": { + "type": "object", + "description": "a directory found where the node expected none", + "properties": { + "module": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "path": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "since": { + "type": "string", + "description": "since when", + "format": "date-time" + } + }, + "required": [ + "module", + "resource", + "path", + "reason", + "since" + ] + }, + "LoginFallback": { + "type": "object", + "description": "a login that fell back to another node", + "properties": { + "node": { + "type": "string" + }, + "from": { + "type": "string" + }, + "why": { + "type": "string" + }, + "at": { + "type": "string", + "description": "when", + "format": "date-time" + } + }, + "required": [ + "node", + "from", + "why", + "at" + ] + }, + "RootAccountVerdict": { + "type": "object", + "description": "the engine's verdict of whether an account's resource is root-free, as the node's health lists it", + "properties": { + "account": { + "type": "string" + }, + "module": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "state": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "since": { + "type": "string", + "description": "since when", + "format": "date-time" + } + }, + "required": [ + "account", + "module", + "resource", + "state", + "since" + ] } } } diff --git a/ts/seats/nodeengine/src/index.ts b/ts/seats/nodeengine/src/index.ts index a03d9ab..dbc0378 100644 --- a/ts/seats/nodeengine/src/index.ts +++ b/ts/seats/nodeengine/src/index.ts @@ -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". */ +export function composedByNodeNodeKey(node: string): string { + return node + ".node"; +} + +/** The parts of a key of the form ".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 ".". */ 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; +} + /** 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; @@ -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: */ + "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 .. */ 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; - /** 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; + "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; - /** the user list written */ - "user-list"?: Record; + /** the sealed values by slot */ + "sealed-at"?: Record; + "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. */ export interface HaveByNodeNodeValue { "busy"?: Record; - "candidates"?: Record>; + /** the node's answer to each placement candidate naming it, by proposal id */ + "candidates"?: Record; + /** 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; "profile"?: Record; - /** the node's record: its tunnel and certificate */ - "record"?: Record; + "record"?: NodeRecord; "sealed"?: string[]; - "sealed-at"?: Record; + "sealed-at"?: Record; "strays"?: Record; "v": number; "windows"?: Record; @@ -757,28 +1063,42 @@ export interface HaveByNodeNodeValue { /** is one value of the state composed under the key ... */ 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; - /** the module's plan */ - "Plan"?: Record; - /** why the composition was refused */ - "Refused"?: string; - "Resources"?: Record[]; - "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; + /** why the module cannot be composed at this fingerprint */ + "refused"?: string; + /** the declaration resources, each saying its module */ + "resources": Record[]; + "v": number; +} + +/** is one value of the state composed under the key .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[]; + "v": number; } /** is one value of the state data under the key .. */ export interface DataByNodeModuleValue { "digest": string; - "items": Record[]; + "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 . */ export interface HealthByNodeValue { + /** when */ "at": string; "contract"?: number; - "fallback"?: Record; - "found"?: Record[]; - "network"?: Record; + "fallback"?: LoginFallback; + "found"?: NodeDirectory[]; + "network"?: NetworkHealth; "node": string; - "peers"?: Record[]; - "relogin"?: Record[]; - "root"?: Record[]; - "units"?: Record; + "peers"?: PeerSeen[]; + "relogin"?: NodeAccount[]; + "root"?: RootAccountVerdict[]; + "units"?: UnitsHealth; "v": number; }