# Yemaya — Technical Specifications

> Technical reference covering data models, database schema, API contracts,
> events, worker queues, configuration, environment variables, and integration
> points. Every entry below is traceable to source code in `libs/yemaya/*` or
> `apps/yemaya/*`.

---

This document is the implementation contract for Yemaya. It defines the exact
data model (table names, field types, foreign key strategies), the API surface
(route files, mount paths, middleware chain), the event bus contracts (12
published events, 6 subscribed events, consumer group names), the BullMQ queue
configuration (queue names, retry policies), and the environment variables
required to run the API and worker processes.

Everything here is derived directly from source: the Prisma schema at
`libs/yemaya/database/prisma/schema.prisma`, the app entry points at
`apps/yemaya/api/src/app.ts` and `apps/yemaya/workers/src/queues.ts`, and the
contracts package at `libs/contracts/src/events/yemaya.ts`.

---

## Database Configuration

Yemaya uses a dedicated PostgreSQL database, separate from the databases of
every other Oshun domain. The Prisma schema lives at
`libs/yemaya/database/prisma/schema.prisma` and the generated client at
`libs/yemaya/database/src/generated/client`.

| Property                 | Value                                       |
| ------------------------ | ------------------------------------------- |
| **Engine**               | PostgreSQL                                  |
| **ORM**                  | Prisma (`prisma-client-js` generator)       |
| **Schema location**      | `libs/yemaya/database/prisma/schema.prisma` |
| **Environment variable** | `YEMAYA_DATABASE_URL`                       |
| **Generated client**     | `libs/yemaya/database/src/generated/client` |
| **Preview features**     | `fullTextSearch`, `fullTextIndex`           |

Source: `libs/yemaya/database/prisma/schema.prisma` lines 21–30.

The schema is intentionally scoped to platform concerns. Capability-domain data
lives in separate databases and is referenced by ID, not by foreign key. This
means no Yemaya migration can break Isis, Hathor, Sophia, or Bellona, and vice
versa:

| Domain  | Database  | Responsibility                                       |
| ------- | --------- | ---------------------------------------------------- |
| Isis    | `isis`    | Generation jobs, GPU execution, workflows            |
| Hathor  | `hathor`  | Worldbuilding, narrative, pre-production, scheduling |
| Sophia  | `sophia`  | Research, knowledge, documents                       |
| Bellona | `bellona` | Engine integration, build pipelines                  |

---

## Data Models

All models below are defined in `libs/yemaya/database/prisma/schema.prisma`.
Primary keys default to `cuid()` (`@db.VarChar(25)`) unless otherwise noted; the
canonical-library models (`Collection`, `Incident`, `SupportCase`) use `uuid()`.
Most platform models carry `createdAt`, `updatedAt`, and a nullable `deletedAt`
for soft deletion.

### User

The core user account model — authentication identity, display profile, and
session tracking all hang off this record.

| Field           | Type         | Default  | Description                  |
| --------------- | ------------ | -------- | ---------------------------- |
| `id`            | `String`     | `cuid()` | Primary key                  |
| `email`         | `String`     | —        | Email address (unique)       |
| `emailVerified` | `DateTime?`  | —        | Email verification timestamp |
| `passwordHash`  | `String?`    | —        | Password hash                |
| `firstName`     | `String?`    | —        | First name                   |
| `lastName`      | `String?`    | —        | Last name                    |
| `displayName`   | `String?`    | —        | Display name                 |
| `avatarUrl`     | `String?`    | —        | Avatar URL                   |
| `bio`           | `String?`    | —        | Bio text                     |
| `locale`        | `String`     | `"en"`   | UI locale                    |
| `timezone`      | `String`     | `"UTC"`  | Timezone                     |
| `status`        | `UserStatus` | `ACTIVE` | Account status               |
| `lastLoginAt`   | `DateTime?`  | —        | Last login timestamp         |

**Table:** `users` **Relations:** `accounts`, `sessions`, `memberships`,
`ownedProjects`, `projectMembers`, `assets`, `comments`, `activities`,
`notifications`, `preferences`, `apiKeys`.

### UserPreference

Per-user UI and notification preferences, stored as a one-to-one extension of
the User record so the core User table stays lean.

| Field              | Type      | Default    | Description                      |
| ------------------ | --------- | ---------- | -------------------------------- |
| `userId`           | `String`  | —          | FK to User (unique — one-to-one) |
| `theme`            | `String`  | `"system"` | UI theme                         |
| `compactMode`      | `Boolean` | `false`    | Compact layout flag              |
| `notifyEmail`      | `Boolean` | `true`     | Email notification opt-in        |
| `notifyInApp`      | `Boolean` | `true`     | In-app notification opt-in       |
| `autoSave`         | `Boolean` | `true`     | Auto-save flag                   |
| `autoSaveInterval` | `Int`     | `60`       | Auto-save interval (seconds)     |
| `preferences`      | `Json`    | `{}`       | Arbitrary additional preferences |

**Table:** `user_preferences`.

### Account

OAuth and social provider accounts linked to a user. A single user can have
multiple OAuth accounts from different providers (Google, GitHub, etc.).

| Field               | Type      | Description                          |
| ------------------- | --------- | ------------------------------------ |
| `userId`            | `String`  | FK to User                           |
| `type`              | `String`  | Account type                         |
| `provider`          | `String`  | Provider name (google, github, etc.) |
| `providerAccountId` | `String`  | Provider-side account ID             |
| `refreshToken`      | `String?` | OAuth refresh token                  |
| `accessToken`       | `String?` | OAuth access token                   |
| `expiresAt`         | `Int?`    | Token expiry (epoch seconds)         |
| `tokenType`         | `String?` | Token type                           |
| `scope`             | `String?` | Granted OAuth scopes                 |
| `idToken`           | `String?` | OIDC ID token                        |
| `sessionState`      | `String?` | Provider session state               |

**Unique:** `[provider, providerAccountId]` **Table:** `accounts`.

### Session and ApiKey

These two models handle the two authentication mechanisms: browser sessions
(JWT-backed, expiring) and server-to-server API keys (hashed, scoped).

| Model     | Key Fields                                                                               |
| --------- | ---------------------------------------------------------------------------------------- |
| `Session` | `userId`, `token` (unique), `expiresAt`, `ipAddress`, `userAgent`                        |
| `ApiKey`  | `userId`, `name`, `keyHash` (unique), `keyPrefix`, `scopes[]`, `lastUsedAt`, `expiresAt` |

**Tables:** `sessions`, `api_keys`.

### Organization

Multi-tenant organizations are the top-level billing and access boundary. Every
project belongs either to an organization or directly to a user, and plan-based
feature gating applies at the organization level.

| Field         | Type               | Default | Description            |
| ------------- | ------------------ | ------- | ---------------------- |
| `name`        | `String`           | —       | Organization name      |
| `slug`        | `String`           | —       | URL slug (unique)      |
| `description` | `String?`          | —       | Description            |
| `logoUrl`     | `String?`          | —       | Logo URL               |
| `website`     | `String?`          | —       | Website URL            |
| `plan`        | `OrganizationPlan` | `FREE`  | Subscription plan tier |
| `settings`    | `Json`             | `{}`    | Organization settings  |

**Table:** `organizations` **Relations:** `members`, `teams`, `projects`,
`invitations`, `subscription`.

