Ghostlight/docs/architecture/ghostlight-eve-native-interface.md
Meta Vens 9de9e53a5f Docs describe the machine on main: README, AGENTS, handoff pointer, live architecture pages
README and AGENTS.md state what exists (sealed kernel, Dungeon, persona
projection, sidecar, browser host) and what does not (the verse, storylets,
cast, episodes). The handoff becomes a pointer to the Eureka mind. The Eve
interface page matches eve.rs (world.create v4, Draft/Active cards, play
card). Live pages cite crates/ghostlight/src/<file> instead of world/*.rs.
The implementation plan gains a historical status line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 19:50:28 +02:00

99 lines
5.2 KiB
Markdown

# Ghostlight Eve-native interface
## Objective
Ghostlight Dungeon exposes one logical surface, `ghostlight.play`, from the
provider `gamecult.ghostlight.dungeon` (`crates/ghostlight-dungeon/src/mesh.rs`).
Eve owns editable bindings, command invocation, receipts, plugin composition,
and browser lowering. Ghostlight owns world creation, seeding, activation, play,
and world admission; Heimdall owns authentication and entitlement decisions.
```text
Ghostlight projection (eve.rs)
-> EveBrowserProviderHost
-> canonical Eve lowering
-> gamecult.eve.command_invocation.v1
-> Ghostlight command ingress (runtime.rs) or Heimdall private command plane
-> gamecult.eve.command_result.v1
-> authoritative surface refresh
```
## Authority map
- Owner: the world kernel owns world changes (through `WorldMailbox`, `SeedPort`
and the play table), Heimdall owns OAuth attempts and claims, and Ghostlight's
app-session owner (`app_session.rs`) binds a verified Heimdall subject to a
local cookie.
- Inputs: the projector reads only the caller's app session, the world
snapshot, and the caller's play-turn view. Command ingress reads one canonical
Eve invocation and derives the principal server-side.
- Outputs: one `gamecult.eve.surface.v1` document and one
`gamecult.eve.command_receipt.v1` inside a command result.
- Derived state: browser drafts, focus, command status, and rendered HTML are
projections or local interaction state. None grants access or mutates
fiction.
- Forbidden writers: the browser, the Eve lowerer, the Heimdall plugin, and the
SSE stream cannot choose a principal or commit world state.
- Shared path: every operation resolves the authenticated account, then calls
the world mailbox, the seed port, or the play port.
SSE carries invalidation only and the host refetches the same surface.
## The surface
`GET /api/eve/surfaces/ghostlight.play` returns a different document by caller
and world phase (`anonymous_surface`, `authenticated_surface` in
`crates/ghostlight-dungeon/src/eve.rs`):
| Caller and world | Content | Operations |
|---|---|---|
| Anonymous | `heimdall.access_gate` | `heimdall.auth.begin`, `heimdall.auth.complete` |
| Authenticated, no world | the create form: title, brief, your name, optional Narrative persona and Operational agent labels, scale target, jurisdiction roots, lens weights | `world.create` (`ghostlight.world_create.v4`) |
| Draft world | world summary card; approve button for a required approver who has not approved; for the owner, the seed form and Seed card, and the activate button once every required approver has approved | `world.approve` (`ghostlight.world_approve.v0`), `world.seed` (`ghostlight.world_seed.v1`), `world.activate` (`ghostlight.world_activate.v0`) |
| Active world, owner | advance-time control and the Play card: narration, the open question, the refusal of the player's own act, one free-text control | `world.advance_time` (`ghostlight.world_advance_time.v0`), `world.play` (`ghostlight.world_play.v0`) |
`world.play` is the only human play action. The open question's identity rides
the play button's own action as an opaque token, never as an editable field.
`operation_schema` in `eve.rs` is the single table from operation id to schema.
## Editable values
Eve bindings are the input model. A component binds a typed value by stable
binding name, document identity, field path, value kind, and access mode.
Renderer-local composers use `local-draft`. An operation captures one or more
named binding values atomically.
The browser lowerer may use an HTML form for keyboard and accessibility
behavior. HTML form structure never enters Eve state or command payload
semantics. Accepted receipts may clear named drafts; rejection and stale
conflict preserve them. An omitted clear-binding list means clear the surface's
drafts; an explicit empty list means clear nothing. Ghostlight validates every
payload at ingress regardless of what the control advertises.
## Authentication membrane
Anonymous projection contains only `heimdall.access_gate`. Its begin and
complete operations cross Heimdall's encrypted, loopback-only CultNet boundary
(`heimdall.rs`). Ghostlight reads the redacted `heimdall:command-boundary`
record from Odin, validates it, and invokes the discovered route. Odin does not
proxy the command and never receives claims or completion payloads. The browser
retains only the opaque attempt handle (`web/src/heimdall-access-adapter.ts`).
Ghostlight redeems the completion, validates the access claim, and creates a
local HttpOnly session cookie (`ghostlight_session`).
Routine requests use local verified session state, persisted in
`app-sessions-v2.cc` (`app_session.rs`): the cookie hash, the Heimdall
session and `access_revision`, expiries, and a wrapped refresh claim.
`access_revision` is Heimdall's revocation epoch.
## Public boundary
- `GET /api/eve/provider`
- `GET /api/eve/surfaces/{surface_id}`
- `POST /api/eve/commands`
- `GET /api/eve/events`
- `GET /health`, a probe of the service's typed CultMesh health
- `POST /cultnet/snapshot` and `POST /cultnet/world-patch`, the loopback
CultNet doors (see `ghostlight-world-consumer-api.md` for the second)
Static assets in `web/` contain only the Eve provider host, the transport, the
Heimdall adapter, and the SSE invalidation hook.