# Bellona Domain — Technical Specifications

> Build Orchestration, Asset Interchange, Engine Bridge, and Remote-Control
> Platform

This document specifies what the Bellona domain **actually implements** in the
Oshun monorepo. Every schema, field, enum value, command, and event listed below
is traceable to source code under `libs/bellona/*` and `apps/bellona/*`. Items
named in a design backlog but not yet present in code are explicitly labelled
`(planned)`.

---

## 1. Domain Layout

Bellona is an Nx domain. It has **34 libraries** under `libs/bellona/` and **12
applications** under `apps/bellona/`. There is no `services/bellona/` directory.

### 1.1 Libraries (`libs/bellona/`)

The following table is the complete, authoritative list of Bellona libraries.
Each row shows the directory name, its NPM package identifier, and a brief
summary of its role.

| Library                 | Package                          | Role                                                              |
| ----------------------- | -------------------------------- | ----------------------------------------------------------------- |
| `3dsmax`                | `@bellona/3dsmax`                | 3ds Max bridge runtime and workflow contracts                     |
| `adapters`              | `@bellona/adapters`              | Engine-adapter infrastructure (BaseBridge, queues, state)         |
| `asset-export`          | `@bellona/asset-export`          | CGI-scene asset export readiness/conversion planning              |
| `audio`                 | `@bellona/audio`                 | Audio processing and transcoding contracts                        |
| `blender`               | `@bellona/blender`               | Blender engine adapter                                            |
| `blender-agent`         | `@bellona/blender-agent`         | Blender-native agent runtime (RPC bridge, action schemas, macros) |
| `bridge-core`           | `@bellona/bridge-core`           | WebSocket bridge protocol foundation                              |
| `client`                | `@bellona/client`                | TypeScript SDK                                                    |
| `cross-dcc-consistency` | `@bellona/cross-dcc-consistency` | Cross-DCC workflow consistency / compatibility contracts          |
| `database`              | `@bellona/database`              | Prisma schema and generated client                                |
| `davinci`               | `@bellona/davinci`               | DaVinci Resolve integration                                       |
| `editor-productization` | `@bellona/editor-productization` | Editor release/onboarding/recovery/diagnostics contracts          |
| `event-handlers`        | `@bellona/event-handlers`        | Cross-domain event subscriptions                                  |
| `event-publisher`       | `@bellona/event-publisher`       | Type-safe cross-domain event publishing                           |
| `gameplay-systems`      | `@bellona/gameplay-systems`      | Engine-agnostic gameplay systems                                  |
| `godot`                 | `@bellona/godot`                 | Godot engine adapter                                              |
| `houdini`               | `@bellona/houdini`               | Houdini integration                                               |
| `integration`           | `@bellona/integration`           | Cross-domain consumers/compilers (Hathor, Isis)                   |
| `interchange`           | `@bellona/interchange`           | 3D asset format conversion pipeline                               |
| `interchange-models`    | `@bellona/interchange-models`    | Interchange data schemas                                          |
| `maya`                  | `@bellona/maya`                  | Maya bridge runtime and workflow contracts                        |
| `mcp-gateway`           | `@bellona/mcp-gateway`           | Remote-control MCP gateway server and stdio transport             |
| `metahuman`             | `@bellona/metahuman`             | MetaHuman pipeline contracts                                      |
| `mocap`                 | `@bellona/mocap`                 | Motion-capture streaming, retargeting, frame-snap                 |
| `openusd`               | `@bellona/openusd`               | OpenUSD pipeline                                                  |
| `remote-protocol`       | `@bellona/remote-protocol`       | Canonical remote-control protocol contracts                       |
| `sdk-cpp`               | `@bellona/sdk-cpp`               | C++ native SDK sources                                            |
| `unity`                 | `@bellona/unity`                 | Unity engine adapter                                              |
| `unity-agent`           | `@bellona/unity-agent`           | Unity Editor MCP server package and orchestration wrapper         |
| `unreal`                | `@bellona/unreal`                | Unreal engine adapter (+ `BellonaUnrealEditor` plugin)            |
| `video`                 | `@bellona/video`                 | Video processing contracts                                        |
| `virtual-production`    | `@bellona/virtual-production`    | Virtual production contracts                                      |
| `xr`                    | `@bellona/xr`                    | XR (visionOS, Meta Quest, WebXR) contracts                        |

### 1.2 Applications (`apps/bellona/`)

The following table is the complete, authoritative list of Bellona applications.

| App              | Role                                                            |
| ---------------- | --------------------------------------------------------------- |
| `build-api`      | Event-driven build/export orchestration module                  |
| `build-worker`   | Background job processor (4 worker types)                       |
| `render-api`     | Render-job caching and output validation module                 |
| `cli`            | `bellona` command-line tool (Commander.js)                      |
| `bridge-unity`   | Unity WebSocket bridge (port 9004)                              |
| `bridge-unreal`  | Unreal WebSocket bridge (port 9003)                             |
| `bridge-godot`   | Godot WebSocket bridge (port 9002)                              |
| `bridge-blender` | Blender WebSocket bridge (port 9001)                            |
| `control-room`   | Browser-first remote-control operator UI (React/Vite)           |
| `remote-gateway` | Remote-control gateway service (device/session/approval broker) |
| `remote-host`    | Remote-control host agent (engine + desktop + browser adapters) |

### 1.3 Implementation Status

All 34 libraries and 11 of 12 apps contain real TypeScript (or C++) source
files. `libs/bellona/sdk-cpp` and `apps/bellona/render-api` ship without a
`package.json` but contain implementation source. The `@bellona/blender-agent`
and `@bellona/unity-agent` packages are **implemented**, not planned — every
module in their runtime catalogs carries `status: 'implemented'`.

The remote-control subsystem (`remote-protocol`, `mcp-gateway`, `control-room`,
`remote-gateway`, `remote-host`) is a self-described "walking skeleton" /
scaffold layer: source and tests exist, and the apps' own package.json
descriptions call them walking-skeleton services.

---

## 2. Database Schema

Bellona has a single PostgreSQL database that persists all build jobs,
artifacts, bridge sessions, worker registrations, and audit records. The schema
is managed by Prisma and lives entirely in the `bellona` datasource.

**Database:** PostgreSQL **ORM:** Prisma (`prisma-client-js` generator) **Schema
file:** `libs/bellona/database/prisma/schema.prisma` (673 lines) **Generated
client output:** `libs/bellona/database/src/generated/client` **Datasource env
var:** `BELLONA_DATABASE_URL` **Preview features:** `fullTextSearch`,
`fullTextIndex` **Migration:**
`libs/bellona/database/prisma/migrations/20260416000000_phase_8_bellona_initial/`

The schema declares **11 models** and **14 enums**.

### 2.1 Models

#### Build (`builds`)

The central record for a build job. It tracks every aspect of the job's
lifecycle: current status, which platforms to target, which assets to include,
which worker is processing it, error details if it fails, and timing for
performance analysis.

| Field                   | Type                | Default   | Notes                           |
| ----------------------- | ------------------- | --------- | ------------------------------- |
| `id`                    | `String` (cuid)     | auto      | Primary key, `VarChar(25)`      |
| `projectId`             | `String`            | —         | Owning project, `VarChar(50)`   |
| `status`                | `BuildStatus`       | `PENDING` | Lifecycle status                |
| `priority`              | `JobPriority`       | `NORMAL`  | Queue priority                  |
| `progress`              | `Int`               | `0`       | 0–100 percentage                |
| `targetPlatforms`       | `Platform[]`        | `[]`      | Target build platforms          |
| `optimizationLevel`     | `OptimizationLevel` | `BASIC`   | NONE / BASIC / FULL             |
| `includeDebugSymbols`   | `Boolean`           | `false`   | Include debug symbol files      |
| `compressionLevel`      | `CompressionLevel`  | `MEDIUM`  | LOW / MEDIUM / HIGH             |
| `incremental`           | `Boolean`           | `false`   | Incremental build flag          |
| `assetIds`              | `String[]`          | `[]`      | Asset IDs to include            |
| `errorMessage`          | `String?`           | —         | Failure message (`Text`)        |
| `errorCode`             | `String?`           | —         | Failure code (`VarChar(50)`)    |
| `retryCount`            | `Int`               | `0`       | Retry attempts made             |
| `maxRetries`            | `Int`               | `3`       | Max retry attempts              |
| `workerId`              | `String?`           | —         | Assigned worker ID              |
| `queuePosition`         | `Int?`              | —         | Position in queue               |
| `userId`                | `String`            | —         | Owner user ID                   |
| `organizationId`        | `String?`           | —         | Owner organization              |
| `metadata`              | `Json`              | `{}`      | Arbitrary metadata              |
| `createdAt`             | `DateTime`          | `now()`   | Creation timestamp              |
| `updatedAt`             | `DateTime`          | auto      | Update timestamp (`@updatedAt`) |
| `startedAt`             | `DateTime?`         | —         | Processing start                |
| `completedAt`           | `DateTime?`         | —         | Processing end                  |
| `estimatedCompletionAt` | `DateTime?`         | —         | ETA                             |
| `deletedAt`             | `DateTime?`         | —         | Soft-delete timestamp           |
| `duration`              | `Int?`              | —         | Duration in milliseconds        |