### Team and Membership Models

Teams sit between organizations and projects, providing an intermediate grouping
level for large studios. The join tables below track who belongs to what and at
what access level.

| Model                    | Key Fields                                                                      | Purpose             |
| ------------------------ | ------------------------------------------------------------------------------- | ------------------- |
| `OrganizationMember`     | `organizationId`, `userId`, `role` (`OrganizationRole`), `joinedAt`             | Org membership      |
| `OrganizationInvitation` | `organizationId`, `email`, `role`, `token` (unique), `expiresAt`, `acceptedAt`  | Pending invitations |
| `Team`                   | `organizationId`, `name`, `slug`, `description`, `color`, `iconUrl`, `settings` | Sub-team grouping   |
| `TeamMember`             | `teamId`, `userId`, `role` (`TeamRole`), `joinedAt`                             | Team membership     |
| `TeamProject`            | `teamId`, `projectId`, `access` (`ProjectAccess`)                               | Team-project access |

`OrganizationMember` is unique on `[organizationId, userId]`; `TeamMember` on
`[teamId, userId]`; `Team` on `[organizationId, slug]`; `TeamProject` on
`[teamId, projectId]`.

### Project

The central creative container in Yemaya. A project holds all assets, scripts,
storyboards, and configuration for a production. Cross-domain capability
references (`hathorWorldId`, `isisWorkflowIds[]`, `sophiaPackIds[]`) are stored
here as plain ID fields, not foreign keys, preserving database isolation.

| Field             | Type            | Default   | Description                                  |
| ----------------- | --------------- | --------- | -------------------------------------------- |
| `name`            | `String`        | —         | Project name                                 |
| `slug`            | `String`        | —         | URL slug                                     |
| `description`     | `String?`       | —         | Description                                  |
| `thumbnailUrl`    | `String?`       | —         | Thumbnail URL                                |
| `type`            | `ProjectType`   | `OTHER`   | Production type                              |
| `status`          | `ProjectStatus` | `DRAFT`   | Lifecycle status                             |
| `visibility`      | `Visibility`    | `PRIVATE` | Access level                                 |
| `organizationId`  | `String?`       | —         | FK to Organization (`SetNull` on delete)     |
| `ownerId`         | `String`        | —         | FK to User (owner)                           |
| `settings`        | `Json`          | `{}`      | Project settings                             |
| `metadata`        | `Json`          | `{}`      | Arbitrary metadata                           |
| `hathorWorldId`   | `String?`       | —         | **Cross-domain:** Hathor world ID            |
| `isisWorkflowIds` | `Json`          | `[]`      | **Cross-domain:** array of Isis workflow IDs |
| `sophiaPackIds`   | `Json`          | `[]`      | **Cross-domain:** array of Sophia pack IDs   |

**Unique:** `[organizationId, slug]` **Table:** `projects` **Relations:**
`organization`, `owner`, `members`, `teams`, `assets`, `folders`, `tags`,
`activities`.

#### Cross-Domain Reference Fields

`hathorWorldId`, `isisWorkflowIds[]`, and `sophiaPackIds[]` are resolved via
internal API calls (or the capability proxy), never by database foreign key, so
each domain database stays isolated.

### Asset

A digital file managed within a project's asset library. Assets carry both a
reference to object storage (`storageKey`) and cross-domain references into Isis
and Bellona for the generation job that produced them and the engine-processed
variant respectively.

| Field              | Type          | Default | Description                              |
| ------------------ | ------------- | ------- | ---------------------------------------- |
| `projectId`        | `String`      | —       | FK to Project (`Cascade` on delete)      |
| `folderId`         | `String?`     | —       | FK to Folder (`SetNull` on delete)       |
| `creatorId`        | `String`      | —       | FK to User                               |
| `name`             | `String`      | —       | Asset name                               |
| `description`      | `String?`     | —       | Description                              |
| `type`             | `AssetType`   | —       | Asset category                           |
| `status`           | `AssetStatus` | `DRAFT` | Review lifecycle status                  |
| `mimeType`         | `String?`     | —       | MIME type                                |
| `fileSize`         | `BigInt?`     | —       | File size in bytes                       |
| `storageKey`       | `String`      | —       | Object storage key (MinIO/S3)            |
| `storageUrl`       | `String?`     | —       | Direct access URL                        |
| `thumbnailUrl`     | `String?`     | —       | Thumbnail URL                            |
| `previewUrl`       | `String?`     | —       | Preview URL                              |
| `metadata`         | `Json`        | `{}`    | Extracted metadata                       |
| `version`          | `Int`         | `1`     | Version number                           |
| `checksum`         | `String?`     | —       | File checksum (`VarChar(64)`)            |
| `isisGenerationId` | `String?`     | —       | **Cross-domain:** Isis generation job ID |
| `bellonaAssetId`   | `String?`     | —       | **Cross-domain:** Bellona asset ID       |

**Table:** `assets` **Relations:** `project`, `folder`, `creator`, `tags`
(`AssetTag[]`), `usages` (`AssetUsage[]`), `comments`.

### Folder

Hierarchical self-referential folder structure for organizing assets within a
project. The `parentId` self-reference creates an arbitrarily deep tree.

| Field       | Type      | Default | Description                                      |
| ----------- | --------- | ------- | ------------------------------------------------ |
| `projectId` | `String`  | —       | FK to Project                                    |
| `parentId`  | `String?` | —       | FK to parent Folder (`FolderHierarchy` self-ref) |
| `name`      | `String`  | —       | Folder name                                      |
| `color`     | `String?` | —       | Display color                                    |
| `icon`      | `String?` | —       | Display icon identifier                          |
| `sortOrder` | `Int`     | `0`     | Sort position                                    |

**Table:** `folders`.

### Tagging Models

Tags are organization-scoped and applied to both assets and projects through
join tables. The `AssetUsage` model tracks where an asset is referenced so
safe-to-delete analysis is always available.

| Model        | Key Fields                                                | Purpose                       |
| ------------ | --------------------------------------------------------- | ----------------------------- |
| `Tag`        | `organizationId?`, `name`, `slug`, `color`, `description` | Tag definition (org-scoped)   |
| `AssetTag`   | `assetId`, `tagId` — unique `[assetId, tagId]`            | Asset ↔ Tag join              |
| `ProjectTag` | `projectId`, `tagId` — unique `[projectId, tagId]`        | Project ↔ Tag join            |
| `AssetUsage` | `assetId`, `context`, `entityId`                          | Tracks where an asset is used |

`Tag` is unique on `[organizationId, slug]`. **Tables:** `tags`, `asset_tags`,
`project_tags`, `asset_usages`.

### Comment

Threaded comments on assets, with resolution and pinning support for formal
review workflows.

| Field      | Type      | Default | Description                             |
| ---------- | --------- | ------- | --------------------------------------- |
| `assetId`  | `String?` | —       | FK to Asset (`Cascade` on delete)       |
| `authorId` | `String`  | —       | FK to User                              |
| `parentId` | `String?` | —       | FK to parent Comment (`CommentReplies`) |
| `content`  | `String`  | —       | Comment content                         |
| `resolved` | `Boolean` | `false` | Resolution status                       |
| `pinned`   | `Boolean` | `false` | Pinned status                           |
| `metadata` | `Json`    | `{}`    | Metadata                                |

