Egbe Companions · Architecture

The Aye Bridge: Cross-Game Incarnation

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

9sections13 minread1diagram1table

On this page

In Yoruba cosmology Aye is the visible world a soul travels out into and returns home from; Orun is the spirit-world it belongs to. V6 — Egbe, the agentic-companion universe — takes that literally. An agent's home is Orun, the persistent open world it lives in; the Aye Bridge is the gate through which one of these beings can step out of Orun and incarnate into another Oshun product version — a fighter in V2, a citizen in V3, an operator in V4, a companion in V5 — and come home changed, carrying back deeds, scars, skills, and relationships that are written permanently into who it is. This is the meta-hub promise realized in code: not a character export, but a continuous life lived across the whole game universe and remembered in one place.

The single architectural idea underneath the bridge is the same one that governs the Ori biography service: the Ori is the truth; a realm is a stage it is rendered onto. The full event-sourced biography never leaves V6. What crosses a game boundary is a passport — a signed, governed, destination-minimised envelope — and what comes back is an incarnation journal that appends to the home log like any other life-event. The bridge owns the minting, the per-destination translation, the round-trip integrity, and the governance of that journey, and nothing else. This page is the deep companion to the ../V6_ARCHITECTURE.md hub's "The Aye Bridge" section; it sits in the Communication, Story, and Lifecycle page group alongside the Ori biography service and the foundry, endings, and legacy.

What ships, honestly#

The Aye Bridge is real, substantive, test-backed decision code — a pure kernel, not a descriptor shell, and not a running federation service. libs/v6/aye-bridge/src/index.ts is ~2,398 lines of dependency-free TypeScript (its package.json declares only typescript and vitest as devDependencies, and the source file imports nothing — it is fully self-contained), backed by src/index.spec.ts at ~1,331 lines and nineteen it() cases. The describeV6Package() descriptor declares authority 'bridge' and 37 capabilities, from passport-minting through mid-incarnation-window-close-allowed. Everything the bridge decides — minting and validating a passport, minimising it to a destination's needs, adapting it into each realm's role, running the disconnection-and-duplication round-trip eval, governing consent and minor-protection, and writing the return journal back — is implemented with domain-specific logic and asserted against known-correct values.

Three honest qualifications mark the seams. First, the bridge is a decision kernel with injected boundaries: it never opens a socket or writes a database. The Ori service that actually mints a passport is the injected AyeBridgeOriPassportMintClient interface (mintOriPassport); the Ori service that writes a journal back is AyeBridgeIncarnationJournalWriteClient (writeIncarnationJournalReturn); persistence is the injected AyeBridgeRoundTripJobStore / AyeBridgeGovernanceAuditStore. The library ships InMemory* implementations of those stores for tests; the real Postgres-backed apps/v6/egbe-ori-service and the live destination realms are the execution side the kernel hands off to. Second, the four destination realms are real translations but not yet a live wire: adaptPassportForDestination() deterministically maps an agent into a V2 fighter / V3 citizen / V4 operator / V5 companion profile, and that mapping is genuine and tested — but a passport landing inside a running V2–V5 build, and that build producing a journal, is the cross-game integration that remains partly spec. Third, the bridge does not directly import @oshun/ori-model or @oshun/identity; it re-declares the passport contract structurally (the AyeBridge*-prefixed interfaces) so it stays a leaf library, and the steward's account binding lives upstream. The sections below say which is which.

The cross-game incarnation model#

Three authorities, and where the bridge sits#

