Dandori is a multi-tenant, agent-first project management control plane.
This repository contains the Phase 1 domain/store/API+MCP vertical slice for the Rust-first rewrite.
just bootstrapThis installs core tooling and activates git hooks via lefthook install when available.
Run the canonical gate locally:
just ciRun the phase-specific gate directly:
just phase1-gateRun explicit database migrations:
just db-migrateAPI and MCP now run in strict fail-closed mode and require OIDC/JWKS configuration.
Required environment variables:
DANDORI_OIDC_ISSUERDANDORI_OIDC_AUDIENCE- exactly one of:
DANDORI_OIDC_JWKS_PATHDANDORI_OIDC_JWKS_URL
- optional strict algorithm allowlist:
DANDORI_OIDC_ALLOWED_ALGS(comma-separated, e.g.RS256,ES256,EdDSA)
- optional JWKS rotation tuning:
DANDORI_OIDC_JWKS_REFRESH_INTERVAL_MILLISDANDORI_OIDC_JWKS_REFRESH_TIMEOUT_MILLISDANDORI_OIDC_JWKS_REFRESH_MAX_BACKOFF_MILLIS
No fallback dev secrets are enabled.
- API, MCP, and worker binaries default to
run_migrations = false. - To explicitly allow startup migrations for controlled local workflows, set:
DANDORI_RUN_MIGRATIONS=true
- API/MCP/worker/migrate all require
DANDORI_DATABASE_URLexplicitly.
dandori-mcp is a long-running stdio JSON-RPC server.
Example request flow:
- Send
initialize - Send
tools/list - Send
tools/call(issue.create,issue.get) with:token(bearer JWT)arguments(tool payload)
Phase 1 integration tests require Docker (for ephemeral PostgreSQL testcontainers).
The worker routes typed outbox events (EventType::IssueCreatedV1) through a
pluggable publisher. The default startup policy is fail-closed: the worker
refuses to run without an explicit publisher configuration.
DANDORI_OUTBOX_PUBLISH_URL(required for production)- when set: worker publishes envelopes to this URL via the hardened HTTP publisher (explicit connect + request timeouts, bounded connection pool, and an in-process circuit breaker).
DANDORI_OUTBOX_ALLOW_NOOP_PUBLISHER(dev escape hatch only)- set to
1ortrueto explicitly opt in to the no-op publisher whenDANDORI_OUTBOX_PUBLISH_URLis unset. The worker logs a warning on every start. Never set this in production.
- set to
- Transport tuning (optional):
DANDORI_WORKER_HTTP_CONNECT_TIMEOUT_MS(default2000)DANDORI_WORKER_HTTP_REQUEST_TIMEOUT_MS(default10000)DANDORI_WORKER_PUBLISH_CONCURRENCY(default8)DANDORI_WORKER_RETRY_JITTER_MS(default2000)DANDORI_WORKER_CIRCUIT_FAILURE_THRESHOLD(default10)DANDORI_WORKER_CIRCUIT_COOLDOWN_SECONDS(default30)
- Dynamic partition leasing (replaces the former static workspace list):
DANDORI_WORKER_INSTANCE_ID(optional UUID; strongly recommended for stable lease ownership across restarts)DANDORI_WORKER_PARTITION_BATCH(default64)DANDORI_WORKER_PARTITION_LEASE_SECONDS(default60)
Workers discover workspaces directly from the database and acquire leases on
worker_partition_lease rows atomically (INSERT … ON CONFLICT DO UPDATE WHERE leased_until <= now). Multiple workers can run in parallel without any
external coordinator and without a static sharding configuration.
Outbox publish failures are classified into transient vs terminal. Transient errors (5xx, network, breaker open) honour the retry budget and backoff with jitter. Terminal errors (4xx, unknown event type, serialization) dead-letter immediately so the DLQ stays meaningful and the retry queue does not bloat.
bin/binaries (dandori-api,dandori-mcp,dandori-worker,dandori-migrate)crates/library crates for domain, contracts, policy, store, orchestration, and app servicesdocs/adr/architecture decision recordsfrontend/frontend scaffold placeholder