Quickstart
Self-host the full NovaCortex stack in about 5 minutes with Docker Compose, then mint your admin token from a one-time bootstrap code.
This is the supported ~5-minute self-host path. It brings up the full stack (SurrealDB, Qdrant, Redis, the API, and the Web UI) from pinned images, then walks you through minting your first admin token.
Requirements: Docker and Docker Compose. Works on Unraid and any Docker host. See Installation for details.
1. Clone the repo
git clone https://github.com/Nova-Cognitive-Systems/novacortex.git
cd novacortex
2. Generate secrets
scripts/gen-env.sh writes a .env with strong random secrets (chmod 600). It
refuses to overwrite an existing .env, so it is safe to run.
./scripts/gen-env.sh
This generates SURREALDB_PASS, REDIS_PASSWORD, NEXTAUTH_SECRET, and
JWT_SECRET, plus sensible defaults: APPDATA=./data, API_PORT=3001,
WEB_PORT=3000, and a pinned NOVACORTEX_VERSION. The self-host compose fails
fast if the required secrets are missing, so don't skip this step.
Optionally edit .env before starting:
- Data location.
APPDATA=./datakeeps data next to the repo. On Unraid, setAPPDATA=/mnt/user/appdata/novacortex. - Semantic search. Set
OPENAI_API_KEYto enable embeddings. Leave it unset to run with local substring search only. See Configuration.
3. Start the stack
docker compose -f docker-compose.unraid.yml up -d
This pulls pinned multi-arch images from GHCR
(ghcr.io/nova-cognitive-systems/novacortex-api and -web) and starts all five
services. The API waits for SurrealDB, Qdrant, and Redis to report healthy before
it boots.
4. Get the bootstrap code
On first boot the API has no tokens, so it prints a one-time bootstrap code to
its logs. The code starts with nc_boot_ and is valid for 1 hour.
docker logs novacortex-api 2>&1 | grep -A1 "Bootstrap code"
You'll see something like:
═══════════════════════════════════════════════════════════
NovaCortex Setup Required
Bootstrap code: nc_boot_xxxxxxxxxxxxxxxxxxxxxxxx
Valid for 1 hour
Exchange via:
novacortex setup --url <URL> --code nc_boot_xxxxxxxxxxxxxxxxxxxxxxxx
Or visit the /login page in the web UI
═══════════════════════════════════════════════════════════
5. Mint your admin token
Exchange the bootstrap code for an admin token (scopes admin:*,
tokens:read, tokens:write). Use whichever method you prefer — both hit the same
POST /setup/exchange endpoint.
Option A — Web UI
Open the Web UI at http://localhost:3000 (or http://<host-ip>:3000), go to
the login page, and paste the nc_boot_… code to mint your admin token. You're in.
Option B — CLI
novacortex setup --url http://localhost:3001 --code nc_boot_xxxxxxxxxxxxxxxxxxxxxxxx
This stores the resulting token in a local profile (--profile <name>, default
default). Add --json for machine-readable output.
Option C — REST
curl -X POST http://localhost:3001/setup/exchange \
-H 'Content-Type: application/json' \
-d '{"code":"nc_boot_xxxxxxxxxxxxxxxxxxxxxxxx"}'
Response:
{
"token": "<your-admin-token>",
"whoami": {
"kind": "selfhosted",
"name": "...",
"scopes": ["admin:*", "tokens:read", "tokens:write"],
"server": { "version": "1.2.1", "mode": "selfhosted" }
}
}
This endpoint is rate-limited to 5 requests per minute. Store the returned token
securely — it is the credential for all subsequent API calls.
6. Verify
The REST API is at http://localhost:3001, with interactive Swagger docs at
/docs (the root / redirects there).
# Stack health (also reports namespace stats)
curl http://localhost:3001/health
# Open the API docs in a browser
open http://localhost:3001/docs
7. Store your first memory
With your admin token:
curl -X POST http://localhost:3001/memories \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"content":"The user prefers dark mode","memoryType":"semantic","namespace":"agent"}'
Or from the TypeScript SDK:
import { NovaCortexClient } from '@novacortex/sdk';
const nc = new NovaCortexClient({ baseUrl: 'http://localhost:3001', token });
await nc.memories.create({
content: 'The user prefers dark mode',
memoryType: 'semantic',
namespace: 'agent',
});
const { data, mode } = await nc.search({
query: 'what does the user like?',
namespace: 'agent',
});
Where data lives
All state is bind-mounted under ${APPDATA} (surrealdb/, qdrant/, redis/),
so it survives container and image rebuilds and is covered by Unraid appdata
backups.
Next steps
- Configuration — tune backends, embeddings, the background processor, CORS, and licensing.
- Authentication — token scopes and minting scoped tokens beyond the bootstrap admin token.
- Installation — compose variants, ports, volumes, and upgrades.