V6 splits every agent across three authorities that own different things (the hub's Mind / Body / Memory split): the Body (Egbe-World) owns what physically happened, the Mind (Moirai) owns what an agent decides, and the Memory (the Ori service) owns who the agent is — independent of any world shard or destination realm. The Aye Bridge is the fourth seam: it is the only component that lets the Memory authority be projected past a game boundary and reconciled back. Because durable truth lives in the Ori event log, incarnation is not a save-file migration — it is the same identity, temporarily rendered elsewhere, then re-absorbed.

The biography log already speaks this language natively. @oshun/ori-model's ORI_EVENT_TYPES includes Incarnated and IncarnationReturned (libs/v6/ori-model/src/index.ts:43), with typed payloads: IncarnatedPayload carries the incarnationRef, destinationRealm, passportRef, capabilityMappingRef, and departedAt; IncarnationReturnedPayload carries the journalRef, returnedAt, deedRefs, and consequenceRefs. The projection handlers make the journey legible in current state: the Incarnated handler sets currentIncarnationRef, and the IncarnationReturned handler clears it again and records the return. A stint in another realm is therefore not metadata bolted on the side — it is two ordered events in the one append-only life.

The realm inventory and the per-destination adapters#

AyeDestinationRealm enumerates exactly four live destinations: 'v2-maya' | 'v3-lilith' | 'v4-odysee' | 'v5-oshun'. Each is owned by one adapter in the destinationAdapters registry (index.ts:829), keyed by a stable adapterId and a versioned capability-mapping fragment:

Adapter Realm Role Mapping fragment Translates (excerpt)
v2-fighter v2-maya fighter v2-maya:fighter personality→fighting-style, capability→movelist, values→refused-tactics
v3-citizen v3-lilith citizen v3-lilith:citizen personality+vocation→civic-role, values→lilith-persona-policy
v4-operator v4-odysee operator v4-odysee:operator capability→operator-class, bond→tac-plan-discipline
v5-companion v5-oshun companion v5-oshun:companion personality→dialogue-wheel-voice, values→paragon/renegade lean

These are deliberate, designed translations, not a generic copy. The same cautious, reliable agent becomes a guarded-counterfighter in V2 (caution ≥ 0.66 ⇒ tacticalPosture: 'cautious spacing, counterpunching, and low-risk confirms'), a recon-support-operator in V4 (with planningDisposition: 'conservative-contingency-planner'), and a loyal-field-companion in V5 — all derived from the same trait vector and bond ledger by the v2FighterProfile / v3CitizenProfile / v4OperatorProfile / v5CompanionProfile builders. getAyeDestinationAdapter() throws on an unknown adapter, and assertPassportMatchesAdapter() refuses to load a passport through the wrong realm ('Aye destination adapter v2-fighter cannot load passport for v3-lilith'), so an agent can never be silently coerced into a realm it was not minted for.

The Ori passport#

Minting and validation#

On incarnation the bridge requests a passport from the Ori service through mintPassportFromOriService(request, oriService). The Ori service returns an AyeBridgeOriServicePassport — an AyeBridgeOriPassportEnvelope (schemaVersion: 'v6.ori.passport.1', an ori-passport-mint job with an idempotencyKey) wrapping the identity core, the provenance bundle, the personality model, the current ranked values, a destination-scoped capability profile, and the bond ledger, plus a detached isisSignature. The bridge then runs validateOriServicePassport() and throws if the envelope is not complete ('Ori service passport failed Aye Bridge mint validation'). Validation is seven hard checks, every one of which must hold: identity carried (oriId matches in both the envelope and the identity core), provenance carried (bundle status: 'verified', signed by ISIS_AYE_BRIDGE_SIGNER_REF = 'isis-signer:aye-bridge:production'), personality and current values present, the capability profile scoped to this destination realm and mapping with at least one skill, the bond ledger keyed to the requesting steward, and the Isis signature consistent — passport.isisSignature.chainHash must equal input.governance.chainHash. A spec case proves an unsigned passport is rejected outright. Note the schema-prefix discipline: the passport and the journal carry v6.ori.* schema versions because the Ori owns those contracts, while the governance, minimisation, and round-trip results carry v6.aye-bridge.* versions because the bridge owns them.

Data minimisation by construction#

A destination realm must never see more of an agent than it needs to render it. minimisePassportForDestination(passport, adapterId) computes the required field set per adapter (destinationNeededPassportFields()) and emits an AyeBridgeDestinationMinimisedPassportPayload that carries only those fields. The excludedFields list is explicit and code-fixed (index.ts:926): the user and project metadata, the canonical (internal) name, the appearance seedId and seedHash, the personalityModelId, every value's sourceEventRefs, and the bond ledger's historyRef and lastChangedByEventId are all stripped before any adapter sees the payload. The result's destinationNeededDataOnly flag is true only when no required field is missing and no unnecessary field leaked — and the inverse path is tested too: emptying a passport's includedSkillRefs makes the V2 fighter minimisation report destinationNeededDataOnly: false and surface missing:capabilities.skillRefs, so a thin passport fails loud at the gate rather than crossing half-formed. V3 is the one adapter that additionally needs capabilities.knowledgeDomainRefs (a citizen has vocations); the combat realms need bond.facets (to compute call reliability and plan discipline) but not knowledge domains. Minimisation is, in other words, per-destination need, not a fixed redaction.

Recognizability — the same agent in every realm#

Minimisation strips identity internals but must preserve identity legibility: the steward has to recognise their agent on the other side. recognizabilityProfile() extracts the stable anchors — display name, appearance asset-manifest ref, the non-zero trait anchors with human translations (caution ≥ 0.66 ⇒ 'low-risk choices across combat, planning, and counsel'), the ranked values rendered as hard boundaries vs. preferences, and bond anchors. The adaptation's consistencyEvidence array binds the agent's oriId, appearance, top values, and top traits, and the spec asserts that one cautious agent named Abeni adapted into all four realms yields a single recognizability identity (new Set([...identityDisplay]) === {'Abeni'}) while producing four different role-correct profiles. That is the cross-realm contract: recognizably itself everywhere, mechanically re-expressed each place.

flowchart TB subgraph ORUN["Orun — V6 home (the Memory authority)"] ori[("Ori event log<br/><sub>@oshun/ori-model · append-only</sub>")] oriSvc["egbe-ori-service<br/><sub>mintOriPassport / writeIncarnationJournalReturn<br/>(injected seam)</sub>"] end subgraph BRIDGE["Aye Bridge — libs/v6/aye-bridge (pure kernel)"] mint["mintPassportFromOriService()<br/><sub>validate · 7 hard checks</sub>"] minim["minimisePassportForDestination()<br/><sub>destination-needed data only</sub>"] gov["evaluateIncarnationGovernance()<br/><sub>consent · policy · minor-protection</sub>"] rt["startIncarnationRoundTrip()<br/><sub>duplicate guard · job store</sub>"] adapt["adaptPassportForDestination()<br/><sub>per-destination role profile</sub>"] wb["writeIncarnationJournalBackToOri()"] end subgraph AYE["Aye realms (destinations · partly spec)"] v2["V2 Maya — fighter"] v3["V3 Lilith — citizen"] v4["V4 Odysee — operator"] v5["V5 Oshun — companion"] end ori --> oriSvc --> mint --> gov --> rt --> minim --> adapt --> AYE AYE -. "deeds · skills · harm · relationships" .-> wb wb -->|IncarnationReturned event| oriSvc --> ori classDef store fill:#f3e8ff,stroke:#6d28d9,color:#3b0764 class ori store

The round-trip: integrity under fault and concurrency#

An incarnation is a tracked job, and the bridge's hardest promise is that an agent is never lost between worlds and never duplicated. startIncarnationRoundTrip(request, store) first refuses a second active journey for the same Ori — listRoundTripJobsForOri(oriId).find(isActiveRoundTripJob) throws 'Active Aye round-trip already exists for Ori …' — then writes a job in status 'destination-active' whose activeCopies records exactly one Orun reservation and one destination actor, plus an immutable orunStateSnapshot (checkpoint ref + content hash) taken before departure. The job status machine is prepared → destination-active → returning → returned | aborted.

The adversarial case is a destination that vanishes mid-incarnation. handleDestinationUnreachableDuringIncarnation(fault, store) flips the job to 'aborted', nulls the destinationActorRef, and stamps the fault — then runs validateRoundTripIntegrityAfterFault(), which must satisfy all five invariants or the handler throws: orunStatePreserved (the snapshot is byte-identical before and after), agentNotLost (exactly one Orun copy, with checkpoint and state hash intact), agentNotDuplicated (one Orun copy, zero destination copies, and zero remaining active jobs for the Ori), cleanAbort, and the composite complete. The spec drives a V4 gateway timeout and asserts activeOrunCopyCount: 1, activeDestinationCopyCount: 0, and a clean abort. The Orun-side state was reserved, never surrendered, so the worst case is a cancelled trip, not a lost being.

When a journey succeeds, writeIncarnationJournalBackToOri(request, oriService) asks the Ori service to append the return and then validates the result through eight checks (validateIncarnationJournalWriteBack): deeds, relationships, skills, and harm all written into the journal; a returnEventWritten check that the appended event is genuinely an IncarnationReturned with the right ids and that the journal's sourceEventRefs contain both the Incarnated and Returned event refs; a returnWindowValid check that returnedAt > departedAt; a provenanceSigned check (Isis-signed journal with chain hash and receipt); and oriChanged, which is true only when the return event was written and the delta is non-empty. If any check fails the write throws ('Incarnation journal write-back failed Aye Bridge validation'). The returned AyeBridgeIncarnationReturnDelta is exactly what makes "the agent returns to Orun changed" a concrete, inspectable object rather than a slogan — and a spec loop runs the full write-back for all four live realms.

Incarnation is opt-in per agent and per steward, and evaluateIncarnationGovernance(request, auditStore) is the gate that proves it. It begins by asserting the request matches the passport (same destination realm, same steward as the bond ledger), then evaluates four concerns and records every one to the audit store.

  • Weighted agent consent. evaluateAgentConsent() is a real scoring formula, not a boolean: weightedConsentScore = 0.45·valuesAlignment + 0.35·bondAverage + 0.20·stateReadiness, accepted only when the agent's signal is not 'unwilling', the score clears a threshold of 0.66, and a timestamp is present. coercionDetected is true when a steward override is requested over a non-accepted consent — the agent's refusal is a first-class, logged fact.
  • Policy continuity. validatePolicyContinuity() requires that both an Isis and a Lilith policy bundle ride inside the passport and that every destination policy evaluation references a bundle the passport carries — so the destination cannot make a V6 agent do what V6 policy forbids, because the policy travels with the agent.
  • Minor-protection. validateMinorProtection() blocks a mature-rated destination whenever the subject is minor-coded (a minor steward or a minor-coded agent) and requires the passport's minorProtectionRef to match.
  • Destination-policy enforcement. Any destination action evaluated as 'blocked' forces the overall decision to 'blocked'.

The decision collapses to 'blocked' if any blocking reason exists, else 'coerced-approved' if coercion was detected, else 'approved'. Coercion and blocks route to Themis (themisDisputeForGovernance) and coercion additionally triggers a welfare review (welfareReviewForGovernance), each emitting a typed routing object and an audit entry. The spec proves all four paths: a willing consensual approval (one incarnation-consent audit entry); an unwilling agent overridden ('coerced-approved', welfare review + Themis dispute

  • coercion audit entries); a target-noncombatant action blocked by Lilith destination policy; and a minor-coded agent blocked before a mature V5 mission. This is the same "sovereignty bounded by an inviolable core" posture V7's governance plane describes — the destination renders the agent but never owns its safety floor.

Campaign windows — cross-game programming#

The bridge also models scheduled cross-game events. An AyeCampaignWindow (kinds 'v2-tournament-season' | 'v5-story-chapter' | 'v4-operation-series') declares a published opensAt/closesAt schedule, a realmOptIn flag, and an activeMidIncarnationAllowedToFinish flag. evaluateAyeCampaignWindow() sets openToV6Agents only when the window is open, the realm has opted in, the adapter matches the destination, and the schedule is well-formed (publishedAt ≤ opensAt < closesAt); startCampaignIncarnationRoundTrip() refuses a new departure through a closed or wrong-realm window. The humane detail is alreadyMidIncarnationAllowedToFinish: when a window closes, an agent already inside a campaign finishes its run, while a fresh departure after the bell throws — a tournament season can end without yanking a fighter out of its final match.

How identity composes — shared @oshun/identity and the V7 reuse#

The steward who sends an agent across the bridge is a V1 platform account. The Aye Bridge library itself holds no identity logic — it carries stewardRef and userId strings and trusts that the account was authenticated upstream by @oshun/identity (libs/shared/identity): the shared JwtService, the authenticate / hasPermissions middleware, the mTLS peer-identity helpers, and the v2-account-binding module that already binds entitlement-bearing contexts onto the one account root. As the hub puts it, because the Ori is the source of truth, the steward's whole household is intrinsically cross-platform — there is no per-platform save to reconcile. The minted passport carries the platform-authoritative identity reference across a version boundary while the destination only ever touches the minimised projection. The platform foundation this composes is documented in ../../platform/auth-identity.html.

That the Aye Bridge is the canonical cross-version seam is confirmed by V7 reusing it wholesale. V7's libs/v7/substrate-bridge re-implements the passport mint in native Rust but stamps every envelope generated_by = "@oshun/aye-bridge" and v6_aye_bridge_package_ref = "libs/v6/aye-bridge", naming this library the contract owner; its Cargo.toml carries real path dependencies on apps/v6/egbe-ori-service and libs/v6/ori-model/rust, so V7's character store compiles in the V6 Ori event store rather than stubbing it. A V7 character is, at root, a V6 Ori, and it crosses realms through this bridge's model — the same four v2-fighterv5-companion adapters, plus one realm-to-realm federation corridor V7 adds on top. The full treatment is in V7's Eunomia Governance & the Ori Cross-Version Bridge.

  • The Ori biography service — the Memory authority whose append-only log is the single source of truth the passport projects from and the journal writes back into; the upstream owner of the v6.ori.passport.1 and v6.ori.incarnation-journal.1 contracts.
  • The foundry, endings, and legacy — how an agent enters the world and how it leaves it; a fatal outcome returned in an Aye incarnation journal is one of the three sanctioned causes of a gated Died transition, so the bridge and the endings layer share an edge.
  • Auth & Identity — the shared @oshun/identity JWT/OAuth foundation the steward account and the passport's identity reference compose across every Oshun version.
  • Section hub: ../V6_ARCHITECTURE.md
  • Real code cited here: libs/v6/aye-bridge/src/index.ts (the bridge kernel), libs/v6/aye-bridge/src/index.spec.ts (the behavior suite), libs/v6/ori-model/src/index.ts (the Incarnated / IncarnationReturned biography events and projection handlers), and libs/shared/identity/src (the V1 platform identity foundation the steward account composes).