Skip to main content
Getting Curia running takes one command and about five minutes. pnpm run setup generates your secrets, starts the database and app in Docker, and runs migrations — the only thing you provide is an Anthropic API key. Once it’s running, you finish configuration inside the app and add optional capabilities (email, search, Signal) whenever you need them.

Prerequisites

Before you start, install the following:
  • Node.js 24 or later — verify with node --version
  • Docker and Docker Compose — Postgres and the app both run in Docker; install Docker Desktop or the standalone CLI
  • pnpm — install with npm install -g pnpm
Setup also uses openssl to generate secrets. It ships by default on macOS and most Linux distributions, so you likely already have it.

Quick start

1

Clone the repository

git clone https://github.com/josephfung/curia.git
cd curia
2

Run setup

pnpm run setup
This single command does everything needed to get Curia running:
  • Checks that Docker, Node, and pnpm are available
  • Generates the database password and the vault master key (SECRET_ENCRYPTION_KEY) and writes them to .env
  • Prompts you for an Anthropic API key — get one at console.anthropic.com
  • Starts Postgres, waits for it to be healthy, and runs database migrations
  • Seeds the encrypted vault with the generated API token, the login secret, and your Anthropic key (these are never written to .env)
  • Starts the full Curia stack in Docker
The Anthropic key is the only value you supply. Everything else is generated for you. Secrets land in the encrypted vault; only the four bootstrap values needed to reach and unlock it stay in .env.
3

Save your bootstrap secret

When setup finishes, it prints a summary box with your login URL and a bootstrap secret:
╔══════════════════════════════════════════════════════════════════════╗
║   Curia is running.                                                    ║
║   Open:    http://localhost:3000                                       ║
║   Bootstrap secret (save this to a password manager):                  ║
║   a1b2c3d4...                                                          ║
╚══════════════════════════════════════════════════════════════════════╝
Save the bootstrap secret to a password manager now. You’ll use it to log in, and it won’t be shown again.
4

Log in and finish in the app

Open http://localhost:3000 and enter your bootstrap secret to create your account.Once you’re in, the setup wizard at /setup runs automatically. It’s a short five-step form: your name (the CEO Curia works for), the assistant’s identity, baseline tone, decision-making posture, and a review screen. Submitting the last step writes everything through, restarts external channels if needed, and drops you directly into the chat view at /chat.At /chat, Curia greets you, asks what takes up most of your time, and walks through working hours and a recommended debrief cadence — this is the setup-wizard specialist agent running its first-conversation flow. Reply to it the same way you’d talk to any other agent; the preferences it captures get appended to the same office identity the form wizard wrote. The whole personalization step takes a few minutes and makes every subsequent interaction noticeably more useful.
Checkpoint: Curia is running on Anthropic and Postgres. Agents are live, the web app is working, and the assistant knows enough about you to be useful. Add email, semantic search, or Signal below whenever you need them.
If .env already exists, pnpm run setup won’t overwrite it. Instead it offers a short menu:
  • Start the stack — bring the containers back up (docker compose up -d)
  • Resume setup — re-run database migrations and startup against your existing .env, useful if a previous run didn’t finish
  • Full reset — regenerate all secrets. This invalidates active sessions and requires typing yes to confirm
Your secrets are never silently regenerated, because rotating them would invalidate live sessions.

Full setup

The quick start gives you a running instance on Anthropic and Postgres. The capabilities below are optional and independent — add them in any order, only when you need them. Each one follows the same pattern: seed the relevant credential into the encrypted vault, then recreate the Curia container to pick up the change. To seed a secret, set it transiently on the command line and run the seed script — the value is read from the environment, encrypted, and stored:
ANTHROPIC_API_KEY=sk-ant-... pnpm run seed-vault
docker compose up -d --force-recreate curia

Email (Nylas)

Curia uses Nylas as its email layer — a unified API that handles IMAP and SMTP complexity and provides a consistent interface across Gmail, Outlook, and other providers.
1

Create a Nylas account

Sign up at app.nylas.com. The free tier is sufficient for development.
2

Create an application

In the Nylas dashboard, create a new application and choose Email as the product. Once created, copy your API key — this is your NYLAS_API_KEY.
3

Connect an email account