**Relations:** `artifacts` (`BuildArtifact[]`), `logs` (`BuildLog[]`), `exports`
(`ExportJob[]`). **Indexes:** `status`, `priority`, `projectId`, `userId`,
`organizationId`, `workerId`, `createdAt`, `[status, priority]`,
`[projectId, status]`, `deletedAt`.

---

#### BuildArtifact (`build_artifacts`)

Output files produced by a build, stored in S3-compatible object storage. The
`storageBucket` and `storageKey` together form the canonical address for
retrieving the artifact. The `checksum` enables content-addressable cache
lookups.

| Field           | Type        | Default | Notes                              |
| --------------- | ----------- | ------- | ---------------------------------- |
| `id`            | `String`    | auto    | Primary key                        |
| `buildId`       | `String`    | —       | FK → Build (`onDelete: Cascade`)   |
| `name`          | `String`    | —       | Artifact filename (`VarChar(255)`) |
| `platform`      | `Platform`  | —       | Target platform                    |
| `path`          | `String`    | —       | File path (`VarChar(500)`)         |
| `size`          | `BigInt`    | —       | File size in bytes                 |
| `checksum`      | `String`    | —       | File checksum (`VarChar(128)`)     |
| `storageBucket` | `String`    | —       | Object storage bucket              |
| `storageKey`    | `String`    | —       | Object storage key                 |
| `mimeType`      | `String?`   | —       | MIME type                          |
| `metadata`      | `Json`      | `{}`    | Arbitrary metadata                 |
| `createdAt`     | `DateTime`  | `now()` | Creation timestamp                 |
| `expiresAt`     | `DateTime?` | —       | Artifact expiration                |
| `deletedAt`     | `DateTime?` | —       | Soft-delete timestamp              |

**Unique:** `[storageBucket, storageKey]`. **Indexes:** `buildId`, `platform`,
`createdAt`, `expiresAt`, `deletedAt`. No `updatedAt` field.

---

#### BuildLog (`build_logs`)

Structured log entries emitted during build execution. Each entry captures the
emitting component (`source`), the pipeline step (`step`), and a severity level
alongside the message text, enabling structured querying for debugging.

| Field       | Type       | Default | Notes                            |
| ----------- | ---------- | ------- | -------------------------------- |
| `id`        | `String`   | auto    | Primary key                      |
| `buildId`   | `String`   | —       | FK → Build (`onDelete: Cascade`) |
| `level`     | `LogLevel` | —       | DEBUG / INFO / WARNING / ERROR   |
| `message`   | `String`   | —       | Log message text (`Text`)        |
| `details`   | `Json?`    | —       | Structured detail object         |
| `source`    | `String?`  | —       | Emitting component name          |
| `step`      | `String?`  | —       | Pipeline step name               |
| `timestamp` | `DateTime` | `now()` | Log timestamp                    |

**Indexes:** `buildId`, `level`, `timestamp`. No `updatedAt` field.

---

#### ExportJob (`export_jobs`)

An export packaging job that wraps build artifacts into a downloadable
engine-native format. The `format` enum selects the packaging strategy; the
`downloadUrl` is a presigned URL provided once the export is `COMPLETED`.

| Field            | Type               | Default   | Notes                                            |
| ---------------- | ------------------ | --------- | ------------------------------------------------ |
| `id`             | `String`           | auto      | Primary key                                      |
| `projectId`      | `String`           | —         | Project reference                                |
| `buildId`        | `String?`          | —         | Optional FK → Build                              |
| `status`         | `ExportStatus`     | `PENDING` | Export lifecycle status                          |
| `progress`       | `Int`              | `0`       | 0–100 percentage                                 |
| `format`         | `ExportFormat`     | —         | ZIP / TAR_GZ / UNITYPACKAGE / UASSET / GODOT_PCK |
| `targetEngine`   | `EngineType?`      | —         | Target game engine                               |
| `includeSource`  | `Boolean`          | `false`   | Include source files                             |
| `compression`    | `CompressionLevel` | `MEDIUM`  | Compression intensity                            |
| `assetIds`       | `String[]`         | `[]`      | Asset selection                                  |
| `downloadUrl`    | `String?`          | —         | Presigned download URL                           |
| `size`           | `BigInt?`          | —         | Output size in bytes                             |
| `checksum`       | `String?`          | —         | Output checksum                                  |
| `storageBucket`  | `String?`          | —         | Object storage bucket                            |
| `storageKey`     | `String?`          | —         | Object storage key                               |
| `errorMessage`   | `String?`          | —         | Failure error message                            |
| `errorCode`      | `String?`          | —         | Failure error code                               |
| `userId`         | `String`           | —         | Owner user ID                                    |
| `organizationId` | `String?`          | —         | Owner organization                               |
| `metadata`       | `Json`             | `{}`      | Arbitrary metadata                               |
| `createdAt`      | `DateTime`         | `now()`   | Creation timestamp                               |
| `updatedAt`      | `DateTime`         | auto      | Update timestamp                                 |
| `completedAt`    | `DateTime?`        | —         | Completion timestamp                             |
| `expiresAt`      | `DateTime?`        | —         | Download link expiration                         |
| `deletedAt`      | `DateTime?`        | —         | Soft-delete timestamp                            |
| `duration`       | `Int?`             | —         | Duration in milliseconds                         |

**Relations:** `build` (`Build?`). **Indexes:** `status`, `projectId`,
`buildId`, `userId`, `organizationId`, `format`, `createdAt`, `expiresAt`,
`deletedAt`.

---

#### EngineProject (`engine_projects`)

A record of a generated engine project scaffold — the set of files and
configurations generated for a specific engine and platform combination. The
`settings` JSON field holds engine-specific configuration that does not fit into
the normalized columns.

| Field            | Type          | Default   | Notes                      |
| ---------------- | ------------- | --------- | -------------------------- |
| `id`             | `String`      | auto      | Primary key                |
| `projectId`      | `String`      | —         | Source project reference   |
| `engine`         | `EngineType`  | —         | Target engine              |
| `engineVersion`  | `String`      | —         | Engine version string      |
| `modules`        | `String[]`    | `[]`      | Engine modules to include  |
| `plugins`        | `String[]`    | `[]`      | Engine plugins to include  |
| `platforms`      | `Platform[]`  | `[]`      | Target platforms           |
| `outputPath`     | `String`      | —         | Output directory path      |
| `templateId`     | `String?`     | —         | Build template reference   |
| `includeAssets`  | `Boolean`     | `true`    | Include project assets     |
| `assetIds`       | `String[]`    | `[]`      | Specific asset selection   |
| `settings`       | `Json`        | `{}`      | Engine-specific settings   |
| `status`         | `BuildStatus` | `PENDING` | Generation status          |
| `errorMessage`   | `String?`     | —         | Failure error message      |
| `fileCount`      | `Int?`        | —         | Number of generated files  |
| `totalSize`      | `BigInt?`     | —         | Total output size in bytes |
| `dependencies`   | `String[]`    | `[]`      | External dependencies      |
| `warnings`       | `String[]`    | `[]`      | Generation warnings        |
| `userId`         | `String`      | —         | Owner user ID              |
| `organizationId` | `String?`     | —         | Owner organization         |
| `metadata`       | `Json`        | `{}`      | Arbitrary metadata         |
| `createdAt`      | `DateTime`    | `now()`   | Creation timestamp         |
| `updatedAt`      | `DateTime`    | auto      | Update timestamp           |
| `completedAt`    | `DateTime?`   | —         | Completion timestamp       |
| `deletedAt`      | `DateTime?`   | —         | Soft-delete timestamp      |
| `duration`       | `Int?`        | —         | Duration in milliseconds   |