**Table:** `comments`.

### Activity and Notification

Activity records form the project-level audit feed visible to team members.
Notifications are per-user inbox items delivered to the UI and optionally via
email or push.

| Model          | Key Fields                                                                                         | Purpose               |
| -------------- | -------------------------------------------------------------------------------------------------- | --------------------- |
| `Activity`     | `projectId?`, `userId`, `type` (`ActivityType`), `action`, `entityId?`, `details`, `ipAddress`     | Project activity feed |
| `Notification` | `userId`, `type` (`NotificationType`), `title`, `message?`, `link?`, `read`, `readAt?`, `metadata` | User notifications    |

**Tables:** `activities`, `notifications`.

### Canonical Library, Support, and Incident Records

These three UUID-keyed models back cross-domain library curation and operational
support workflows. They use `uuid()` primary keys (rather than `cuid()`) to
align with cross-domain reference conventions.

#### Collection

A curated library collection (`@@map("collections")`).

| Field                                    | Type        | Description                             |
| ---------------------------------------- | ----------- | --------------------------------------- |
| `slug`                                   | `String`    | URL slug (unique)                       |
| `title`, `summary`                       | `String`    | Display title and summary               |
| `primaryDomain`, `domains[]`             | `String`    | Owning domain and additional domains    |
| `origin`, `status`, `visibility`, `kind` | `String`    | Lifecycle/classification fields         |
| `membershipMode`                         | `String`    | How items join (manual, smart, etc.)    |
| `itemCount`                              | `Int`       | Number of items                         |
| `progressPercent`                        | `Float?`    | Completion percentage                   |
| `coverImageUrl`                          | `String?`   | Cover image                             |
| `lastOpenedAt`, `lastPublishedAt`        | `DateTime?` | Activity timestamps                     |
| `tags[]`                                 | `String[]`  | Tags                                    |
| `smartConfig`                            | `Json?`     | Smart-collection rules                  |
| `collaborators`, `sections`, `items`     | `Json`      | Embedded collaborator/section/item data |
| `ownerId`                                | `String?`   | Owner (UUID)                            |

#### Incident

An operational incident record (`@@map("incidents")`).

| Field                                                                                        | Type       | Description                       |
| -------------------------------------------------------------------------------------------- | ---------- | --------------------------------- |
| `incidentKey`, `slug`                                                                        | `String`   | Human key and slug (both unique)  |
| `title`, `summary`, `description`                                                            | `String`   | Narrative fields                  |
| `primaryDomain`, `domains[]`                                                                 | `String`   | Owning + related domains          |
| `category`, `severity`, `status`, `queue`                                                    | `String`   | Triage/classification             |
| `blastRadius`                                                                                | `String`   | Impact scope                      |
| `source`, `customerImpact`                                                                   | `Json`     | Structured source and impact data |
| `declaredById`, `commanderId`, `ownerId`, `acknowledgedById`, `resolvedById`, `closedById`   | `String?`  | Role assignments (UUID)           |
| `detectedAt`, `declaredAt`, `acknowledgedAt`, `nextUpdateDueAt`, `resolvedAt`, `closedAt`    | `DateTime` | Lifecycle timestamps              |
| `affectedResources`, `linkedResources`, `handoffs`, `communications`, `slaClocks`, `history` | `Json`     | Append-only operational logs      |
| `mitigation`, `postmortem`                                                                   | `Json`     | Mitigation and postmortem records |
| `tags[]`, `metadata`                                                                         | —          | Tags and metadata                 |

#### SupportCase

A customer support case (`@@map("support_cases")`).

| Field                                                        | Type      | Description                      |
| ------------------------------------------------------------ | --------- | -------------------------------- |
| `caseKey`, `slug`                                            | `String`  | Human key and slug (both unique) |
| `title`, `summary`, `description`                            | `String`  | Narrative fields                 |
| `primaryDomain`, `domains[]`                                 | `String`  | Owning + related domains         |
| `kind`, `category`, `status`, `priority`, `channel`, `queue` | `String`  | Triage/classification            |
| `requester`                                                  | `Json`    | Requester contact data           |
| `openedById`, `assignedToId`                                 | `String?` | Role assignments (UUID)          |
| `linkedResources`, `escalations`, `slaClocks`, `history`     | `Json`    | Operational logs                 |
| `resolution`, `satisfaction`                                 | `Json?`   | Resolution and CSAT records      |
| `tags[]`, `metadata`                                         | —         | Tags and metadata                |

### Webhooks

Webhooks allow external systems to receive Yemaya events via HTTP. Each
`Webhook` configuration has a companion `WebhookDelivery` record for every
delivery attempt, enabling retry tracking and audit.

| Model             | Key Fields                                                                                                                                                          | Purpose               |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
| `Webhook`         | `organizationId?`, `projectId?`, `name`, `url`, `secret?`, `events[]`, `headers`, `enabled`, `retryCount` (default 3), `retryDelay` (default 60), `lastTriggeredAt` | Webhook configuration |
| `WebhookDelivery` | `webhookId`, `event`, `payload`, `status` (`WebhookDeliveryStatus`), `statusCode?`, `response?`, `errorMessage?`, `attempts`, `nextRetryAt?`, `deliveredAt?`        | Delivery tracking     |

**Tables:** `webhooks`, `webhook_deliveries`.

### Subscription and Billing

The billing model tracks the organization's active subscription plan, invoices,
and credit balance. `UsageRecord` provides per-metric metering for credit-based
services like AI generation.

| Model               | Key Fields                                                                                                                                                                                 | Purpose              |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------- |
| `Subscription`      | `organizationId` (unique), `plan` (`SubscriptionPlan`), `status` (`SubscriptionStatus`), `stripeCustomerId?`, `stripeSubId?`, `currentPeriodStart/End`, `cancelAtPeriodEnd`, `trialEndsAt` | Organization billing |
| `Invoice`           | `subscriptionId`, `stripeInvoiceId?`, `number?`, `status` (`InvoiceStatus`), `amount`, `currency` (default `"usd"`), `periodStart/End`, `paidAt?`, `dueDate?`, `pdf?`                      | Invoice records      |
| `InvoiceLineItem`   | `invoiceId`, `description`, `quantity`, `unitAmount`, `amount`                                                                                                                             | Line-item detail     |
| `UsageRecord`       | `subscriptionId`, `metric`, `quantity`, `timestamp`, `metadata`                                                                                                                            | Usage metering       |
| `CreditBalance`     | `subscriptionId` (unique), `balance`, `currency`                                                                                                                                           | Credit wallet        |
| `CreditTransaction` | `creditBalanceId`, `type` (`CreditTransactionType`), `amount`, `description?`, `referenceId?`                                                                                              | Credit movements     |

**Tables:** `subscriptions`, `invoices`, `invoice_line_items`, `usage_records`,
`credit_balances`, `credit_transactions`.

### Plugin Marketplace

The plugin data model stores the plugin registry, version history, per-scope
installation records, and community reviews.

