mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-10-19 22:17:42 +02:00
The console page grows an update-source section: what is in effect and where it comes from (config.yaml or the tree's own tracking), an edit form for the three fields, and - for a directory that is not a git installation - the adopt entry. Adopt is preview-first: the plan lists what would be replaced and what would be kept, the confirm dialog carries the counts, and the restart tick is the same one an update uses. A refused save shows the server's own sentence (a remote name and an address together, an ext:: address) instead of a generic failure, and the typed values survive the failed save and the polling redraws. zh/EN/RU dictionaries carry the 27 new keys; the console page's contract tests grow to 29 (source rendering and save body, the refusal path, adopt gating by installation kind, preview-then-confirm); docs/README describe choosing a source and connecting an unpacked installation.
217 lines
10 KiB
Markdown
217 lines
10 KiB
Markdown
# Developer Guide
|
|
|
|
[中文](../zh-CN/developer-guide.md)
|
|
|
|
This guide is for contributors extending CyberStrikeAI. The project is a Go single-service application with a static frontend, SQLite persistence, Agent/MCP orchestration, and optional high-risk security subsystems.
|
|
|
|
## Project Layout
|
|
|
|
```text
|
|
cmd/server/ service entrypoint
|
|
internal/app/ app wiring, routes, MCP tool registration
|
|
internal/handler/ HTTP handlers
|
|
internal/database/ SQLite access
|
|
internal/security/ auth, rate limits, shell execution
|
|
internal/mcp/ MCP server and external MCP manager
|
|
internal/multiagent/ Eino single-agent, multi-agent, middleware
|
|
internal/workflow/ graph orchestration runtime
|
|
internal/knowledge/ indexing and retrieval
|
|
internal/c2/ built-in C2
|
|
internal/project/ project fact blackboard
|
|
web/static/ frontend JS/CSS/assets
|
|
web/templates/ HTML templates
|
|
tools/ YAML command tools
|
|
roles/ role YAML
|
|
agents/ multi-agent Markdown definitions
|
|
skills/ Agent Skills
|
|
docs/ documentation
|
|
```
|
|
|
|
## Development Startup
|
|
|
|
```bash
|
|
go run ./cmd/server --config config.yaml
|
|
```
|
|
|
|
The frontend is static. Most JS/CSS/template changes only require a browser refresh.
|
|
|
|
## One-Click Update: Letting an Installation Update Its Own Source
|
|
|
|
On a deployed machine the routine is "my tree moved forward by a few dozen commits -> bring this
|
|
installation up -> rebuild -> swap the binary -> restart". This used to be only `upgrade.sh`, which
|
|
pointed at one fixed upstream repository, so for anyone running a fork "upgrade" meant "overwrite
|
|
yourself with somebody else's code". The capability now lives in the platform (`internal/update`) and
|
|
the console page, the REST API and the CLI share one implementation, so the three surfaces cannot
|
|
disagree about why an update was refused.
|
|
|
|
### Three entry points
|
|
|
|
| entry | how it is used |
|
|
|---|---|
|
|
| console | Platform management -> One-click update (`#system-update`). Opening the page reads the local tree only and does not go online; **Check** is what performs the fetch; **Update** starts a job the page polls; the page also carries the "exit after updating" option and a rollback button |
|
|
| REST | `GET /api/system/update` (state on disk, no network), `POST /api/system/update/check` (fetch, then report the gap), `POST /api/system/update/apply` (`202` with `job_id`, polled through `GET /api/system/update/job`), `POST /api/system/update/rollback` |
|
|
| CLI | `./cyberstrike-ai -check-update`, `./cyberstrike-ai -update`, `./cyberstrike-ai -update-rollback`. The install root is the directory holding `--config`, or the current directory when `--config` was not given |
|
|
|
|
Permissions are `update:read` (GET) and `update:apply` (the four mutating endpoints). `update:apply`
|
|
additionally requires a global (`all` scope) session: one machine has one source tree, and an
|
|
`assigned`/`own` session must not be able to move the code everybody else is running.
|
|
|
|
### Where the code comes from
|
|
|
|
From **the remote this directory already tracks**; no third-party repository is hardcoded. The remote
|
|
is picked from the ones this tree has, in the order `mine`, `origin`, `upstream` (falling back to the
|
|
first remote when none of those names exists), and the branch defaults to what the current branch's
|
|
`@{upstream}` names (the current branch itself when there is no upstream). `upgrade.sh` is now a thin
|
|
shell over the same implementation: when this directory is a git work tree it just calls
|
|
`./cyberstrike-ai -update`.
|
|
|
|
### Choosing the source (the update section of config.yaml)
|
|
|
|
Unset, the rules above apply; set, the explicit choice wins:
|
|
|
|
```yaml
|
|
update:
|
|
remote: origin # an existing remote name (mine/origin/upstream/anything), or remote_url instead
|
|
# remote_url: https://github.com/AIPentest/CyberStrikeAI.git
|
|
branch: main # optional; defaults to @{upstream} / the current branch
|
|
```
|
|
|
|
The console page edits and saves all three. Saving validates: remote and branch names go through the
|
|
same allowlist an update uses, and the address allowlist is https/http/ssh/git/file plus local
|
|
absolute paths - git's `ext::` transport executes commands and is rejected outright. Following the
|
|
official repository, your own fork, or somebody else's second-development repository is exactly the
|
|
choice this section expresses.
|
|
|
|
### Connecting a non-git installation (the tarball kind)
|
|
|
|
An installation that started as an unpacked Release archive has no `.git`; once a source is saved it
|
|
can be connected from the same page: the preview fetches in a throwaway repository and first lists
|
|
**the local files the target would replace** and **the operator content that will be kept**; on
|
|
confirm the directory becomes a git work tree (`git init`, origin added, the target branch checked
|
|
out), every replaced file is kept under `.update-backup/<timestamp>/overwritten/`, operator content
|
|
is stashed and restored as usual, and the binary is rebuilt. From then on it is a normal
|
|
installation with one-click update and rollback - but note there is no git history before the
|
|
connection, so the connection itself has no earlier commit to roll back to; the first real update
|
|
writes the first rollback point.
|
|
|
|
### The four refusals
|
|
|
|
| situation | reason | meaning and what to do |
|
|
|---|---|---|
|
|
| product **source** modified locally (a path outside the protected list) | `local_source_edits` | An update is a download, not a conflict decision. The file names are listed; commit or restore them first |
|
|
| branch **diverged** from its remote (ahead and behind at once) | `diverged` | That is a merge decision; the counts are reported so you can resolve it by hand |
|
|
| no Go toolchain on this box | `no_toolchain` | **The source still updates**; only the binary is not swapped. Install `go` and press update again to finish |
|
|
| this directory is not a git work tree | `not_a_repo` (`installed: false` in the status) | A tarball installation has no remote to fast-forward; update it through the release package |
|
|
|
|
`apply` is asynchronous, so it always answers `202` first and the refusals above arrive in the polled
|
|
`job.failure` (`reason` / `message` / `items`). `rollback` is synchronous and maps refusals to status
|
|
codes (`local_source_edits`, `diverged`, `no_state`, `moved_since_update`, `no_binary` -> 409, anything
|
|
else -> 400). Other failures (`merge_failed`, `build_failed`, `swap_failed`) carry git's or the
|
|
compiler's own output; a failed build leaves the previous binary in place while the source has already
|
|
moved and the state file is written, so it stays rollback-able.
|
|
|
|
### How operator content survives
|
|
|
|
`roles/ skills/ tools/ agents/ knowledge_base/ data/ log/ venv/ config.yaml .env` belong to the
|
|
**operator** (`update.Protected` is the judge). Before the merge, only those of those paths the
|
|
update actually writes to are copied into `.update-backup/<timestamp>/` and removed from the tree -
|
|
your extra directories that upstream never touched are not disturbed - and they are put back
|
|
afterwards, which is what "update the code, keep my work" means. The result names every file in
|
|
`keptContent` instead of quietly dropping it: where both you and upstream changed the same file, the
|
|
version that lands is yours, and the result says so.
|
|
|
|
### Rollback
|
|
|
|
`./cyberstrike-ai -update-rollback`, or the button on the page: `git reset --hard` back to the commit
|
|
the update came from, the binary kept as `cyberstrike-ai.prev` is put back, and `.update-state.json`
|
|
is removed. It refuses when there is no update record (`no_state`), no kept binary (`no_binary`), the
|
|
recorded commit is not in this repository (`bad_state`), **HEAD has moved since that update**
|
|
(`moved_since_update` - a rollback undoes the update, not the work done after it), or source is
|
|
modified locally (`local_source_edits`).
|
|
|
|
### The two restart cases
|
|
|
|
The process only stands down (graceful `Shutdown`, then exit 0) when the request explicitly says
|
|
`restart: true` **and** a restart hook was wired at startup. Whether it actually comes back is up to a
|
|
supervisor (systemd, `run.sh`), and the page says exactly that rather than promising a boot that may
|
|
not happen. Otherwise the API only reports `needsRestart` and the old binary keeps running. Asking for
|
|
a restart with no hook wired is a `400`, instead of stopping the service and claiming it restarted.
|
|
|
|
## Adding a Business Module
|
|
|
|
Do not add only a handler. A complete module usually needs:
|
|
|
|
1. Data model and SQLite migration.
|
|
2. Handler: parameters, errors, pagination/filtering.
|
|
3. Audit: management actions.
|
|
4. Monitor: long-running execution state.
|
|
5. MCP: whether Agents should call it.
|
|
6. HITL: approval boundary for MCP tools.
|
|
7. OpenAPI: update `/api/openapi/spec`.
|
|
8. Frontend: i18n, states, empty/error UI.
|
|
9. Tests: DB, handler, edge cases.
|
|
10. Docs: config, usage, troubleshooting, safety impact.
|
|
|
|
Missing one of these usually becomes a later usability or safety bug.
|
|
|
|
## Error Response Design
|
|
|
|
Prefer stable JSON:
|
|
|
|
```json
|
|
{
|
|
"error": "machine_readable_code",
|
|
"message": "human-readable explanation"
|
|
}
|
|
```
|
|
|
|
Frontend needs stable fields, users need actionable messages, and logs need detailed internal errors.
|
|
|
|
## Long-Running Tasks
|
|
|
|
For scanning, indexing, batch tasks, C2, or external operations, answer:
|
|
|
|
- Can it be cancelled?
|
|
- Can progress be queried?
|
|
- Can it be retried?
|
|
- Where is the result stored?
|
|
- Does state survive page refresh?
|
|
- Does it block the HTTP request?
|
|
|
|
If not, use task tables, event streams, or monitoring.
|
|
|
|
## Extending Tools
|
|
|
|
Prefer `tools/*.yaml` for command tools. Use Go built-in tools when the tool needs internal state or structured integration.
|
|
|
|
Built-in tools should define clear input schemas, handle timeouts and errors, and respect HITL for risky actions.
|
|
|
|
## Frontend Changes
|
|
|
|
Use existing helpers such as `apiFetch`, modal utilities, notifications, and i18n. Update both `web/static/i18n/zh-CN.json` and `web/static/i18n/en-US.json` for new visible text.
|
|
|
|
Avoid putting secrets or provider keys in frontend code.
|
|
|
|
## Test Priority
|
|
|
|
High-value tests:
|
|
|
|
- config hot-apply;
|
|
- HITL branches;
|
|
- shell timeout/no-output;
|
|
- external MCP recovery;
|
|
- KB indexing and post-processing;
|
|
- WebShell OS/encoding detection;
|
|
- SQLite migration compatibility.
|
|
|
|
## Source Anchors
|
|
|
|
- App wiring: `internal/app/app.go`
|
|
- Config apply: `internal/handler/config.go`
|
|
- OpenAPI: `internal/handler/openapi.go`
|
|
- Tool executor: `internal/security/executor.go`
|
|
- Skill package: `internal/skillpackage/`
|
|
- One-click update: `internal/update/` (status, apply, rollback), HTTP surface in
|
|
`internal/handler/update.go`, CLI switches in `cmd/server/update_cli.go`, thin shell in
|
|
`upgrade.sh`
|