**Indexes:** `projectId`, `engine`, `status`, `userId`, `organizationId`,
`createdAt`, `deletedAt`.

---

#### BridgeSession (`bridge_sessions`)

A session record for a live connection between the Bellona platform and a game
engine editor. Tracks connection state (via `status`), sync progress (via
`syncedAssetCount`), and the heartbeat timestamp used to detect dead sessions.

| Field              | Type               | Default        | Notes                                      |
| ------------------ | ------------------ | -------------- | ------------------------------------------ |
| `id`               | `String`           | auto           | Primary key                                |
| `projectId`        | `String`           | —              | Project reference                          |
| `engine`           | `EngineType`       | —              | Connected engine                           |
| `engineVersion`    | `String?`          | —              | Engine version                             |
| `status`           | `ConnectionStatus` | `DISCONNECTED` | CONNECTED / DISCONNECTED / SYNCING / ERROR |
| `hostname`         | `String?`          | —              | Engine hostname                            |
| `port`             | `Int?`             | —              | Engine port                                |
| `pid`              | `Int?`             | —              | Engine process ID                          |
| `sessionToken`     | `String?`          | —              | Session auth token                         |
| `lastSyncAt`       | `DateTime?`        | —              | Last sync timestamp                        |
| `syncDirection`    | `SyncDirection?`   | —              | PUSH / PULL / BIDIRECTIONAL                |
| `syncedAssetCount` | `Int`              | `0`            | Number of synced assets                    |
| `userId`           | `String`           | —              | Owner user ID                              |
| `organizationId`   | `String?`          | —              | Owner organization                         |
| `metadata`         | `Json`             | `{}`           | Arbitrary metadata                         |
| `createdAt`        | `DateTime`         | `now()`        | Creation timestamp                         |
| `updatedAt`        | `DateTime`         | auto           | Update timestamp                           |
| `connectedAt`      | `DateTime?`        | —              | Connection established                     |
| `disconnectedAt`   | `DateTime?`        | —              | Connection dropped                         |
| `deletedAt`        | `DateTime?`        | —              | Soft-delete timestamp                      |
| `lastHeartbeat`    | `DateTime?`        | —              | Last heartbeat received                    |

**Relations:** `conflicts` (`SyncConflict[]`). **Indexes:** `projectId`,
`engine`, `status`, `userId`, `organizationId`, `createdAt`, `lastHeartbeat`,
`deletedAt`.

---

#### SyncConflict (`sync_conflicts`)

An asset version conflict detected during bidirectional sync, where the same
asset was modified independently on both the Oshun side (`localVersion`) and the
engine side (`remoteVersion`). The `resolution` field records how the conflict
was resolved once a decision is made.

| Field            | Type        | Default | Notes                                         |
| ---------------- | ----------- | ------- | --------------------------------------------- |
| `id`             | `String`    | auto    | Primary key                                   |
| `sessionId`      | `String`    | —       | FK → BridgeSession (`onDelete: Cascade`)      |
| `assetId`        | `String`    | —       | Conflicting asset ID                          |
| `assetPath`      | `String?`   | —       | Asset file path                               |
| `localVersion`   | `String`    | —       | Local version identifier                      |
| `remoteVersion`  | `String`    | —       | Remote (engine) version identifier            |
| `localChecksum`  | `String?`   | —       | Local file checksum                           |
| `remoteChecksum` | `String?`   | —       | Remote file checksum                          |
| `resolution`     | `String?`   | —       | `local` / `remote` / `manual` (`VarChar(20)`) |
| `resolvedAt`     | `DateTime?` | —       | Resolution timestamp                          |
| `resolvedBy`     | `String?`   | —       | User who resolved                             |
| `createdAt`      | `DateTime`  | `now()` | Creation timestamp                            |
| `updatedAt`      | `DateTime`  | auto    | Update timestamp                              |

**Indexes:** `sessionId`, `assetId`, `createdAt`.

---

#### BuildWorker (`build_workers`)

Registration record for a build worker node. Each worker declares its
capabilities at registration time (`jobTypes`, `platforms`, `engines`) so the
scheduler can route jobs only to workers capable of processing them. The
`lastHeartbeat` timestamp is used to detect dead workers.

| Field                | Type           | Default   | Notes                                               |
| -------------------- | -------------- | --------- | --------------------------------------------------- |
| `id`                 | `String`       | auto      | Primary key                                         |
| `name`               | `String`       | —         | Worker display name                                 |
| `hostname`           | `String`       | —         | Hostname (unique)                                   |
| `jobTypes`           | `JobType[]`    | `[]`      | Supported job types                                 |
| `platforms`          | `Platform[]`   | `[]`      | Supported platforms                                 |
| `engines`            | `EngineType[]` | `[]`      | Supported engines                                   |
| `concurrency`        | `Int`          | `1`       | Max concurrent jobs                                 |
| `status`             | `WorkerStatus` | `OFFLINE` | IDLE / BUSY / PAUSED / STOPPING / STOPPED / OFFLINE |
| `currentJobId`       | `String?`      | —         | Currently processing job ID                         |
| `lastHeartbeat`      | `DateTime?`    | —         | Last heartbeat timestamp                            |
| `totalJobsProcessed` | `Int`          | `0`       | Lifetime job count                                  |
| `successfulJobs`     | `Int`          | `0`       | Successful job count                                |
| `failedJobs`         | `Int`          | `0`       | Failed job count                                    |
| `averageJobDuration` | `Float?`       | —         | Average duration in ms                              |
| `version`            | `String?`      | —         | Worker software version                             |
| `metadata`           | `Json`         | `{}`      | Arbitrary metadata                                  |
| `createdAt`          | `DateTime`     | `now()`   | Creation timestamp                                  |
| `updatedAt`          | `DateTime`     | auto      | Update timestamp                                    |

**Unique:** `hostname`. **Indexes:** `status`, `lastHeartbeat`.

---

#### AssetCache (`asset_cache`)

Cached metadata for assets imported from other domains (Isis, Hathor), scoped
per project. Storing this metadata locally avoids round-trips to the upstream
domain's asset store for every build. The `engineData` JSON column holds
engine-specific derived data, such as pre-computed import settings.

| Field            | Type        | Default | Notes                       |
| ---------------- | ----------- | ------- | --------------------------- |
| `id`             | `String`    | auto    | Primary key                 |
| `projectId`      | `String`    | —       | Project reference           |
| `assetId`        | `String`    | —       | Asset reference             |
| `name`           | `String`    | —       | Asset name                  |
| `type`           | `AssetType` | —       | Asset classification        |
| `path`           | `String`    | —       | Asset file path             |
| `size`           | `BigInt`    | —       | File size in bytes          |
| `checksum`       | `String`    | —       | File checksum               |
| `tags`           | `String[]`  | `[]`    | Searchable tags             |
| `metadata`       | `Json`      | `{}`    | General metadata            |
| `engineData`     | `Json`      | `{}`    | Engine-specific cached data |
| `createdAt`      | `DateTime`  | `now()` | Creation timestamp          |
| `updatedAt`      | `DateTime`  | auto    | Update timestamp            |
| `lastAccessedAt` | `DateTime?` | —       | Last access timestamp       |

**Unique:** `[projectId, assetId]`. **Indexes:** `projectId`, `type`,
`updatedAt`.

---

#### BuildTemplate (`build_templates`)

A reusable build configuration that captures a complete set of build settings so
teams do not need to re-specify platform combinations, optimization levels, and
engine settings for every build. Templates can be marked as the organization
default with `isDefault`.

