Self-Hosting

Run Reliant Locally

Reliant has no embedded/library mode — it is always a client talking to an HTTP server. Running it "locally, off the network" means hosting the api/ service yourself and pointing the SDK's baseUrl at it. Nothing leaves your machine.


How local mode works

The SDK (reliant-js) only speaks HTTP. The whole engine — schema validation (AJV), intelligent retry, provider fallback, Guard, Contracts, Agents — lives in the api/Fastify service. To run without the hosted API you start that service on localhost and set baseUrl accordingly.

Set DATA_BACKEND=postgres and the api runs entirely against one local PostgreSQL database — no Supabase project, no SUPABASE_* variables. Every feature route runs through Prisma. This is the on-prem / air-gapped path.

Scope: only the api/ service runs fully local on Postgres. The web/ dashboard still expects Supabase for auth and a few server routes — if you only need the API and SDK, you can ignore web/.

Prerequisites

RequirementNotes
Node.js≥ 22requiredThe api targets Node 22.
PostgreSQL14+requiredLocal Docker container or a native install. One database is enough.
LLM provider keystringrequiredAn Anthropic / OpenAI / Gemini / Groq / Mistral key. There is no env-var fallback — it is read from the database (step 5).

1
Configure api/.env
Copy api/.env.example to api/.env and set the values below. With DATA_BACKEND=postgres no Supabase variables are needed —lib/supabase.ts is null-safe and the server boots without them.
env
DATABASE_URL="postgresql://user:pass@localhost:5432/reliant?schema=public" DATA_BACKEND=postgres # 32+ characters each PROVIDER_KEY_ENCRYPTION_SECRET="a-string-of-32-plus-characters-xxxxxxxx" INTERNAL_API_SECRET="another-32-plus-character-string-xxxxxxx" PORT=3100 HOST=127.0.0.1 NODE_ENV=development
2
Install, migrate, run
npm run dev does not run migrations (only npm start does), so apply them once by hand first.
bash
cd api npm install npx prisma migrate deploy # applies all migrations, incl. unify_data_layer npm run dev # tsx watch — serves on http://localhost:3100 curl http://localhost:3100/health # { "status": "ok", "version": "1.0.1", ... }
3
Create the account row
Pick a fixed UUID for your local user. An accounts row is required —provider_keys.user_id has a foreign key to it, and the plan is read from here.
sql
INSERT INTO accounts (id, plan, created_at, updated_at) VALUES ('11111111-1111-1111-1111-111111111111', 'pro', now(), now());
4
Create a project and API key
Use the internal route — it stores the key hashed and returns the raw key once. Pass the same UUID asuser_id so /execute can resolve the project owner.
bash
curl -X POST http://localhost:3100/projects \ -H "Content-Type: application/json" \ -H "x-internal-secret: <INTERNAL_API_SECRET from .env>" \ -d '{"name":"Local","user_id":"11111111-1111-1111-1111-111111111111"}'
json — response
{ "id": "clxxxxxxxxxxxxxxxxxxxxxxxx", "name": "Local", "api_key": "rel_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "message": "Store your API key securely — it will not be shown again." }
Do not use npm run db:seed for this. The seed script stores the API key in plain text, but auth looks it up by SHA-256 hash, so a seeded project will not authenticate. Always mint keys through POST /projects.
5
Add a provider key
/execute reads the LLM key from the provider_keystable (there is no env-var fallback). For local use you may store it in plain text —decrypt() returns the value unchanged when it is not iniv:tag:ciphertext form. To store it encrypted, runencrypt() from api/src/lib/crypto.ts with the samePROVIDER_KEY_ENCRYPTION_SECRET.
sql
INSERT INTO provider_keys (id, user_id, provider, encrypted_key, is_active, created_at, updated_at) VALUES ( gen_random_uuid(), '11111111-1111-1111-1111-111111111111', 'anthropic', 'sk-ant-your-real-key', true, now(), now() );
6
Create a schema and execute
From here everything is the normal API, just against localhost. Authenticate feature routes with the X-Reliant-Key header.
bash
# schema curl -X POST http://localhost:3100/schemas \ -H "X-Reliant-Key: rel_..." -H "Content-Type: application/json" \ -d '{ "name": "Contact", "slug": "contact", "definition": { "type": "object", "required": ["name", "email"], "properties": { "name": { "type": "string" }, "email": { "type": "string", "format": "email" } }, "additionalProperties": false } }' # execute curl -X POST http://localhost:3100/execute \ -H "X-Reliant-Key: rel_..." -H "Content-Type: application/json" \ -d '{ "prompt": "John Smith, john@acme.com", "schema_id": "<id from previous call>", "provider": "anthropic", "model": "claude-sonnet-4-20250514", "user_id": "11111111-1111-1111-1111-111111111111" }'
7
Point the SDK at your local server
The only change from the hosted setup is baseUrl. Without it the SDK defaults to https://reliant.api.br.
typescript
import { Reliant } from 'reliant-js' const reliant = new Reliant({ apiKey: 'rel_...', userId: '11111111-1111-1111-1111-111111111111', baseUrl: 'http://localhost:3100', // <- local, off the network }) const result = await reliant.execute({ prompt: 'John Smith, john@acme.com', schemaId: '<id>', provider: 'anthropic', model: 'claude-sonnet-4-20250514', })

Environment variables

VariableDescription
DATABASE_URLstringrequiredPostgreSQL connection string used by Prisma.
DATA_BACKENDenumoptionalsupabase (default, cloud) or postgres (fully local). Set to postgres for self-hosting.
PROVIDER_KEY_ENCRYPTION_SECRETstringrequired32+ chars. Used to encrypt/decrypt stored provider keys.
INTERNAL_API_SECRETstringrequired32+ chars. Guards POST /projects and other internal routes.
PORTnumberoptionalDefault 3100.
HOSTstringoptionalDefault 0.0.0.0. Use 127.0.0.1 to bind loopback only.
NODE_ENVstringoptionaldevelopment returns full error messages; production hides them.
RATE_LIMIT_MAXnumberoptionalRequests per window per key/IP. Default 100.
RATE_LIMIT_WINDOW_MSnumberoptionalRate-limit window in ms. Default 60000.
SUPABASE_URL / SUPABASE_SERVICE_ROLE_KEYstringnot usedIgnored when DATA_BACKEND=postgres.

Notes

Usage limits. If the Plan table is empty the monthly cap falls back to 1000 executions — enough for local development. Populating Plan enforces the real per-plan limits (free is 7/month).

Agents. The Agents feature works identically in local mode — same server, same reliant.agents / reliant.agentRun() calls against http://localhost:3100. Its migrations are included in prisma migrate deploy.

Encryption. crypto.ts currently derives its key by truncating/padding the secret. It is fine for local development but should be hardened before any production or enterprise deployment.