# System architecture

Al-Amr uses a narrow-waist architecture:

```text
Developer Portal / Hub / Environments / Apps / Plugins
                    |
      React and R3F public adapters
                    |
       framework-neutral Al-Amr SDK
                    |
 contracts + Registry API + desktop sessions + grants + presence
                    |
 Registry modular monolith / Environment and App backends / Plugin backend components
```

## Where this lives

- `packages/contracts/src/index.ts` — the narrow waist every surface on this
  page validates against.
- `services/registry/src/application/create-app.ts` — the Registry's
  composition root, and the only source of truth named here. It had a barrel
  beside it (`src/index.ts`) whose only readers were a build entry and a
  dependency rule; deleting it exposed a boundary violation the barrel had been
  hiding, and `src/bootstrap.ts` is the process entry.
- `packages/sdk/src/bridge-client.ts` and `packages/sdk/src/environment-boot.ts`
  — the SDK an Environment boots with, over the platform bridge.
- `packages/react/src/app-frame.tsx` — the browser-only App admission adapter;
  it is not the current desktop AppHost.
- `platform/desktop/src/main/apps/app-view-manager.ts` — the desktop AppHost
  that verifies and opens the held App bundle and owns its protocol session.
- `packages/r3f/src/EnvironmentCanvas.tsx` — the rendering surface.
- `environments/hub/` — `environment-zero`, which uses only the public paths
  above.
- `ops/staging/` — the shared staging delivery described at the end.

## Surfaces

- `developer.al-amr.com` serves public, server-rendered documentation and
  catalog pages. `/console` is authenticated.
- `registry.al-amr.com` hosts public catalog, management, Runtime session,
  identity/JWKS, presence, settings, and review APIs.
- `packages.al-amr.com` initially serves immutable content-addressed tarballs.
- The Hub is an Environment with server-assigned roles; it has no private client
  imports or privileged Plugin path. Its `/z/:slug` routes select local scenes
  without creating another Environment Runtime.

### Platform calls

Calling is a platform service (`ADR-0133`), not a feature of any one world. A
call is addressed by account through the companion list, authorised by the
companionship graph on every invite, and rung as a live account CallAlert. The Registry
owns the ring, the answer and the durable record (`platform_calls`); the Media
control service gains a second admission door opened by a sixty-second
`media_call_admission` token whose scope key is the account pair rather than
an Environment scope, so the session survives every door. The independent Call App renders that service; Main presents its live alerts
through Hamsaz and the shared attention-audio lease. Hub no longer installs
in-world direct calling. Apps do not own platform call state.

## App bundle delivery

Apps are independently authored bundles, not packages imported by an
Environment and not publisher-hosted documents. Registry selects an exact
published App revision; the desktop client verifies and installs its immutable
bundle, then hosts it in a sandboxed `WebContentsView` at
`alamr-app://<appId>/` with a product-specific storage partition. The App uses
the injected `window.alamr` bridge to obtain its own short-lived session;
Environment Runtime credentials never enter the App document.

App icons and Environment covers cross a separate publication-media boundary.
Their immutable manifests name bundle-relative files; Registry reads the
reviewed archive, performs bounded raster-container validation and
metadata-allowlist rewrite, and binds the admitted object to the exact revision.
Public catalog and panel DTOs carry only the Registry-managed descriptor.
Platform chrome never loads a publisher URL. The small mark on a running App
remains a closed glyph drawn by the platform rather than the richer catalog icon.

Every App supplies Compact mode; Workspace is an optional wide desktop
presentation of the same App document. Workspace preserves the designed edge
inset and is not fullscreen. Changing mode updates presentation in place, so
the App subscribes to the host presentation rather than treating mode as a new
session. The platform panel owns host chrome, focus, input capture, animation,
confirmation, and lifecycle deadlines; an Environment receives only typed
panel interaction state through its public adapter.

