Tudor is a pi-native standalone application. The backend owns SQLite state, the dashboard, memory, SkillFactory, fleet orchestration, integrations, and vault. Each Tudor agent is intended to run as a separate pi --mode rpc process with its own session directory.
- Node.js
>=22 piavailable onPATH, or setPI_BIN- a workspace directory for agent tool execution
- disk encryption or an encrypted home directory for production vault storage
- no plaintext LLM/provider secrets in
.env, logs, prompts, artifacts, or dashboard HTML
Copy .env.example and keep secrets out of it:
cp .env.example .envImportant variables:
TUDOR_HOME=.tudor
TUDOR_API_PORT=3000
TUDOR_WORKSPACE_DIR=.
TUDOR_AGENT_HEARTBEAT_INTERVAL_SECONDS=10
TUDOR_VAULT_KEY_PATH=.tudor/vault/.vault-key
PI_BIN=pi
PI_DEFAULT_MODEL=openai/gpt-4.1
For CI or smoke tests, set a test vault key explicitly:
export TUDOR_VAULT_KEY="$(openssl rand -base64 32)"In production, prefer a protected .vault-key file or platform secret injection. Do not commit TUDOR_VAULT_KEY.
npm ci --ignore-scripts
npm run check
npm test
npm audit --jsonExpected results:
- TypeScript typecheck passes
- deterministic test suite passes
- no npm audit vulnerabilities
- no lingering Tudor server or
pi --mode rpcprocesses after tests
Development:
npm startProduction build first:
npm run build
node dist/src/server/index.jsHealth check:
curl http://127.0.0.1:3000/health
curl http://127.0.0.1:3000/fleet
curl http://127.0.0.1:3000/dashboard/summaryCreate a vault secret through the API or dashboard, then bind it to a Tudor pi agent target:
curl -X POST http://127.0.0.1:3000/vault/secrets \
-H 'content-type: application/json' \
-d '{"label":"OpenAI API key","service":"openai","scope":"global","plaintext":"..."}'
curl -X POST http://127.0.0.1:3000/vault/bindings \
-H 'content-type: application/json' \
-d '{"secretId":"openai-api-key","targetType":"pi-agent","targetId":"planner","envName":"OPENAI_API_KEY"}'Start the agent with explicit vault injection:
curl -X POST http://127.0.0.1:3000/fleet/planner/start \
-H 'content-type: application/json' \
-d '{"injectVaultEnv":true}'The runtime env resolver injects only bound secrets into the new pi process environment. Tudor records a checksummed vault_runtime_injections row and never returns the plaintext through the runtime env API.
Production hardening is documented in:
docs/production-hardening.mddocs/operator-runbook.md
Use the production helper scripts:
npm run production:check
npm run production:backup
npm run production:restore:dry-runOperator CLI equivalents are available after build/link or through tsx:
npx tsx src/cli/tudor.ts production:check
npx tsx src/cli/tudor.ts backup --home "${TUDOR_HOME}" --backupDir /secure/path/backups
npx tsx src/cli/tudor.ts restore:dry-run --home "${TUDOR_HOME}" --backupFile /secure/path/backups/tudor-home-YYYYmmddTHHMMZ.tar.gzThe backup script excludes ${TUDOR_HOME}/vault/.vault-key by default. Restore the vault key separately through Keychain, platform secret injection, or a protected secret store.
For production, run Tudor under a supervisor such as systemd, launchd, Docker, or a small wrapper script.
Minimum systemd checklist:
- working directory is the Tudor deployment directory
- environment file does not contain plaintext provider secrets
TUDOR_HOMEpoints to a protected directory- restart policy is
on-failure - logs are rotated
- dashboard/API is behind a local reverse proxy or authenticated edge
- vault key file is
0600 - workspace directory is isolated per deployment
piis installed and reachableTUDOR_HOMEis writableTUDOR_VAULT_KEY_PATHparent directory is protected- no plaintext secrets in
.env, logs, memory, artifacts, or dashboard output - deterministic tests pass before enabling real pi execution
- real pi execution remains opt-in through Tudor delivery/workflow APIs
- every agent is a separate
piprocess with its own session directory - review queue and autonomy approvals remain explicit
- unsupported integration channels/providers fail closed