Egbe Companions · Architecture

Architecture, Topology & Project Layout

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

8sections11 minread1diagram

On this page

V6 — Egbe — is the only product in the Oshun catalogue whose architectural bet is not "render a world" but "run a population of minds inside a world and keep it affordable." It ships behind two substrates held deliberately apart: a reused metaverse substrate (the UE5 client, an authoritative world server, the realtime gateway, the Pixel Streaming fleet, the three.js fallback — all carried from V3 rather than rebuilt) and a new agent-simulation substrate (an event-sourced agent biography store, a tiered cognition kernel, a voice-to-intent pipeline, a story engine, and a cross-game passport bridge). That split is not slideware — it is wired through the repository three ways at once. The UE project at V6/ue/V6.uproject pins "EngineAssociation": "5.5", sets "DisableEnginePluginsByDefault": true, declares 18 C++ modules (16 Runtime, 1 Editor, 1 DeveloperTool) and force-enables 55 engine plugins; under V6/ue/Source/ those 18 modules carry 67 .cpp + 47 .h (114 files, ~11,386 lines of .cpp) each with a real *.Build.cs and a Public//Private/ split. Off-engine, apps/v6/ is a Rust Cargo workspace (resolver = "2", rust-version = "1.82", unsafe_code = "forbid") of seven authoritative services plus four shared crates, and libs/v6/ holds 18 shared libraries — four of them dual-language Rust+TypeScript — feeding a libs/contracts/src/v6 registry of 14 typed contracts.

This page is the orientation map for how those pieces compose at runtime, on disk, and by concern. As with the rest of this set, the reason to read the code rather than the architecture monolith's prose is that the two have drifted in a few load-bearing places — and where the .uproject, the Cargo.toml, the Build.cs files, and the on-disk content tree disagree with the monolith, this page treats the compiler's view as authoritative and labels the monolith as the aspirational spec. The full section index for the catalogue is ../V6_ARCHITECTURE.md.

What ships, honestly#

Real and on-disk (the compiler agrees). All 18 modules named in the .uproject Modules array exist under V6/ue/Source/, each with a *.Build.cs and a Public//Private/ split. Unlike V5 — whose glossary claimed 48 engine modules where the disk had 74 — V6's module table matches the disk exactly at 18, so the monolith's UE5 module split can be trusted. Depth is concentrated where the agent simulation needs it: V6Agent is the richest gameplay module (8 .cpp/8 .h), followed by V6World and V6Core/V6UI/V6Editor; the deepest single files are V6VoiceIntentTypes.cpp (793 lines), V6OrunDistricts.cpp (763), V6AgentDensityLODTypes.cpp (589), and V6OperationalHUDWidget.cpp (519). Testing is genuine: the V6Tests DeveloperTool module carries 21 .cpp automation specs and depends on 14 of the runtime modules, so the test harness sees almost the whole graph. The Rust plane is substantial — the world server alone is 14,363 lines, the Ori service 4,949, the realtime gateway 3,945, and the Moirai kernel crate (libs/v6/moirai-kernel/rust) 5,732 — all under one unsafe_code = "forbid" workspace.

A point of fidelity V5 lacked: determinism is stamped by the build. Every V6 target sets bUseUnityBuild = false and appends strict floating-point flags (/fp:strict /fp:except- on Win64, -fno-fast-math -ffp-contract=off elsewhere) plus ProjectDefinitions V6_EGBE=1 and V6_ENGINE_UE55=1. V5's topology page had to note that "no determinism is stamped by the build"; V6's V6.Target.cs, V6Editor.Target.cs, and V6PixelStreamingWorker.Target.cs all stamp it, which matters because the perception→cognition→action loop must replay identically for audit (see ./world-server-and-shard-continuum.md).