The injected bridge remains one versioned Host Services narrow waist. Registry
authorizes closed App capability requests and consent, the desktop main process
enforces them, and the Environment adapter owns actual spatial context and
navigation. Stable Zone/location vocabulary is part of the immutable
Environment revision. Apps store semantic or opaque revision-bound location
references rather than inspecting coordinates or scene objects.

An App backend depends on contracts and the backend App-session introspector.
It does not depend on the Environment or platform panel. Durable state stays
with the App backend; storage in the product partition is cache and never
identity or portable authority. App-to-Plugin composition and in-world App
placement are future public contracts, not hidden host APIs.

Registry also owns one durable notification Subject projection. A reviewed App
event signals an idempotent Occurrence which Registry reduces onto a keyed or
per-occurrence Center row. Keyed Subjects use source-monotonic ordering and
acknowledgement watermarks. Open Apps use their App session; App backends
combine a scoped Project token with an opaque per-user delegation. The additive
legacy App and Environment publish paths remain bounded to reviewed source
identity, declared events, plain text, and closed semantic actions.

Desktop Main reads the account-complete Center on the device plane. An
Environment Widget reads only that Environment's projection and receives no
account-complete content. Realtime transport carries content-free invalidation;
a Pulse is a separate live presentation decision, while Registry state remains
authoritative. Deadline-bound CallAlerts and future external push are separate
planes rather than inbox rows.

## Environment-owned backends

An Environment may declare one owned backend boundary in its immutable
manifest: exact audience, endpoint and health URLs, allowed Grant scopes,
bounded context schema, and reviewed data disclosures. This backend owns
destination-specific state such as conference events and organizer roles. It
is not a Plugin and is not a fourth catalog product.

The browser SDK requests a short-lived Environment Backend Grant from Registry.
Registry binds it to the exact Environment revision, active Runtime lease,
desktop-hosted bundle origin, pairwise actor, declared endpoint, approved scopes
and validated context. `@al-amr/backend` verifies the signature and endpoint
and introspects the live lease by default. Registry may derive the initial
Environment `admin` from Project ownership; delegated domain roles such as
`organizer` are stored and audited by the Environment backend.

## Hub composition

Hub remains the ordinary `env_hub` Environment. A Zone is an application-local
scene: all Zones share the Hub origin, Runtime session, Presence identity,
Plugin settings, exact Plugin installations, immutable Environment revision,
and one persistent R3F Canvas. Moving between Zones is allowlisted local
navigation; entering an independent Environment asks the desktop Shell to
resolve the exact active held bundle and open its
`alamr://environments/<environmentId>` destination with a fresh Runtime session.

The desktop re-entry target identifies only the safe Hub Environment destination.
Before an explicit outbound navigation, Hub may arm a versioned, one-shot
checkpoint in same-tab `sessionStorage` containing its current Zone, player
pose, and camera view. On return, Hub validates and resolves that local state
through the current Zone surface before Player Rig or Multiplayer mounts, then
consumes it. The checkpoint never enters a URL, Runtime token, Registry record,
or destination Environment; absent or invalid state uses the authored spawn.

Hub owns the Zone graph, terrain and path data, spawn points, authored landmark
geometry, the finite logical slot inventory, and composition. Registry owns
only the current assignment of an existing logical slot to a stable Environment
ID and the administrator audit history. It cannot create or move geometry.
Reusable capabilities come from the same public R3F Plugin path available to
every Environment:

Hub reads public placements and the public Environment catalog together. An
assignment becomes enterable only while its destination has the exact active,
published, public, catalog-eligible revision. Empty manual slots are `open`;
missing, ineligible, or unverifiable assignments are `reserved` and expose no
launch target. Assignment unavailability never falls back to a checked-in
destination. Fixed bindings remain read-only; a Registry administrator can set,
replace, or clear only an authored manual slot through the same API used by
Admin Panel and CLI.

