The console now knows what it is running: the status endpoint compares the binary identity captured at startup (size + nanosecond mtime) with the file on disk, so an update, a rollback or a CLI run that swapped the binary without a restart is reported as needsRestart (with the build time) - a state that used to live only in one job's result and vanished with the process. That state grows a persistent banner and POST /api/system/update/restart (409 when there is nothing to activate or a job owns the tree, 400 without a hook), so a forgotten tick is one click instead of a shell login. The restart tick defaults to on exactly where it is safe: launchd/systemd leave markers in the environment (XPC_SERVICE_NAME, INVOCATION_ID/JOURNAL_STREAM) and the box follows them; an operator's own choice is never second-guessed afterwards. While the process is down the console becomes a recovery view that probes the status endpoint every 2 seconds: 200 means the old process still answers, a dead socket means it is on its way out, and a 401 - only a new process can fail to know the session - reloads the page back to #system-update. A job poll that dies during a requested restart is the restart in flight, not a failure to toast. Also fixed: updateT now asks i18next not to escape interpolated values, because the console escapes at render time - the apply confirm dialog has been showing branch names with / ever since they contained a slash, and the new banner showed dates the same way. Tests: 7 new console contract tests (36 total: banner, tick default, restart confirm/refusal, watchdog 200-vs-401, timeout copy, the in-flight poll death) plus handler tests for the stamp and the endpoint, all mutation-probed; docs describe the pending-restart state and the self-recovery.
8.0 KiB
Deployment Guide
CyberStrikeAI can run as a local testing tool, an internal team service, or a production red-team platform. Treat it as a high-privilege security system: it can execute commands, call MCP tools, manage WebShell connections, and optionally run C2 listeners.
Prerequisites
- Go for source runs and binary builds.
- Python for some MCP servers and tool scripts.
- SQLite files under
data/; no external DB is required by default. - Actual security tools installed in PATH. YAML files under
tools/only describe commands. - At least one
ai.channelsentry. Useprovider: openai_compatiblefor OpenAI-compatible endpoints, orprovider: claudefor Eino's native Claude component.
Important persistent paths:
config.yaml
data/
tools/
roles/
skills/
agents/
knowledge_base/
chat_uploads/
Back these up before upgrades.
Startup Modes
Local quick start:
chmod +x run.sh && ./run.sh
run.sh is the most common startup path for local use, development, small temporary internal deployments, and quick post-upgrade verification.
For long-running service, boot-time startup, managed logs, and crash recovery, prefer a binary managed by systemd.
Source run:
go run ./cmd/server --config config.yaml
Binary build:
go build -o cyberstrike-ai ./cmd/server
./cyberstrike-ai --config config.yaml
The binary still needs web/templates, web/static, and the runtime resource directories.
HTTPS and Reverse Proxy
For local testing, self-signed HTTPS is acceptable:
server:
tls_enabled: true
tls_auto_self_sign: true
For production, use real certificates or terminate TLS at a reverse proxy. If the proxy terminates TLS and forwards HTTP to the app, avoid enabling app-side TLS on the same upstream unless proxy_pass uses HTTPS.
Nginx must not buffer SSE:
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Upgrading
Prefer the platform's own one-click update (console System settings -> One-click update,
POST /api/system/update/apply, or ./cyberstrike-ai -update at a keyboard). It pulls the remote
this install directory already tracks: fetch, fast-forward, go build, atomic binary swap (the old
binary is kept as cyberstrike-ai.prev), while operator content - roles/ skills/ tools/ agents/ knowledge_base/ data/ config.yaml - is put aside and restored, and the result names every file it
kept. For a fork this is the only path that does not overwrite you with somebody else's code; when
source is modified locally or the branch has diverged it refuses and says why instead of forcing.
See the developer guide for the full semantics.
When a supervisor is detected (systemd/launchd startup markers in the environment), the "exit after updating" tick is pre-ticked: the process stands down after a successful update, the supervisor brings the new binary back, and the page reloads itself once the service answers (sessions live in memory, so one fresh login follows). Forgot the tick, or swapped the binary from the CLI? The console carries a persistent banner with Restart now - no shell login needed.
The source is chosen either in the update section of config.yaml (remote or remote_url, plus
an optional branch) or right on the console page: the official repository, your own fork, or
somebody else's second-development repository are all just this one setting; unset, it follows the
remote this directory already tracks. An installation unpacked from a Release archive (no .git)
can be connected from the same page: the preview lists the files the target would replace and the
operator content that is kept, replaced files are backed up under
.update-backup/<timestamp>/overwritten/, and confirming turns the directory into a normal
installation with one-click updates.
upgrade.sh still works and is now a thin shell over that implementation, with two paths decided by
the installation kind:
- git work tree: calls
./cyberstrike-ai -updatedirectly (buildinggo build -o cyberstrike-ai ./cmd/serverfirst when there is no binary yet, and failing with instructions on installing Go when there is neither).--checkinspects only, through-check-update. - not a git work tree (Release tarball install): only here does it fall back to downloading a
Release tarball and
rsync --delete-ing it in. The source repository then comes from--repo owner/nameor theGITHUB_REPOenvironment variable, and the built-in default is used only when neither is given - with a warning naming which repository the code is coming from.config.yaml,data/,tools/,roles/,skills/,agents/,venv/(skippable with--no-venv) and the rollback points.update-backup/,.update-state.json,cyberstrike-ai.prevare on the rsync keep list, so--deletecannot remove them.
Recommended flow either way:
- Stop the service.
- Back up
config.yaml,data/and your custom directories. - Pull or replace the new code/binary.
- Keep your own configuration and add fields the new
config.yamlexample introduced. - Start the service and check login, model test, tool list and knowledge-base status.
The one-click update performs step 3 (plus the rebuild); 1, 2, 4 and 5 stay operator decisions - a
database migration does not become compatible just because the binary changed. upgrade.sh suits
quick upgrades without compatibility risk; in production still back up first. Python dependencies:
venv/ is created and managed by run.sh, and keeping it means an upgrade does not remove it - only
--no-venv deletes it and lets run.sh reinstall.
Rollback
The one-click update leaves its own rollback point: ./cyberstrike-ai -update-rollback, or the button
on the page, goes back to the commit before that update and puts cyberstrike-ai.prev back. It runs
only while HEAD is still the commit that update wrote, so work done afterwards is not discarded.
Content that was put aside also survives under .update-backup/<timestamp>/.
Beyond that, roll back together:
- The previous binary or code.
- The pre-upgrade
config.yaml. - The pre-upgrade
data/.
If the new version already wrote database schema changes, rolling back the binary alone may not be enough; restore the whole backup.
Deployment Decision Table
| Scenario | Recommended setup | Key settings | Avoid |
|---|---|---|---|
| Personal testing | ./run.sh + self-signed HTTPS |
tls_auto_self_sign: true |
Public exposure |
| Internal team | Binary + systemd + internal HTTPS | strong password, audit, backup, IP restrictions | Shared weak password |
| Production red-team platform | Reverse proxy + dedicated OS user + log collection | real certs, proxy auth, C2 only when needed | Direct public admin UI |
| Chat/KB only | Disable C2 and unnecessary MCP | c2.enabled: false |
All tools enabled by default |
| Tool automation | Isolated workspace + HITL | workspace_root_dir, hitl, monitor |
Shell tools globally allowlisted |
Acceptance Checklist
After startup:
- Open
/and verify no HTTP/HTTPS redirect loop. - Login and validate
/api/auth/validate. - Run model test in settings.
- Check tool list and schemas.
- If KB is enabled, check index status.
- If external MCP is enabled, verify connection and tool visibility.
- If C2 is enabled, start and stop a test listener only in an authorized network.
- Check audit logs for login and config activity.
Runtime File Layers
- Replaceable: binary,
web/, default docs/resources. - Preserve:
config.yaml,data/, custom tools/roles/skills/agents,knowledge_base, uploads. - Cleanup candidates: checkpoints, temporary workspaces, stale payloads, old tool execution records.
Source Anchors
- App wiring and routes:
internal/app/app.go - TLS bootstrap:
internal/app/main_server_tls.go - HTTP to HTTPS redirect:
internal/app/main_server_http_redirect.go - Config structs:
internal/config/config.go - Config apply:
internal/handler/config.go