In your application, go to Grants and add a new grant. This connects an email account (Gmail, Outlook, and others) to your Nylas application via OAuth. Use the address you want Curia to read and send from.After completing the OAuth flow, copy the Grant ID that appears in your dashboard — this is your NYLAS_GRANT_ID.
Use a dedicated email account rather than your primary inbox for development. Curia reads and processes all incoming messages.
4

Seed the Nylas credentials into the vault

Seed all three values at once. All three must be present for the email channel to activate:
NYLAS_API_KEY=nyk_v0_... \
NYLAS_GRANT_ID=<grant-id-from-dashboard> \
[email protected] \
pnpm run seed-vault
They are stored under the vault keys nylas_api_key, nylas_grant_id, and nylas_self_email. nylas_self_email is the address Curia reads and sends from — it filters self-sent messages and signs outbound emails.
5

Apply the change

docker compose up -d --force-recreate curia
The email channel activates automatically when all three Nylas variables are present.

Semantic search and entity memory (OpenAI)

OpenAI’s text-embedding-3-small model powers entity memory and semantic search in the knowledge graph. Without it, knowledge graph lookups are exact-match only. Get an API key from platform.openai.com and seed it into the vault (stored as openai_api_key):
OPENAI_API_KEY=sk-... pnpm run seed-vault
Then recreate the container to apply it:
docker compose up -d --force-recreate curia

Web research (Tavily)

Tavily powers the web-search skill, which lets agents research topics and look up current information during tasks.
1

Get a Tavily API key

Sign up at tavily.com and copy your API key from the dashboard.
2

Seed the key into the vault

TAVILY_API_KEY=tvly-... pnpm run seed-vault
It is stored under the vault key tavily_api_key.
3

Enable the web-search skill

web-search declares tavily_api_key in its install.requires_secrets, so it stays disabled until the key is in the vault. Once seeded, enable the skill from the Settings registry in the console, then recreate the container:
docker compose up -d --force-recreate curia
See Managing skills, agents, and channels for the registry workflow.

Signal messaging

Signal messaging runs via signal-cli. Docker Compose manages the socket path automatically — the only thing you need to provide is your phone number.
1

Bootstrap signal-cli

Signal requires registering a phone number with signal-cli and seeding the signal-data Docker volume with the resulting credentials. Refer to your deployment documentation for the bootstrap procedure.
2

Seed your phone number into the vault

Seed your E.164-formatted phone number — the number you registered via signal-cli register and signal-cli verify:
SIGNAL_PHONE_NUMBER=+12223334444 pnpm run seed-vault
It is stored under the vault key signal_phone_number. Do not set SIGNAL_SOCKET_PATH — the deployment layer manages this automatically.
3

Apply the change

docker compose up -d --force-recreate curia
The Signal channel activates when signal_phone_number is in the vault and the signal-data volume is populated.

Troubleshooting

This is expected on a second run. pnpm run setup never overwrites an existing .env — instead it offers a menu to start the stack, resume setup, or do a full reset. Full reset is the only option that regenerates secrets, and it asks for confirmation first.
Confirm the container is running:
docker compose ps
The database password is generated during pnpm run setup and shared with the container automatically through .env, so the credentials match by default. If you’ve edited .env by hand, make sure DATABASE_URL is still consistent with DB_USER and DB_PASSWORD.
All three Nylas secrets must be in the vault: nylas_api_key, nylas_grant_id, and nylas_self_email. If any is missing, the channel disables itself at startup. Confirm what is seeded with pnpm run seed-vault (it verifies and lists keys), then check the startup logs for a “email channel disabled” or “email adapter disabled” message.Remember to recreate the container after seeding: docker compose up -d --force-recreate curia.
Node installed via nvm or fnm bundles its own CA store and doesn’t trust macOS system certificates. Export your system certs and point Node at them:
security find-certificate -a -p /System/Library/Keychains/SystemRootCertificates.keychain > ~/.config/curia/macos-ca-certs.pem
Then uncomment this line in .env and recreate the container:
NODE_EXTRA_CA_CERTS=/Users/yourname/.config/curia/macos-ca-certs.pem

Next steps

Configure your instance

Set up multiple email accounts, tune the autonomy engine, and customize security rules.

Core concepts

Understand how agents, skills, memory, and channels fit together.