For each active Zone, Hub creates one `ZoneSurfaceModel` from the host-owned
path. The model samples the route at one-metre intervals and is the common query
source for progress, tangents, terrain, road, skyway and landmark surfaces,
normals, obstacles, and navigation collision. Terrain and road meshes and the
Player Rig therefore consume the same geometry instead of reproducing spline or
height calculations.

- Player Rig provides the avatar, configurable desktop input, movement, flight,
  animation adapter, hybrid camera, allocation-free local replica source, and
  optional instanced remote Fleet.
- Hub owns sky, terrain, vegetation, both roads, skyway art, period gates,
  landmark presentation, portal proximity, and signage because those elements
  are authored parts of a Zone rather than independently useful capabilities.
  All of them sample the same Zone-owned `ZoneSurfaceModel`. ADR-0060 retired
  the Plugins that once held sky, scatter, and portal proximity: each had only
  ever had the Hub as its consumer.

These releases are independent `frontend_only` trusted-library Plugins. They
do not depend on one another or import Hub code; Hub supplies typed data and
callbacks and pins every installed release exactly.

A Zone resolves one render policy — quality tier, backend, and reduced-motion
mapped to concrete scene cost — and one scene manifest of world systems before
the lazy Zone renderer mounts. Systems read the resolved policy rather than the
tier, so replacing or retuning one visual capability does not require cloning
the whole Zone.

Multiplayer is a separate full-stack Plugin: Hub composes its renderer-neutral
Browser Core with Player Rig through structural semantic ports, but neither
Plugin imports the other. Hub sends the stable active Zone ID as an opaque
scope. The Grant-protected backend hashes the signed `environmentId` plus that
scope into one single-writer room, accepts complete binary transform state at a
fixed cadence, and emits one personalized AOI batch per receiver tick. Redis
RoomDirectory leases fence multi-node ownership and publish the owner's private
routing origin. A non-owner Gateway forwards browsers through one pooled,
mutually authenticated HTTP/2 session per owner and one bounded stream per
browser; Redis never transports pose state. Browser Core uses a bundled module
Worker for WebSocket I/O, binary decode, network clock, and client
backpressure, then feeds per-tier server-clock ring buffers on the main thread.
Tier-delay changes slew on a continuous presentation timeline. v3 carries
explicit grounded/flying mode, commits membership snapshots atomically, and
renews fresh single-use Grants on the same socket through an exact actor-fence
CAS. Dormant, wake and mode transitions bypass cadence; packet gaps and
dormancy retain the presented avatar. Player Rig samples each buffer once from
one instanced Fleet frame callback rather than mounting one React avatar per
peer.

The reference capacity unit has 150 participant credits and 16 non-empty-room
credits; one room may consume all 150 participants. An empty owner descriptor
may remain for a 60-second reuse grace without consuming a credit. Browser Core
acquires the next Zone Grant before leaving, waits for acknowledged release of
the old membership, and commits the new scope only after the complete atomic
snapshot. Restricted Zones use the Environment backend admission binding;
Multiplayer itself never infers access from coordinates or UI routing.

Same-room reconnect is an atomic transport handoff rather than a new member
admission. A fresh Grant and rotating resume ticket may replace an overlapping
old WebSocket; exactly one CAS winner can mutate the roster. Ordinary Grant
renewal does not replace the socket, member, directory or presentation buffers.

Media is another independent full-stack Plugin and does not import Multiplayer.
The Environment owns movement, Zone and proximity policy from Player Rig and
Multiplayer state and supplies Media only an opaque scope and a selected scoped
`actorKey`. Environment code uses the managed Media Runtime; raw control and
media-plane implementations are private package internals. Media's
Grant-protected control plane owns invitations, declared
meeting/stage membership, moderation, reconnect grace, and a single
screen-share lease. A self-hosted LiveKit cluster carries WebRTC
media with source-restricted short-lived tokens; direct calls and meetings add
SFU-blind E2EE while stages use transport encryption. Registry remains the
source of installation, deployment, trust, and Runtime authorization truth.
Redis is Media v2.2's authoritative operational store for schema-v2 sessions,
actor fences, room versions, reconnect deadlines, stage state, offers, and
leases. Every transition atomically coalesces an independent version/owner-
fenced desired-state Projection job. Signed LiveKit webhooks, Projection
workers, and cursor-fair full repair converge transport to that state without
placing LiveKit admin latency on the command path.
Stage membership requires `media:join-space`; only a verified Grant carrying
`media:publish` receives publish-capable LiveKit authority or a screen-share
lease. Participant arrival order never grants the presenter role.