Modeled as data, with one recent advance over the monolith's own disclosure. The architecture document's 2026-06-12 disclosure states that V6/ue/Content/ is "empty apart from .gitkeep placeholders." The disk is now ahead of that prose. Six procedurally-authored district grounds exist and are git-tracked — L_AncestorGrove, L_Ateliers, L_CommonsHeart, L_GroveOfBeginnings, L_Thresholds, L_Wilds (each a ~27 KB .umap under Content/Districts/, authored 2026-06-21) — built from the C++ in V6World/Private/V6OrunDistricts.cpp and V6DistrictGround.cpp rather than hand-sculpted art. Alongside them sit the 10 GameFeatureData.uasset plugin descriptors and the six DistrictScene_*.v6district.json scene descriptors. So the six Districts of Orun are now authored in-engine as lightweight procedural maps, not merely declared — but the asset-heavy versions (Nanite meshes, MetaHuman casts, baked lighting) are still not in tree. Where a claim depends on that art, this page says so. The 487 .uasset/22 .umap under Plugins/VRM4U/ are vendored third-party content (the VRM avatar importer), untracked in git (? V6/ue/Plugins/VRM4U), and are not V6-authored.

The structural inversion from V5. V5 was self-contained under V5/ with no libs/v5, no libs/contracts/src/v5, and no apps/v5. V6 is the opposite: it is a full polyglot monorepo citizen. Its agent mind is assembled from V1 substrates (Iris/Psyche/Isis/Sophia) and its services, libraries, and contracts live in the shared apps/, libs/, and libs/contracts/ trees, while only the UE client lives under V6/ue/. That is the whole reason V6 can carry beings into V2–V5: it shares the monorepo's contract and identity spine.

Runtime topology: mind, body, memory over a reused edge#

The single most important structural decision in V6 is that an agent is split across three authorities, none of which owns the others, and the three map onto three Rust services on sequential ports (4610146107):

  • The Body — egbe-world-server (port 46101). Authoritative for the world: agent and player transforms, physics, navmesh, props, world time, co-presence. Its SERVICE_DESCRIPTOR advertises a twenty-hz-tick capability and WorldTickConfig defaults to tick_hz: 20; it embeds rapier3d for physics, a NavmeshRegion model, ShardState, and a V1CrossShardPresenceBus publishing to topic v1.presence.cross-shard. An agent's intent is never authoritative here — only what physically happened is.
  • The Mind — egbe-moirai-cluster (port 46102). Authoritative for agent decisions. The cluster app is a thin dispatch shell (~523 lines: ShardedDispatchReport, assignment_map) over the real logic in the moirai-kernel crate (5,732 lines), whose CognitionTier { Atropos, Lachesis, Clotho } enum and budget constants (CLOTHO_ACTIVE_MINUTE_TOKEN_BUDGET = 50_000, LACHESIS_EXECUTION_HZ = 10, ATROPOS_DEFAULT_SUMMARY_CALLS_PER_GAME_DAY = 4) are the cost governor that makes a population affordable. It owns no world state.
  • The Memory — egbe-ori-service (port 46105). Authoritative for who the agent is. It is event-sourced: a StoredOriEvent log with a vector_clock JSONB column, a pgvector-memory-index capability, ori-passport-minting, incremental projections snapshotted every ORI_PROJECTION_SNAPSHOT_INTERVAL events, and an ORI_PROJECTION_READ_P99_BUDGET_MS = 42 read budget. The same record holds whether the agent stands in a Solo homestead, the Commons, or a V2 arena.

The per-tick loop ties them together: the world server computes each agent's perception and ships it to Moirai; Moirai runs cognition at the assigned tier and returns action intents; the world server validates those against authoritative state and applies the valid ones; durable life-events flush to the Ori. Crucially, the cheap deterministic execution layer (tier assignment, behavior-tree stepping) is designed to run co-located with the world server, so a Moirai cognition outage costs richness, never the world. The cognition detail lives in ./cognition-stack-and-agent-behavior.md.

