Development & Test Environment Setup

This doc covers a local (non-Docker) setup for development and testing.

Prerequisites

  • macOS or Linux
  • Git
  • Docker + Docker Compose (optional but recommended)

Haskell toolchain (local dev)

  • Stack
  • GHC (managed by Stack)

Protobuf toolchain (optional, for core codegen)

  • protoc
  • proto-lens-protoc
  • buf (optional)

TLS/dev certs

  • openssl

Build

make build

Generate protobuf modules

  • Core only:
make proto-core
  • Runner only:
make proto-runner
  • Both:
make proto

Dev certs

make dev-certs

Outputs: - certs/dev/ca.{crt,key} - certs/dev/server.{crt,key}

Local run

Runner

make runner

CLI

make cli

With CLI args:

make cli CLI_ARGS="--host 0.0.0.0 --port 50051"

Docker-based dev

On‑prem stack:

cp deploy/.env.onprem.example deploy/.env.onprem
docker compose --env-file deploy/.env.onprem -f deploy/docker-compose.onprem.yml pull console
docker compose --env-file deploy/.env.onprem -f deploy/docker-compose.onprem.yml up --build

Cloud stack:

cp deploy/cloud/.env.cloud.example deploy/cloud/.env.cloud
docker compose --env-file deploy/cloud/.env.cloud -f deploy/cloud/docker-compose.cloud.yml up --build

Tear down:

make onprem-down
make cloud-down

Tests

Integration security smoke skeleton:

cp tests/integration/.env.integration.example tests/integration/.env.integration
set -a; source tests/integration/.env.integration; set +a
bash tests/integration/security_smoke.sh

LLM routing (dev)

Routing rules: - Tenant override takes priority - Otherwise: default chain with round-robin - Failover to next provider on error

Environment variables (example):

LLM_DEFAULT_CHAIN="internal,openai,anthropic,gemini,grok"
LLM_ROUND_ROBIN="true"
LLM_TENANT_OVERRIDE_TENANT_A="openai"
LLM_TENANT_OVERRIDE_TENANT_B="internal"

Provider settings (OpenAI-compatible APIs):

LLM_PROVIDER_INTERNAL_BASE_URL="http://llm-gateway:8080"
LLM_PROVIDER_INTERNAL_MODEL="internal-default"
LLM_PROVIDER_OPENAI_API_KEY="sk-..."
LLM_PROVIDER_OPENAI_MODEL="gpt-4o-mini"
LLM_PROVIDER_ANTHROPIC_BASE_URL="https://api.anthropic.com"
LLM_PROVIDER_ANTHROPIC_MODEL="claude-3-5-sonnet-20240620"
LLM_PROVIDER_GEMINI_BASE_URL="https://generativelanguage.googleapis.com"
LLM_PROVIDER_GEMINI_MODEL="gemini-1.5-pro"
LLM_PROVIDER_GROK_BASE_URL="https://api.x.ai"
LLM_PROVIDER_GROK_MODEL="grok-2"

LLM decision endpoint (core)

Core can call the LLM, enforce policy, and dispatch a job in one request:

curl -s -X POST http://localhost:8080/api/agent/llm-decide-and-dispatch \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id":"default",
    "runner_id":"runner-1",
    "env":"prod",
    "action_kind":"ActionShell",
    "prompt":"Deploy failed on svc-a. Suggest next step.",
    "approval": {
      "channel":"slack",
      "message":"Approve remediation for svc-a",
      "execution_id":"exec-123",
      "step_id":"step-1",
      "approval_timeout_seconds": 3600
    },
    "job":{
      "workflow_id":"wf-deploy",
      "kind":{
        "tag":"JobShell",
        "contents":{
          "command":"kubectl rollout undo deploy/svc-a",
          "cwd":"/",
          "shell_timeout_seconds": 300,
          "allowlist_tag":"deploy"
        }
      }
    }
  }'

Response example:

{
  "llm_provider": "ProviderInternal",
  "llm_decision": "LlmRequestApproval",
  "llm_rationale": "Risky change in prod; approval recommended.",
  "final_decision": "FinalApprovalRequired"
}

Core can call the LLM and return recommendation only (no dispatch):

curl -s -X POST http://localhost:8080/api/agent/llm-recommend \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id":"default",
    "env":"prod",
    "action_kind":"ActionShell",
    "prompt":"Deploy failed on svc-a. Suggest next step.",
    "job":{
      "workflow_id":"wf-deploy",
      "kind":{
        "tag":"JobShell",
        "contents":{
          "command":"kubectl rollout undo deploy/svc-a",
          "cwd":"/",
          "shell_timeout_seconds": 300,
          "allowlist_tag":"deploy"
        }
      }
    }
  }'

Response example:

{
  "llm_provider": "ProviderInternal",
  "llm_decision": "LlmRequestApproval",
  "llm_rationale": "Shell rollback in prod; approval required."
}

DB step (Runner)

DB execution is driver-based and uses CLI tools: - postgrespsql - mysqlmysql - sqlitesqlite3

Configuration:

RUNNER_DB_DSN_TENANTDB="postgres://user:pass@host:5432/db"
RUNNER_DB_ALLOWLIST="TENANTDB,REPORTING"

Notes: - read_only=true blocks obvious write queries (INSERT/UPDATE/DELETE/CREATE/DROP/ALTER/REPLACE/TRUNCATE).