| Field                 | Type                | Default  | Notes                 |
| --------------------- | ------------------- | -------- | --------------------- |
| `id`                  | `String`            | auto     | Primary key           |
| `name`                | `String`            | —        | Template name         |
| `description`         | `String?`           | —        | Template description  |
| `platforms`           | `Platform[]`        | `[]`     | Target platforms      |
| `engines`             | `EngineType[]`      | `[]`     | Target engines        |
| `optimizationLevel`   | `OptimizationLevel` | `BASIC`  | NONE / BASIC / FULL   |
| `includeDebugSymbols` | `Boolean`           | `false`  | Include debug symbols |
| `compressionLevel`    | `CompressionLevel`  | `MEDIUM` | LOW / MEDIUM / HIGH   |
| `settings`            | `Json`              | `{}`     | Additional settings   |
| `isActive`            | `Boolean`           | `true`   | Template is available |
| `isDefault`           | `Boolean`           | `false`  | Is the org default    |
| `organizationId`      | `String?`           | —        | Owning organization   |
| `createdBy`           | `String`            | —        | Creator user ID       |
| `createdAt`           | `DateTime`          | `now()`  | Creation timestamp    |
| `updatedAt`           | `DateTime`          | auto     | Update timestamp      |

**Indexes:** `isActive`, `organizationId`.

---

#### AuditLog (`audit_log`)

An immutable audit trail for all entity changes within the Bellona database.
Every mutation to a tracked entity produces an audit record that stores the
actor, the before state, the after state, and the diff. The `requestId`
correlates an audit entry back to the originating HTTP request.

| Field           | Type       | Default | Notes                            |
| --------------- | ---------- | ------- | -------------------------------- |
| `id`            | `String`   | auto    | Primary key                      |
| `action`        | `String`   | —       | Action performed (`VarChar(50)`) |
| `entityType`    | `String`   | —       | Entity type changed              |
| `entityId`      | `String`   | —       | Entity ID changed                |
| `previousState` | `Json?`    | —       | State before change              |
| `newState`      | `Json?`    | —       | State after change               |
| `changes`       | `Json?`    | —       | Diff of changes                  |
| `userId`        | `String?`  | —       | Actor user ID                    |
| `ipAddress`     | `String?`  | —       | Actor IP address (`VarChar(45)`) |
| `userAgent`     | `String?`  | —       | Actor user agent string (`Text`) |
| `requestId`     | `String?`  | —       | Correlation request ID           |
| `metadata`      | `Json`     | `{}`    | Arbitrary metadata               |
| `createdAt`     | `DateTime` | `now()` | Creation timestamp               |

**Indexes:** `[entityType, entityId]`, `userId`, `action`, `createdAt`. No
`updatedAt` field.

---

### 2.2 Enumerations

The 14 enums below define all categorical values used throughout the schema.
Every enum value is referenced by at least one model column above.

| Enum                | Values                                                                                      |
| ------------------- | ------------------------------------------------------------------------------------------- |
| `BuildStatus`       | `PENDING`, `QUEUED`, `RUNNING`, `COMPLETED`, `FAILED`, `CANCELLED`                          |
| `JobPriority`       | `LOW`, `NORMAL`, `HIGH`, `CRITICAL`                                                         |
| `Platform`          | `WINDOWS`, `MACOS`, `LINUX`, `IOS`, `ANDROID`, `WEBGL`, `PLAYSTATION`, `XBOX`, `SWITCH`     |
| `EngineType`        | `UNITY`, `UNREAL`, `GODOT`, `BLENDER`, `WEB`                                                |
| `ExportFormat`      | `ZIP`, `TAR_GZ`, `UNITYPACKAGE`, `UASSET`, `GODOT_PCK`                                      |
| `ExportStatus`      | `PENDING`, `PROCESSING`, `COMPLETED`, `FAILED`, `EXPIRED`                                   |
| `OptimizationLevel` | `NONE`, `BASIC`, `FULL`                                                                     |
| `CompressionLevel`  | `LOW`, `MEDIUM`, `HIGH`                                                                     |
| `LogLevel`          | `DEBUG`, `INFO`, `WARNING`, `ERROR`                                                         |
| `ConnectionStatus`  | `CONNECTED`, `DISCONNECTED`, `SYNCING`, `ERROR`                                             |
| `SyncDirection`     | `PUSH`, `PULL`, `BIDIRECTIONAL`                                                             |
| `AssetType`         | `TEXTURE`, `MODEL`, `AUDIO`, `ANIMATION`, `MATERIAL`, `PREFAB`, `SCRIPT`, `CONFIG`, `OTHER` |
| `JobType`           | `ASSET_BAKE`, `VALIDATE`, `ENGINE_PROJECT_GENERATE`, `EXPORT_PACKAGE`                       |
| `WorkerStatus`      | `IDLE`, `BUSY`, `PAUSED`, `STOPPING`, `STOPPED`, `OFFLINE`                                  |

### 2.3 Schema Conventions

The following conventions apply uniformly across all 11 models, ensuring
consistency that simplifies queries and ORM integration.

- All primary keys use CUID (`@default(cuid())`, `@db.VarChar(25)`).
- Every model has `createdAt`; eight models have `updatedAt` (`@updatedAt`).
  `BuildArtifact`, `BuildLog`, and `AuditLog` do **not** have `updatedAt`.
- `Build`, `BuildArtifact`, `ExportJob`, `EngineProject`, `BridgeSession` carry
  a `deletedAt` soft-delete timestamp.
- `size` / `totalSize` use `BigInt` to support files larger than 2 GB.
- `metadata`, `engineData`, `settings`, `previousState`, `newState`, `changes`,
  and `details` are PostgreSQL `jsonb`.
- Array fields (`assetIds`, `modules`, `plugins`, `platforms`, `engines`,
  `tags`, `warnings`, `dependencies`, `jobTypes`) are PostgreSQL arrays.
- `BuildWorker.hostname` is unique; `AssetCache` is unique on
  `[projectId, assetId]`; `BuildArtifact` is unique on
  `[storageBucket, storageKey]`.

---

## 3. Build / Export Job Lifecycle

### 3.1 Build Status State Machine

The `BuildStatus` enum drives the complete build lifecycle from submission to
final outcome. A build enters `PENDING` when enqueued, moves to `QUEUED` when a
worker picks it up, and progresses to `RUNNING` while the worker executes. It
then either reaches `COMPLETED`, `FAILED` (retryable up to `maxRetries`), or
`CANCELLED` by user request.

```
PENDING ──> QUEUED ──> RUNNING ──> COMPLETED
                          │
                          ├──────> FAILED      (retryable up to maxRetries)
                          └──────> CANCELLED   (user request)
```

A build's `retryCount` increments on transient failure until it reaches
`maxRetries` (default 3). The `build-worker` job queue (`JobStatus`: `pending`,
`queued`, `running`, `completed`, `failed`, `cancelled`) mirrors this lifecycle
at the worker level.

### 3.2 Export Status State Machine

Export jobs follow a simpler lifecycle. Once `COMPLETED`, a presigned download
URL is generated that expires at `expiresAt`, at which point the job status
transitions to `EXPIRED`.

```
PENDING ──> PROCESSING ──> COMPLETED ──> EXPIRED   (after expiresAt)
                              │
                              └────────> FAILED
```

### 3.3 Worker Status State Machine

A build worker's status reflects its current operational mode. Normal operation
cycles through `OFFLINE → IDLE → BUSY`. The `PAUSED`, `STOPPING`, and `STOPPED`
states support controlled drains during deployments. Dead workers are identified
when their `lastHeartbeat` becomes stale.

`BuildWorker.status` transitions across `OFFLINE → IDLE → BUSY` during normal
operation, and `PAUSED`, `STOPPING`, `STOPPED` during controlled drains.

### 3.4 Job Priority

The `build-worker` priority queue
(`apps/bellona/build-worker/src/queues/job-queue.ts`) orders pending jobs by
numeric priority: `critical=4`, `high=3`, `normal=2`, `low=1`. Higher-priority
jobs are always dequeued before lower-priority ones regardless of submission
order.

---

## 4. Build API (`apps/bellona/build-api`)

The Build API is a **Node.js module**, not an HTTP server. It exposes a
programmatic lifecycle API and subscribes to cross-domain events. It does not
bind a TCP port itself; the `4005` port number is the documented domain
assignment (`libs/bellona/README.md`) used by sibling services.

### 4.1 Programmatic API

`@bellona/build-api` exports the following entry points from `index.ts`. The
`initialize()` function is the only required setup step — it wires the cache,
queues, and event subscriptions. After that, the module stays alive processing
events passively.

