Egbe Companions · Architecture

The World Server & Shard Continuum

A focused page within the Egbe Companions Architecture documentation. The full map and every sibling page live in the Architecture hub.

4sections12 minread1diagram

On this page

V6 — Egbe — bets that you can run a population of minds inside a shared world named Orun and keep it affordable. The world server is the half of that bet that owns physical truth. In V6's Mind/Body/Memory split the world server is the Body: it is authoritative for transforms, physics, navmesh, props, world time, and co-presence, and it is the place where an agent's intent is turned into what physically happened — or rejected. The Mind (Moirai) decides; the Memory (Ori) remembers; but neither can move an avatar through a wall, because only the Body applies state, and it validates every action against the authoritative world before it does. That single rule — decision and truth held apart, with the world server as the validating authority between them — is what lets V6 schedule LLM-driven cognition across thousands of agents without ever letting a hallucinated action corrupt the world.

The shard continuum is the second half of the same idea. Solo, Co-op, and Commons are not three codebases or three save formats; they are three binding contexts for the same world server and the same agent. They differ only in who runs the instance and who is authoritative, never in the simulation code. Because durable truth lives in the Ori and not in any instance, an agent can move from a private homestead to the always-on Commons and home again with its identity, memory, and relationships intact — travel is an Ori rebind, not a file copy, so there is never a "which save is canonical" problem. The world server (apps/v6/egbe-world-server/src/lib.rs, ~14,360 lines of Rust with 33 #[test] cases) implements all three contexts as data over one tick loop, speaks the engine-agnostic @oshun/egbe-protocol wire to every client tier, and publishes its ShardKind enum across Rust, TypeScript, and UE C++.

This page is the deep dive behind the "World, Edge, and Client" group of the orientation hub ../V6_ARCHITECTURE.md. It is the simulation companion to the edge and client tiers (the gateway and Pixel Streaming fleet that terminate the connections the world server serves), it consumes the cognition arbitrated by Moirai (whose tiers this page's perception-LOD feeds and bounds), and it is rendered by the UE5 client modules (the embodiment side of the same wire). The biography this server flushes life-events to is detailed in the Ori biography service.

What ships, honestly#

The world-model logic is real, deterministic, and covered by falsifiable tests — but, as with the rest of the Oshun catalogue, the daemon that wraps it is a health endpoint, the physics stage is a collision mirror rather than a stepped integrator, and the UE network module is deliberately empty. Four honest layers:

  • Real and exercised by tests. The 20 Hz authoritative tick(), geometric action validation (teleport / out-of-navmesh / prop-blocked rejection), perception that is LOD'd by visible density rather than population, a real rapier3d body/collider model, Solo offline-advance with a returning-player chronicle, the Co-op host-bridge and its home-Ori reconciliation, Solo↔Commons Ori-rebind travel, the regional Commons fleet with residency routing, cognition-capacity degradation with a behavior-tree floor, and deterministic seeded + golden replay are all genuine algorithms with named numeric budgets, covered by the crate's 33 tests. The capability list in SERVICE_DESCRIPTOR (lib.rs:12) — twenty-hz-tick, offline-atropos-advance, shard-travel-ori-rebind, persistent-commons-fleet, deterministic-core-golden-replay, and ~50 more — maps onto implemented code, not aspiration.
  • The daemon is health-only. run_service() (lib.rs:101) binds a TcpListener on port 46101 and handle_connection (:111) answers GET /health with the ServiceDescriptor JSON and 404s everything else; main.rs is one line. There is no accept loop ingesting client packets, no live gateway socket, no QUIC listener — the rich logic is library-grade and test-driven, and the client-serving daemon is not assembled here.
  • Physics is a collision mirror, not an integrator. RapierPhysicsStage::sync_and_step (lib.rs:7156) rebuilds a real rapier3d RigidBodySet/ColliderSet from the shard each tick — capsule colliders (capsule_y(0.9, 0.25)) for agents, ball colliders for blocking props — and returns the body/collider counts, but it takes _tick_hz (ignored) and never calls a PhysicsPipeline::step. Authoritative movement is decided geometrically in validate_move_action, not integrated. The rapier sets are a faithful collision model; they are honestly not a dynamics solve.
  • The UE network module is intentionally empty. Unlike V3, whose V3Net re-implemented the wire codec in C++, V6's V6Net.cpp (V6/ue/Source/V6Net/) is an empty IModuleInterface whose header documents the choice: it "declares NO UCLASS/UFUNCTION capability surface, so it implies no networking features that do not exist," and defers real multiplayer to "the Oshun services layer." A protoc-generated UE C++ codec does exist (libs/v6/egbe-protocol/ue/generated/.../egbe.pb.{h,cc}, ~33,440 lines) but is not wired into any Source/ module; the client's shard model is the independent EV6WorldShardKind { Solo, Coop, Commons } enum in V6World (V6ClientWorldModel.h). Where the topology page says "the UE-C++ V6Net module adapts to this protocol," the disk says V6Net is empty — treat the protocol peer on the UE side as the client modules page describes it, not V6Net.

The world server — the Body authority#

The 20 Hz authoritative tick#

Everything pivots on WorldTickLoop::tick (lib.rs:7411). With the canonical WorldTickConfig::default() (lib.rs:786) — tick_hz: 20, snapshot_hz: 20, frame_budget_ms: 50.0, checkpoint_interval_ticks: 20 — one tick advances world time by 1_000 / tick_hz ms and then runs a fixed pipeline: evaluate cognition-capacity pressure, choose the execution mode, resolve the incoming ActionBatch against authoritative state, sync the physics bodies, compute the next perception batch, emit a snapshot on an accumulator boundary, flush life-events to the Ori, and checkpoint on the interval. The snapshot cadence is an accumulator (snapshot_accumulator += snapshot_hz; emit when it crosses tick_hz), and each emitted AgentStateSnapshot is encoded through the same encode_wire_packet the protocol crate uses, so encoded_snapshot_bytes (:7501) is the literal on-wire size, not an estimate. The result carries within_frame_budget measured from a real Instant. The load test world_tick_holds_twenty_hz_with_one_hundred_fifty_agents (lib.rs:14297) drives 150 agents over 80 snapshots and asserts held_twenty_hz against the p99 tick time — a falsifiable performance gate, not a comment.

Validating intent against the world#

This is the heart of the Body's authority. resolve_action_batch (lib.rs:7909) first sorts the batch by a stable_action_ordering_key so the same intents always apply in the same order (a determinism prerequisite), then validates each MoveTo. validate_move_action (lib.rs:10042) is four real rejections, each returning a typed fact-ref:

  • world/action/teleport-rejected — the requested distance exceeds max_intended_move_mm (default 1_200 mm/tick).
  • world/action/target-outside-navmesh — the destination has no navmesh_region_for_transform.
  • world/action/target-blocked-by-propvalidate_transform_against_ground finds the target inside a blocking prop.
  • world/action/path-blocked-by-prop — a point-to-segment test (distance_point_to_segment_xz_squared) finds a navmesh-blocking prop within its collision_radius_mm of the move path.

Only after passing does the action step the avatar toward its target by action_step_mm_per_tick (300 mm). Say/Emote flip activity state; everything else is a no-op-applied. The test world_tick_rejects_moirai_actions_that_clip_geometry_or_teleport (lib.rs:10624) proves a Moirai action batch that tries to clip geometry or teleport produces exactly the rejection count and the teleport-rejected fact — the monolith's "an agent cannot walk through a wall because it intended to" is enforced, not asserted. Rejections become WorldEventKind::ActionRejected world events, so the client and the Ori both learn that the world refused the Mind.

Perception, LOD'd by what the player can see#

compute_perception_batch (lib.rs:8050) is the Body→Mind half of the loop, and it is where cognition cost is bounded. For each agent it collects other actors within coarse_perception_radius_mm (24 m), sorts them by distance with a stable id tiebreak, and assigns rich LOD (lod = 1, confidence 9000 bp) to the nearest few inside rich_perception_radius_mm (6 m) up to max_rich_perception_items_per_agent (8), then coarse LOD (lod = 3, confidence 6500 bp) up to max_coarse_perception_items_per_agent (4), and nothing beyond. The crucial property is in the test name: perception_lod_scales_items_with_visible_agents_not_total_agents (lib.rs:10771). Because perception volume tracks visible density, not population, the cognition spend Moirai pays is bounded by what a player can actually see — the same idea that makes Moirai's tiering affordable, expressed on the perception side.

Physics: a rapier collision model, honestly#

sync_and_step (lib.rs:7156) clears and rebuilds the RigidBodySet and ColliderSet every tick from authoritative transforms: each agent becomes a kinematic_position_based body with a capsule_y(0.9, 0.25) collider (350 mm radius), each navmesh-blocking prop a fixed body with a ball collider sized to its collision radius, positions converted mm→m. It returns the body and collider counts (world_tick_holds_twenty_hz_* asserts max_physics_body_count == 151 for 150 agents plus one prop). The honest caveat stated above bears repeating here: this builds a real, queryable rapier collision world but does not call the integrator — the integration is geometric, in validate_move_action. Rapier is the collision representation; the movement contract is the segment/navmesh math.

Cognition-capacity pressure and the behavior-tree floor#

The resilience story — "a cognition outage costs richness, never the world" — is real branching in the tick. evaluate_cognition_capacity_pressure (lib.rs:7532) compares the agent count against cognition_capacity_high_fidelity_agent_limit; when agents exceed the limit, degraded_mode_active flips, external Moirai actions are dropped for that tick, and execution_mode (from agent_behavior) selects BehaviorExecutionMode::DeterministicFallback, under which the world server computes a fallback ActionBatch from co-located behavior trees so agents keep acting believably. A PlayerDegradedModeNotice (player_degraded_mode_notice, :7560) is player_visible: true with a plain-language message, emitted once on the degradation edge so players are told, not silently downgraded. The default limit is usize::MAX (degradation off until an operator configures a budget), and homestead_rest_caps_offline_days_and_lowers_cognition_spend (lib.rs:11352) proves the Solo pace control lowers spend.

Durable flush and determinism#

Each tick flushes life-events to the Ori (flush_life_events) and, on the checkpoint interval, writes a world-state checkpoint through DurableWorldPersistence — the durable truth that makes a shard rebind safe. Determinism is gated twice: seeded_world_replays_identically (lib.rs:14314) runs a seeded world twice and asserts the FNV-64 trace hashes are equal, and deterministic_core_golden_replay_matches_committed_fixtures (lib.rs:14334) replays against a committed golden fixture (V6/evals/golden-replay/deterministic-core-golden.json). This is why the build stamps strict floating-point flags: the perception→cognition→action loop must replay identically for audit.

The shard continuum#

The continuum is one world server, three binding contexts. The wire enum is shared across every runtime: ShardKind { SHARD_KIND_SOLO = 1, COOP = 2, COMMONS = 3 } in egbe.proto, mirrored as EV6WorldShardKind in the UE client. A ShardState (lib.rs:530) simply carries a shard_kind, a region, an instance id, world time, and its grounds; the same tick(), the same validation, the same perception run regardless of which kind it is. What differs is who hosts it and how it advances when no one is watching.

flowchart TB ori[("Ori biography service<br/><sub>durable truth — identity, memory,<br/>relationships, values (:46105)</sub>")] subgraph continuum["The Shard Continuum — one world server, three contexts"] direction LR solo["<b>Solo</b> — private homestead<br/><sub>advance_offline_absence_with_rest<br/>offline → Atropos batch + chronicle</sub>"] coop["<b>Co-op</b> — host's instance<br/><sub>begin_coop_session: visiting Oris<br/>read-mostly; host authoritative</sub>"] commons["<b>Commons</b> — always-on fleet<br/><sub>CommonsShardRuntime.advance_real_time<br/>regional shards, residency-routed</sub>"] end solo -- "travel_agent_as_ori_rebind<br/>(Solo ⇄ Commons only)" --> commons commons -- "consolidate → detach →<br/>attach → rehydrate" --> solo coop -- "reconcile_to_home_ori<br/>on session end" --> ori solo -- "life-events / checkpoint" --> ori commons -- "life-events / checkpoint" --> ori ori -- "rehydrate identity<br/>at destination" --> commons classDef store fill:#f3e8ff,stroke:#6d28d9,color:#3b0764 class ori store

Solo — the private homestead and offline advance#

A Solo shard is a private instance per player. Its distinctive code is what happens while the player is away: advance_offline_absence_with_rest (lib.rs:3733) refuses to run on a non-Solo shard, then forces every agent to AgentTier::Atropos and ActivityState::OfflineSummary, advances world time by the absence window at SOLO_HOMESTEAD_OFFLINE_TICK_HZ, generates summary life-events, builds a returning-player chronicle ( SoloHomesteadChronicle), flushes to the Ori, and writes a checkpoint. A HomesteadRestPaceControl caps how many game-days an absence can advance, so a month away does not produce a month of compute. The test solo_homestead_persists_and_multi_day_absence_produces_chronicle (lib.rs:11256) exercises the multi-day round trip — this is the monolith's "while offline the world advances in Atropos as a low-cost batch job," made real and bounded.

Co-op — the host's instance with read-mostly visiting Oris#

Co-op is not a separate server kind; it is a Solo host that has accepted visitors. begin_coop_session (lib.rs:3798) takes a CoopBridgeRequest of visiting agent seeds and binds each into the host's instance read-mostly — the host stays authoritative. When the session ends, reconcile_to_home_ori (lib.rs:3900) walks each visiting agent's earned events and reconciles them back to that agent's home Ori, so nothing a visitor experienced is lost or trapped in the host's world. coop_bridge_reconciles_visiting_events_to_home_ori (lib.rs:11404) proves the reconciliation. This is the cheapest point on the continuum: no new fleet, just another agent's biography loaded alongside the host's.

Commons — the always-on regional fleet#

Commons is the persistent, regionally-sharded world that advances in real time whether or not any individual player is present. CommonsShardRuntime holds a map of regional shards; advance_real_time (lib.rs:4480) accumulates elapsed wall time per shard, advances the right number of fixed ticks, and — critically — runs interest_manage_commons_cognition first, which enables high-fidelity cognition only on shards where players are present and reassigns the rest of the population to Atropos. The test empty_commons_region_runs_atropos_only_without_players (lib.rs:11769) and its companion commons_region_with_player_preserves_high_fidelity_for_present_agents (lib.rs:11851) pin both halves of that rule. Region boundaries honor data residency: commons_residency_zone_for_region (lib.rs:8615) maps a region string (eu-, uk-, ca-, latam-, apac-…) to an OshunResidencyZone, and travel_agent_across_commons_with_residency (lib.rs:4217) gates cross-region travel on explicit consent — commons_region_residency_constrains_cross_region_travel_without_consent (lib.rs:12319) proves a no-consent crossing is refused.

Travel is an Ori rebind#

The seam that makes the continuum one world is travel_agent_as_ori_rebind (lib.rs:3956). It first checks valid_shard_travel_route (lib.rs:8604), which permits only Solo↔Commons — Co-op is a visit, not a travel endpoint. It then captures the source actor, asserts it is an Agent, builds a rehydrated transform at the destination, validates that transform against the destination ground (navmesh + props), records the Ori life-event counts, detaches the actor from the source instance, attaches it to the destination via spawn_actor_on_ground carrying its cognition tier, and produces a ShardTravelReport with a travel_event_count. Because the Ori is the durable record, the rehydrated agent arrives with its identity, memory, and relationships intact — the test shard_travel_ori_rebind_preserves_identity_memory_and_relationships (lib.rs:11504) asserts exactly that. A relationship an agent formed in the Commons is simply a remembered, written-to relationship when it walks back into a Solo homestead; there is no canonical-save problem because no instance ever held the truth. The V1CrossShardPresenceBus (lib.rs:551, topic v1.presence.cross-shard) carries the presence signal across the seam.