| Model                | Key Fields                                                                                                                                                                                                                                                                                                                                              | Purpose            |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
| `Plugin`             | `slug` (unique), `name`, `shortDescription?`, `description?`, `category` (`PluginCategory`), `iconUrl?`, `coverUrl?`, `authorName`, `authorUrl?`, `repositoryUrl?`, `documentationUrl?`, `supportUrl?`, `license?`, `pricing` (`PluginPricing`), `price?`, `status` (`PluginStatus`), `featured`, `verified`, `downloadCount`, `rating?`, `reviewCount` | Marketplace plugin |
| `PluginVersion`      | `pluginId`, `version`, `changelog?`, `minAppVersion?`, `maxAppVersion?`, `downloadUrl`, `checksum?`, `fileSize?`, `published` — unique `[pluginId, version]`                                                                                                                                                                                            | Version record     |
| `PluginInstallation` | `pluginId`, `organizationId?`, `projectId?`, `userId?`, `version`, `enabled`, `settings` — unique `[pluginId, organizationId, projectId, userId]`                                                                                                                                                                                                       | Install record     |
| `PluginReview`       | `pluginId`, `userId`, `rating` (`SmallInt`), `title?`, `content?`, `helpful`, `verified` — unique `[pluginId, userId]`                                                                                                                                                                                                                                  | Community review   |

**Tables:** `plugins`, `plugin_versions`, `plugin_installations`,
`plugin_reviews`.

### AuditLog

The `AuditLog` model captures an immutable before/after snapshot of every
audited action for GDPR and SOC2 compliance reporting.

| Field            | Type      | Description                        |
| ---------------- | --------- | ---------------------------------- |
| `organizationId` | `String?` | Organization scope                 |
| `userId`         | `String?` | Actor user ID                      |
| `action`         | `String`  | Action performed                   |
| `entityType`     | `String?` | Entity type (Project, Asset, etc.) |
| `entityId`       | `String?` | Entity ID                          |
| `oldValues`      | `Json?`   | Previous state snapshot            |
| `newValues`      | `Json?`   | New state snapshot                 |
| `ipAddress`      | `String?` | Client IP address                  |
| `userAgent`      | `String?` | Client user agent                  |
| `metadata`       | `Json`    | Additional metadata                |

**Table:** `audit_logs`.

---

## Key Enumerations

All Prisma enums below are defined in
`libs/yemaya/database/prisma/schema.prisma`. These values are the only valid
states for their respective fields — the database and Prisma client both enforce
membership.

### UserStatus

`ACTIVE`, `INACTIVE`, `SUSPENDED`, `PENDING_VERIFICATION`

### OrganizationPlan / SubscriptionPlan

`FREE`, `STARTER`, `PROFESSIONAL`, `ENTERPRISE` (two separate enums with the
same members).

### OrganizationRole

`OWNER`, `ADMIN`, `MEMBER`, `VIEWER`

### TeamRole

`LEAD`, `MEMBER`

### ProjectAccess

`VIEW`, `EDIT`, `ADMIN`

### ProjectType

`FILM`, `GAME`, `ANIMATION`, `COMMERCIAL`, `MUSIC_VIDEO`, `DOCUMENTARY`, `OTHER`

### ProjectStatus

`DRAFT`, `ACTIVE`, `REVIEW`, `COMPLETED`, `ARCHIVED`

### ProjectRole

`OWNER`, `ADMIN`, `EDITOR`, `CONTRIBUTOR`, `VIEWER`

### Visibility

`PRIVATE`, `TEAM`, `ORGANIZATION`, `PUBLIC`

### AssetType (15 values)

`IMAGE`, `VIDEO`, `AUDIO`, `MODEL_3D`, `DOCUMENT`, `SCRIPT`, `STORYBOARD`,
`CHARACTER_DESIGN`, `ENVIRONMENT_DESIGN`, `ANIMATION`, `VFX`, `MATERIAL`,
`TEXTURE`, `FONT`, `OTHER`

### AssetStatus

`DRAFT`, `PROCESSING`, `REVIEW`, `APPROVED`, `REJECTED`, `ARCHIVED`

### SubscriptionStatus

`ACTIVE`, `PAST_DUE`, `CANCELLED`, `TRIALING`, `PAUSED`

### InvoiceStatus

`DRAFT`, `OPEN`, `PAID`, `VOID`, `UNCOLLECTIBLE`

### CreditTransactionType

`PURCHASE`, `USAGE`, `REFUND`, `BONUS`, `ADJUSTMENT`, `EXPIRATION`

### ActivityType

`PROJECT`, `ASSET`, `USER`, `TEAM`, `ORGANIZATION`, `SYSTEM`

### NotificationType

`MENTION`, `COMMENT`, `INVITATION`, `ASSIGNMENT`, `APPROVAL`, `SYSTEM`, `ALERT`

### WebhookDeliveryStatus

`PENDING`, `SUCCESS`, `FAILED`, `RETRYING`

### PluginCategory

`GENERATION`, `EDITING`, `EXPORT`, `INTEGRATION`, `UTILITY`, `COLLABORATION`,
`ANALYTICS`, `AUTOMATION`, `OTHER`

### PluginPricing

`FREE`, `PAID`, `FREEMIUM`, `SUBSCRIPTION`

### PluginStatus

`PENDING`, `APPROVED`, `REJECTED`, `PUBLISHED`, `DEPRECATED`, `REMOVED`

### Library-Level Enumerations

Several Yemaya libraries define their own typed constant objects (TypeScript
`as const` unions, not Prisma enums). These live inside individual library
packages rather than in the database schema.

**`@yemaya/canon-enforcement`** (`libs/yemaya/canon-enforcement/src/types.ts`):

These constants drive the tier hierarchy and workflow states used by the canon
management system to classify established creative facts.

| Constant            | Values                                                                        |
| ------------------- | ----------------------------------------------------------------------------- |
| `CANON_TIER`        | `primary`, `secondary`, `tertiary`, `non-canon`                               |
| `CANON_STATUS`      | `verified`, `pending`, `conflict`, `retconned`                                |
| `STYLE_DOMAIN`      | `visual`, `writing`, `dialogue`, `audio`, `ui`, `animation`, `cinematography` |
| `CONSISTENCY_LEVEL` | `perfect`, `high`, `moderate`, `low`, `inconsistent`                          |

**`@yemaya/human-override`** (`libs/yemaya/human-override/src/types.ts`):

These constants define the types of human interventions available, the
conditions that trigger automatic escalation, and the recovery lifecycle.

| Constant              | Values                                                                                                                                                                    |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `OVERRIDE_TYPE`       | `emergency-stop`, `decision-injection`, `constraint-override`, `quality-override`, `budget-override`, `timeline-override`, `scope-override`                               |
| `ESCALATION_CATEGORY` | `quality-failure`, `budget-overrun`, `timeline-slip`, `repeated-failure`, `creative-deadlock`, `ethical-concern`, `legal-compliance`, `security-concern`, `system-health` |
| `RECOVERY_STATUS`     | `pending`, `in-progress`, `completed`, `failed`                                                                                                                           |

**`@yemaya/production-verification`**
(`libs/yemaya/production-verification/src/types.ts`):

These constants model the asset completeness taxonomy and the four-state
sign-off workflow used by pre-delivery verification checks.