```typescript
import {
  initialize,
  shutdown,
  getCacheService,
  getBuildCacheService,
  isHealthy,
  getHealthStatus,
} from '@bellona/build-api';

await initialize();

const health = await getHealthStatus();
// {
//   status: 'healthy' | 'degraded' | 'unhealthy',
//   cache: boolean,
//   uptime: number,
//   cacheStats?: { hitRate: number, totalEntries: number }
// }

const cache = getCacheService(); // BuildCacheService

await shutdown();
```

`initialize()` performs three steps in order:

1. Constructs the `BuildCacheService` (Redis-backed content-addressable cache)
2. Initializes a dedicated Redis connection for job queues
3. Wires cross-domain event handlers via `@bellona/event-handlers`

On startup the process logs "Service ready and listening for events" and stays
alive to process events; it does not accept HTTP requests.

### 4.2 Cache Service exports

`@bellona/build-api` also exports `BuildCacheService`,
`createBuildCacheService`, `setBuildCacheService`, `clearBuildCacheService`, and
the `BuildCacheServiceConfig` type. The cache service supports
content-addressable storage, compression, deduplication, in-memory + Redis
tiers, and Redis PubSub invalidation.

### 4.3 Type surface

The module re-exports a large typed surface from `types.ts`. The types are
organized into the following groups:

- **Branded IDs** — `BuildJobId`, `ArtifactId`, `ProjectId`, `AssetId`,
  `ContentHash` (and their constructors `createBuildJobId`, `createArtifactId`,
  `createProjectId`, `createAssetId`, `createContentHash`)
- **Job types** — `BuildJob`, `BuildJobType`, `BuildJobStatus`,
  `BuildJobPriority`, `BuildError`, `BuildProgress`
- **Artifact types** — `Artifact`, `ArtifactType`, `ArtifactMetadata`,
  `TargetPlatform`, `TargetEngine`
- **Cache types** — `CachedArtifact`, `CachedBuildJob`, `CacheEntry`,
  `CacheEntryMetadata`, `CacheLookupResult`, `CacheStats`, `CacheConfig`
- **Build input types** — `BuildInput`, `TextureBuildInput`, `MeshBuildInput`,
  `ShaderBuildInput`, `AudioBuildInput`, `AnimationBuildInput`
- **Dependency types** — `DependencyNode`, `DependencyGraph`
- **Event types** — `BuildEvent`, `BuildEventType`
- **Config types** — `BuildApiConfig`, `BuildApiHealth`

### 4.4 Job-queue adapters

`main.ts` builds a `BuildQueueInterface` and `ExportQueueInterface`. When
`REDIS_URL` is set, jobs are persisted in Redis (`bellona:queue:*` keys: a job
hash, a priority-scored `:pending` sorted set, and a per-project index set).
When `REDIS_URL` is unset, an in-memory `Map` is used as a fallback. The build
queue supports `enqueue`, `getStatus`, and `cancel`; the export queue supports
`enqueue` and `getStatus`.

---

## 5. Build Worker (`apps/bellona/build-worker`)

A poll-based background processor. The `JobQueue` class is a priority queue with
poll-based dequeue (`pollInterval` default 1000 ms, `concurrency` default 1).

### 5.1 Worker types

`JobType` is the union
`asset-bake | validate | engine-project-generate | export-package`. Each job
type has a dedicated worker module under
`apps/bellona/build-worker/src/workers/`:

| Job type                  | Worker module              | Responsibility                             |
| ------------------------- | -------------------------- | ------------------------------------------ |
| `asset-bake`              | `asset-bake-worker.ts`     | Texture / model / audio / animation baking |
| `validate`                | `validation-worker.ts`     | Build-input and output validation          |
| `engine-project-generate` | `engine-project-worker.ts` | Engine project scaffold generation         |
| `export-package`          | `export-package-worker.ts` | Package and export build artifacts         |

### 5.2 Worker type surface

`build-worker/types.ts` declares branded IDs (`JobId`, `WorkerId`, `ProjectId`,
`AssetId`), `JobType`, `JobStatus`, `JobPriority`, and per-job-type request and
result types:

- **asset-bake** — `AssetBakeRequest` / `AssetBakeResult` / `AssetBakeJob` (with
  `TextureBakeOptions`, `ModelBakeOptions`, `AudioBakeOptions`,
  `AnimationBakeOptions`)
- **validate** — `ValidationRequest` / `ValidationResult` / `ValidateJob` (with
  `ValidationSeverity`, `ValidationType`, `ValidationIssue`)
- **Format unions** — `AssetFormat`, `TextureFormat`, `ModelFormat`,
  `CompressionQuality`

---

## 6. Render API (`apps/bellona/render-api`)

An internal Node.js module (no HTTP port, no `package.json`) providing
render-job caching, GPU queueing, and output validation.

```typescript
import {
  initialize,
  shutdown,
  getCacheService,
  getRenderCacheService,
  isHealthy,
  getHealthStatus,
} from '@bellona/render-api';
```

### 6.1 Components

The render-api is composed of three service components, each with factory and
singleton accessor functions:

- **`RenderCacheService`** — Redis-backed render cache
  (`createRenderCacheService`, `setRenderCacheService`,
  `clearRenderCacheService`, config type `RenderCacheServiceConfig`).
- **`OutputValidator`** — render output quality validation
  (`createOutputValidator`, `getOutputValidator`, `setOutputValidator`), with
  `ValidationSeverity`, `ValidationCategory`, `ValidationIssue`,
  `OutputValidationResult`, `JobValidationResult`, `ValidationOptions`.
- **`GpuRenderQueue`** — GPU device allocation and queued job management
  (`createGpuRenderQueue`, `getGpuRenderQueue`, `setGpuRenderQueue`,
  `clearGpuRenderQueue`), with `GpuDevice`, `GpuAllocationRequest`,
  `GpuAllocation`, `QueuedJob`, `GpuQueueStats`, `GpuQueueEvents`,
  `GpuQueueConfig`.

### 6.2 Type surface

`render-api/types.ts` declares the following types for render job management:

- **Job and output** — `RenderJobId`, `RenderOutputId`, `RenderJob`,
  `RenderOutput`, `RenderConfig`, `RenderSettings`, `RenderProgress`,
  `RenderError`
- **Classification** — `RenderEngine`, `OutputFormat`, `QualityPreset`,
  `RenderStatus`
- **Animation** — `AnimationSettings`, `FrameRange`, `Resolution`
- **Cache** — `CachedRenderJob`, `CachedRenderOutput`, `RenderCacheMetadata`,
  `RenderCacheStats`
- **Queue** — `RenderQueueItem`, `RenderQueueStatus`
- **Events** — `RenderEvent`, `RenderEventType`
- **Config** — `RenderApiConfig`
- **ID constructors** — `createRenderJobId`, `createRenderOutputId`

---

## 7. WebSocket Bridge Protocol

The four engine bridge apps (`bridge-blender`, `bridge-godot`, `bridge-unreal`,
`bridge-unity`) all build on `@bellona/bridge-core` (`createBridgeServer`). The
protocol follows the same pattern for every engine: a `handshake` and `ping`
command are registered first (identical across all bridges), followed by
engine-specific commands.

### 7.1 Connection endpoints

| Engine  | Default endpoint              | Port env var          | Host env var          |
| ------- | ----------------------------- | --------------------- | --------------------- |
| Blender | `ws://localhost:9001/blender` | `BLENDER_BRIDGE_PORT` | `BLENDER_BRIDGE_HOST` |
| Godot   | `ws://localhost:9002/godot`   | `GODOT_BRIDGE_PORT`   | `GODOT_BRIDGE_HOST`   |
| Unreal  | `ws://localhost:9003/unreal`  | `UNREAL_BRIDGE_PORT`  | `UNREAL_BRIDGE_HOST`  |
| Unity   | `ws://localhost:9004/unity`   | `UNITY_BRIDGE_PORT`   | `UNITY_BRIDGE_HOST`   |

Ports default to `9001`–`9004` and host to `localhost` in each app's `main.ts`.

### 7.2 Common commands

Every bridge registers these two commands regardless of engine. `handshake`
establishes mutual version identification; `ping` verifies liveness.

| Command     | Request data                    | Response data                                    |
| ----------- | ------------------------------- | ------------------------------------------------ |
| `handshake` | `{ clientName, clientVersion }` | `{ serverName, serverVersion, protocolVersion }` |
| `ping`      | `{}`                            | `{ timestamp }`                                  |

