Skip to main content
The fastest path to a running server with seeded data and a working API key. The Compose stack runs Postgres (with pgvector) and Redis, builds the server from source, and uses a one-shot init service to migrate the database and provision the runtime role before the server starts. Everything runs on stock Postgres — the schema, row-level-security policies, and role grants have no cloud-specific dependencies.

Prerequisites

  • Docker with Compose v2
  • Node.js 22 or later and pnpm 10 (corepack enable)
  • A psql client (optional, for sanity checks)

Quickstart

1

Clone and configure

git clone https://github.com/B3dmar/3ngram && cd 3ngram
cp .env.example .env
The defaults work as-is for a local Docker stack.
2

Initialize the database

docker compose --profile init up migrations
The one-shot migrations service migrates the schema, provisions the runtime role, and sets its password, then exits.
3

Start the stack

docker compose up -d postgres redis server
Postgres listens on 54320 and Redis on 63790 — non-default ports so they never collide with system services. The server exposes port 3000 with a /health probe.
4

Seed data and mint an API key

set -a; source .env; set +a
pnpm install --frozen-lockfile
pnpm seed
The seed loads an anonymized golden set (158 memories, including supersession chains) under a dev user and prints a demo API key once, in the form 3ng_<prefix>_<secret>. Re-running is idempotent: existing rows are skipped, never deleted, and the key is not re-issued.
5

Call the API

curl -H "X-API-Key: 3ng_<prefix>_<secret>" \
  "http://localhost:3000/api/v1/briefing?kind=all"
A 200 response means the stack is up end to end.

Configuration notes

  • pnpm seed runs on the host, not in a container, because it loads committed fixtures. It connects to Postgres on the published port 54320.
  • Changing the runtime password: APP_USER_PASSWORD defaults to app-user-dev. Override it in .env for anything beyond a throwaway local stack — but change it in both APP_USER_PASSWORD and the password segment of DATABASE_URL. Compose derives the server’s connection string from the former, while the host-run seed reads the latter literally; updating one alone causes an auth error.
  • No phone-home by default: leaving SENTRY_DSN empty fully disables error tracking and tracing. This is the self-host default.
  • Append-only below the application: the runtime database role has no DELETE grant anywhere in the memory domain. Destructive operations are impossible at the grant level, not just discouraged in code.
  • Worker container: background consolidation jobs are not part of the Compose stack yet; the server and data plane run without them.
  • Seeded embeddings are empty: the seed does not call any external API, so seeded memories have no embeddings until a write path with an embedding provider runs. Full-text and recency search work immediately.

Resetting

docker compose down -v
Dropping the volume is the supported delete path — runtime-role deletes do not exist by design. Then repeat from the initialize step.