flowchart TB subgraph CLIENTS["Client Surfaces"] direction LR ueNative["UE5 Native<br/><sub>V6/ue · desktop/console/VR/mobile</sub>"] webPx["Browser → Pixel Streaming<br/><sub>apps/v6/egbe-web (vite :3061)</sub>"] webFB["Browser fallback<br/><sub>apps/v6/egbe-web-fallback · three.js</sub>"] steward["Egbe Steward App"] end pxRelay["<b>egbe-pxstream-relay</b><br/><sub>:46104 · POP match + signaller</sub>"] gateway["<b>egbe-realtime-gateway</b><br/><sub>:46103 · webtransport-edge ·<br/>webrtc/websocket fallback · voice-sfu</sub>"] bff["V1 Oshun BFF<br/><sub>identity · catalog · billing</sub>"] world["<b>egbe-world-server</b> — the BODY<br/><sub>:46101 · rapier3d · 20 Hz tick ·<br/>navmesh · shard state · presence</sub>"] moirai["<b>egbe-moirai-cluster</b> — the MIND<br/><sub>:46102 · over moirai-kernel crate<br/>Clotho / Lachesis / Atropos</sub>"] ori[("<b>egbe-ori-service</b> — the MEMORY<br/><sub>:46105 · event log + vector_clock<br/>+ pgvector + passport mint</sub>")] clio["egbe-clio-service :46106<br/><sub>over libs/v6/clio-story (TS)</sub>"] foundry["egbe-foundry-service :46107<br/><sub>Ninhursag agent-gen</sub>"] cog["cognition-stack (TS)<br/><sub>libs/v6/cognition-stack</sub>"] subgraph V1["V1 substrates (reused)"] direction LR psyche["Psyche"]; isis["Isis"]; sophia["Sophia"]; iris["Iris"]; lilith["Lilith"] end subgraph DATA["Data & Runtime"] direction LR pg[("PostgreSQL + pgvector")]; redis[("Redis")]; minio[("MinIO / S3 + CDN")] end ueNative --> gateway webPx --> pxRelay --> gateway webFB --> gateway steward --> bff CLIENTS -->|HTTPS| bff gateway --> world world <-->|"perception out / actions in<br/>(egbe-protocol)"| moirai world -->|durable life-events| ori moirai -->|context reads| ori moirai --> cog --> V1 foundry --> isis foundry --> ori clio -->|reads event log| ori ori --> pg world --> pg world --> redis classDef mind fill:#dbeafe,stroke:#1e40af,color:#1e3a8a classDef store fill:#f3e8ff,stroke:#6d28d9,color:#3b0764 class moirai mind class ori store

The edge: reused gateway and Pixel Streaming, agent-extended#

The edge is the part V6 deliberately did not rebuild. The egbe-realtime-gateway (port 46103) reuses the V3 gateway design: it terminates client transport with graceful fallback (webtransport-edge, webrtc-fallback, websocket-fallback capabilities), routes clients to the world-server shard holding their instance, and hosts the voice SFU (voice-sfu, squad-voice-routing). V6 extends it with a family of positional voice capabilities named for Phemepheme-positional-proximity-voice, pheme-scriptable-voice-range, pheme-voice-accessibility-indicators, pheme-parental-voice-controls, pheme-platform-voice-safety-tap — which is the agent-and-squad communication layer riding on the V3 SFU foundation. The egbe-pxstream-relay (port 46104) is the browser-fidelity broker: it matches a session to a POP and brokers the Epic signaller exchange so a UE Pixel Streaming worker reaches the gateway as just another client. The headless render target that worker runs is a real, separate UE build: V6PixelStreamingWorker.Target.cs is Monolithic, sets bBuildWithEditorOnlyData = false, and stamps V6_PIXEL_STREAMING_WORKER=1 / V6_HEADLESS_PIXEL_STREAMING=1.

Two client shells live in apps/v6/ but are TypeScript, not Rust workspace members: egbe-web (@oshun/v6-egbe-web, a vite browser shell on port 3061 depending on @oshun/egbe-web-pxstream and @oshun/ori-model) is the Pixel Streaming entry, and egbe-web-fallback (@oshun/v6-egbe-web-fallback, three from the catalog) is the Tier-2 locally-rendered floor. Both speak the same wire protocol to the same world server, so the only thing that degrades across tiers is rendered fidelity, never identity or presence.

The shared wire protocol — the spine across three runtimes#