The `bridge-core` server emits `connection`, `disconnection`, `command`, and
`error` events.

### 7.3 Engine command sets

The engine-specific commands registered by each bridge app are listed below.
Implementation of each command lives in the corresponding engine adapter
library.

| Engine  | Command count | Commands                                                                                                                                                                                         |
| ------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Blender | 6             | `getSceneInfo`, `createObject`, `importAsset`, `exportAsset`, `setFrame`, `render`                                                                                                               |
| Godot   | 9             | scene-tree access, node create/property/method, scene load, resource load, signal emit, GDScript execution, project settings                                                                     |
| Unreal  | 11            | world info, level load/unload, actor spawn/destroy/property, Blueprint function call, material parameter, Sequencer control, console command, screenshot                                         |
| Unity   | 14            | scene info / load / unload, GameObject create/destroy, prefab instantiate, transform set, component add / property set, animation play, animator parameter, physics force, raycast, message send |

The Unity, Unreal, Godot, and Blender command implementations live in
`@bellona/unity`, `@bellona/unreal`, `@bellona/godot`, and `@bellona/blender`
respectively.

### 7.4 Blender bridge session tracking

`bridge-blender` is the only bridge that publishes cross-domain events. On
connection and disconnection it calls
`BellonaEventPublisher.publishSessionStarted` and `publishSessionEnded` (see
§9). The other bridges track sessions in the database but do not emit
cross-domain events.

---

## 8. Remote-Control Subsystem

A browser-first remote-control layer for operating engine hosts, desktop
applications, and headless browsers from a web operator console. The apps'
package.json descriptions identify the gateway and host as "walking skeleton"
services.

### 8.1 `@bellona/remote-protocol`

Canonical protocol contracts for the entire remote-control subsystem. This
library defines the type system and namespace structure that all three remote
apps share.

Exports: `BELLONA_REMOTE_PROTOCOL_PACKAGE_NAME`,
`BELLONA_REMOTE_PROTOCOL_VERSION` (`0.1.0`),
`BELLONA_REMOTE_PROTOCOL_SCHEMA_STATUS` (`version-negotiation`), and
`createBellonaRemoteProtocolDescriptor()`. The library is partitioned into
contract modules: `actor`, `audit`, `blender`, `browser`, `command`, `common`,
`device`, `desktop`, `dry-run`, `logging`, `namespaces`, `policy`, `result`,
`schema-registry`, `session`, `state`, `stream`, `unreal`, `version`, plus
`browser-compensating-actions` and `desktop-compensating-actions`.

The `REMOTE_COMMAND_NAMESPACE` constant defines 13 command namespaces: `device`,
`session`, `stream`, `agent`, `state`, `blender`, `unreal`, `browser`,
`desktop`, `file`, `process`, `approval`, `diagnostic`.

### 8.2 `@bellona/mcp-gateway`

Remote-control MCP gateway server and local stdio transport. Ships a
`bellona-mcp-gateway` binary (`src/stdio-cli.js`). Modules include
`stdio-server`, `remote-control-tools`, `remote-control-resources`,
`auth-session`, `command-remediation`, `mcp-composition-rules`, and an agent
control layer with the following modules: `agent-capability-memory`,
`agent-autonomous-stop-criteria`, `agent-deep-integration-evals`,
`agent-dry-run-cost-time-estimator`, `agent-dry-run-impact-summary`,
`agent-dry-run-planners`, `agent-handoff-paths`, `agent-long-running-jobs`,
`agent-mutation-locking`, `agent-preflight-checks`,
`agent-tool-selection-policy`, `agent-visual-verification-policy`.

### 8.3 Remote-control apps

| App              | Source highlights                                                                                                                                                |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `remote-gateway` | Device registry, session lifecycle, approval service, command dispatcher, pairing codes, host tokens, audit log, redaction, telemetry                            |
| `remote-host`    | Adapter registry with Blender / Unreal / browser / desktop adapters, command executor, gateway connection, pairing client, identity store, macOS permissions     |
| `control-room`   | React/Vite operator UI: approval queue, command palette, device list, session timeline, stream preview, WebRTC loopback, mobile approval PWA, first-session tour |

The `control-room` is gated behind the `VITE_BELLONA_CONTROL_ROOM_ENABLED`
environment flag.

### 8.4 Phase 180 Roadmap Contract

The remote-control subsystem is governed by the Phase 180 roadmap: the operating
checklist is `TODOS/phase-180.md` (`180.C` canonical sequential checklist), the
expanded requirements live in `TODOS/phase-180-reference.md` (`180.R` /
`180.0`–`180.20`, including the `180.19` acceptance criteria), and the audit of
known scope gaps is `TODOS/phase-180-gaps.md`. The implementation tracker
required by `180.C.01` lives at
`docs/domains/bellona/extras/remote-control/implementation-tracker.md`.

Planned target layout not yet present in the repo:

| Package                          | Role                                                                           |
| -------------------------------- | ------------------------------------------------------------------------------ |
| `libs/bellona/remote-adapters`   | Adapter interfaces for Blender, Unreal, browser, desktop, files, processes     |
| `libs/bellona/mac-host-runtime`  | macOS TCC permissions, process launch, window focus, capture, signing/updates  |
| `libs/psyche/desktop-fallback`   | Reusable screenshot/click/type/window fallback shared with Psyche Computer Use |
| `testing/bellona/remote-control` | Integration, e2e, fixture, and network-impairment test suites                  |

Binding protocol rules from Phase 180: command schemas live only in
`libs/bellona/remote-protocol` and are imported by gateway, host, MCP, adapters,
tests, and docs (single source of truth); Blender/Unreal/browser debug and
control ports remain localhost-only on the host; deep typed adapters must be
preferred over desktop fallback; and no privileged path (arbitrary shell,
arbitrary Blender Python, Unreal console, desktop control, or real browser
profiles) may ship before policy, approval, and audit primitives are complete.

---

## 9. Events

### 9.1 Events Published

`@bellona/event-publisher` exposes `BellonaEventPublisher` with eight typed
publish methods. The event-type string constants are defined in
`@oshun/contracts` (`libs/contracts/src/events/bellona.ts`).

The table below shows each event, its publish method, and the key fields in its
contract Zod schema payload.

| Event                     | Publish method          | Payload key fields (from contract Zod schema)                                                                                                      |
| ------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `bellona.session.started` | `publishSessionStarted` | `sessionId`, `projectId`, `userId`, `engine`, `engineVersion`, `workerId`, `connectionType`, `capabilities`                                        |
| `bellona.session.ended`   | `publishSessionEnded`   | `sessionId`, `projectId`, `userId`, `engine`, `durationMs`, `commandsExecuted`, `assetsTransferred`, `bytesTransferred`, `reason`, `errorMessage?` |
| `bellona.build.started`   | `publishBuildStarted`   | `buildId`, `projectId`, `userId`, `engine`, `engineVersion`, `platform`, `configuration`, `features?`, `sourceCommit?`, `workerId`                 |
| `bellona.build.progress`  | `publishBuildProgress`  | `buildId`, `stage`, `progress`, `message?`, `warnings`, `errors`                                                                                   |
| `bellona.build.completed` | `publishBuildCompleted` | `buildId`, `projectId`, `userId`, `engine`, `platform`, `configuration`, `success`, `durationMs`, `artifacts[]`, `metrics`                         |
| `bellona.export.started`  | `publishExportStarted`  | `exportId`, `projectId`, `userId`, `targetFormat`, `assets[]`                                                                                      |
| `bellona.export.ready`    | `publishExportReady`    | `exportId`, `projectId`, `userId`, `targetFormat`, `files[]`, `totalSizeBytes`, `processingTimeMs`, `expiresAt`                                    |
| `bellona.asset.synced`    | `publishAssetSynced`    | `syncId`, `projectId`, `sessionId`, `engine`, `direction`, `assets[]`, `totalSizeBytes`, `durationMs`                                              |

The publisher also exports payload types (`BellonaSessionStartedPayload`,
`BellonaSessionEndedPayload`, `BellonaBuildStartedPayload`,
`BellonaBuildProgressPayload`, `BellonaBuildCompletedPayload`,
`BellonaExportStartedPayload`, `BellonaExportReadyPayload`,
`BellonaAssetSyncedPayload`) and the `GameEngine`, `BuildPlatform`,
`BuildStatus` types, plus `getBellonaEventPublisher`,
`createBellonaEventPublisher`, `resetBellonaEventPublisher`.

