Skip to content

Installation & Setup

Get Stentor running locally in under five minutes. This guide covers system requirements, Docker-based deployment, environment configuration, and your first login.


System Requirements

Before you begin, make sure your machine meets these prerequisites:

Component Minimum Recommended
Docker Engine 24+ Latest stable
Docker Compose v2+ (integrated docker compose) Latest stable
Git 2.x Latest stable
RAM 4 GB 8 GB
Disk 2 GB free 5 GB free

Port availability -- the following ports must be free:

Port Service Description
5433 PostgreSQL Database (mapped from container port 5432)
8082 Backend API Go/Gin REST API and WebSocket server
3001 Frontend React UI (Vite dev server or nginx in Docker)
1080 SOCKS Proxy Beacon SOCKS5 tunnel endpoint
8083 Adminer Database admin panel (optional)

Supported operating systems:

  • Linux -- recommended for production deployments
  • macOS -- recommended for development
  • Windows (WSL2) -- requires Docker Desktop with WSL2 backend

Quick Start

Follow these steps to go from zero to a running Stentor instance.

1. Clone the repository

git clone https://github.com/your-org/stentor.git
cd stentor
# Run inside your WSL2 terminal
git clone https://github.com/your-org/stentor.git
cd stentor

2. Configure environment variables

cp .env.example .env

The defaults work for local development. See Environment Configuration below for details on each variable.

Change JWT_SECRET before production use

The default .env.example ships with JWT_SECRET=change-me-to-a-random-secret. Generate a real secret before deploying anywhere beyond your local machine:

# Generate a random 64-character secret
openssl rand -hex 32

Paste the output as your JWT_SECRET value in .env.

3. Start all services

docker compose up -d

Docker Compose builds and starts all four services: PostgreSQL, the backend API, the frontend UI, and Adminer. The first build takes 2--3 minutes; subsequent starts are nearly instant.

Watch the logs

Monitor startup progress in real time:

docker compose logs -f

Wait for Backend ready on :8080 before proceeding.

4. Open the UI

Once all services are running:

5. Log in

Use the seed operator account:

Field Value
Email [email protected]
Password your-password

These credentials come from database/seed.sql, which is automatically loaded when the database initializes for the first time.

Seed account details

The seed account has the operator role, which grants full access to all Stentor features. In a production deployment, create dedicated operator accounts and remove the seed account.


Environment Configuration

The .env file controls all runtime settings. Copy .env.example and customize as needed.

Database

Variable Required Default Description
POSTGRES_USER Yes postgres PostgreSQL username
POSTGRES_PASSWORD Yes postgres PostgreSQL password
POSTGRES_DB Yes stentor Database name
DATABASE_URL Yes postgres://postgres:postgres@postgres:5432/stentor?sslmode=disable Full connection string used by the backend
DB_MAX_CONNS No 25 Maximum connection pool size
DB_MIN_CONNS No 5 Minimum idle connections
DB_MAX_CONN_LIFETIME_MINUTES No 60 Connection max lifetime

Internal vs external ports

The DATABASE_URL uses port 5432 (the container-internal port). The host-mapped port is 5433. When connecting from your host machine (e.g., via psql), use port 5433.

Authentication

Variable Required Default Description
JWT_SECRET Yes change-me-to-a-random-secret Secret key for signing JWT tokens. Must be changed for production.
ACCESS_TOKEN_EXPIRY_MINUTES No 15 Access token lifetime
REFRESH_TOKEN_EXPIRY_DAYS No 7 Refresh token lifetime

JWT_SECRET is critical

Anyone with your JWT_SECRET can forge authentication tokens and gain full access to Stentor. Use a strong, unique random value and never commit it to version control.

Server

Variable Required Default Description
PORT No 8080 Backend listen port (inside container)
ENVIRONMENT No development Set to production for production deployments
STENTOR_VERSION No dev Version string shown in the UI
ALLOWED_ORIGINS No http://localhost:3001 CORS allowed origins

Relay (optional)

These variables are only needed if you are connecting a relay agent.