Avatar UI follows [ADR-0041](../decisions/ADR-0041-player-rig-fleet.md).
Player Rig exposes semantic anchors, the fixed-pixel `PlayerAvatarOverlay`, and
large-room Fleet/overlay providers; Multiplayer exposes server-stamped peer
descriptors. Media deliberately exposes no UI contribution or composition
surface: its public boundary is the managed Runtime, selectors, copy, and native
track abstraction. The Environment correlates cooperating Plugin data through a
scope-bound pseudonymous `actorKey` and renders its own call action inside
`avatar.overhead` or the single `avatar.fleet-overhead` projection root. It also
owns stage-screen attachment, placement, accessibility, and fullscreen. No
Plugin imports another Plugin, and no Hub-only attachment API exists. Frontend
adapter manifests, Registry `integration.md`, reviewed declarations, and
Developer Portal version pages expose this exact boundary to people and coding
agents.

The `fazl` Zone has exactly four visual Landmarks: Lumen Gallery bound
to `env_1317c83f-50a1-4607-9307-b0f852b1a740`, Conference
Hall bound to `env_f2a8ca72-9584-44dc-b809-6a79587fc176`, Park bound to
`env_0632e375-4956-4bf8-8877-3e4fcaa440d6`, the neutral
`community-showcase-01` manual slot, and the Developer Portal construction
site. Each assigned destination remains
non-enterable until its active public revision appears in the Registry catalog.
Remaining slot data
is not rendered as rings, platforms, empty-site markers, HUD, or interactions.
Hub registers these Landmarks as interaction targets in its own Zone-owned
registry and renders the active portal card in the main document outside the
persistent R3F Canvas. Proximity alone never launches a destination; `E`, the
card button, or an explicit active-Landmark click is still required.

The `fazl` ground is a mirrored valley cross section: `fazlValleySectionM` is a
pure function of the absolute lateral distance from the road, so both verges
fall and climb identically. The section holds flat out to 26 m, drops nine
metres to a meadow floor at 120 m, then climbs to a wooded ridge fifteen metres
above the causeway at 238 m, just inside the Zone's 240 m half-width. A walker
on the stone therefore looks down on one layer and up at the next instead of
seeing the same plane at every distance. Both stages are smoothsteps, so the
profile is monotone and arrives flat at the corridor blend; a kink there would
draw a seam the length of the road. Its vegetation system owns seamless
clustered blue-noise scatter,
visual variants, quality tiers, instancing, and LOD against the Zone surface
sampler and authored clearings. Its three Environment destinations are resolved from one
replaceable placement map rather than duplicated in Landmark art. Two
always-visible ground thresholds, their Persian labels, the sacred skyway,
placement, routing, approach road, and current-period sign are all Zone-authored
content.

This composition adds no Registry entity and changes no Registry, SDK,
`PortalPayload`, desktop entry, manifest schema, or wire contract. Independent
Environment entry continues through the Shell-resolved held bundle.

## Rendering

First-party Environments render through the shared profile recorded in
[ADR-0048](../decisions/ADR-0048-three-r185-webgpu-first-rendering.md):
Three.js r185 with the WebGPU-first `WebGPURenderer` and a real WebGL2
fallback, one shared `EnvironmentRenderingGate` and `EnvironmentCanvas` from
`@al-amr/r3f` probing graphics before presence, Plugins, or scene code mount,
TSL-only custom materials, and privacy-safe diagnostics. The full runtime
contract — modes, retry and device-loss recovery, reason codes, and quality
tiers — is in
[Rendering runtime](rendering-runtime.md); the user-facing browser and device
policy is in [Rendering support](../product/rendering-support.md).