### 9.2 Events Consumed

`@bellona/event-handlers` defines `BELLONA_SUBSCRIPTIONS` — four cross-domain
subscriptions that drive Bellona's automated build reactions. Each handler
receives the event payload and enqueues the appropriate build or export job.

| Event                     | Source domain | Handler                       | Action                            |
| ------------------------- | ------------- | ----------------------------- | --------------------------------- |
| `hathor.world.published`  | Hathor        | `handleHathorWorldPublished`  | Enqueues a build for the world    |
| `isis.asset.generated`    | Isis          | `handleIsisAssetGenerated`    | Enqueues a build for asset baking |
| `yemaya.build.requested`  | Yemaya        | `handleYemayaBuildRequested`  | Enqueues a build job              |
| `yemaya.export.requested` | Yemaya        | `handleYemayaExportRequested` | Enqueues an export job            |

`setupBellonaEventHandlers` accepts an `eventBus`, a `consumerGroup`, a
`BuildQueueInterface`, an `ExportQueueInterface`, an optional logger, and an
optional `metrics` config. Handler execution metrics are collected through
`@oshun/metrics` via `withMetrics` / `HandlerStatsTracker`.

### 9.3 Event Bus Transport

The event bus is `@oshun/event-bus`, which is **Redis-backed** (it imports
`ioredis`). `build-api/main.ts` constructs the bus with
`createEventBus({ redisUrl, sourceDomain: 'bellona', keyPrefix: 'oshun:events', persistence: true, eventTtl: 86400, ... })`.

| Setting        | Value                                               |
| -------------- | --------------------------------------------------- |
| Transport      | Redis                                               |
| Source domain  | `bellona`                                           |
| Key prefix     | `oshun:events`                                      |
| Event TTL      | 86400 s (24 hours)                                  |
| Retry          | Exponential, max 3 attempts, 1 s–30 s, multiplier 2 |
| Dead-letter    | Enabled, 7-day retention, max 1000 entries          |
| Consumer group | `bellona-build-api` (for the build-api subscriber)  |

---

## 10. Asset Interchange (`@bellona/interchange`)

The interchange library exposes a format conversion pipeline (import → validate
→ transform → export) under `src/{gltf,fbx,usd,transform,validation}`.
`@bellona/interchange-models` provides the shared data schemas.

### 10.1 `AssetFormat` union

The `AssetFormat` type (`interchange/src/types.ts`) defines 13 3D-asset format
values. This is the complete list of formats the interchange pipeline can
process on the import side:

`gltf`, `glb`, `usd`, `usda`, `usdc`, `usdz`, `fbx`, `obj`, `abc` (Alembic),
`ply`, `stl`, `dae` (Collada), `blend`.

### 10.2 `ImageFormat` union

The `ImageFormat` type defines 9 image format values used for texture
interchange:

`png`, `jpg`, `jpeg`, `webp`, `exr`, `hdr`, `tga`, `bmp`, `tiff`.

### 10.3 `@bellona/asset-export`

A planning layer that sits on top of the interchange pipeline. Rather than
executing conversions, it produces export plans and manifests that describe what
conversions are needed and whether each target is ready or blocked.

Exports:

- `BELLONA_ASSET_EXPORT_FORMATS` — the supported export target formats: `usd`,
  `usda`, `usdc`, `usdz`, `gltf`, `glb`, `fbx`
- `BellonaAssetExportStatus` union — `ready` / `needs-attention` / `blocked`
- Planning functions — `evaluateBellonaAssetExportTarget`,
  `evaluateBellonaAssetExportPlan`, `buildBellonaAssetExportManifest`,
  `createBellonaAssetExportTarget`, `createBellonaAssetExportPlan`

Plan evaluation produces per-target readiness ratios, blocking/warning issue
counts, and a checksum/package manifest.

---

## 11. Cross-Domain Integration (`@bellona/integration`)

The integration library is the translation boundary between Oshun domain models
and engine-native artifacts. It exports four factory functions, each producing a
specialized consumer or compiler instance.

- **`createHathorConsumer`** — imports lore content (worlds, quests, dialogue)
  from Hathor world-publication events, with a configurable default engine and
  output directory.
- **`createLoreCompiler`** — compiles lore content for a target engine at a
  configurable optimization level.
- **`createIsisConsumer`** — imports generated assets from Isis, optionally
  auto-converting them.
- **`createAssetConverter`** — converts raw assets to engine-native formats via
  the interchange pipeline.

---

## 12. CLI (`apps/bellona/cli`)

A Commander.js CLI. The program name is `bellona`
(`apps/bellona/cli/src/index.ts`). Seven top-level commands are registered:

| Command   | Source                                  | Aliases                                         | Purpose                                    |
| --------- | --------------------------------------- | ----------------------------------------------- | ------------------------------------------ |
| `build`   | `commands/build.ts`                     | `b` (top-level), sub-aliases `start`/`ls`/`get` | Submit, list, and inspect build jobs       |
| `export`  | `commands/export.ts`                    | `e` (top-level), sub-aliases `ls`/`get`/`rm`    | Create and manage export packages          |
| `sync`    | `commands/sync.ts`                      | sub-aliases `bidirectional`/`ls`                | Push / pull / bidirectional asset sync     |
| `config`  | `commands/config.ts`                    | sub-aliases `ls`, `profile` subcommand          | Manage CLI configuration and profiles      |
| `health`  | `commands/health.ts`                    | —                                               | Check Build API and Render API health      |
| `project` | `commands/project.ts`                   | —                                               | Manage engine project configurations       |
| `detect`  | `commands/project.ts` (`detectCommand`) | —                                               | Auto-detect engine projects in a directory |

The global `--api-key` option and `--api-url` resolution use the
`BELLONA_API_KEY` / `BELLONA_API_URL` / `BELLONA_BUILD_API_URL` environment
variables.

---

## 13. TypeScript SDK (`@bellona/client`)

`@bellona/client` provides a typed client. The README shows
`createBellonaClient({ baseUrl })` with namespaced operations (e.g.
`client.exports.create`, `client.exports.onProgress`,
`client.exports.waitForCompletion`).

---

## 14. C++ Native SDK (`@bellona/sdk-cpp`)

`libs/bellona/sdk-cpp` contains C++ SDK sources (no `package.json`). It targets
native engine plugins and build-machine agents that cannot host a Node.js
runtime.

---

## 15. Configuration and Environment Variables

### 15.1 Build API

| Variable                  | Default                  | Description                                                                                 |
| ------------------------- | ------------------------ | ------------------------------------------------------------------------------------------- |
| `BELLONA_DATABASE_URL`    | —                        | PostgreSQL connection URL                                                                   |
| `REDIS_URL`               | `redis://localhost:6379` | Redis URL (cache + queues + bus). When unset, build-api falls back to in-memory job queues. |
| `BUILD_CACHE_MEMORY_SIZE` | `104857600` (100 MB)     | In-memory cache size in bytes                                                               |
| `BUILD_CACHE_MAX_SIZE`    | `10737418240` (10 GB)    | Maximum total cache size in bytes                                                           |

### 15.2 Render API

| Variable    | Default                  | Description          |
| ----------- | ------------------------ | -------------------- |
| `REDIS_URL` | `redis://localhost:6379` | Redis connection URL |

### 15.3 Bridge Services

| Variable              | Default     | Description                   |
| --------------------- | ----------- | ----------------------------- |
| `BLENDER_BRIDGE_PORT` | `9001`      | Blender bridge WebSocket port |
| `BLENDER_BRIDGE_HOST` | `localhost` | Blender bridge host           |
| `GODOT_BRIDGE_PORT`   | `9002`      | Godot bridge WebSocket port   |
| `GODOT_BRIDGE_HOST`   | `localhost` | Godot bridge host             |
| `UNREAL_BRIDGE_PORT`  | `9003`      | Unreal bridge WebSocket port  |
| `UNREAL_BRIDGE_HOST`  | `localhost` | Unreal bridge host            |
| `UNITY_BRIDGE_PORT`   | `9004`      | Unity bridge WebSocket port   |
| `UNITY_BRIDGE_HOST`   | `localhost` | Unity bridge host             |

### 15.4 CLI