Everything above only coheres because one protocol crosses the Rust/TypeScript/UE-C++ boundary. The schema libs/v6/egbe-protocol/proto/oshun/v6/egbe/v1/egbe.proto (package oshun.v6.egbe.v1) quantizes for compactness exactly as V3 did — Vector3Mm (signed-millimeter zig-zag varint), RotationMilliDegrees — and adds the agent-simulation message set the loop needs: AgentState, AgentStateSnapshot/ AgentStateDelta, PerceptionItem/PerceptionFrame/PerceptionBatch, ActionIntent/ActionBatch, SquadCommsMessage, WorldEvent, the AgentTier and NegotiationStatus enums, and ClientEnvelope/ServerEnvelope with a VersionNegotiation handshake. It is implemented twice from that one schema: the Rust codec (libs/v6/egbe-protocol/rust/src/lib.rs, prost) exposes encode_wire_packet, typed decoders, negotiate_protocol_handshake, project_agent_state_snapshot, encode_agent_state_delta_from_snapshots, apply_agent_state_delta, validate_replication_bandwidth, and a run_ground_replication_fuzz; the TypeScript codec (6,821 lines) round-trips the same canonical fixtures and tests version negotiation (unsupported_version) and stable action ordering under concurrent agents (48 actions). Honest caveat: both sides define the same canonical* fixtures and each round-trips them independently, but I did not find a single byte-for-byte cross-language golden-hex assertion of the V3 kind — parity here rests on shared fixtures plus independent round-trips, not one shared golden blob. The UE-C++ V6Net module adapts to this protocol on the client side.

Project layout: where V6 actually lives#

V6 is split across the monorepo's shared trees and a self-contained UE project:

text
oshun/
├── apps/v6/                                # Rust Cargo workspace + 2 TS shells
│   ├── egbe-world-server/    (:46101, 14,363 rs)   the Body — rapier3d, 20 Hz
│   ├── egbe-moirai-cluster/  (:46102,  ~523 rs)    the Mind — over moirai-kernel
│   ├── egbe-realtime-gateway/(:46103, 3,945 rs)    edge transport + voice SFU
│   ├── egbe-pxstream-relay/  (:46104, 1,290 rs)    Pixel Streaming broker
│   ├── egbe-ori-service/     (:46105, 4,949 rs)    the Memory — event-sourced
│   ├── egbe-clio-service/    (:46106,   71 rs)     story-engine shell (logic in TS)
│   ├── egbe-foundry-service/ (:46107,  703 rs)     Ninhursag agent-gen
│   ├── egbe-web/                                   vite Pixel Streaming shell (TS)
│   └── egbe-web-fallback/                          three.js Tier-2 client (TS)
├── libs/v6/                                # 18 shared libs (4 dual-language)
│   ├── ori-model/        (1,903 rs / 3,134 ts)   schema + event types
│   ├── moirai-kernel/    (5,732 rs / 2,012 ts)   tier scheduler core
│   ├── agent-behavior/   (2,637 rs / 3,732 ts)   behavior trees + HTN
│   ├── egbe-protocol/    (1,685 rs / 6,821 ts)   wire protocol (+ proto/)
│   ├── cognition-stack/  vac-intent/  clio-story/  ereshkigal-legacy/
│   ├── aye-bridge/  egbe-studio/  egbe-web-pxstream/  egbe-engine-web-fallback/
│   └── memory-iris-agent/  psyche-agent/  isis-agent-gen/
│       isis-behavior-policy/  sophia-agent-grounding/  lilith-agent-welfare/
├── libs/contracts/src/v6/                  # 14 registered Zod contracts
└── V6/ue/                                  # the one UE5.5 project (canonical)
    ├── V6.uproject                         18 modules · 55 plugins · UE5.5
    ├── Source/  V6Core … V6Tests           67 .cpp / 47 .h · 3 *.Target.cs
    ├── Content/Districts/  L_*.umap         6 procedural district grounds (tracked)
    └── Plugins/
        ├── GameFeatures/  V6District_* (6) + V6Mode_* (4)   content-only GFPs
        └── VRM4U/                            vendored avatar importer (untracked)

