Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.ixo.world/llms.txt

Use this file to discover all available pages before exploring further.

Reference Dockerfile

QiForge has no runtime dependency on itself — anywhere Node 22+ runs, an oracle runs. This Dockerfile is the shortest path from pnpm dev to a container you can ship.
FROM node:22-slim AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build

FROM node:22-slim
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package.json ./
RUN mkdir -p /data
ENV MATRIX_STORE_PATH=/data/matrix-storage
ENV SQLITE_DATABASE_PATH=/data/sqlite
EXPOSE 5678
CMD ["node", "dist/main.js"]
Everything below explains the moving parts. The reference oracle (apps/qiforge-example) builds with pnpm build and runs with node dist/main.js — see apps/qiforge-example/src/main.ts.

Prerequisites

  • Node.js 22+
  • A reachable Matrix homeserver
  • A persistent volume for the Matrix store and SQLite checkpointer
  • Core env vars (per baseEnvSchema)
  • Optional: Redis (only if credits or tasks plugins are loaded)

Build and ship

1

Build the production bundle

pnpm install --frozen-lockfile
pnpm build
For the example app this runs tsc -p tsconfig.build.json and outputs dist/. Your package.json’s start script should be node dist/main.js.
2

Pin every required env var

Production .env (or your platform’s secret manager) needs every core var plus the vars for each plugin you’ve kept.
NODE_ENV=production
PORT=5678
ORACLE_NAME=My Oracle
CORS_ORIGIN=https://your-portal.example
NETWORK=mainnet

# Identity (validated by baseEnvSchema)
ORACLE_DID=did:ixo:ixo1...
ORACLE_ENTITY_DID=did:ixo:entity:...
SECP_MNEMONIC=...
RPC_URL=https://rpc.ixo.world
BLOCKSYNC_GRAPHQL_URL=https://blocksync.ixo.world/graphql

# Matrix
MATRIX_BASE_URL=https://matrix.ixo.world
MATRIX_ORACLE_ADMIN_USER_ID=@my-oracle-bot:ixo.world
MATRIX_ORACLE_ADMIN_PASSWORD=...
MATRIX_ORACLE_ADMIN_ACCESS_TOKEN=...
MATRIX_ACCOUNT_ROOM_ID=...
MATRIX_VALUE_PIN=...
MATRIX_RECOVERY_PHRASE=...
MATRIX_STORE_PATH=/data/matrix-storage   # MUST persist across restarts

# Storage
SQLITE_DATABASE_PATH=/data/sqlite

# LLM provider
LLM_PROVIDER=openrouter
OPEN_ROUTER_API_KEY=...
# or LLM_PROVIDER=nebius + NEBIUS_API_KEY

# Optional tracing
LANGSMITH_TRACING=true
LANGSMITH_API_KEY=...
LANGSMITH_PROJECT=my-oracle
Per-plugin vars come on top — see environment variables reference for the full list. Boot validates every var declared by every loaded plugin’s configSchema; missing required vars fail loudly.
3

Run the entity setup once per deployment

Identity and Matrix keys are provisioned via the CLI — see identity and auth for the full flow.
qiforge create-entity --no-interactive --network mainnet ...
qiforge setup-encryption-key

Persistent volumes

1

Mount /data as a persistent volume

Two things must survive restarts. Lose either and you lose state.
PathWhat it storesWhat breaks if you lose it
MATRIX_STORE_PATH (default /data/matrix-storage)Matrix sync state, room keys, decrypted cacheFull re-sync on next boot; potential decrypt issues
SQLITE_DATABASE_PATH (e.g. /data/sqlite)Per-user LangGraph checkpointerThread continuity across restarts
In Docker Compose:
services:
  oracle:
    image: my-oracle:latest
    environment:
      - MATRIX_STORE_PATH=/data/matrix-storage
      - SQLITE_DATABASE_PATH=/data/sqlite
    volumes:
      - oracle-data:/data
volumes:
  oracle-data:
On Railway / Fly.io: attach a persistent volume mounted at /data.
2

Add Redis only when needed

Redis is required only if you load the credits plugin (or the tasks plugin once it ships). Without those, skip it entirely.
services:
  redis:
    image: redis:7
    volumes:
      - redis-data:/data
  oracle:
    environment:
      - REDIS_URL=redis://redis:6379
    depends_on:
      - redis
volumes:
  redis-data:
Pass the Redis client to CreditsPlugin explicitly — see enable bundled plugins.

Health probes

1

Use /health as the liveness probe

The framework exposes GET /health, always public, never goes through AuthHeaderMiddleware. Returns 200 once Nest is up.Docker:
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
  CMD curl -fsS http://localhost:5678/health || exit 1
Kubernetes:
livenessProbe:
  httpGet:
    path: /health
    port: 5678
2

There is no separate readiness probe

Matrix init runs in the background. The process accepts requests immediately after Nest boots. If you need to delay traffic until Matrix is up, subscribe via app.onPluginStatusChange and signal your platform when matrix transitions to loaded — see observability.
3

The /docs Swagger UI is also auth-excluded

Useful for debugging in production behind a private network. To remove it in public deployments, host-supply your own Nest module that re-mounts /docs behind auth, or block it at your reverse proxy.

CORS

CORS_ORIGIN controls who can call your oracle from a browser.
ValueBehaviour
*Open. Credentials disabled (browsers require a specific origin to send credentials).
https://your-portal.exampleSpecific origin; credentials: true enabled.
The framework allows these headers on every request:
Content-Type, Authorization,
x-ucan-delegation, x-matrix-access-token, x-matrix-homeserver,
x-did, x-request-id, x-timezone

Logging

The runtime uses NestJS’s default Logger. Pipe stdout/stderr to your aggregator. Override the bootstrap logger with createOracleApp({ logger }) if you need a custom format — see createOracleApp reference. For structured tracing instead of free-form logs, set the LangSmith env vars — see observability.

Graceful shutdown

The runtime registers a SIGTERM / SIGINT handler that closes the Nest app and disconnects Matrix.
// To disable (rare — only when your platform's process manager
// owns signal handling and you've verified it calls app.close()):
const app = await createOracleApp({
  config,
  skipGracefulShutdown: true,
});
Disable graceful shutdown only when your platform’s process manager guarantees a clean app.close() on termination. Otherwise you’ll get partial flushes and Matrix sync corruption.

Environment variables

Every var per plugin, exhaustively.

Observability

LangSmith, plugin lifecycle, logger.

Identity and auth

Entity setup and per-request UCAN auth.

Troubleshooting

Boot errors, Matrix issues, common runtime errors.