| Variable                 | Default                 | Description                       |
| ------------------------ | ----------------------- | --------------------------------- |
| `BELLONA_API_URL`        | `http://localhost:3020` | CLI target API base URL           |
| `BELLONA_BUILD_API_URL`  | `http://localhost:3020` | Build API URL (used by `health`)  |
| `BELLONA_RENDER_API_URL` | `http://localhost:3021` | Render API URL (used by `health`) |
| `BELLONA_API_KEY`        | —                       | CLI API authentication key        |

### 15.5 Control Room

| Variable                            | Default | Description                            |
| ----------------------------------- | ------- | -------------------------------------- |
| `VITE_BELLONA_CONTROL_ROOM_ENABLED` | unset   | Feature flag to enable the operator UI |

---

## 16. Integration Points

### 16.1 Upstream Domains That Trigger Bellona

The event-driven integration model means no upstream domain calls Bellona over
HTTP. Instead, each domain publishes an event and Bellona's event handlers react
asynchronously.

| Domain | Trigger event             | Bellona response               |
| ------ | ------------------------- | ------------------------------ |
| Hathor | `hathor.world.published`  | Enqueues a world build         |
| Isis   | `isis.asset.generated`    | Enqueues an asset-baking build |
| Yemaya | `yemaya.build.requested`  | Enqueues a build job           |
| Yemaya | `yemaya.export.requested` | Enqueues an export job         |

### 16.2 Shared Infrastructure Dependencies

| Dependency         | Usage                                                  |
| ------------------ | ------------------------------------------------------ |
| `@oshun/event-bus` | Redis-backed cross-domain event pub/sub                |
| `@oshun/logging`   | Structured logging across apps                         |
| `@oshun/cache`     | Redis client / cache primitives                        |
| `@oshun/contracts` | Event-type constants and Zod payload schemas           |
| `@oshun/metrics`   | Event-handler metrics registry                         |
| PostgreSQL         | Bellona schema database                                |
| Redis              | Build/render cache, build/export job queues, event bus |
| MinIO / S3         | Artifact and export package storage                    |

---

## 17. V2 Reciprocal Tasks (Backlog)

The following items are tracked in the Bellona backlog as reciprocal
dependencies of the sister-monorepo V2 fighting-game project.

### 17.1 BellonaUnrealRuntime (planned)

- **Task ID:** `BELLONA-UE-RUNTIME-V2-001`
- **Owner:** Bellona Runtime Integrations
- **ETA:** 2026-06-30

Today the Unreal adapter ships an **editor-only** plugin at
`libs/bellona/unreal/plugin/BellonaUnrealEditor/`. The runtime-shipped companion
plugin `BellonaUnrealRuntime` is **not yet implemented** — no
`BellonaUnrealRuntime` directory exists under `libs/bellona/unreal/plugin/`.

When the runtime plugin is built it will live, like its editor sibling, under
Bellona ownership at `libs/bellona/unreal/plugin/BellonaUnrealRuntime/`. This is
the single source of truth for the plugin. **V2 must not create
`V2/ue/Plugins/BellonaUnrealRuntime/`** as a parallel copy. The V2 monorepo
consumes the Bellona-owned plugin once it ships rather than forking a runtime
module of its own; a V2-side `check-v2-bellona-runtime-gates.py` CI check fails
the build if a `BellonaUnrealRuntime` plugin directory appears under
`V2/ue/Plugins/`.

Until the runtime plugin lands, the **Editor-time `BellonaUnrealEditor` remains
available** and it is the only Bellona Unreal plugin in either tree; all V2
author/cook workflows (including the frame-snap Live Link handoff in §17.2) run
through it. V2 runtime-dependent surfaces stay gated by the `@oshun/config`
feature flags `ENABLE_V2_BELLONA_RUNTIME_HOT_RELOAD`,
`ENABLE_V2_BELLONA_RUNTIME_ASSET_IMPORT`, and
`ENABLE_V2_BELLONA_LIVE_COSMETIC_DELIVERY` (all default `false` in
`libs/shared/config/src/features.ts`).

Planned scope: a runtime UE module (no editor-only dependencies) that ships in
cooked V2 builds for runtime hot-reload of Bellona-cooked artifacts, ad-hoc
runtime asset import on non-rollback surfaces, live cosmetic delivery, and
provenance metadata handoff — each gated behind the corresponding feature flag
above until the Bellona Runtime Integrations team marks the plugin
production-ready.

### 17.2 Aja frame-snap Live Link

- **Task ID:** `BELLONA-MOCAP-V2-FRAME-SNAP-001`

This task is **implemented** in `@bellona/mocap` under `src/frame-snap/`. That
module defines:

- Constants `BELLONA_MOCAP_FRAME_SNAP_ROUTE`
  (`@aja -> @bellona/mocap -> @bellona/unreal`),
  `BELLONA_MOCAP_FRAME_SNAP_TARGET_FPS` (`60`), and
  `BELLONA_MOCAP_FRAME_SNAP_TOLERANCE_SECONDS` (`0.0005`).
- Types `BellonaFrameSnapClip`, `BellonaFrameSnapNotifySegmentKind`,
  `BellonaFrameSnapNotifySegmentInput`, `BellonaFrameSnapNotifySegment`,
  `BellonaFrameSnapExportRequest`, `BellonaFrameSnapLiveLinkManifest` (with
  `BellonaFrameSnapManifestClip`, `BellonaFrameSnapManifestFramePolicy`,
  `BellonaFrameSnapManifestLiveLink`, `BellonaFrameSnapManifestQualityGates`),
  and the `BellonaFrameSnapError` class.
- Functions `createBellonaFrameSnapLiveLinkExport`,
  `normalizeBellonaNotifySegments`, `snapBellonaSecondsToFrame`.

`@bellona/mocap` resamples Aja-retargeted skeleton clips to 60 Hz, assigns
integer frame numbers, normalizes notify segments to integer 60 Hz boundaries,
and rejects sub-frame segments. `@bellona/unreal` receives the editor-only Live
Link handoff for import/cook.

The contract route, written with the canonical arrow notation used by the V2
docs, is `@aja → @bellona/mocap → @bellona/unreal`. Every notify segment carries
`alignment: 'integer-60hz-frame-boundaries'`, so the manifest's gameplay windows
land on deterministic integer frames. The V2 consumer of this manifest is
`@v2/aja-bellona-livelink-export` (Aja spec §11A.1).

Like the rest of the V2 Bellona surface, this path is author/cook-time only: the
manifest sets `runtimeLiveLinkPluginRequired: false`, the handoff is
`editorOnlyHandoff`, and **runtime Live Link plugins remain disabled** in
shipped V2 builds. Gameplay runs against the cooked, frame-snapped
`AnimSequence`, not a live Link stream — consistent with §17.1, where the
runtime plugin is gated until `BellonaUnrealRuntime` ships.

---

## 18. Acceptance Criteria

The Bellona domain meets its specification when all of the following conditions
hold. Each criterion maps directly to a testable behavior.

1. The Prisma schema migrates cleanly via `prisma migrate dev` /
   `prisma migrate deploy`, producing the 11 documented tables with the
   documented indexes and constraints.
2. `@bellona/build-api` `initialize()` stands up the build cache, the Redis job
   queues (or the in-memory fallback when `REDIS_URL` is unset), and the four
   cross-domain event subscriptions; `getHealthStatus()` reports `healthy`.
3. The `build-worker` priority queue dequeues jobs by priority and dispatches
   each `JobType` to its dedicated worker module.
4. Each of the four bridge apps starts a `bridge-core` WebSocket server on its
   documented port and registers `handshake`, `ping`, and the engine-specific
   command set (6 / 9 / 11 / 14 commands).
5. `bridge-blender` publishes `bellona.session.started` /
   `bellona.session.ended` on connect / disconnect.
6. `@bellona/event-publisher` publishes the eight `bellona.*` events with
   payloads conforming to the `@oshun/contracts` Zod schemas, over the
   Redis-backed event bus.
7. `@bellona/event-handlers` consumes `hathor.world.published`,
   `isis.asset.generated`, `yemaya.build.requested`, and
   `yemaya.export.requested`, enqueueing builds/exports.
8. The `bellona` CLI exposes the seven documented commands.
9. The interchange pipeline imports, validates, transforms, and exports the 13
   `AssetFormat` values per its export-capability matrix.