The four dual-language libs/v6 crates — ori-model, moirai-kernel, agent-behavior, egbe-protocol — are exactly the four entries the apps/v6/Cargo.toml workspace pulls in as ../../libs/v6/*/rust members, which is why the performance-critical core (the kernel, the protocol, the event model, the behavior trees) is Rust while orchestration and the V1-extension adapters are TypeScript. The Districts and shard Modes ship as 10 content-only GameFeature plugins under Plugins/GameFeatures/ (six V6District_*, four V6Mode_*), each with "EnabledByDefault": false, "CanContainContent": true, and zero source modules — the same hot-swappable content-plugin model V5 used, where the mechanics live in the shared Source/ modules and a plugin contributes only content and a GameFeatureData.uasset registration.

The module split, by concern#

The 18 UE modules resolve into a clean directed-acyclic dependency graph. The edges below are the verified Build.cs edges. V6Core is the universal sink (it depends on no V6* module, only engine modules), and every edge points toward more-foundational code:

  • Foundation (1). V6Core — engine subsystems, save game, tags registry, the V1-account bridge. Engine-only dependencies.
  • Simulation client (3). V6Agent is the densest gameplay module and the one with the widest fan-in of its own (→ V6Animation, V6Avatar, V6Core, V6Gameplay, V6Voice, V6World); it carries the Mass-Entity density-LOD and perception-LOD playback (V6AgentDensityLODTypes.cpp). V6World (→ V6Core, V6UI) holds the client world model and the procedural district grounds. V6Avatar/V6Animation carry the VRM + MetaHuman runtime.
  • Communication (2). V6Voice (→ V6Core, V6Net) is the Vac/Pheme client with V6VoiceIntentTypes.cpp (the deepest file at 793 lines) and V6SquadCommsTypes.cpp; V6Net (→ V6Core) adapts the egbe-protocol wire format.
  • Presentation & platform (8). V6Gameplay (sparse GAS), V6UI, V6Audio, V6VFX, V6Cinematics, V6Input, V6OnlineServices, V6Persistence (→ V6Core, V6World) — each a thin → V6Core leaf except where noted.
  • Editor & tests (3). V6Editor (→ V6Core, V6UI; it carries the V6GenerateGameFeatureDataCommandlet), the V6Telemetry emitter, and V6Tests — the DeveloperTool harness that depends on 14 of the runtime modules and holds the Gauntlet/automation specs.

The mind never lives in these modules. The UE client is a high-fidelity client of server-authoritative state: it owns no frame-deterministic combat (incarnation combat belongs to the destination Aye realm), and all client-side prediction is reconciled against the world server.

The contract spine#

The cross-game promise rests on typed contracts. libs/contracts/src/v6 ships 18 domain files feeding a V6_CONTRACT_REGISTRY of 14 registered contractsOriIdentityCore, OriEvent, PersonalityModel, RelationshipEdge, CapabilityProfile, OriPassport, Bond, StewardProfile, Objective, GoalArc, WorldShard, Crossroads, IncarnationJournal, and Chronicle — each a contract(name, 'ori-agent', service, route, description, ZodSchema, FIXTURE) entry across two service surfaces (ori and world), validated by a __tests__/roundtrip.spec.ts. The OriEvent contract is the canonical append-only envelope (typed payload, vector clock, attribution, provenance ref) and OriPassport is the governed Aye-Bridge envelope (identity, provenance, personality, current values, destination-scoped capabilities, bond ledger, signed policy continuity) — the two contracts that make an agent both un-rewritable and portable.

Where to go next#

This page is the orientation hub for the Orientation group; three sibling pages take the topology apart:

  • Subsystem glossary — the fixed Egbe/Orun/Ori/Aye vocabulary, the full module/plugin/service/package map, and the monolith-vs-disk reconciliation.
  • Cognition stack & agent behavior — how Moirai's tiers (Clotho/Lachesis/Atropos) arbitrate HTN, LLM cognition, and the behavior-tree believability floor over the V1 substrates.
  • World server & the shard continuum — the 20 Hz authoritative tick, perception LOD, rapier physics validation, and why Solo/Co-op/Commons are one continuum because the Ori is the source of truth.
  • The full catalogue: ../V6_ARCHITECTURE.md.