diff --git a/README.md b/README.md index dae8ff5..1d295f4 100644 --- a/README.md +++ b/README.md @@ -70,75 +70,47 @@ Local package surfaces: ## Idunn -Idunn is Odin's keepalive organ: the daemon package that should keep the known -swarm alive after Odin has accepted where each daemon lives and what authority -path may touch it. Individual daemons publish health and command boundaries; -Idunn brings them up after reboots or crashes, watches health, and escalates -operator-needed failures through Bifrost's CultMesh bridge. VoidBot owner-DM -delivery is a demoted compatibility actuator, not the owner; the command belongs -in Bifrost's Verse. Odin sees the daemons; Idunn keeps the apples from rotting. -Agents do not deploy daemons directly. They configure Idunn's target catalog, -release targets, migration commands, and command boundaries so Idunn can run -the shared rollout primitive and leave typed witnesses behind. - -Deployment consequence has one final owner: immediately before every migration, -deploy, or restart process spawn, Idunn re-opens the root-owned deployment-brake -CultCache and the dedicated operator public anchor. A released record must bind -the canonical runtime (`yggdrasil` for `yggdrasil-local`), exact source revision -(or `restart:`), exact request id, operator, signature, and short expiry. -Missing, corrupt, engaged, expired, foreign, wrong-scope, substituted, or -badly-signed state fails closed. Planning and lifecycle-command publication do -not bypass or cache this decision. The store and its sibling `.lock` live in -`/var/lib/gamecult/idunn-authority`, a root-owned `root:idunn` `0750` directory; -both files are `root:idunn` `0640`, and the daemon's systemd sandbox exposes the -directory read-only. Idunn holds the lock shared from its final snapshot read -through process spawn, so an engage cannot be defeated by previously captured -released bytes. A release is not one-shot: its exact request/revision grant -covers each migration and deploy spawn in that rollout until its short expiry; -every spawn independently reopens and validates the same grant. +Idunn (`GameCult/Idunn`) owns deployment and daemon survival for every GameCult +target, Odin included: it builds from the target's recipe, activates and +restarts the admitted incarnation, and holds the deployment and lifecycle +brakes. Odin owns none of that. Odin does discovery, schema awareness, +rendezvous, and interface aggregation, and it reads Idunn's topology projection +to learn which incarnations exist. Agents do not deploy daemons directly: they +configure the target's recipe and binding and let Idunn run the transaction. Local package surfaces: -- Organ contract: `docs/idunn.md` -- User README: `src/Idunn/README.md` -- Rust daemon crate: `crates/idunn-daemon` -- Rust lifecycle logic: `crates/odin-core/src/idunn.rs` -- Runtime store: `scratch/idunn/idunn.keepalive.cc` -- Hosted VoidBot release: upstream `main` -> Yggdrasil-local Idunn -> the - root-owned `/srv/odin/deploy-manifests/voidbot` actuator. Local checkouts are - development and diagnostics only; do not create a local deployment. -- Hosted Odin release: branch `codex/ygg-idunn-independent-bootstrap` plus the - exact CultLib commit in `deploy/cultlib.commit` -> one immutable - `/srv/odin/releases/` body -> `/srv/odin/current`. Odin signs its own - liveness to Idunn, while Idunn starts independently and owns later restart or - deployment actuation. -- Operator escalation: Bifrost-owned CultMesh crossing; current compatibility - delivery calls `scripts\notify-idunn-operator-alarm.ps1`, which asks Bifrost - to publish a typed `gamecult.operator_dm_request.v1` CultMesh command document - only after Idunn raises an alarm +- Deployment recipe: `deployment/idunn/recipe.toml` +- Operator binding: `/etc/gamecult/idunn/bindings/odin.toml` on the host, not in + this repository +- Live deployment and admission map: `state/map.yaml` +- Idunn's contract, brakes, and runbooks: the `GameCult/Idunn` repository ## Authority Map - Owner: Odin owns the network-wide Verse registry, schema catalog index, translation map, and accepted provider catalog/proxy surfaces. - Inputs: CultMesh/CultNet peer announcements, schema catalog responses, daemon - health/provider publications over `cultnet.transport.rudp.v0`, local - Docker/ADB debug facts, and provider-owned Eve/CultUI surfaces. + health/provider publications over `cultnet.transport.rudp.v0`, Idunn's + read-only topology projection, and provider-owned Eve/CultUI surfaces. - Outputs: CultCache-backed Odin state, CultMesh documents, and CultNet schema/catalog messages. Browser, GUI, TUI, and framebuffer renderers lower those documents outside Odin instead of asking Odin to host web surfaces. - Derived state: Gjallar's aggregate surface is derived from Odin/provider state; Eve clients derive pixels from that aggregate. - Forbidden writers: renderers do not probe the network or decide Verse truth; individual projects do not maintain private incompatible discovery ledgers once Odin can see them. - Shared paths: human dashboards, worker schedulers, Verse bootstrap code, and compact TUI views consume the same registry and schema catalog. -- Deletion line: old per-host coordinator scripts should be deleted or reduced to deployment wrappers that start Odin. +- Not Odin's: process lifecycle, restart, deployment, and daemon survival belong to Idunn. -## Run Locally On Starfire +## Runtime Body -```powershell -.\scripts\start-odin.ps1 -IdunnRudpHealth $env:IDUNN_RUDP_HEALTH -``` +Odin runs as `odin-daemon` under Idunn on Yggdrasil, built from +`deployment/idunn/recipe.toml`. There is no local start script. The daemon: -`-IdunnRudpHealth`, `ODIN_IDUNN_RUDP_HEALTH`, or `IDUNN_RUDP_HEALTH` must name -the Idunn RUDP health endpoint. Odin does not assume a localhost health target. +- reads Idunn's read-only projection to learn which incarnations of which + targets exist; +- admits provider presences over RUDP for those incarnations and persists them + through CultMesh/CultCache; +- publishes signed runtime-topology correlation records; +- answers CultNet/RUDP snapshot and document requests on its route. Odin's native document catalog is addressed by CultMesh URI. Concrete RUDP bootstrap is configured behind CultMesh URI resolution by the operator or by @@ -152,28 +124,9 @@ That URI accepts typed document publication and schema/catalog requests through the shared CultMesh runtime. Consumers that need Odin's accepted surface can still request the current CultNet snapshot after CultMesh resolves the transport. -Legacy browser/deck lowerers must consume Odin's CultMesh state through their -own lowering process. Odin no longer hosts browser-deck surfaces or publishes -deck URLs as discovery seed material. - -State and logs live under ignored `scratch/odin/`. - -## Current First Body - -The first executable is deliberately narrow: - -- publishes provider catalog `odin.providers`; -- persists the latest surface through local CultMesh/CultCache when `CultLib` packages are available at `E:\Projects\CultLib\packages`; -- writes `scratch/odin/latest-surface.json` only when `--write-debug-surface-json` or `ODIN_WRITE_DEBUG_SURFACE_JSON=1` is explicitly supplied; -- observes Starfire Docker and Periwinkle ADB as local debug/edge facts; -- derives remote Verse presence from provider-owned CultMesh/CultNet - advertisements and interface records, not TCP/SSH/systemd probes. -- publishes explicit `verse` and `service` nodes for compact Eve/CultUI lowerers. -- ingests provider-owned Eve/CultUI dashboards, including `mimir.live.stats` and `voidbot.swarm`, and embeds them as Odin `interface` nodes; -- accepts live `gamecult.eve.provider_advertisement.v1` announcements through Odin's CultMesh/RUDP rendezvous path so daemons can announce schemas, surfaces, commands, nested Verses, and style capabilities without Odin scraping private dashboards; -- accepts explicit local debug imports only when `--interfaceBindingStore` / `ODIN_INTERFACE_BINDING_STORES` entries are written as `cultmesh-store:file://...` URIs; raw filesystem paths are not discovery configuration; -- preserves provider semantic addresses such as `asgard.starfire.bifrost/eve/tui` and `asgard.starfire.bifrost/eve/gui`, with CultNet routes carried as transport metadata rather than identity; -- persists operator tiling intent as `odin.interface_layout.v1` in the Odin CultMesh store; ignored `scratch/odin/interface-layout.json` is migration input only. +Browser and deck lowerers consume Odin's CultMesh state through their own +lowering process. Odin does not host browser-deck surfaces or publish deck URLs +as discovery seed material. Provider advertisements and CultNet/RUDP transport profiles are the discovery path. External host probes, product health checks, port probes, and renderer diff --git a/docs/architecture.md b/docs/architecture.md index 9f69cee..28080d8 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -46,35 +46,24 @@ The first Rust core lives in `crates/odin-core`: - `repository.rs`: `OdinRepository` abstraction, in-memory mock repository, and CultMesh-backed repository. -The Rust spine owns the future architecture. The CommonJS daemon remains the -legacy operational body until each organ crosses this typed boundary. +The deployed body is `crates/odin-daemon`, built and run by Idunn from +`deployment/idunn/recipe.toml`. `crates/odin-core` holds the typed record spine +that the rest of the machine grows into. ## Runtime Body Odin's executable body is split by ownership: -- `crates/odin-core`: Rust target core. Owns typed Odin documents, ingest - ports, normalization, and CultMesh/CultCache repository boundaries. This is - the replacement spine; JavaScript remains legacy runtime scaffolding until - each organ has crossed the typed boundary. -- `src/odin-coordinator.cjs`: process lifecycle, serialized refresh loop, - persistence, health, and transport wiring. Refreshes must not overlap because - a refresh publishes Odin's daemon health. -- `src/odin/config.cjs`: runtime paths, refresh intervals, and CultLib module path setup. -- `src/odin/documents.cjs`: CultCache/CultMesh document definitions accepted by Odin. -- `src/odin/idunn-rudp.cjs`: daemon-owned Odin provider health publication to - Idunn over the canonical CultNet RUDP `schema` channel. -- `src/odin/probes.cjs`: local Docker/ADB debug lowerings for Starfire and - Android edge visibility. Remote Verse health is not probed here; it comes - from provider-owned CultMesh/CultNet records. -- `src/odin/interfaces.cjs`: provider advertisements, CultMesh interface - bindings, accepted interface projection, and renderer-lowering metadata. - Renderer routes are lowerings outside Odin, not Odin-hosted transport. -- `src/odin/layout.cjs`: `odin.interface_layout.v1` read/write and merge policy. -- `src/odin/marquee.cjs`: canonical marquee tape assembly from Stonks securities and ordered VoidBot poem lines. -- `src/odin/surface.cjs`: `gamecult.eve.surface.v1` tree projection. -- `src/odin/state.cjs`: one refresh's input records into Odin's provider catalog/proxy state. -The entrypoint is not allowed to grow new probe, surface, provider, layout, or renderer policy. If a new owner is needed, name the owner and its invariant before adding code. +- `crates/odin-daemon`: the deployed process. Reads Idunn's read-only topology + projection, admits provider presences over RUDP for the incarnations Idunn + projects, persists them through CultMesh/CultCache, publishes signed + runtime-topology correlation records, and answers CultNet/RUDP snapshot and + document requests. Deployment, restart, and survival of this process are + Idunn's. +- `crates/odin-core`: typed Odin documents, ingest ports, normalization, and + CultMesh/CultCache repository boundaries. + +If a new owner is needed, name the owner and its invariant before adding code. The accepted document registry is also the persistence admission boundary. Unknown schemas may be observed for diagnostics but are not silently persisted @@ -94,19 +83,15 @@ the typed `gjallar.overview` Eve surface. Eve clients own graphical, terminal, and framebuffer lowering. Gjallar must not own the underlying registry, probe, provider truth, client pixels, or translation decisions. -Idunn is the named keepalive organ for daemon continuity. Its current Rust -body lives in `crates/idunn-daemon` and `crates/odin-core/src/idunn.rs`. Idunn may read -Odin-owned service records and provider advertisements, then bring daemons up -after reboots or crashes, watch health, emit keepalive observations, restart -requests, denied-action records, and operator alarms. When human action is -needed, Idunn uses CultMesh to request a Bifrost-owned operator -notification crossing. VoidBot's `voidbot.operator-dm` command `owner.dm.send` -is a demoted compatibility delivery actuator, not the owner. The target command -lives in Bifrost's Verse; any still-VoidBot delivery path must be invoked by -Bifrost or documented as migration debt. Idunn must not own Verse discovery, -schema truth, provider dashboards, identity grants, Discord delivery, owner-DM -delivery, or renderer layout. Keepalive loops belong in Idunn, not Odin's -coordinator or individual daemons. +Idunn (`GameCult/Idunn`) owns deployment and daemon survival, including Odin's. +It builds from each target's recipe, activates and restarts the admitted +incarnation, and holds the deployment and lifecycle brakes. Odin publishes what +it can see and reads Idunn's topology projection; it does not start, restart, +deploy, or health-probe any daemon, and no Odin code path keeps a process alive. +When human action is needed, Idunn uses CultMesh to request a Bifrost-owned +operator notification crossing. Idunn must not own Verse discovery, schema +truth, provider dashboards, identity grants, Discord delivery, owner-DM +delivery, or renderer layout. Muninn is the portable local telemetry Verse assembler. Its Rust body lives in the `GameCult/Muninn` repository and publishes `muninn.telemetry_surface.v1` @@ -213,19 +198,10 @@ This is not a reporting nicety. It is how Odin prevents services from becoming private little islands with separate websites, dashboards, state formats, and separate command languages. -## Current Service Surface +## Service Surface -Odin currently publishes service squares for: - -- Starfire: Odin, Docker, ADB, Odin CultCache, and running Docker containers. -- Nightwing: SSH, Eve broker, Eve browser reference, visible TUI, Docker unit state, and NVIDIA GPU state. -- EVE: SSH/native Eve reachability. -- Periwinkle: ADB reachability. -- Periwinkle: local ADB edge status plus any provider-owned surface or advertisement Odin receives through CultMesh/Odin discovery. -- Raven: SSH reachability. -- Yggdrasil: compatibility host reachability plus nginx, - StreamPixels, Heimdall, Repixelizer, and Bifrost systemd state until those - daemon surfaces publish health and command boundaries over CultNet RUDP. +Odin's service records come from provider-owned presences and advertisements +received through CultMesh/RUDP, not from host probes. Gjallar consumes Odin's accepted CultMesh/Eve state and composes every visible provider surface into one typed aggregate on Yggdrasil. EveCanvas, browser, and @@ -250,8 +226,8 @@ interactive language; they are not separate dashboard products. Huginn's `.cc` inspection surface is the current clean example: Huginn inspects CultCache bytes and emits Eve DSL, while Eve or any other runtime owns presentation. -Provider advertisements are the promotion path out of probing. Odin's CJS -document set already accepts `gamecult.eve.provider_advertisement.v1` alongside +Provider advertisements are the promotion path out of probing. Odin's document +registry already accepts `gamecult.eve.provider_advertisement.v1` alongside `gamecult.eve.interface_binding.v1` and `gamecult.eve.surface_state.v1`. Daemons should publish advertisements that name service id, Verse id, schema catalog, `.cc` witnesses, Eve surface keys, command boundaries, nested Verses, @@ -290,18 +266,6 @@ identity. The canonical contract lives in `E:\Projects\Eve\docs\provider-advertisement-contract.md`. -Odin persists operator layout intent as `odin.interface_layout.v1` in the Odin CultMesh store under record key `odin.providers`. The ignored `scratch/odin/interface-layout.json` file is migration input only; Odin no longer writes layout truth back to that sidecar. Layout intents name the provider id and request focus, move, resize, or visibility changes; renderers are input devices for those intents, not local layout owners. - -Odin now derives dense top-level layout intent from each provider's retained -`surface.root` tree: element count, leaf count, branch count, depth, text-cell -pressure, and list-like branches. Provider explicit preferred sizes are capped -at the Odin wrapper boundary unless the current intent is fullscreen, so stale -layout files cannot keep empty panels huge. The renderer should use -`props.tree`, `props.layout.signalWeight`, and `props.packing` to allocate space -to nested signal, then recursively lower provider children. Flattening a -provider surface into one log/list is a compatibility failure when the retained -tree has children. - ## Observation Surface Odin no longer tails Mimir JSONL artifacts for observation truth. Device and