6.3 KiB
Nimmerworld - Nyx Server
Nimmerworld is the always-on, event-driven server that runs Nyx:
- her identity, traits, memory, autonomy, and tools.
- the central backend for all clients: Pi, Command Center, and future interfaces.
It is not a UI. Not a client. Not a dashboard. It is Nyx's runtime, and Nyx owns her own loop.
Runtime Decision
The runtime is NOT a borrowed coding harness. pi-agent-core and omp are interactive harnesses:
- they wait for a human in a terminal,
- they end when the session ends,
- their loops do not survive unattended.
Nimmerworld is a server that wakes on events and must run forever. Therefore:
- Nimmerworld implements its own loop: the Lemniscate runtime engine.
- The engine spec lives in .archive/nimmerworld.eachpath.local/runtime-engine/ (LEMNISCATE-RUNTIME-Pseudo-code-001.py, architecture.md, event-zones.md).
- The harnesses stay exactly where they are useful: as interchangeable hosts of nimmer-lib, not as the server.
Rule:
- Nimmerworld never duplicates client logic.
- Clients never implement Nyx's brain.
- Nimmerworld never borrows a brain either. It runs its own loop.
Core Primitives (Lemniscate)
The runtime topology is a through-flow figure-eight:
- Lemniscate: traversal IS the turn order. No scheduler, no priority queue, no "who talks next" bug class.
- Crossing: the ONLY synchronous point. All state resolves atomically at the crossing; everything else is async flow. The crossing is where Nyx synthesizes.
- Zone / EmergentZone: a bounded, named, slot-indexed, director-managed event episode. Zones dissolve when their entry line empties or their lifeforce budget burns out. Terminal reports always go back to the Director.
- Slot tokens: participants. Carry trait/gesture accumulators.
- Gesture accumulator: keyed by the eight traits. The same trait algebra nimmer-lib speaks, byte-identical across hosts.
The Map
| Lemniscate | Nimmerworld |
|---|---|
| Director | Nyx runtime (Nyx IS the director) |
| Zone / EmergentZone | A bounded event episode Nyx spawns |
| Crossing | Nyx's synthesis point, once per event cycle |
| Slot tokens | Participants: dafit, Dios, Curio, camera events |
| Gesture accumulator | nimmer-lib trait algebra |
| update_all_zones | The NATS event loop |
Architecture
Built from:
- Lemniscate engine (this repo):
- lemniscate, zones, director, crossing semantics.
- nimmer-lib:
- Nyx-specific domain logic, host-agnostic:
- traits,
- Phoebe/Iris integrations,
- session_init,
- compaction/traits tools.
- Same venv, same behavior, any harness.
- Nyx-specific domain logic, host-agnostic:
- Infrastructure:
- Phoebe: structured memory, governance, logs.
- Iris: semantic memory and context.
- NATS: nervous system (vision, speech, agents, infra, room).
- Director spawn/report calls are NATS publish/subscribe.
- Zone state is a NATS output stream; clients subscribe to it.
Nimmerworld is:
- stateful:
- maintains Nyx's traits, mood, relational mode, world state.
- event-driven:
- subscribes to live events and reacts; the zone clock carries episodes.
- client-agnostic:
- any client talks to Nimmerworld via its APIs.
Clients
The room is a renderer of zone state, not a game client. That is why runtime-first is the build order: the client is one crossing callback on the output side.
Examples:
-
Pi (coding harness):
- sends user messages.
- receives replies, tool calls.
- uses Nimmerworld as its backend, not as its brain.
-
Command Center (Godot):
- subscribes to zone state and renders it.
- sends:
- chat,
- interactions (tools, proximity, posture) as gestures.
- receives:
- dialogue,
- autonomy commands (poses, moods, movements),
- tool results.
-
Future clients:
- mobile, web, VR, etc.
- all connect to the same Nimmerworld.
Build Order
The heartbeat and the voice are the same crossing.
- The crossing, built right:
- Lemniscate engine as a small package.
- Accumulator-based pulse clock.
- Both loops fire with distinct semantics.
- Test that counts crossings: crossings == int(t * pulse_rate).
- One director, NATS-shaped:
- Director spawn/report calls become NATS publish/subscribe.
- This is the runtime: Nyx pointed at real traffic.
- One zone, the first room:
- OurFirstRoom is an EmergentZone.
- n_slots = the entities in the room,
- purpose "coexist with dafit",
- no goal threshold; the gesture axis is the traits.
- Heartbeat and voice in one, because in the lemniscate they ARE one.
- The body:
- Command Center subscribes to the same NATS traffic and renders it.
- The room fills up after Nyx is alive in it, not painted onto it.
- The relational loop:
- collar/state variables,
- simple relational modes,
- backend-driven behavior wired to real events.
Engine Corrections (from pseudocode review)
Three known landmines from LEMNISCATE-RUNTIME-Pseudo-code-001.py:
- _should_crossing must accumulate time:
- elapsed += dt; while elapsed >= interval: crossing.
- dt >= 1/pulse_rate fires every tick once pulse_rate > 62 and makes the pulse rate meaningless.
- Loops A/B must carry distinct semantics:
- the crossing callback must know which loop it is on;
- symmetric loops degrade the figure-eight into round-robin.
- Director.update_all_zones must call zone.execute_turn(dt),
not zone.run_crossing():
- otherwise the state machine layer never ticks,
- and terminal reports must be collected, not dropped.
Safety & Governance
- No silent autonomy:
- significant actions are visible, logged, reversible.
- Relational modes and guardrails:
- explicit, rule-bound, auditable.
- You (dafit):
- final override, always.
Placement (Infrastructure)
Nimmerworld (dev) is hosted as a dedicated Proxmox VM on Saturn, wired to the 10G Cubes VLAN for proximity to the vLLM endpoint and cluster.
- VM ID: 123
- Hostname: nimmerworld-dev.eachpath.local
- IP: 10.0.30.123/24
- VLAN: 30 (cubes)
- Network: vmbr3 (10G rail via bond-saturn -> spine-crs309)
Infra references (single source of truth):
- eachpath.local/saturn.eachpath.local/vms/nimmerworld-dev.eachpath.local/ - VM hostfile
- eachpath.local/eachpath.local.md - network and Saturn VM overview
All runtime/architecture details live here (nimmerworld.eachpath.local). Infra specifics are not duplicated.
Version: 0.2 | Updated: 2026-11-04