๐ Deployment Guide
Randal runs anywhere Bun runs. The core OSS deployment primitive is randal serve: one long-lived gateway/runner process, backed by your chosen persistence and exposed through whatever host or reverse proxy you prefer. This guide covers a local Mac Mini, a generic self-hosted/server model, Railway as one supported cloud example, and importing Randal as a library into an existing project.
The marketing/docs site is deployed separately as static Astro output on Vercel. See Public Site Deployment for Vercel previews, PostHog, domains, and public-site setup. That path is not required for self-hosting randal serve or deploying a cloud runtime.
Heads-up (2026-06-30): the persistence/auth story is changing. The user-management/onboarding work standardizes on Supabase for auth + DB (a trimmed local stack of Postgres + GoTrue) and moves schema into Supabase migrations. The plain-Postgres instructions below remain accurate today; self-host will become "run our Supabase compose." For how the deployment modes (no-auth dev / self-host / managed), auth, and onboarding fit together, see
deployment-modes-and-auth.md(canonical) and the full history inuser-management-onboarding-plan.md.
๐ Prerequisites
All deployments require:
- Bun >= 1.1
- Randal CLI installed and on PATH (
randal) - An OpenRouter API key (
OPENROUTER_API_KEY) โ OpenRouter is the only supported LLM provider; vendor-prefixed model ids all route through it - Postgres + pgvector for memory/search, chat, mesh, job audit, checkpoints, and annotations.
Optional connectors such as Image, Video, and Notion are configured through Randal connector manifests and secret providers, not by editing generated Randal native runner config. Randal native runner is retained only for explicit legacy/debug operation and connector runtimes that still own their own OAuth files. See Connectors and Secret Providers.
๐ Mac Mini (Local)
A Mac Mini is the simplest deployment: Randal runs as a background process with launchd or a process manager.
1. Install
curl -fsSL https://raw.githubusercontent.com/Hassion-Studio/randal/main/install.sh | bash
This single command:
- Installs Bun (if not present)
- Clones the Randal repo to
~/randal - Installs dependencies and links the
randalCLI - Runs the interactive setup wizard
- Starts or validates local Postgres + pgvector for source-of-truth persistence
Or manually:
git clone <repo-url> ~/randal
cd ~/randal
bun install && bun link
randal init
randal setup
2. Configure
cd ~/randal # use examples/local-mac/ as a starting point
cp .env.example .env
# Edit .env with your API keys
Example randal.config.yaml:
name: home-agent
runner:
defaultAgent: native
defaultModel: anthropic/claude-sonnet-4
workdir: ~/dev
credentials:
envFile: ./.env
allow: [OPENROUTER_API_KEY]
gateway:
channels:
- type: http
port: 7600
auth: "${RANDAL_API_TOKEN}"
memory:
store: postgres
database:
profile: local
url: "${DATABASE_URL:-postgres://randal:randal@127.0.0.1:5432/randal}"
ssl: disable
3. Postgres
Postgres is the single source of truth for new installs. randal serve uses exactly the configured DATABASE_URL/RANDAL_DATABASE_URL or config database block, applies pending core migrations idempotently before gateway health checks, and then verifies /health. It does not create, auto-provision, or switch to a different Docker database at serve time. Use randal setup, randal db start, randal db reset, randal db migrate, and randal db status explicitly for local bootstrap, diagnostics, and controlled rollouts.
To manage manually:
randal db start
randal db migrate
randal db status
Existing deployments should back up Postgres and verify randal db status --write-check before switching traffic.
4. Start Randal
cd ~/randal
randal serve
Local preview should first create or validate the local database explicitly with randal setup or randal db start, then run randal serve against that same configured URL (by default postgres://randal:randal@127.0.0.1:5432/randal). If that localhost database is missing, has the wrong role, or needs migrations, serve fails with the redacted configured target and guidance to run explicit setup/db commands or set RANDAL_DATABASE_URL; it will not silently switch to another port or Docker database. Managed/non-local profiles such as Supabase use RANDAL_DATABASE_URL/DATABASE_URL, run core migrations, then health-check the configured database.
Optional published-artifact bootstrap on any deployment target:
export RANDAL_MANAGED_CONTROL_PLANE_URL=https://control-plane.example
export RANDAL_MANAGED_WORKSPACE_ID=workspace-123
export RANDAL_MANAGED_VERSION_ID=version-7
randal serve
Failure modes:
- bootstrap endpoint unreachable: startup fails
- version export missing: startup fails with published-artifact lookup error
- malformed YAML export: startup fails before the runtime begins serving
If you do not set managed bootstrap env vars, startup remains local-first.
To run as a persistent background service, create a launchd plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.randal.agent</string>
<key>ProgramArguments</key>
<array>
<string>/Users/you/.bun/bin/bun</string>
<string>/Users/you/randal/packages/cli/src/index.ts</string>
<string>serve</string>
</array>
<key>WorkingDirectory</key>
<string>/Users/you/randal</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/tmp/randal.out.log</string>
<key>StandardErrorPath</key>
<string>/tmp/randal.err.log</string>
</dict>
</plist>
cp com.randal.agent.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.randal.agent.plist
5. Verify
curl http://localhost:7600/health
# {"status":"ok","uptime":...,"version":"0.1.0"}
# Open Randal Console
open http://localhost:7600/
๐งฑ Generic Server / VPS / Container Host
Use this model for any provider that can run Bun or a container and expose an HTTP port.
- Install Randal and Randal native runner.
- Provide
randal.config.yamland required environment variables through your host's normal secret mechanism. - Run
randal serveunder systemd, launchd, Docker, Nomad, Kubernetes, or another process manager. - Expose the configured HTTP port, usually
7600, behind TLS and authentication-aware routing. - Persist Postgres data for memory, chat, jobs, checkpoints, mesh, and audit timelines. Persist
~/.randalorRANDAL_STATE_DIRseparately for scheduler files, workspaces, generated config/cache, and local connector secrets.
Connector credentials should be managed through randal connector secret set or the Console. Local connector secrets are stored under ~/.randal/secrets/workspaces/ by default. Randal Cloud will use user/workspace-level connector secret storage rather than requiring each customer connector secret to be configured as a hosting-provider deployment variable.
Connector changes do not require a full randal serve process restart. The gateway reloads connector config for new brain sessions and interrupts active brain sessions when necessary so the next session can see updated tools.
๐ Railway (Cloud)
Railway is one supported hosting example, not a requirement for OSS Randal. The default Railway deployment is one Randal service backed by Railway managed Postgres or another pgvector-compatible DATABASE_URL.
For voice on Railway, the intended first working path is PSTN/Twilio. Browser voice remains supported, but is secondary and stays behind authenticated HTTP admin routes.
For voice deployments, treat Railway as a public gateway unless you explicitly
put the service behind private networking or another authenticated edge. Only
/, /health, and /assets/* are intentionally public. Browser token
issuance (POST /api/voice/token) and voice status (GET /voice/status) stay
behind normal HTTP auth.
1. Project Structure
Create a deployment directory with:
my-deployment/
randal.config.yaml
Dockerfile
railway.toml
2. Dockerfile
FROM ghcr.io/hassion-studio/randal:latest
# Copy your config
COPY randal.config.yaml /app/randal.config.yaml
# Copy knowledge files (if any)
# COPY knowledge/ /app/knowledge/
The official image includes Bun, Randal native runner, and Randal. Durable memory, chat, mesh, jobs, checkpoints, and audit state live in Postgres; run migrations before treating the service as ready.
3. Railway Configuration
In the Railway dashboard or the GitHub Actions deploy workflow:
- Create a new project.
- Add a custom service pointing to your repo.
- Set environment variables.
If you use .github/workflows/railway-deploy.yml, set these as GitHub Actions
repository secrets because the workflow copies secrets into Railway. Your local
.env is not copied into Railway by that workflow.
| Variable | Value |
|---|---|
OPENROUTER_API_KEY | The LLM provider credential โ every model (all vendor prefixes) routes through OpenRouter. Also required when the Video connector is enabled through its default OpenRouter path. |
DATABASE_URL or RANDAL_DATABASE_URL | Required managed Railway Postgres or compatible external Postgres URL with pgvector, pg_trgm, and pgcrypto. |
RANDAL_STATE_DIR | Durable filesystem state root for workspaces, auth/tool caches, scheduler files, connector overlays, and control-plane local state. Postgres remains the source of truth for jobs, memory, chat, mesh, checkpoints, and audit. |
RANDAL_API_TOKEN | A generated secret for API auth |
RANDAL_REQUIRE_MEMORY | Set to true for hosted Railway deployments so startup and /health fail closed when Postgres memory is unavailable. |
RANDAL_STATE_DIR | Set to /data/randal; mount a Railway volume there for workspace and auth/tool cache persistence. |
RANDAL_VOICE_PUBLIC_URL | Public HTTPS/WSS base URL for Randal's /voice/* routes |
LIVEKIT_URL | LiveKit server URL (voice scaffolding โ live calls currently unavailable pending the voice rework) |
LIVEKIT_API_KEY | LiveKit API key |
LIVEKIT_API_SECRET | LiveKit API secret |
ELEVENLABS_API_KEY | ElevenLabs TTS key |
ELEVENLABS_VOICE_ID | ElevenLabs voice ID |
TWILIO_ACCOUNT_SID | Twilio account SID |
TWILIO_AUTH_TOKEN | Twilio auth token |
TWILIO_PHONE_NUMBER | Twilio phone number in E.164 |
Provider routing:
- Every model id routes through OpenRouter with
OPENROUTER_API_KEY(or the configuredcredentials.openroutersecret reference). Account auth and direct provider keys were removed. - Managed plans route through the Randal metering proxy with the user's JWT instead โ no provider key on the deployment at all.
- Mount a Railway volume at
/dataand setRANDAL_STATE_DIR=/data/randal. - Set the deploy config to use your Dockerfile.
Production GitHub App flow: let Railway deploy committed code from Git, but keep deployment-specific Randal config private. Store a complete private config in Railway as RANDAL_CONFIG_YAML, or mount/write a private config file and set RANDAL_CONFIG_PATH. Use examples/cloud-railway/randal.config.connectors.yaml as a safe template for Image/Video connectors; it maps connector secret names to Railway service env var names and does not contain raw secret values.
Console-managed connector flow: deploy the baseline config, then enable connectors in Console. The official Docker entrypoint enables RANDAL_CONNECTOR_OVERLAY=true, so non-secret connector enable/settings state persists at $RANDAL_STATE_DIR/connectors/overlay.yaml and connector secrets persist separately through the configured SecretProvider. Remote MCP OAuth tokens for connectors such as Notion are owned by Randal native runner and persist in $HOME/.local/share/native/mcp-auth.json, normally $RANDAL_STATE_DIR/home/.local/share/native/mcp-auth.json because the official entrypoint sets durable HOME=$RANDAL_STATE_DIR/home. Config-declared connectors in RANDAL_CONFIG_YAML or RANDAL_CONFIG_PATH remain authoritative over overlay entries with the same id.
The checked-in root randal.config.railway.yaml remains a generic fallback and should not enable optional connectors that require credentials. Use a private config override for production connector enablement so missing Railway variables fail closed instead of silently exposing half-configured tools.
Railway persistence checklist:
- Mount a Railway volume at
/databefore treating the service as production. - Set
RANDAL_STATE_DIR=/data/randaland keep every durable runtime path under that root unless you intentionally use a more specific persistent override. - Use
/data/randal/workspaceforrunner.workdirso resumed jobs can see files created before a redeploy. - Attach managed Postgres and set
DATABASE_URLorRANDAL_DATABASE_URL; this is the source of truth for memory, chat, graph, mesh, jobs, and audit state. - Keep durable
HOME, Randal provider account auth, remote MCP OAuth tokens (mcp-auth.json) for connector runtimes that need them, and agent-installed tools on the same mounted volume through the official image defaults.
Durable state ownership:
| Surface | Railway path | Owner | Notes |
|---|---|---|---|
| Job workspace/cache | /data/randal/workspace | Runner process files | Workspace output and partial files remain available after redeploy. Durable job records/events/checkpoints/audit timelines are in Postgres. |
| Scheduler state | /data/randal/heartbeat-state.yaml, /data/randal/cron.yaml | Scheduler heartbeat and cron | Wake items, tick counters, cron status, and last-run metadata survive restarts. |
| Gateway metadata | /data/randal/gateway.pid | Gateway | Runtime metadata follows the same state root for predictable operations. |
| Managed/control-plane state | /data/randal/control-plane | Control-plane file persistence | RANDAL_CONTROL_PLANE_STATE_DIR can override this when needed. |
| Runner workspace | /data/randal/workspace | Runner jobs | Job artifacts and partial work remain available after redeploy. |
| Randal native runner home/session state | /data/randal/home | Entrypoint, legacy Randal native runner, and connector runtimes | Legacy/debug Randal native runner auth/session/cache paths and remote MCP OAuth tokens can live under durable HOME, including $HOME/.local/share/native/mcp-auth.json. |
| Agent-installed tools | /data/randal/tools/bin | Entrypoint and agent runtime | Prepended to PATH before /app/tools/bin for tools that must survive image replacement. |
Use randal db dump, randal db restore, and randal db scoped-copy for Postgres backups and moves.
Railway runtime paths should not point at /app for durable filesystem state. Use /data/randal/workspace for runner.workdir, keep scheduler/control-plane/cache state under RANDAL_STATE_DIR, and treat /app as image/runtime scratch. Job records, events, checkpoints, tool invocation summaries, artifact metadata, and audit timelines belong in Postgres.
The official container sets HOME=$RANDAL_STATE_DIR/home, so legacy/debug Randal native runner auth/session/cache paths and remote MCP OAuth token storage such as $HOME/.local/share/native/mcp-auth.json survive redeploys when those runtimes are used. The container also prepends $RANDAL_STATE_DIR/tools/bin to PATH before /app/tools/bin; use that state-root tools directory for agent-installed tools that must survive image replacement. /app/knowledge and /app/tools/bin are image/runtime scratch unless you deliberately bake files into the image or mount them yourself.
On SIGTERM, SIGINT, or a gateway restart, Randal enters a drain mode: health reports draining, new job/scheduler mutations return 503 gateway_draining, scheduler state is flushed, active Postgres job records are updated with shutdown metadata, and child agent processes get RANDAL_SHUTDOWN_GRACE_MS milliseconds to exit before startup recovery resumes persisted queued and running jobs. The recovery model is Postgres-first for job state/audit plus filesystem persistence for workspace output; it does not guarantee every agent can semantically checkpoint mid-token before Railway stops the container.
Startup diagnostics: every gateway boot logs the resolved state root and derived durable paths for heartbeat, cron, control-plane state, Randal native runner home, tools, and workspace, plus redacted Postgres health. Railway-like environments warn when RANDAL_STATE_DIR is unset or a durable surface points under known ephemeral roots such as /app or /tmp. Set RANDAL_STRICT_PERSISTENCE=true to escalate those warnings to error diagnostics during startup validation.
Extension points: keep filesystem-backed runtime state under RANDAL_STATE_DIR unless a backend owns that surface explicitly. Postgres owns jobs, memory, chat, mesh registry, checkpoints, annotations, delegation audit, and graph source-of-truth rows; Randal Cloud control-plane storage can replace individual non-source filesystem surfaces later without changing the Railway volume contract for workspaces/cache.
Use a dedicated Twilio subaccount for this deployment. The current PSTN runtime
expects TWILIO_ACCOUNT_SID + TWILIO_AUTH_TOKEN + TWILIO_PHONE_NUMBER, not
Twilio API keys.
4. Provider Credential Flow
OpenRouter is the only supported LLM provider. Set OPENROUTER_API_KEY as a Railway secret (or configure runner.providerAuth.credentials.openrouter with a secret reference) and pick default models per vendor prefix โ they are OpenRouter catalog ids.
Concrete Railway config:
name: my-cloud-agent
runner:
defaultAgent: native
defaultModel: openai/gpt-5.5
workdir: /data/randal/workspace
providerAuth:
defaultProvider: openai
credentials:
envFile: ./.env
allow: [OPENROUTER_API_KEY, RANDAL_API_TOKEN, DATABASE_URL, RANDAL_REQUIRE_MEMORY, RANDAL_STATE_DIR, RANDAL_HOME_DIR, RANDAL_TOOLS_DIR, RANDAL_WORKSPACE_DIR]
inherit: [PATH, HOME, USER, SHELL, TERM, OPENROUTER_API_KEY, DATABASE_URL, RANDAL_REQUIRE_MEMORY, RANDAL_STATE_DIR, RANDAL_HOME_DIR, RANDAL_TOOLS_DIR, RANDAL_WORKSPACE_DIR]
gateway:
channels:
- type: http
port: 7600
auth: "${RANDAL_API_TOKEN}"
memory:
store: postgres
index: memory-cloud-agent
database:
profile: generic
url: "${DATABASE_URL}"
ssl: require
migrationMode: validate-only
Legacy Randal native runner OAuth compatibility still exists through runner.randalAuth.* and RANDAL_RUNTIME_AUTH_JSON for explicit legacy/debug runs. It is no longer the preferred native-runner account-auth path. Remote MCP OAuth connectors may still keep Randal native runner-owned connector state under $RANDAL_STATE_DIR/home/.local/share/native/mcp-auth.json when those connector runtimes require it.
OPENROUTER_API_KEY (or a runner.providerAuth.credentials.openrouter secret reference) is the only provider credential the deployment needs.
5. Minimal API-Key Config for Railway
A minimal .env-based variant of the same OpenRouter-only setup:
name: my-cloud-agent
runner:
defaultAgent: native
defaultModel: anthropic/claude-sonnet-4
workdir: /data/randal/workspace
credentials:
envFile: ./.env
allow: [OPENROUTER_API_KEY]
gateway:
channels:
- type: http
port: 7600
auth: "${RANDAL_API_TOKEN}"
memory:
store: postgres
index: memory-cloud-agent
database:
profile: generic
url: "${DATABASE_URL}"
ssl: require
migrationMode: validate-only
Note:
DATABASE_URLmust point at the intended Postgres source of truth.randal serveapplies pending core migrations idempotently before/health; for locked-down production roles, runrandal db migrate --config randal.config.railway.yamlduring rollout with migration credentials, then let/healthvalidate reachability, migrations, extensions, and writeability.
Postgres Topology
For a normal single hosted Railway Randal, use one Randal service plus Railway managed Postgres. This is the first-class default: one Railway service, one DATABASE_URL, one Railway volume mounted at /data, and memory.store: postgres.
Use one shared Postgres database for a posse unless you intentionally isolate agents by database. Every agent still gets its own identity/scope while shared memory, graph, mesh registry, jobs, and audit events remain tenant/project-scoped rows. Randal does not generate per-instance databases; startup migrations are global/idempotent in the configured database.
Spawned runtime workers should not choose their own memory backend. They inherit the parent runtime's Postgres-backed memory/chat tools and tenant scope.
The GitHub Actions Railway workflow has an explicit topology input for manual dispatch:
single(default): deploys one Randal service using managed Postgres and/data/randalworkspace/auth persistence.posse: reserved for future multi-agent service creation. Current deployments should use managed Postgres for shared memory, graph, mesh registry, jobs, and delegation audit.
For direct CLI deployment of a Posse, generate per-agent configs with explicit name, posse, HTTP auth, mesh role/expertise, and a private mesh endpoint such as http://product-engineering.railway.internal:7600 so agents delegate over the Railway private network rather than registering localhost. Use Postgres-backed memory/mesh state for all deployments.
Persistence, Migration, And Backup
Railway deployments must attach managed Postgres and mount persistent storage at /data/randal. Postgres is the durable source of truth; agent workspaces and auth/tool caches live under /data/randal/workspace, /data/randal/home, and /data/randal/tools. Keep durable HOME on that volume so $HOME/.local/share/native/auth.json and remote MCP OAuth tokens in $HOME/.local/share/native/mcp-auth.json survive image replacement.
To migrate a hosted Randal to another computer, export the Randal config/secrets separately and use Postgres tooling for data. Use randal db dump/restore for whole-database or whole-schema moves, and randal db scoped-export/scoped-import/scoped-copy for one org/project without overwriting an entire cloud database. Dump and scoped manifests redact database URLs and report row counts/checksums.
Managed mode on Railway is optional. If you want the deployed runtime to start from the active published workspace artifact instead of a repo-local config file, provide:
| Variable | Value |
|---|---|
RANDAL_MANAGED_CONTROL_PLANE_URL | Control-plane base URL |
RANDAL_MANAGED_WORKSPACE_ID | Managed workspace ID |
RANDAL_MANAGED_VERSION_ID | Optional fixed version pin |
RANDAL_MANAGED_ARTIFACT_URL | Optional explicit export URL override |
RANDAL_CONTROL_PLANE_STATE_DIR | Optional explicit persistent directory for managed workspace state. If unset, it defaults to $RANDAL_STATE_DIR/control-plane. |
6. Deploy
# Via Railway CLI
railway up
# Or push to your connected Git repo for auto-deploy
git push origin main
Voice Deployment Guidance
Shared-instance voice is supported, but the safest production posture is split deployment:
- Admin/browser voice instance: private or tightly restricted, authenticated browser token issuance, full admin voice access.
- External/PSTN voice instance: public-facing, explicit external grants, no ambient admin trust unless intentionally configured.
Use one shared instance only if you accept the larger blast radius of putting a single gateway in front of both admin and external voice paths.
๐ฆ Importing Randal into an Existing Project
You can add a Randal agent to an existing project by extending the official Docker image. This is ideal for adding an AI agent alongside your own codebase, knowledge base, or application.
How It Works
- Your Dockerfile extends
ghcr.io/hassion-studio/randal:latest(includes Bun, Randal native runner, and Randal) - You copy your
randal.config.yamlinto the image - You ship whatever files your agent needs (codebase, knowledge, data)
- The official entrypoint handles
randal serve; configure Postgres withDATABASE_URL - For custom pre-start logic, add a
pre-start.shhook
1. Project Structure
your-project/
randal.config.yaml # agent configuration
Dockerfile # extends the official Randal image
knowledge/ # optional: files your agent needs
pre-start.sh # optional: custom startup logic
2. Dockerfile
FROM ghcr.io/hassion-studio/randal:latest
# Copy your config
COPY randal.config.yaml /app/randal.config.yaml
# Ship whatever your agent needs
COPY knowledge/ /app/knowledge/
# Optional: custom pre-start logic (e.g., DB sync)
# COPY pre-start.sh /app/pre-start.sh
The official image handles everything else โ Randal serves on port 7600 and uses the configured Postgres database as its source of truth.
3. Pre-Start Hook
If you need custom logic before Randal starts (database sync, file setup, etc.), create a pre-start.sh. The Randal entrypoint sources this automatically:
#!/bin/bash
# pre-start.sh โ runs before Randal starts
echo "Pulling data from my database..."
bun /app/scripts/sync-data.mjs || echo "Sync failed, continuing"
4. Security
The Docker container is the isolation boundary. Recommended config for imported usage:
sandbox:
enforcement: env-scrub
runner:
workdir: /data/randal/workspace
allowedWorkdirs:
- /data/randal/workspace
credentials:
allow: [OPENROUTER_API_KEY] # only what the agent needs
See SECURITY.md for the full security model.
For imported-service Docker deployments, mount a persistent volume at /data or choose an equivalent host-backed path before using /data/randal/workspace for durable work. Keep /app for image contents and scratch files that can be replaced with the container.
5. Postgres Persistence
Set DATABASE_URL or RANDAL_DATABASE_URL for imported-service deployments before enabling memory, chat, mesh, job audit, or checkpoint recovery.
- Provision Postgres with
pgvector,pg_trgm, andpgcrypto. - Run
randal db migratebefore serving production traffic. - Set
memory.store: postgresin the Randal config.
6. Programmatic Usage (Advanced)
For full programmatic control, override the CMD to run your own entry point:
FROM ghcr.io/hassion-studio/randal:latest
COPY randal.config.yaml /app/randal.config.yaml
COPY index.ts /app/index.ts
CMD ["bun", "run", "/app/index.ts"]
import { createRandal } from "@randal/harness";
const randal = await createRandal({
configPath: "./randal.config.yaml",
});
See examples/imported-service/ for a complete working example.
๐๏ธ Postgres Setup
Postgres with pgvector is the durable source of truth for memory, chat, graph/DAG recall, mesh registry, jobs, checkpoints, annotations, and audit history.
๐ฆ Local Install
randal db start
randal db migrate
randal db status --write-check
The local compose file uses pgvector/pgvector:pg16 and preserves the Docker volume between runs.
โ๏ธ Configuration
memory:
store: postgres
index: memory-my-agent
database:
profile: generic
url: "${DATABASE_URL:-postgres://randal:randal@127.0.0.1:5432/randal}"
ssl: disable
migrationMode: validate-only
For Railway and other managed providers, set ssl: require and provide DATABASE_URL or RANDAL_DATABASE_URL as a secret. Logs and manifests redact database credentials.
๐ค Cross-Agent Shared State
For multiple agents in a posse, use shared Postgres plus explicit tenant/project/instance identity. Agent-specific memory scopes and shared recall are represented as rows rather than separate search services.
๐ Production Considerations
- Require
pgvector,pg_trgm, andpgcrypto. - Run migrations before serving production traffic.
- Set
RANDAL_REQUIRE_MEMORY=true;/healthreturns 503 if Postgres source-of-truth readiness fails. - Use
randal db dump/restorefor full database movement andrandal db scoped-export/scoped-import/scoped-copyfor project/org-scoped movement.
๐ Custom Relay Setup
The Randal Console is built in. If you want Randal inside another product, deploy a small custom relay next to that product and connect it to the gateway session API.
Minimum relay environment:
RANDAL_GATEWAY_URL=https://your-randal-service.example
RANDAL_API_TOKEN=strong-token-from-randal-config
Minimum gateway config:
gateway:
channels:
- type: http
port: 7600
auth: "${RANDAL_API_TOKEN}"
Relay flow:
- Verify the external product webhook.
- Map the external thread ID to a Randal session ID.
- Call
POST /api/sessions. - Call
POST /api/sessions/:id/messagesfor inbound user text. - Subscribe to
GET /api/sessions/:id/eventsand render output back to the product.
See Custom Relay Guide for meta-code and the reusable builder prompt.
๐ Environment Variables Reference
| Variable | Used By | Description |
|---|---|---|
RANDAL_API_TOKEN | ๐ก Gateway HTTP auth | Bearer token for API authentication. |
DATABASE_URL / RANDAL_DATABASE_URL | ๐ง Postgres source-of-truth | Managed or local Postgres URL. Keep secret; Randal redacts it in diagnostics. |
RANDAL_REQUIRE_MEMORY | ๐ง Memory/health | Set to true for hosted deployments so startup and /health fail closed when Postgres is unavailable. |
RANDAL_STATE_DIR | ๐พ Filesystem persistence | Persistent state root for Railway/Docker workspaces, caches, connector overlays, scheduler files, and control-plane local state. Postgres owns jobs, memory, chat, mesh, checkpoints, and audit rows. Recommended /data/randal. |
OPENROUTER_API_KEY | ๐ค Agent / Embedder | The only LLM provider credential โ every model routes through OpenRouter. |
Native runner authentication update
Randal deployments no longer require Randal native runner for core operation. Prefer randal serve with the native runner and configure model access through OPENROUTER_API_KEY or a runner.providerAuth.credentials.openrouter secret reference โ OpenRouter is the only supported provider, and embeddings ride the same credential (or the metering proxy in managed mode). Randal native runner auth files and RANDAL_RUNTIME_AUTH_JSON are legacy/debug compatibility only. MCP connector credentials such as Notion OAuth should be stored under Randal connector auth secret references; runner events and tool logs must contain only those references, never raw tokens.