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¶
2. Configure environment variables¶
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:
Paste the output as your JWT_SECRET value in .env.
3. Start all services¶
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:
Wait for Backend ready on :8080 before proceeding.
4. Open the UI¶
Once all services are running:
- Frontend UI: http://localhost:3001
- Backend API: http://localhost:8082/api/v1
- DB Admin (Adminer): http://localhost:8083
5. Log in¶
Use the seed operator account:
| Field | Value |
|---|---|
[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 port5432) - Data volume:
stentor-c2-postgres-data(persistent across restarts) - Schema:
database/schema.sqlis mounted to/docker-entrypoint-initdb.d/001_schema.sqland runs automatically on first start - Seed data:
database/seed.sqlcreates the default operator account - Health check:
pg_isreadyevery 5 seconds with 5 retries
backend (stentor-c2-backend)¶
- Build context:
server/Dockerfile - Host port:
8082(mapped from container port8080) - Depends on:
postgres(waits for health check to pass) - Volumes:
./binariesmounted read-only at/app/binariesfor payload generation - Additional port:
1080for SOCKS5 proxy tunnels
frontend (stentor-c2-frontend)¶
- Build context:
ui/Dockerfile - Host port:
3001(mapped from container port80) - 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:
- Email:
[email protected] - Password:
your-password
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
Authorizationheader - 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¶
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:
Development Mode¶
For active development, run the backend and frontend outside Docker with hot-reload support.
Backend (Go + Air)¶
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)¶
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¶
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 tohttp://localhost:8082/ws/*WebSocket connections are forwarded tohttp://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:
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:
Ensure the postgres service shows healthy status. If it remains unhealthy:
Login fails with default credentials¶
Symptom: [email protected] / your-password returns "invalid credentials."
Fix: The seed data may not have loaded. Rebuild the database:
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.