Installation
Install DurinDoor with npm, from source, or Docker, and set DATA_DIR and JWT_SECRET.
Choose npm, a source checkout, or Docker. Native installs use Node.js 20.20.2 and npm 10.8.2. Docker includes Node.js. You need disk for persistent data, a browser for the dashboard and OAuth, and access to your upstream providers.
Before a native start, set a strong INITIAL_PASSWORD and a persistent JWT_SECRET in the process environment. For a source install, copy .env.example to .env and replace its CHANGE_ME values. Keep the same signing secrets on later starts. A source deployment without an explicit DATA_DIR also needs API_KEY_SECRET to create client keys.
export JWT_SECRET="$(openssl rand -hex 32)"
export API_KEY_SECRET="$(openssl rand -hex 32)"
export INITIAL_PASSWORD="CHANGE_ME_STRONG_PASSWORD"npm install -g durindoor
durindoor --host 127.0.0.1The CLI binary name is durindoor. Default port is 20128. Default bind is 0.0.0.0. Use --host 127.0.0.1 for loopback only, --port to change the port, --no-browser to skip opening the dashboard.
Data directory
DATA_DIR is the persistent root. Set it at deploy time. If it is unset, the process uses ~/.9router on macOS and Linux, or %APPDATA%\9router on Windows. The directory is created if missing. A Unix-style path on Windows is ignored and the default is used.
Layout:
DATA_DIR/
├── db/
│ ├── data.sqlite
│ └── backups/
├── auth/
├── logs/
├── mitm/
└── runtime/SQLite lives at DATA_DIR/db/data.sqlite. Pre-upgrade copies go in DATA_DIR/db/backups.
Environment
Copy .env.example and fill every CHANGE_ME value before a production boot. Environment variables is the full list.
| Variable | Default | Role |
|---|---|---|
PORT | 20128 | HTTP listen port |
HOSTNAME | 127.0.0.1 for npm start; CLI default bind is 0.0.0.0 | Listen address. Do not set 0.0.0.0 on a public network without a proxy in front |
DATA_DIR | ~/.9router | Persistent data |
JWT_SECRET | none (legacy DATA_DIR/jwt-secret still accepted) | Dashboard session signing. Fresh installs fail closed without env or file |
INITIAL_PASSWORD | 123456 | First dashboard password when none is stored |
API_KEY_SECRET | minted under DATA_DIR when DATA_DIR is set | HMAC secret for API key checksums. Set it when keys must survive redeploys |
BASE_URL | local URL | Server-side URL for callbacks |
NEXT_PUBLIC_BASE_URL | local URL | Browser-visible URL |
JWT_SECRET is required. DurinDoor does not write a new DATA_DIR/jwt-secret file. An existing file from an older install is reused with a warning. Generate a value with openssl rand -hex 32.
Upgrading
Back up DATA_DIR first. Then follow Upgrading. Global npm installs update with npm update --global durindoor.
Migration from 9router
DurinDoor can reuse a 9router data directory. Stop the old process, back up that directory, start DurinDoor with the same DATA_DIR, then check providers, keys, combos, and usage in the dashboard. Identifiers the runtime still honors, and the optional cutover script, are in Migrating from 9router.
Check the process
curl http://localhost:20128/api/healthA JSON body { "ok": true } means the HTTP server is up. Open http://localhost:20128/dashboard and sign in with your initial password. Add a provider and create a client key, then list models:
curl http://localhost:20128/v1/models \
-H "Authorization: Bearer YOUR_DURINDOOR_API_KEY"The response is a model list. Send a first request to verify provider access. If the process cannot start, check for an occupied port and missing JWT_SECRET. If Docker cannot reach a local model server, use a host gateway or compose service name instead of container localhost.