| Constant              | Values                                                                                                                           |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `ASSET_CATEGORY`      | `visual`, `audio`, `narrative`, `gameplay`, `ui`, `localization`, `accessibility`, `documentation`, `marketing`, `certification` |
| `COMPLETENESS_STATUS` | `complete`, `partial`, `missing`, `not-applicable`                                                                               |
| `BENCHMARK_RESULT`    | `exceeds`, `meets`, `below`, `fails`                                                                                             |
| `SIGNOFF_STATUS`      | `pending`, `approved`, `approved-with-exceptions`, `rejected`                                                                    |

---

## Pipeline Schema

The orchestration pipeline model is defined as Zod schemas in
`libs/yemaya/orchestration/src/schemas/pipeline.ts`. These schemas are the
runtime contract: every pipeline created or received by the orchestration layer
is validated against them.

### Pipeline

A pipeline is the top-level execution unit: it holds a list of steps with
explicit dependencies, a progress counter, and the variables injected at
runtime.

| Field           | Type                        | Description                   |
| --------------- | --------------------------- | ----------------------------- |
| `id`            | `uuid`                      | Pipeline ID                   |
| `projectId`     | `uuid`                      | Owning project                |
| `name`          | `string` (1–255)            | Pipeline name                 |
| `description`   | `string` (≤2000, optional)  | Description                   |
| `status`        | `PipelineStatus`            | Lifecycle status              |
| `steps`         | `PipelineStep[]`            | Ordered step list             |
| `progress`      | `number` (0–100, default 0) | Progress percentage           |
| `currentStepId` | `uuid?`                     | Currently executing step      |
| `variables`     | `Record<string, unknown>`   | Execution context variables   |
| `stopOnError`   | `boolean` (default `true`)  | Halt pipeline on step failure |
| `errorMessage`  | `string?`                   | Pipeline-level error          |
| `createdBy`     | `uuid`                      | Creator user ID               |

**`PipelineStatus`:** `draft`, `ready`, `running`, `paused`, `completed`,
`failed`, `cancelled`.

### PipelineStep

Each step in a pipeline carries its own retry configuration, timeout, and
dependency list. The `dependsOn` field specifies which step IDs must complete
before this step can start, enabling the orchestrator to build a correct
topological execution order.

| Field                                  | Type                      | Default  | Description                |
| -------------------------------------- | ------------------------- | -------- | -------------------------- |
| `id`                                   | `uuid`                    | —        | Step ID                    |
| `name`                                 | `string` (1–255)          | —        | Step name                  |
| `type`                                 | `StepType`                | —        | Step type (see below)      |
| `status`                               | `StepStatus`              | —        | Step status                |
| `config`                               | `Record<string, unknown>` | `{}`     | Step configuration         |
| `dependsOn`                            | `uuid[]`                  | `[]`     | Prerequisite step IDs      |
| `condition`                            | `string?`                 | `null`   | Conditional-run expression |
| `maxRetries`                           | `number` (0–10)           | `3`      | Retry limit                |
| `retryCount`                           | `number`                  | `0`      | Retries used               |
| `retryDelay`                           | `number` (ms)             | `5000`   | Delay between retries      |
| `timeout`                              | `number` (ms)             | `300000` | Step timeout               |
| `inputs`, `outputs`                    | `Record<string, unknown>` | `{}`     | Step I/O payloads          |
| `error`                                | `string?`                 | `null`   | Step error                 |
| `queuedAt`, `startedAt`, `completedAt` | `date?`                   | `null`   | Timing                     |

**`StepStatus`:** `pending`, `queued`, `running`, `completed`, `failed`,
`skipped`, `cancelled`.

### StepType (40 values)

The `StepType` enum defines every action a pipeline can request. Values are
namespaced by domain so it is always clear which system will execute a given
step. The 40 values span generation (Isis), research (Sophia), worldbuilding
(Hathor), engine integration (Bellona), DCC orchestration (MCP), studio actions
(Yemaya), and data-flow utilities.

| Group                 | Step Types                                                                                                                                                                                                                              |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Isis (Generation)     | `isis:generate_image`, `isis:generate_3d`, `isis:generate_audio`, `isis:generate_video`, `isis:inpaint`, `isis:upscale`, `isis:style_transfer`                                                                                          |
| Sophia (Research)     | `sophia:research`, `sophia:analyze`, `sophia:summarize`, `sophia:embed`                                                                                                                                                                 |
| Hathor (Worldbuild)   | `hathor:create_character`, `hathor:create_location`, `hathor:compile_lore`, `hathor:generate_dialogue`                                                                                                                                  |
| Bellona (Build)       | `bellona:export`, `bellona:build`, `bellona:sync`, `bellona:convert`                                                                                                                                                                    |
| MCP DCC orchestration | `mcp:execute_dcc`, `mcp:cross_dcc_transfer`, `mcp:material_consistency`, `mcp:animation_round_trip`, `mcp:production_status`, `mcp:resource_schedule`, `mcp:voice_trigger`, `mcp:voice_steer`, `mcp:voice_review`, `mcp:voice_progress` |
| Yemaya (Studio)       | `yemaya:review`, `yemaya:approve`, `yemaya:publish`, `yemaya:notify`                                                                                                                                                                    |
| Utility               | `transform`, `filter`, `merge`, `split`, `conditional`, `loop`, `parallel`, `wait`                                                                                                                                                      |

### PipelineTemplate

A `PipelineTemplate` is a runtime-state-free pipeline definition — it stores the
step graph, variable schema, and metadata but no execution state. Templates can
be public (shared across the platform), built-in (shipped with Yemaya), or
private to an organization.

A reusable, runtime-state-free pipeline definition: `id`, `name`,
`description?`, `category`, `stepTemplates[]` (`name`/`type`/`config`/
`dependsOn` only), `variables` (typed schema with `type` ∈
`string`/`number`/`boolean`/`array`/`object`, optional `default`, `required`
flag), `isPublic`, `isBuiltIn`, `tags[]`, `usageCount`.

### Built-In Pipelines

`@yemaya/autonomous-pipelines` exports factory functions that produce
`CreatePipelineInput` objects. These are the ready-made production pipeline
definitions shipped with Yemaya:

- **Film pipeline** (`createFilmPipeline`) — `FilmPipelineStage` =
  `pre_production`, `world_building`, `asset_generation`, `scene_assembly`,
  `animation`, `rendering`, `post_production`, `export`. Steps cover script
  analysis (`sophia:analyze`), visual research (`sophia:research`), character/
  location creation (`hathor:*`), 3D asset generation (`isis:generate_3d`),
  audio (`isis:generate_audio`), scene assembly / camera / lighting / render /
  composite / export via `mcp:execute_dcc` against Blender and DaVinci, and a
  closing `yemaya:review` step.
- **Game pipeline** (`createGamePipeline`) —
  `libs/yemaya/autonomous-pipelines/src/pipelines/game.ts`.
- **Curation and QC pipelines** —
  `libs/yemaya/autonomous-pipelines/src/pipelines/curation.ts` and `qc.ts`.

The film pipeline's `qualityPreset` accepts `draft`, `preview`, `production`,
`final`.

---

## API Surface

The Yemaya REST API is an `OpenAPIHono` (`@hono/zod-openapi`) application
defined in `apps/yemaya/api/src/app.ts`, listening on port `3000` by default.
Routes respond with RFC 7807 problem details on error
(`problemDetailsErrorHandler`). The OpenAPI document is served at
`GET /openapi.json`; interactive docs at `GET /docs` (Swagger UI),
`GET /reference` and `GET /playground` (Scalar).