Variable Required Default Description
RELAY_WS_URL No -- WebSocket URL the relay connects to (e.g., ws://localhost:8082/ws/relay)
RELAY_SECRET No -- Shared secret for relay authentication

C2 (optional)

These variables configure the Command & Control subsystem.

Variable Required Default Description
C2_IMPLANT_PATH No -- Path to the compiled implant binary for payload generation
C2_RSA_KEY_PATH No c2_private.pem RSA private key for beacon encryption
C2_TASK_TTL_HOURS No 24 How long pending tasks remain valid
C2_BEACON_STALE_MINUTES No 5 Beacon considered stale after this many minutes without check-in
C2_CLEANUP_INTERVAL_SECONDS No 30 Interval for stale beacon cleanup
C2_MAX_RETRIES No 3 Task delivery retry count
C2_RETRY_BASE_DELAY_SECONDS No 1 Initial retry backoff
C2_RETRY_MAX_DELAY_SECONDS No 30 Maximum retry backoff

Docker Services

Stentor runs as four Docker containers orchestrated by Docker Compose.

graph LR
    FE["Frontend<br/>(React + nginx)<br/>:3001"] -->|REST / WebSocket| BE["Backend<br/>(Go + Gin)<br/>:8082"]
    BE -->|SQL| DB["PostgreSQL 16<br/>:5433"]
    ADM["Adminer<br/>:8083"] -->|SQL| DB
    BE -->|SOCKS5| SOCKS[":1080"]

postgres (stentor-c2-db)

  • Image: postgres:16-alpine
  • Host port: 5433 (mapped from container port 5432)
  • Data volume: stentor-c2-postgres-data (persistent across restarts)
  • Schema: database/schema.sql is mounted to /docker-entrypoint-initdb.d/001_schema.sql and runs automatically on first start
  • Seed data: database/seed.sql creates the default operator account
  • Health check: pg_isready every 5 seconds with 5 retries

backend (stentor-c2-backend)

  • Build context: server/Dockerfile
  • Host port: 8082 (mapped from container port 8080)
  • Depends on: postgres (waits for health check to pass)
  • Volumes: ./binaries mounted read-only at /app/binaries for payload generation
  • Additional port: 1080 for SOCKS5 proxy tunnels

frontend (stentor-c2-frontend)

  • Build context: ui/Dockerfile
  • Host port: 3001 (mapped from container port 80)
  • Depends on: backend
  • Serves: Production-built React app via nginx

adminer (stentor-c2-adminer)

  • Image: adminer:latest
  • Host port: 8083
  • Purpose: Web-based database browser for debugging and inspection

First Login

1. Navigate to the login page

Open http://localhost:3001 in your browser. You will be redirected to the login page.

2. Enter credentials

Use the seed operator account:

3. Authentication flow

When you log in, the backend issues two JWT tokens:

  • Access token -- short-lived (15 minutes by default), sent with every API request in the Authorization header
  • Refresh token -- long-lived (7 days by default), used to obtain new access tokens without re-entering credentials

The UI handles token refresh automatically. You stay logged in until the refresh token expires.

4. Next steps

After login you will land on the operator dashboard. For a guided tour of the interface, continue to the UI Walkthrough page.


Database Management

Stentor provides several Make targets for common database operations.

Access the database shell

make db-shell

This opens a psql session connected to the Stentor database inside the Docker container.

Reset the database

# Drop and recreate (preserves the Docker volume)
make db-reset

# Fresh start from schema.sql (drops volume, reseeds)
make db-fresh

When to use db-fresh

Use make db-fresh if login fails with default credentials, or if you want a completely clean database. This reloads schema.sql and seed.sql from scratch.

How the schema loads

The docker-compose.yml mounts database/schema.sql into the PostgreSQL container at /docker-entrypoint-initdb.d/001_schema.sql. PostgreSQL automatically executes all scripts in this directory on first initialization only (when the data volume is empty). To force re-initialization, remove the volume:

docker compose down -v   # removes volumes
docker compose up -d     # fresh start

Development Mode

For active development, run the backend and frontend outside Docker with hot-reload support.

Backend (Go + Air)

make dev

Starts the Go backend with Air for automatic recompilation on file changes. The API runs on http://localhost:8082.

Database still runs in Docker

make dev only runs the backend natively. The PostgreSQL container must still be running. Start it with docker compose up -d postgres.

Frontend (Vite HMR)

make dev-ui

Starts the Vite development server on http://localhost:3001 with Hot Module Replacement. Changes to React components and styles are reflected instantly in the browser.

Both at once

make dev-all

Starts both the backend (Air) and frontend (Vite) in parallel.

Vite proxy configuration

In development mode, the Vite dev server proxies API requests to the backend:

  • /api/* requests are forwarded to http://localhost:8082
  • /ws/* WebSocket connections are forwarded to http://localhost:8082

This eliminates CORS issues during local development -- you access everything through http://localhost:3001.


Troubleshooting

Port already in use

Symptom: docker compose up fails with "port is already allocated."

Fix: Check what is using the port and stop it:

# Find the process using port 5433
lsof -i :5433

# Or kill it directly
kill $(lsof -t -i :5433)
# Find the process using port 5433
netstat -ano | findstr :5433

# Kill by PID (replace 1234 with actual PID)
taskkill /PID 1234 /F

Common port conflicts:

Port Likely conflict
5433 Another PostgreSQL instance
8082 Another API server
3001 Another Vite/Node dev server

Database not ready

Symptom: Backend logs show connection refused errors to PostgreSQL.

Fix: The backend depends on the PostgreSQL health check, but if you started services manually, the database may not be ready yet. Wait 10--15 seconds and check:

docker compose ps

Ensure the postgres service shows healthy status. If it remains unhealthy:

docker compose logs postgres

Login fails with default credentials

Symptom: [email protected] / your-password returns "invalid credentials."

Fix: The seed data may not have loaded. Rebuild the database:

make db-fresh

This drops the database volume, re-initializes from schema.sql, and reloads seed.sql.

Docker Compose version mismatch

Symptom: docker-compose command not found, or syntax errors in docker-compose.yml.

Fix: Stentor requires Docker Compose v2, which uses the docker compose command (without the hyphen):

# Check your version
docker compose version

# If you get "command not found", install the Compose plugin:
# https://docs.docker.com/compose/install/

Legacy docker-compose (v1)

The hyphenated docker-compose command is deprecated. If you only have v1 installed, upgrade to Docker Compose v2 or Docker Desktop, which includes it by default.

Container build fails

Symptom: docker compose up --build fails during the Go or Node build step.

Fix: Ensure you have sufficient disk space and memory. Docker builds can be resource-intensive:

# Clean up unused Docker resources
docker system prune -f

# Retry the build
docker compose up -d --build

WebSocket connection issues

Symptom: The UI shows "Disconnected" or the cockpit does not receive real-time updates.

Fix: Ensure the backend is running and accessible on port 8082. In development mode, the Vite proxy handles WebSocket forwarding automatically. In Docker mode, nginx inside the frontend container handles the proxy.

# Verify backend is responding
curl http://localhost:8082/api/v1/health