CT-OpsCT-Ops
Home
Getting Started
Architecture
Features
Deployment
GitHub
GitHub
Home
Getting Started
Architecture
Features
Deployment
GitHub
GitHub
  • Introduction
  • Getting Started

    • Installation
    • Configuration
    • Offline Agent Install Bundle
  • Architecture

    • Architecture Overview
    • Agent Architecture
    • Ingest Service
    • Deployment Profiles
  • Features

    • Hosts & Inventory
    • Host Groups
    • Networks
    • Monitoring
    • Certificate Management
    • SSL Certificate Checker
    • Alerts
    • Notifications
    • Reports
    • Operations Calendar
    • Terminal
    • Service Accounts & Identity
    • Directory User Lookup
    • Tasks & Runbooks
    • Scheduled Tasks
    • Tags
    • Notes
    • Feature Flags
  • Deployment

    • Docker Compose Deployment
    • Air-Gap Deployment
    • Load Testing
  • Development

    • Local Dev Stack
    • End-to-end testing
  • Licensing
  • Security

    • Security & Vulnerability Disclosure
    • mTLS: agent ↔ server authentication

Local Dev Stack

Use ./dev-stack.sh from the repository root when you want fast local feedback without waiting for a release, image publication, or a test-server update.

The command starts the production-adjacent dependencies in Docker and runs the web app in a Docker dev container with your source checkout bind-mounted for hot reload:

  • http://localhost:3000 — local dev proxy and the only browser URL you need
  • http://localhost:3001 — direct Next.js dev server with hot reload inside Docker
  • localhost:9443 — ingest gRPC for agents
  • localhost:55432 — CT-Ops Postgres, bound to loopback only
  • Password Manager API and database in Docker

Start

Prerequisite:

  • Docker Desktop or Docker Engine with the Compose plugin
./dev-stack.sh

The first run creates .dev/dev.env and apps/web/.env.local with local-only secrets and URLs. The root .env used by the release/test-server path is not modified.

For local Docker agent containers, the generated config sets AGENT_DOWNLOAD_BASE_URL=http://dev-proxy, CT_OPS_AGENT_CONTAINER_INGEST_ADDRESS=ingest-dev:9443, and an empty CT_OPS_ENROLMENT_TOKEN= placeholder. Paste a valid token into that placeholder before running deploy/scripts/create-agent-dev-container.sh. The helper joins the dev stack Docker network so these service names resolve inside the test container without exposing the dev stack on your LAN.

The web-migrate and web-dev containers run pnpm install --frozen-lockfile inside Docker using named volumes for node_modules, so Linux container dependencies do not overwrite host dependencies. The ingest-dev container uses Go module and build-cache volumes for the same reason.

Open:

http://localhost:3000

The dev proxy forwards:

  • / to the Next.js dev server
  • /password-manager-api/ to the bundled Password Manager API container
  • /ws/terminal/ to the local ingest container

Connect Agents

Use public mode when you need real hosts or VMs to connect agents back to your local development stack:

./dev-stack.sh --public-host 100.x.y.z

Use your Tailscale IP for 100.x.y.z when possible. It is more reliable than a changing Wi-Fi address and avoids exposing your development laptop to the public internet. If you omit the host, ./dev-stack.sh --public tries to pick a Tailscale IP first, then a local network IP.

Public mode changes only the externally required ports:

  • web proxy: 0.0.0.0:3000
  • ingest HTTP health/JWKS: 0.0.0.0:8080
  • ingest gRPC for agents: 0.0.0.0:9443
  • Postgres remains private on 127.0.0.1:55432

The script updates .dev/dev.env, regenerates the development ingest TLS cert when the public host changes, and prints the agent connection details:

Agent ingest address: 100.x.y.z:9443
Agent CA cert:        deploy/dev-tls/server.crt

Validate the reachable endpoints from your laptop:

./dev-stack.sh --check-public

Agents must be able to reach 100.x.y.z:9443 and trust the generated CA cert. For browser-based agent install bundles, the app will use AGENT_DOWNLOAD_BASE_URL=http://100.x.y.z:3000, so the remote host must also be able to reach port 3000.

For same-machine Docker agent containers, leave the stack in local mode and run:

deploy/scripts/create-agent-dev-container.sh

The helper reads .dev/dev.env, creates one long-lived systemd Ubuntu container, publishes the container SSH service on a random loopback host port, and runs the normal CT-Ops agent install script inside it. The selected SSH port is printed after creation and can also be checked with docker port <container> 22/tcp. The helper creates an SSH login user named ssh-user with password password by default.

Switch back to loopback-only mode with:

./dev-stack.sh --local

Stop Or Reset

Stop the stack while preserving local database volumes:

./dev-stack.sh --down

Remove local dev volumes and generated local config:

./dev-stack.sh --reset

Checks

After the stack is running, check the proxy, Password Manager API, and ingest health endpoints:

./dev-stack.sh --check

Show current local service status:

./dev-stack.sh --status

Rebuilds

The script builds agent binaries if they are missing. Rebuild them explicitly after changing agent code:

./dev-stack.sh --rebuild-agents

Next.js Flags

By default the script runs Next.js with Turbopack inside Docker:

CT_OPS_DEV_NEXT_FLAGS=--turbopack

If you need the webpack dev server instead, set this in .dev/dev.env before starting:

CT_OPS_DEV_NEXT_FLAGS=--webpack

Environment Separation

The local dev stack deliberately uses separate config, ports, and Docker volumes from the release bundle:

  • local dev config: .dev/dev.env and apps/web/.env.local
  • release/test-server config: root .env
  • local Compose project: ct-ops-dev
  • release Compose project: the bundle directory default

This means you can use ./dev-stack.sh locally and still use ./update.sh or ./start.sh on a test server without manually changing environment variables between modes.

Edit this page on GitHub
Last Updated: 5/16/26, 7:40 PM
Contributors: Simon Carr
Next
End-to-end testing