### Route Mounting

`app.ts` makes 30 `app.route()` calls across 24 distinct base paths. Several
base paths host multiple routers (for example `/v1/schedules` and
`/v1/budgets`), and one router file (`websocket.ts`) provides the `/ws` upgrade
endpoint. The table below maps every router file to its mount path and describes
its responsibility:

| Router file (in `apps/yemaya/api/src/routes/`) | Mount path                       | Description                                                            |
| ---------------------------------------------- | -------------------------------- | ---------------------------------------------------------------------- |
| `auth.ts`                                      | `/v1/auth`                       | Login, registration, OAuth, sessions, API keys                         |
| `users.ts`                                     | `/v1/users`                      | User profiles, preferences, profile export                             |
| `projects.ts`                                  | `/v1/projects`                   | Project CRUD, members, comments, activity, archive/restore             |
| `assets.ts`                                    | `/v1/projects/:projectId/assets` | Asset upload, versioning, folders, metadata                            |
| `organizations.ts`                             | `/v1/organizations`              | Organizations, teams, invitations, members, audit logs                 |
| `scripts.ts`                                   | `/v1/scripts`                    | Scripts, scenes, script elements, story beats, characters              |
| `storyboards.ts`                               | `/v1/storyboards`                | Storyboards, panels, annotations, panel layers                         |
| `schedules.ts`                                 | `/v1/schedules`                  | Production schedules, milestones, schedule items, resources            |
| `schedule-optimization.ts`                     | `/v1/schedules`                  | AI schedule optimization, critical path, suggestions                   |
| `schedule-sharing.ts`                          | `/v1/schedules`                  | Schedule sharing and multi-format export (PDF/CSV/ICS/XLSX/HTML/XML)   |
| `crew.ts`                                      | `/v1/crew`                       | Crew, skills, availability, assignments, role definitions              |
| `call-sheets.ts`                               | `/v1/call-sheets`                | Call sheet generation, entries, distribution, export                   |
| `location.ts`                                  | `/v1/locations`                  | Location scouting, contacts, permits, checklists, media, weather, maps |
| `budgets.ts`                                   | `/v1/budgets`                    | Budgets, categories, expenses, purchase orders, invoices               |
| `budget-line-items.ts`                         | `/v1/budgets`                    | Line items with variance, forecasting, bulk ops, import/export         |
| `budget-templates.ts`                          | `/v1/budget-templates`           | Industry-standard and custom budget templates                          |
| `cost-prediction.ts`                           | `/v1/budgets`                    | ML cost prediction, trend analysis, scenario planning                  |
| `variance-tracking.ts`                         | `/v1/budgets`                    | EVM metrics, thresholds, alerts, approvals, trend analysis             |
| `multi-currency.ts`                            | `/v1/currency`                   | Exchange rates, conversions, FX impact, hedges                         |
| `collaboration.ts`                             | `/v1/collaboration`              | Collaboration sessions, threads, reviews, locks, presence              |
| `websocket.ts`                                 | `/ws`                            | WebSocket upgrade and real-time event channels                         |
| `agent-management.ts`                          | `/v1/agents`                     | Agent registration, commands, tasks, pools, messaging, metrics         |
| `plugins.ts`                                   | `/v1/plugins`                    | Plugin installation, hooks, marketplace browse                         |
| `webhooks.ts`                                  | `/v1/webhooks`                   | Webhook config, deliveries, events, statistics                         |
| `search.ts`                                    | `/v1/search`                     | Unified search, saved searches, search history, index admin            |
| `analytics.ts`                                 | `/v1/analytics`                  | Metrics, events, funnels, cohorts, dashboards, reports                 |
| `score-editor.ts`                              | `/v1/score-editor`               | Entitlement-gated AAA scene-score editor BFF surface                   |
| `admin.ts`                                     | `/v1/admin`                      | System administration, users, moderation, feature flags, jobs          |
| `versioning.ts`                                | `/v1/versions`                   | API version info, migration guides, deprecation status                 |
| `capabilities.ts`                              | `/v1/capabilities`               | Reverse proxy to Isis, Sophia, Hathor, Bellona domain APIs             |

> Engine integrations (Blender, Godot, Unreal, Houdini, DaVinci) and AI
> generation (image/3D/audio/video) are not separate Yemaya route files. They
> are reached either through the `/v1/capabilities/{domain}/...` proxy or
> through pipeline steps (`isis:*`, `bellona:*`, `mcp:execute_dcc`).

### Project Endpoints (representative)

`projects.ts` defines the following `createRoute` operations. The pattern
follows OpenAPIHono convention and every operation gets a unique `operationId`
in the generated OpenAPI document:

| Method   | Path                         | Operation                |
| -------- | ---------------------------- | ------------------------ |
| `GET`    | `/`                          | List projects            |
| `POST`   | `/`                          | Create project           |
| `GET`    | `/{id}`                      | Get project              |
| `PATCH`  | `/{id}`                      | Update project           |
| `DELETE` | `/{id}`                      | Delete project           |
| `POST`   | `/{id}/archive`              | Archive project          |
| `POST`   | `/{id}/restore`              | Restore project          |
| `POST`   | `/{id}/duplicate`            | Duplicate project        |
| `POST`   | `/{id}/thumbnail`            | Upload project thumbnail |
| `GET`    | `/{id}/members`              | List members             |
| `POST`   | `/{id}/members`              | Add member               |
| `PATCH`  | `/{id}/members/{userId}`     | Update member role       |
| `DELETE` | `/{id}/members/{userId}`     | Remove member            |
| `POST`   | `/{id}/leave`                | Leave project            |
| `GET`    | `/{id}/comments`             | List comments            |
| `POST`   | `/{id}/comments`             | Create comment           |
| `PATCH`  | `/{id}/comments/{commentId}` | Update comment           |

The remaining routers follow the same Zod-OpenAPI `createRoute` pattern; the
OpenAPI tag list in `app.ts` enumerates the full set of resource groups
(Authentication, Sessions, API Keys, Users, Organizations, Teams, Projects,
Assets, Scripts, Scenes, Characters, Worlds, Storyboards, Schedules, Budgets,
Crew, Call Sheets, Locations, Collaboration, Agents, Plugins, Webhooks, Search,
Analytics, Admin, API Versioning, Capabilities, WebSocket events, and more).

### Score Editor Entitlement Gate

The score-editor BFF surface is tier-gated to prevent free or starter plan users
from accessing AAA features. `score-editor.ts` resolves an `EditorTier` from
request headers (`x-yemaya-tier` ∈ `contemplative` / `aaa-creator` / `operator`)
and a roles list (`x-yemaya-roles` / `x-user-role`). Access to the scene-score
editor requires the `aaa-creator` or `operator` tier, or an `admin` role.

### Health and Metrics

These endpoints do not require authentication and are used by load balancers,
monitoring systems, and CI/CD pipelines:

| Endpoint                  | Description                                                   |
| ------------------------- | ------------------------------------------------------------- |
| `GET /health`             | Liveness summary with version                                 |
| `GET /health/ready`       | Readiness — checks database, cache, storage (503 if degraded) |
| `GET /health/live`        | Minimal liveness probe                                        |
| `GET /metrics`            | Performance metrics (JSON or Prometheus based on `Accept`)    |
| `GET /metrics/validation` | Benchmark validation endpoint                                 |

