# @gzl10/nexus-backend > Core runtime: engine, DB layer, entity services, HTTP (Hono), events, cache, storage. Consumed by modules (internal) and plugins (external, 3-char `code`). ## ctx (everything flows through this) ```typescript interface NexusContext { services: { get(name): T; getOptional(name): T | undefined; register(name, svc) } helpers: { /* crypto, dates, ids, paths */ } db: { knex: Knex t(name: string): string // auto-prefix table name for current module/plugin formatTimestamp(knex, date?): string // driver-safe timestamp } logger: { info, warn, error, debug } errors: { NotFoundError, ConflictError, ValidationError, ForbiddenError, UnauthorizedError, ErrorCodes } events: { on(event, handler) notify(event, payload) // fire-and-forget query(event, payload): Promise // aggregate responses command(event, payload): Promise// single responder } engine: { hasModule(name): boolean hasPlugin(name): boolean } core: { events: EventEmitter2 } // raw emitter, backward compat } ``` ## Layers ### Engine (`src/kernel/engine/`) - `registry.ts` — module/plugin registration with cycle-safe dependency resolution (hard `dependencies` + soft `optionalDependencies`). - `loader.ts` — auto-discovers plugins via `PLUGIN_PATTERN`; uses lazy `await import()` for peer SDKs. - `table-prefix.ts` — plugins prefix tables by 3-char `code` (e.g. `ait__conversations`). Enforced by `ctx.db.t()`. - `capabilities-registry.ts` — plugins announce capabilities consumed via `ctx.engine`. - `definition-extractors.ts`, `subject-extractor.ts` — extract CASL subjects and entity defs from manifests. - `plugin-ops.ts` — enable/disable plugins at runtime. - `events-api.ts` — semantic wrappers (`notify/query/command`) over the raw emitter. ### DB (`src/db/`) - Adapters: `knex-adapter` (prod), `memory-adapter` (tests), `redis-adapter`, `schema-adapter`. - Migrations (multi-source, one per plugin + core): - `migration-generator.ts` — auto-diffs entity schema → migration file. - `migration-engine.ts` / `migration-runner.ts` — per-source batch tracking. - `migration-lock.ts` — cross-process lock. - `query-interceptor.ts` — CASL push-down into Knex query builder. - `filter-helpers.ts` — DSL (`$eq`, `$ne`, `$in`, `$like`…) → Knex where clauses. - `boolean-registry.ts` — per-driver boolean normalization (SQLite 0/1, Postgres/MySQL native). - `sqlite-compat.ts` — `.returning()`, JSON ops, constraint error mapping. - `seed-runner.ts` — idempotent seeds at startup; `seed-context` exposes `ctx.db` + helpers. - `memory-knex.ts` — `:memory:` SQLite for fast integration tests. ### Runtime / Services (`src/runtime/`) Seven entity service types, all via `ctx.services.get('Service')`: - `collection` — CRUD + paginate + filter (default). - `single` — key-value single record, optional `scopeField`. - `tree` — parent_id + move ops. - `dag` — multi-parent via pivot table. - `view` — read-only SQL view. - `computed` — derived/aggregated, multi-source resolver. - `external` — adapter-backed (HTTP/SDK). All service methods pass through: - **Hooks pipeline** — `compose-hooks.ts` runs `beforeCreate/afterCreate/beforeUpdate/...` from module + plugins. - **CASL filter** — `casl-filter.ts` auto-filters `findMany` by current user ability. - **Sensitive fields** — `sensitive-fields.ts` strips `casl.sensitiveFields` from responses (write-only). - **Env mapping** — `helpers/env-mapping.ts` (`resolveEnvMapping` for singles, `resolveEnvDefaults` for collection seeds, `stripEnvLockedFields`). - **Workflow FSM** — `workflow/` validates transitions on `update`, auto-injects state field at boot. - **Zod validation** — `validation/schema-builder.ts` generates Zod from `FieldDefinition[]`. - **Live observer** — `live-observer.ts` emits realtime updates for subscribers. Repositories: `repositories/factory.ts` + `repositories/knex.ts` — only abstraction services talk to. ### Core / HTTP (`src/core/`, `src/http/`) Hono-based. Middleware chain: - `middleware/ability.ts` — loads CASL ability for request. - `middleware/error.ts` — formats `ctx.errors.*` into `{ error: { code, message, requestId }, details }` with i18n. - `middleware/rate-limit.ts` — per-IP + per-user. - `middleware/request-id.ts`, `timeout.ts`, `validate.ts`, `nexus-client.ts`. Authorization: - `auto-entity-access.ts` — synthesizes CASL rules from `casl.permissions` in entity defs. - `abilities/field-access.ts` — per-role `FieldAccessConfig` allowlist (bypasses superuser gate). - `jwt/` — access tokens + PATs. HTTP layer (`src/http/`): - `spa-handler.ts` — multi-SPA (Vite middleware dev / static prod), per-SPA mount. - `entity-factory.ts` — builds dynamic routes from entity defs. - `routes/entity.routes.ts`, `module-routes.ts`, `action.routes.ts` — generated routers. Other: - `sse/batch-reporter.ts` — SSE for batch actions (configurable `timeout`). - `openapi/` — OpenAPI spec generator from entity/action defs. - `tunnel.ts` — frpc inbound tunnel (orphan cleanup, race-free config rename). ### Events / Realtime (`src/events/`) - `emitter.ts` — `nexusEvents` (EventEmitter2). - `event-bridge.ts` — declarative `BridgeRule[]` (event pattern → socket emission), debounced per key. - `realtime-debouncer.ts` — coalesces bursts. - `socket.ts` — Socket.IO setup + room helpers. - `room-utils.ts` — room naming convention (`entity::`, `user:`, `role:`). Prefer `ctx.events.notify/query/command` over raw emitter in new code. ### Cache (`src/core/cache/`) - `cache-manager` — unified interface. - `lru-cache` — in-memory LRU. - `managed-cache` — TTL + tag-based invalidation. - `redis-managed-cache` — same API, Redis-backed. - `scoped-cache-manager` — per-tenant/per-user scoped keys. Entity definitions declare `cache: { ttl }` — the service wraps `findMany`/`get` transparently. ### Storage (`src/modules/storage/`) Drivers: `local`, `s3` (`drivers/s3.driver.ts`). - `image-processing.service.ts` — Sharp: resize/quality/fit/format/cache via URL params. - `video-processing.service.ts` + `ffmpeg.utils.ts` — thumbnails, conversion. - Dedup by content hash, scopes, attachments entity, content negotiation via `Accept`. ## Common Patterns **Raw Knex in service/hook:** ```typescript const { knex, t, formatTimestamp } = ctx.db await knex(t('schedules')) .where({ active: true }) .update({ last_run_at: formatTimestamp(knex) }) ``` **Cross-module integration — prefer events:** ```typescript // Side effect ctx.events.notify('user.created', { userId }) // Aggregate data from N modules const extensions = await ctx.events.query('dashboard.widgets', { userId }) // Delegate to an optional handler const result = await ctx.events.command('invoice.export', { id }) ``` **Never throw `new Error()`:** ```typescript throw new ctx.errors.NotFoundError('User not found', { code: ctx.errors.ErrorCodes.NOT_FOUND }) ``` **Standalone actions** — `manifest.actions[]` (scope: `module | entity | row`), batch + timeout at top-level, `output` (shape) or `outputPage` (PageDefinition ref). **Cross-module action injection** — `ModuleManifest.injectActions: [{ target: { module, entity }, action }]`. ## Boot Sequence 1. env (`dotenv`) → validated 2. config loaded (auto-discovered plugins + modules) 3. engine: register modules → register plugins → resolve cycles 4. db: connect → run pending migrations (core + per-plugin) 5. seeds (idempotent) — env-defaults materialized here 6. http: build Hono app → mount SPAs, entities, actions → listen ## Testing Import from subpath exports: - `@gzl10/nexus-backend/testing` — `createMockContext`, error classes, `createCoreTables`, entity factories. - `@gzl10/nexus-backend/migrations` — `runGeneratedMigration`, schema helpers for `:memory:` integration tests. Mock `ctx.db.formatTimestamp` in tests: `formatTimestamp: (_db, date?) => (date ?? new Date()).toISOString()`. ## Where to put new code - **Business logic** → `src/modules//` (isolated from `core/db/engine`, access via `ctx`). - **Framework-level feature** → decide between `kernel/engine/` (registration-time), `runtime/` (per-entity pipeline), `core/` (HTTP/cross-cutting), `db/` (storage layer). - **External integration** → prefer a plugin (separate package, `code: 'xxx'`). ## Quick Agent Onboarding 1. Read `/CLAUDE.md` (monorepo rules, package layout, CLI commands). 2. `project_get` in Neural MCP → `capabilities_index` (catalog of what exists backend-wide). 3. This file — in-package API reference. 4. `src/kernel/engine/context.ts` — exact `ctx` shape. 5. `src/types.ts` (or `index.ts`) — public exports. See also: `packages/sdk/` (shared DTOs, single source of truth), `plugins/*/llms.txt` (per-plugin reference).