## Storage

- PGlite is the local/test Postgres implementation.
- Production uses PostgreSQL through the same repository boundary.
- Redis is used only for ephemeral presence, rate limits, and runtime session
  coordination in production.
- Package and media blobs use private, self-hosted S3-protocol object storage;
  SeaweedFS is the supported baseline and no Amazon account is required. Public
  package bytes still cross the Registry distribution boundary rather than
  exposing the object store.
- Registry-managed App icons and Environment covers are immutable private media
  objects authorized by an exact published revision binding. Their public
  content-addressed route resolves the recorded object version and never
  proxies a publisher source on demand.
- Registry maintains a derived immutable cache of every authorized published
  Managed Store artifact. Cache identity includes the byte-derived SHA-512 and
  exact Object Storage VersionId. Registry state still authorizes every read;
  the cache preserves published downloads during a provider outage but cannot
  authorize publication, uploads, drafts, or blocked bytes.

Every public response is derived from Registry state. UI code and 3D scenes do
not hardcode official catalog records.

## Shared staging delivery

Shared staging keeps three deployment roles: Core for Registry and Developer
Portal authority, Runtime for product-owned backends and support services, and
Media for LiveKit/Media. Environment and App frontend bytes are held bundles,
not Runtime-hosted sites. Managed PostgreSQL is stateful; Gallery retains
host-local state; Media's authoritative Redis state is AOF-persistent, while
LiveKit transport and room coordination remain transient and rebuildable. This
placement is a staging operational concern and does not create new public
domain entities.

GitLab builds deterministic role archives from a clean commit and activates
each host-local `current` symlink under systemd/Caddy. Registry catalog state,
held-bundle bindings and role serving state are coordinated by a journaled
transaction, but the system does not claim cross-host atomicity. N/N-1 compatibility and
expand migrations bound the mixed window. Typed infrastructure, deployment
profile and first-party catalog sources generate the deploy views; endpoint
materialization is field-aware and Plugin source bytes do not embed a staging
origin. Deployment profiles keep reviewed `pluginOrigins` separate from live
service placement: moving a backend advances `BackendDeploymentRevision`
without silently changing Plugin bytes, manifest permissions, or SemVer.

### Registry migration history and publication 0102 recovery

Applied migration SQL is immutable, including comments. The shared migrator
validates recorded checksums before executing pending expands. A service may
prepare a pending expand's transaction through `beforeExpandMigration`; that
hook neither changes SQL bytes nor reconciles ledger checksums. Registry uses
it before 0102 to validate the deferred ownership checks backfilled by 0090
before rebuilding the publication-case index in the same transaction.

The original `0102_live_publication_case_bridge.sql` has SHA-256
`1f7a5d1b6c9c21805a9998ec4478fc31b071ddc983ff24e7b799fafbe7c754e2`.
Commit `35c33fa93` edited that already-applied file in place. The original bytes
are restored; forward expand 0112 applies the completion-function correction.
Databases carrying the original checksum upgrade normally while retaining
their complete ledger, users and publication history. Fresh databases reach
the same corrected functions.

A database carrying the edited checksum
`185ccb8f53a78c0693813c2c5d2b06e577bccf66ab36b2662b7c71a95cdd23b9`
is a divergent migration history and remains fail-closed. Do not overwrite its
checksum, skip the guard, or declare it repaired by creating an empty database.
Keep its backup and the matching old binary available; inspect its applied
history and actual function definitions read-only. Such a database requires a
separate, explicitly reviewed data-preserving history reconciliation before
upgrading to this line. The ordinary original-0102 upgrade does not authorize
that reconciliation or mutate a preserved acceptance database automatically.