### Middleware Chain

The middleware chain runs in a fixed order on every request. Understanding this
order is important when debugging: each middleware may short-circuit the chain
if it rejects a request.

`app.ts` applies, in order: request ID (`X-Request-ID`), request context (async
local storage), performance metrics (p95 target 100 ms), RFC 7807 error handler,
security headers (CSP, HSTS, frame options), CORS (origin allowlist), request
logging (non-production only), gzip compression, server timing, input
sanitization (`/v1/*`), asset metadata sanitization (`/v1/assets/*`), rate
limiting (`/v1/*`), and API versioning (`path` + `header`, header
`X-API-Version`). Additional middleware modules in
`apps/yemaya/api/src/middleware/` include `circuit-breaker.ts`,
`gdpr-compliance.ts`, `soc2-compliance.ts`, and `proxy-auth.ts`.

---

## Event Contracts

Yemaya publishes and consumes events via `@oshun/event-bus`, which is
**Redis-backed** — it uses `ioredis` for Redis pub/sub fan-out plus TTL-bounded
keys for replay, durable sorted sets for delayed/retried delivery, and consumer
groups for at-most-once delivery. It is **not** Kafka. (Source:
`libs/shared/event-bus/src/event-bus.ts`, module header and
`import { Redis } from 'ioredis'`.)

The API server initializes the event bus with `keyPrefix: 'oshun:events'`,
`persistence: true`, `eventTtl: 86400` (24 h), exponential retry
(`maxAttempts: 3`), and a 7-day dead-letter retention
(`apps/yemaya/api/src/index.ts`).

### Events Published by Yemaya

`YemayaEventTypes` is defined in `libs/contracts/src/events/yemaya.ts` and is
the canonical source of published event names. Publishing is handled by
`YemayaEventPublisher`
(`libs/yemaya/event-publisher/src/yemaya-event-publisher.ts`), which exposes a
typed method per event and fails soft — a publish error is logged but does not
break the request flow. Each payload has a corresponding Zod schema in
`libs/contracts/src/events/yemaya.ts`.

The 12 events and their default notification targets are:

| Event                     | Constant           | Default targets       |
| ------------------------- | ------------------ | --------------------- |
| `yemaya.project.created`  | `PROJECT_CREATED`  | Hathor, Isis          |
| `yemaya.project.updated`  | `PROJECT_UPDATED`  | —                     |
| `yemaya.project.archived` | `PROJECT_ARCHIVED` | Hathor, Isis, Bellona |
| `yemaya.member.joined`    | `MEMBER_JOINED`    | —                     |
| `yemaya.member.left`      | `MEMBER_LEFT`      | —                     |
| `yemaya.asset.uploaded`   | `ASSET_UPLOADED`   | Isis, Bellona         |
| `yemaya.asset.processed`  | `ASSET_PROCESSED`  | —                     |
| `yemaya.asset.approved`   | `ASSET_APPROVED`   | Isis, Bellona         |
| `yemaya.asset.rejected`   | `ASSET_REJECTED`   | —                     |
| `yemaya.comment.created`  | `COMMENT_CREATED`  | —                     |
| `yemaya.comment.resolved` | `COMMENT_RESOLVED` | —                     |
| `yemaya.session.joined`   | `SESSION_JOINED`   | Bellona               |

### Events Consumed by Yemaya

`YEMAYA_SUBSCRIPTIONS` in `libs/yemaya/event-handlers/src/index.ts` is the
canonical list of subscribed events. Handlers run under the consumer group
`yemaya-api` (set by the API server) or `yemaya-handlers` (default). Each
handler updates the Yemaya project state in response to a completed action in a
capability domain.

The 6 subscribed events and their handlers are:

| Event                      | Source  | Handler                        | Concurrency |
| -------------------------- | ------- | ------------------------------ | ----------- |
| `isis.asset.generated`     | Isis    | `handleIsisAssetGenerated`     | 10          |
| `isis.job.failed`          | Isis    | `handleIsisJobFailed`          | 5           |
| `sophia.document.ingested` | Sophia  | `handleSophiaDocumentIngested` | 5           |
| `hathor.world.published`   | Hathor  | `handleHathorWorldPublished`   | 3           |
| `bellona.build.completed`  | Bellona | `handleBellonaBuildCompleted`  | 3           |
| `bellona.export.ready`     | Bellona | `handleBellonaExportReady`     | 10          |

---

## Worker Queues

Background job processing uses BullMQ over Redis. Queue names and worker types
are defined in `apps/yemaya/workers/src/queues.ts` and
`apps/yemaya/workers/src/config.ts`.

Per the `queues.ts` header comment, capability-domain execution queues live with
their owning domains (Isis owns generative/3D execution; Bellona owns media,
render, export, and engine execution). Yemaya workers only coordinate those
queues and process studio-level events.

### Queues (`QUEUE_NAMES`)

Each queue has its own retry and backoff policy tuned to the expected job
duration. Notification retries are faster (short backoff, 5 attempts) while
pipeline orchestration jobs are slower and less likely to benefit from rapid
retries.

| Queue                 | Default attempts | Backoff (initial) | Purpose                                   |
| --------------------- | ---------------- | ----------------- | ----------------------------------------- |
| `yemaya:notification` | 5                | 1 s exponential   | Email, webhook, push, and digest delivery |
| `yemaya:rendering`    | 3                | 5 s exponential   | Rendering orchestration                   |
| `yemaya:export`       | 3                | 5 s exponential   | Export orchestration                      |
| `yemaya:pipeline`     | 2                | 10 s exponential  | Pipeline orchestration                    |
| `yemaya:event`        | 3                | 2 s exponential   | Cross-domain event consumption            |

### Worker Processes

Worker implementations in `apps/yemaya/workers/src/workers/`:
`notification-worker.ts`, `rendering-orchestration-worker.ts`,
`export-orchestration-worker.ts`, `pipeline-orchestration-worker.ts`,
`event-consumer.ts`.

The `WORKER_TYPE` value (parsed from a `--worker=` CLI argument; defaults to
`all`) selects which worker(s) run. Valid values from the config Zod enum:
`all`, `notification`, `rendering`, `export`, `pipeline`, `event-consumer`,
`orchestration`. Concurrency is set via `WORKER_CONCURRENCY` (1–100, default 5).

### Notification Job Types

The notification queue handles four job types dispatched under different
delivery channels. These job type strings are the discriminant used inside the
notification worker to route each job to the right delivery path:

`notification.send-email`, `notification.send-webhook`,
`notification.send-push`, `notification.digest`.

---

## Environment Variables

### API Server (`apps/yemaya/api`)

| Variable                | Default                  | Purpose                                            |
| ----------------------- | ------------------------ | -------------------------------------------------- |
| `PORT`                  | `3000`                   | HTTP listen port                                   |
| `HOST`                  | `0.0.0.0`                | HTTP bind address                                  |
| `REDIS_URL`             | `redis://localhost:6379` | Event bus Redis connection                         |
| `YEMAYA_DATABASE_URL`   | —                        | PostgreSQL connection (Prisma datasource)          |
| `NODE_ENV`              | —                        | `development` / `production` / `test`              |
| `ALLOWED_ORIGINS`       | —                        | Comma-separated CORS origin allowlist (production) |
| `YEMAYA_EVENTS_ENABLED` | `true`                   | Set to `false` to disable event publishing         |

