- Replace all docker-compose.yml/docker-compose.monitoring.yml references with compose.yaml/compose.monitoring.yaml (modern Compose Spec naming) - Replace all `docker-compose` CLI commands with `docker compose` (plugin syntax) - Update Dockerfile stage descriptions: node:18-alpine → node:20.11-bookworm-slim, built-in node user → explicit nodeapp:1001 non-root user - Update image version references: postgres:14-alpine → postgres:14.12-alpine3.19, redis:7-alpine → redis:7.2-alpine3.19 - Externalize postgres credentials: hardcoded values → POSTGRES_USER/PASSWORD/DB env vars - Externalize Grafana admin password: hardcoded 'agentidp' → GF_ADMIN_PASSWORD env var - Add Docker Compose Variables section to environment-variables.md (POSTGRES_*, GF_ADMIN_PASSWORD) - Update local-development.md Step 3: cp .env.example .env, document POSTGRES_* purpose - Update quick-start.md: cp .env.example .env, use awk/sed for JWT key injection - Update 07-dev-setup.md: remove 'no .env.example' claim, reference cp .env.example - Update docker-compose.yml key file description in 04-codebase-structure.md - Update monitoring overlay launch commands across all docs (compose.yaml + compose.monitoring.yaml) - Update volume names to kebab-case: postgres_data → postgres-data, redis_data → redis-data - Fix compliance encryption-runbook: docker-compose restart agentidp → docker compose restart app All docs now consistent with compose.yaml in repo root. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
15 KiB
07 — Development Environment Setup
This guide takes you from a fresh machine to a running AgentIdP server with a passing smoke test. Estimated time: 15–20 minutes.
8.1 Prerequisites
Install all of these before proceeding.
| Prerequisite | Minimum version | Install link |
|---|---|---|
| Node.js | 18.x LTS | https://nodejs.org/en/download — use the LTS version |
| npm | 9.x (ships with Node.js 18) | Included with Node.js |
| Docker Desktop | Latest stable | https://docs.docker.com/get-docker/ |
| Git | 2.x | https://git-scm.com/downloads |
Verify your versions:
node --version # Should print v18.x.x or higher
npm --version # Should print 9.x or higher
docker --version # Should print Docker version 24.x or higher
git --version # Should print git version 2.x
8.2 Clone and Install
# Clone the repository
git clone https://github.com/sentryagent-ai/sentryagent-idp.git
cd sentryagent-idp
# Install Node.js dependencies
npm install
This installs all production dependencies (Express, pg, Redis, etc.) and development dependencies (TypeScript, Jest, ts-jest, eslint).
8.3 Environment Variables Setup
The server requires a .env file at the project root. Copy the template:
cp .env.example .env
The template includes all required variables with sensible local defaults. Edit .env to set your values. Key variables are documented below.
# ─────────────────────────────────────────────────────────────
# PostgreSQL — individual credentials for compose.yaml
# ─────────────────────────────────────────────────────────────
POSTGRES_USER=sentryagent
POSTGRES_PASSWORD=sentryagent
POSTGRES_DB=sentryagent_idp
# ─────────────────────────────────────────────────────────────
# PostgreSQL connection (application reads this directly)
# ─────────────────────────────────────────────────────────────
DATABASE_URL=postgresql://sentryagent:sentryagent@localhost:5432/sentryagent_idp
# ─────────────────────────────────────────────────────────────
# Redis connection
# ─────────────────────────────────────────────────────────────
REDIS_URL=redis://localhost:6379
# ─────────────────────────────────────────────────────────────
# HTTP server port
# ─────────────────────────────────────────────────────────────
PORT=3000
# ─────────────────────────────────────────────────────────────
# JWT RSA keys (generate these below)
# ─────────────────────────────────────────────────────────────
JWT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
# ─────────────────────────────────────────────────────────────
# CORS (optional — defaults to '*' which allows all origins)
# ─────────────────────────────────────────────────────────────
CORS_ORIGIN=*
# ─────────────────────────────────────────────────────────────
# Node environment
# ─────────────────────────────────────────────────────────────
NODE_ENV=development
# ─────────────────────────────────────────────────────────────
# OPA policy directory (optional — defaults to ./policies)
# ─────────────────────────────────────────────────────────────
# POLICY_DIR=/path/to/policies
# ─────────────────────────────────────────────────────────────
# HashiCorp Vault (optional — omit to use bcrypt mode)
# ─────────────────────────────────────────────────────────────
# VAULT_ADDR=http://127.0.0.1:8200
# VAULT_TOKEN=your-vault-token
# VAULT_MOUNT=secret
Complete environment variable reference:
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL |
Yes | — | PostgreSQL connection string. Format: postgresql://user:password@host:port/dbname |
REDIS_URL |
Yes | — | Redis connection URL. Format: redis://host:port[/db] |
PORT |
No | 3000 |
TCP port the HTTP server listens on |
JWT_PRIVATE_KEY |
Yes | — | PEM-encoded RSA private key (2048-bit minimum) for signing tokens |
JWT_PUBLIC_KEY |
Yes | — | PEM-encoded RSA public key (matching the private key above) for verifying tokens |
CORS_ORIGIN |
No | * |
CORS allowed origin. Use * for development, set to your dashboard domain in production |
NODE_ENV |
No | development |
Set to test to suppress Morgan HTTP logging during tests |
POLICY_DIR |
No | ./policies |
Absolute path to the directory containing authz.wasm or data/scopes.json |
VAULT_ADDR |
No | — | HashiCorp Vault server address (e.g. http://127.0.0.1:8200). When omitted, bcrypt mode is used |
VAULT_TOKEN |
No | — | Vault authentication token. Required when VAULT_ADDR is set |
VAULT_MOUNT |
No | secret |
Vault KV v2 mount path. Only used when Vault is configured |
Generating JWT keys:
# Generate RSA 2048-bit private key
openssl genrsa -out private.pem 2048
# Extract public key
openssl rsa -in private.pem -pubout -out public.pem
# Print private key as single-line for .env (replace newlines with \n)
awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' private.pem
# Print public key as single-line for .env
awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' public.pem
Paste the output (including the -----BEGIN/END----- lines) as the value for
JWT_PRIVATE_KEY and JWT_PUBLIC_KEY in your .env file, surrounded by double
quotes.
8.4 Docker Compose Startup and Health Checks
Docker Compose starts PostgreSQL 14 and Redis 7. The application reads the
DATABASE_URL and REDIS_URL from your .env file to connect to them.
# Start PostgreSQL and Redis in the background
docker compose up postgres redis -d
# Wait for health checks to pass (usually 10-15 seconds)
docker compose ps
Expected output when both services are healthy:
NAME STATUS PORTS
sentryagent-idp-postgres-1 Up (healthy) 0.0.0.0:5432->5432/tcp
sentryagent-idp-redis-1 Up (healthy) 0.0.0.0:6379->6379/tcp
Manual health check:
# Test PostgreSQL connection
docker exec sentryagent-idp-postgres-1 pg_isready -U sentryagent -d sentryagent_idp
# Expected: /var/run/postgresql:5432 - accepting connections
# Test Redis connection
docker exec sentryagent-idp-redis-1 redis-cli ping
# Expected: PONG
8.5 Database Migrations
Run the migration script to create all required tables:
npm run db:migrate
Expected output:
Running database migrations...
✓ Applied: 001_create_agents.sql
✓ Applied: 002_create_credentials.sql
✓ Applied: 003_create_audit_events.sql
✓ Applied: 004_create_tokens.sql
✓ Applied: 005_add_vault_path.sql
Migrations complete. 5 migration(s) applied.
Running npm run db:migrate a second time is safe — it skips already-applied migrations:
- Skipped (already applied): 001_create_agents.sql
...
Migrations complete. 0 migration(s) applied.
Migration internals:
The migration runner (scripts/migrate.ts) reads .sql files from src/db/migrations/
in alphabetical order, wraps each in a transaction, and records the filename in the
schema_migrations table. If a migration fails, the transaction rolls back and
the runner exits with code 1.
8.6 Start the Server
npm run dev
# Expected: SentryAgent.ai AgentIdP listening on port 3000
npm run dev uses ts-node to execute src/server.ts directly without compiling.
This is faster for development. For a production-style start, compile first:
npm run build
npm start
8.7 Smoke Test
Verify the server is working with these three curl commands.
1. Health check:
curl http://localhost:3000/health
Expected response (200 OK):
{
"status": "healthy",
"checks": {
"database": "healthy",
"redis": "healthy"
}
}
2. Register an agent:
First, you need a token to authenticate. But to get a token, you need credentials.
And to get credentials, you need an agent. The chicken-and-egg is resolved by the
fact that agent registration requires an agents:write scoped token — which means
you need to bootstrap the first agent another way.
For local development, temporarily test without auth by using the /api/v1 prefix
directly (the server accepts requests; OPA will enforce scope).
The easiest approach: generate a test token programmatically:
# Generate test keys
openssl genrsa -out /tmp/test_private.pem 2048 2>/dev/null
openssl rsa -in /tmp/test_private.pem -pubout -out /tmp/test_public.pem 2>/dev/null
# Set them in your environment temporarily
export JWT_PRIVATE_KEY="$(cat /tmp/test_private.pem)"
export JWT_PUBLIC_KEY="$(cat /tmp/test_public.pem)"
# Start server with these keys and use a tool or short Node.js script to mint a test token
3. Token endpoint with seeded credentials:
Once you have an agent with credentials (e.g. created via the API or seeded in development), issue a token:
curl -X POST http://localhost:3000/api/v1/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_AGENT_ID" \
-d "client_secret=sk_live_YOUR_SECRET" \
-d "scope=agents:read agents:write"
Expected response (200 OK):
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "agents:read agents:write"
}
8.8 Troubleshooting
Error: connection refused on PostgreSQL or Redis
Cause: Docker services not running or not yet healthy. Fix:
docker compose ps # Check status
docker compose up postgres redis -d # Start if not running
docker compose logs postgres # Check for startup errors
Error: DATABASE_URL environment variable is required
Cause: .env file missing or not being loaded.
Fix: Ensure .env exists at the project root. npm run dev loads it via dotenv.config() in src/server.ts.
Error: JWT_PRIVATE_KEY and JWT_PUBLIC_KEY environment variables are required
Cause: JWT keys not in .env, or newlines in the PEM keys are not properly escaped.
Fix: Ensure the keys are wrapped in double quotes and newlines are represented as \n. Use the awk command from section 8.3 to format them correctly.
Error: Cannot find module 'ts-node'
Cause: npm install was not run, or ran against a different Node.js version.
Fix:
node --version # Confirm Node.js 18+
rm -rf node_modules package-lock.json
npm install
Error: Cannot connect to Redis (during migration or server start)
Cause: Redis container not running or REDIS_URL is incorrect.
Fix:
docker exec sentryagent-idp-redis-1 redis-cli ping
# If container is not running:
docker compose up redis -d
Port 3000 already in use
Cause: Another process is listening on port 3000. Fix:
lsof -i :3000 # Find the process
kill <PID> # Kill it
# Or: set PORT=3001 in .env and restart
8.9 Running Tests Locally
# Run all tests (unit + integration)
npm test
# Run tests with coverage report
npm run test:unit -- --coverage
# Coverage report: coverage/lcov-report/index.html
# Run only unit tests
npm run test:unit
# Run only integration tests (requires running PostgreSQL and Redis)
npm run test:integration
# Run a single test file
npx jest tests/unit/services/AgentService.test.ts
# Run tests matching a pattern
npx jest --testNamePattern="registerAgent"
# Watch mode (re-runs on file changes)
npx jest --watch
Integration test requirements: Integration tests connect to real PostgreSQL and Redis. Set these environment variables before running integration tests:
TEST_DATABASE_URL=postgresql://sentryagent:sentryagent@localhost:5432/sentryagent_idp_test
TEST_REDIS_URL=redis://localhost:6379/1
The integration tests create their own tables (using CREATE TABLE IF NOT EXISTS)
and clean up after themselves with DELETE FROM statements in afterAll.
8.10 Web Dashboard Local Development
The web dashboard is a separate Vite project in the dashboard/ directory.
# From the project root
cd dashboard
npm install
# Start the Vite development server
npm run dev
# Dashboard available at http://localhost:5173
The Vite dev server proxies all /api/* requests to http://localhost:3000,
so the API server must be running concurrently (in a separate terminal).
Build for production:
cd dashboard
npm run build
# Output: dashboard/dist/ (served by Express at /dashboard)
After building, the Express server serves the built dashboard at
http://localhost:3000/dashboard. You do not need to run the Vite dev server
for this — the static files are served directly by Express.