In production, CORS defaults to `https://yemaya.io`, `https://app.yemaya.io`,
`https://api.yemaya.io` plus any `ALLOWED_ORIGINS` entries.

### Workers (`apps/yemaya/workers`)

| Variable                   | Default                                            | Purpose                       |
| -------------------------- | -------------------------------------------------- | ----------------------------- |
| `REDIS_HOST`               | `localhost`                                        | Redis host                    |
| `REDIS_PORT`               | `6379`                                             | Redis port                    |
| `REDIS_PASSWORD`           | —                                                  | Redis password                |
| `REDIS_DB`                 | `0`                                                | Redis database index          |
| `WORKER_CONCURRENCY`       | `5`                                                | Concurrency per worker        |
| `WORKER_JOB_TIMEOUT_MS`    | `300000`                                           | Job timeout                   |
| `WORKER_STALLED_INTERVAL`  | `30000`                                            | Stalled-job check interval    |
| `WORKER_MAX_STALLED_COUNT` | `3`                                                | Max stalled count before fail |
| `DATABASE_URL`             | `postgresql://yemaya:yemaya@localhost:5432/yemaya` | PostgreSQL connection         |
| `MINIO_ENDPOINT`           | `localhost`                                        | MinIO host                    |
| `MINIO_PORT`               | `9000`                                             | MinIO port                    |
| `MINIO_ACCESS_KEY`         | `minioadmin`                                       | MinIO access key              |
| `MINIO_SECRET_KEY`         | `minioadmin`                                       | MinIO secret key              |
| `MINIO_USE_SSL`            | `false`                                            | MinIO TLS flag                |
| `LOG_LEVEL`                | `info`                                             | Log level                     |
| `LOG_PRETTY`               | non-production                                     | Pretty-print logs             |

The worker type is selected by a `--worker=<type>` process argument.

---

## Integration Points

### Storage

Asset files are stored in MinIO (development) or S3-compatible storage
(production). Each `Asset` row references storage by `storageKey`; `storageUrl`
holds a direct (pre-signed in production) access URL. The API's storage client
lives in `apps/yemaya/api/src/services/storage-client.ts` and
`storage-utils.ts`; chunked uploads are handled by `chunked-upload.ts`.

### Authentication

Yemaya supports three authentication mechanisms, all registering as OpenAPI
security schemes in `app.ts`:

- **JWT Bearer tokens** — registered OpenAPI security scheme `bearerAuth`
  (`http` / `bearer` / `JWT`). Obtained via login or OAuth.
- **API keys** — registered scheme `apiKey`, header `X-API-Key`, for
  server-to-server access. Backed by the `ApiKey` model (hashed `keyHash`,
  `keyPrefix`, `scopes[]`).
- **OAuth 2.0** — social login; provider records in the `accounts` table. API
  OAuth logic in `apps/yemaya/api/src/services/oauth.ts`.
- The `@yemaya/auth` library provides JWT and session primitives.

### Capability Domain Proxy

The capabilities proxy is the synchronous integration path to the four
capability domains. It handles authentication, circuit-breaking, and health
checking so that individual feature routes do not need to implement these
concerns.

`apps/yemaya/api/src/routes/capabilities.ts` plus
`apps/yemaya/api/src/services/proxy.ts` implement a reverse proxy to the four
capability domains. A `CapabilityDomain` request is forwarded with service
headers built from the caller's proxy-auth claims; `proxy-auth.ts` middleware
gates access. The proxy includes circuit-breaker protection and health checks
(OpenAPI tag `Capabilities - Health`).

### Cross-Domain References

The Yemaya database stores only lightweight IDs pointing into other domain
databases (`Project.hathorWorldId`, `Project.isisWorkflowIds[]`,
`Project.sophiaPackIds[]`, `Asset.isisGenerationId`, `Asset.bellonaAssetId`).
Resolution happens at query time through internal service calls or the
capability proxy — never through SQL joins.

### Real-Time Collaboration

WebSocket connections terminate at `/ws` on the same Hono process as the REST
API (`apps/yemaya/api/src/routes/websocket.ts`,
`apps/yemaya/api/src/websocket/`). The `@yemaya/collaboration` library provides
the Yjs-CRDT sync, presence, and cursor logic. Presence data is ephemeral and is
not persisted to PostgreSQL.

### Webhook Delivery

Webhook delivery runs asynchronously through the BullMQ notification queue. Each
configured `Webhook` row is delivered by the notification worker
(`notification.send-webhook` job). Each attempt creates a `WebhookDelivery` row;
failures retry with exponential backoff. `Webhook.retryCount` defaults to 3 and
`retryDelay` to 60 seconds. Delivery status follows `WebhookDeliveryStatus`:
`PENDING → SUCCESS | FAILED → RETRYING → …`.

---

## Authentication and Security

All API routes are protected by the layered middleware described in the
Middleware Chain section. The key security rules are:

- All `/v1/*` routes pass through input sanitization (XSS / SQL / command
  injection protection) and rate limiting; documentation, health, and metrics
  endpoints are exempt from rate limiting.
- Security headers: Content-Security-Policy, `Strict-Transport-Security`
  (`max-age=31536000; includeSubDomains`), `X-Content-Type-Options: nosniff`,
  `X-Frame-Options: DENY`, `X-XSS-Protection`, and
  `Referrer-Policy: strict-origin-when-cross-origin`.
- Compliance middleware (`gdpr-compliance.ts`, `soc2-compliance.ts`) applies
  audit logging uniformly; the `AuditLog` model captures actor, action, entity,
  before/after snapshots, IP, and user agent.
- Errors are RFC 7807 problem details with a propagated `requestId`.

---

## Acceptance Criteria

A Yemaya deployment is correct when all of the following conditions hold
simultaneously. These criteria are the observable contract between the
specifications and a running system:

1. **Schema parity** — the running database matches
   `libs/yemaya/database/prisma/schema.prisma`; `GET /health/ready` reports the
   database check healthy.
2. **API contract** — `GET /openapi.json` returns a valid OpenAPI 3.1 document
   covering all 30 mounted route operations; every operation has a unique
   `operationId`.
3. **Event bus** — the API logs the count of active event subscriptions on
   startup; all 6 entries of `YEMAYA_SUBSCRIPTIONS` subscribe successfully, and
   the bus connects to Redis with prefix `oshun:events`.
4. **Workers** — each BullMQ queue in `QUEUE_NAMES` is created with its
   configured retry/backoff policy; the selected `WORKER_TYPE` starts without
   error.
5. **Pipeline validity** — pipelines created from `@yemaya/autonomous-pipelines`
   factories validate against `PipelineSchema`; every step `type` is a member of
   the 40-value `StepType` enum.
6. **Cross-domain isolation** — no foreign keys exist from the Yemaya database
   into Isis/Hathor/Sophia/Bellona databases; all such references are stored as
   ID columns and resolved over the network.
7. **Capability proxy** — `/v1/capabilities/{domain}/...` forwards to the
   configured domain API, with circuit-breaker protection and a passing health
   check.
8. **Compliance** — GDPR/SOC2 middleware records an `AuditLog` entry for audited
   actions; problem-details responses carry a `requestId`.
