#Quickstart
You have nyx-agent built and nyx-agent doctor is green. This page
takes you from a fresh state directory to a first scan and a
findings list. Five minutes if the prerequisites are already in
place; see docs/install.md if doctor reports anything missing.
The shipping binary is nyx-agent. The product brand is "Nyx Agent".
Both names appear here for the reasons explained in README.md.
#Start the daemon
nyx-agent serve
serve is the default subcommand, so nyx-agent with no arguments
does the same thing. It binds 127.0.0.1:8765 (from [ui] listen_addr), opens your default browser, and prints the ready URL
on stdout:
ready on http://127.0.0.1:8765
first launch detected; wizard at http://127.0.0.1:8765/setup
The "first launch detected" line only appears when nyx-agent.toml
is not yet on disk. The browser lands on /setup in that case;
once you submit the wizard, subsequent starts go to /.
Common flags:
| Flag | Effect |
|---|---|
--listen ADDR |
Override [ui] listen_addr. Pass 0.0.0.0:8765 for a non-loopback bind. Pair with TLS + the bearer token; see docs/security-posture.md (forthcoming). |
--no-open |
Skip the browser launch. Daemon still binds and serves. |
--headless |
Skip the browser launch AND disable bearer-token auth, since headless mode is intended for CI smoke tests. Do not run a public daemon in --headless. |
--open-cmd CMD |
Replace the default browser launcher. --open-cmd /bin/echo is the test recipe: the daemon prints the ready URL through your launcher instead of opening a window. |
--log-level FILTER |
Global. Passed to tracing-subscriber. Examples: info, debug, nyx-agent=trace,sqlx=warn. |
Ctrl+C shuts the daemon down cleanly: it closes the HTTP listener,
stops the in-process scheduler, drains any in-flight scan worker,
and closes the SQLite store.
#Create a project
Nyx Agent groups one or more repos under a Project. Everything below
(the wizard, the TOML, the scan command) operates per project, so a
fresh deployment starts with a project row.
The fastest path is the CLI:
nyx-agent project create acme-app \
--description "Acme web product" \
--target-base-url http://localhost:3000
project create writes a row to the SQLite store and returns the
generated project_id. The project name must be unique. From here
you can attach repos via nyx-agent project add-repo (worked
example below) or by hand-editing nyx-agent.toml.
You can also create the project from the SPA after the first-launch
wizard completes. The projects list lives at /projects.
#Walk the first-launch wizard
The SPA at /setup asks for five things. Each maps to a single
config field; the daemon writes nyx-agent.toml only after you
submit. The wizard refuses to submit until you tick
i_own_this = true.
| Field | Writes to | Notes |
|---|---|---|
| AI runtime | [ai] runtime |
none, anthropic, local-llm, claude-code, or codex. none is the default and keeps scans fully local with no model provider. |
| Anthropic API key | OS keychain (not TOML) | Required only when runtime is anthropic. This is the official Claude BYOK path. Persisted under secrets::ACCOUNT_AI_ANTHROPIC. |
| Local LLM URL + token | [ai] api_base + keychain |
Required only when runtime is local-llm. OpenAI-compatible endpoint; set [ai].model if your server requires a specific model id. |
| Sandbox backend | [sandbox] backend |
auto is the default and resolves at runtime; process is the safe fallback that always works. See docs/sandbox.md (forthcoming) for the full backend matrix. |
i_own_this |
(consent gate) | Must be true to submit. |
The wizard is mounted on the same daemon, so you do not need to
restart after submitting. The SPA redirects to / once the POST
returns 200.
Claude Code and Codex CLI runtimes are optional local adapters. Nyx Agent does not include or resell model access; use them only with credentials and usage patterns allowed by the provider terms.
#Attach a repo to the project
The wizard does not register repos. Two paths: the CLI or
hand-editing nyx-agent.toml.
CLI (uses the project you created above):
nyx-agent project add-repo acme-app acme-backend \
--path /abs/path/to/your/checkout \
--i-own-this
For a remote git source:
nyx-agent project add-repo acme-app acme-backend \
--git-url https://github.com/your-org/demo.git \
--branch main \
--i-own-this
TOML form (every repo lives inside a [[project]] block):
[[project]]
name = "acme-app"
description = "Acme web product"
target_base_url = "http://localhost:3000"
[project.launch]
mode = "custom-commands"
[[project.launch.build]]
command = "npm ci"
repo = "acme-frontend"
[[project.launch.start]]
command = "npm run dev"
repo = "acme-frontend"
timeout_secs = 120
[[project.launch.health]]
url = "http://localhost:3000/health"
timeout_secs = 60
[[project.launch.seed]]
command = "npm run seed:test"
repo = "acme-backend"
[[project.launch.reset]]
command = "npm run db:reset"
repo = "acme-backend"
[[project.repo]]
name = "acme-backend"
i_own_this = true
enabled = true
[project.repo.source]
kind = "local-path"
path = "/abs/path/to/your/checkout"
[[project.repo]]
name = "acme-frontend"
i_own_this = true
enabled = true
[project.repo.source]
kind = "git"
url = "https://github.com/your-org/frontend.git"
branch = "main" # optional; defaults to the remote HEAD
# auth = "token-env:GITHUB_TOKEN"
Save the file and the daemon picks up the change on the next scan. There is no SIGHUP reload; the in-flight HTTP / WS / scheduler state keeps running, the config struct is re-read per scan.
With [project.launch] present, a scan builds and starts the local app,
waits for the health URL, runs seed hooks, and captures lifecycle logs
under <state>/logs/environment/<run-id>/. Leave the launch table out
when the app is already running and you only want static source context.
Field reference:
| Field | Meaning |
|---|---|
[[project]] name |
Unique project name. Used in CLI flags (--project NAME), in API paths (/api/v1/projects/:id), and in run records. |
[[project]] description |
Optional free-form description rendered in the SPA. |
[[project]] target_base_url |
Optional deployed-target base URL. Flows into the sandbox env-builder as a compose override so confirmed exploits can address the right host. |
[[project.repo]] name |
Operator-facing repo identifier. Used in --repo NAME, the UI, and run records. Must be unique within the project. |
[[project.repo]] i_own_this |
Per-repo consent gate. The daemon refuses to ingest without it. |
[[project.repo]] enabled |
Defaults to true. Set false to keep the entry in the file but skip it during scans. |
[project.repo.source] kind |
local-path or git. |
[project.repo.source] path |
local-path only. Absolute path to a checkout on disk. |
[project.repo.source] url |
git only. Clone URL. |
[project.repo.source] branch |
git only. Optional branch override. |
[project.repo.source] auth |
git only. Credential descriptor: ssh-key:<path>, token-env:<var>, or gh-app:<id>. |
#Trigger a scan
Three ways to kick a scan, all using the same dispatcher.
From the UI: open the project detail page and click Start
pentest. The button opens a safety modal. Leave exploit mode and
state-changing probes off for the default non-destructive run; turn
both on only for an owned, disposable target where mutating live
probes are acceptable. The page subscribes to the WebSocket and
streams RunStarted, RepoStarted, RepoFinished, and
RunCompleted frames as they arrive.
From the CLI:
nyx-agent scan --project acme-app
nyx-agent scan --project acme-app --repo acme-backend
Without --project, every enabled project runs. --repo NAME
narrows within the selected projects and is rejected unless at
least one --project is set. With --output report.json the scan
writes a machine-readable report you can pass to pr-comment --report or to an external dashboard. With --since-ref main the
report is filtered to findings whose path the working tree
changed against main (uses git diff --name-only); the scan
exits non-zero if the diff cannot be computed.
Via HTTP (assumes the loopback default and an auth token):
TOKEN=$(cat ~/.local/share/nyx-agent/auth_token)
curl -sS -X POST \
-H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:8765/api/v1/projects/<project_id>/scan
The dispatcher responds 202 with a run_id you can poll on
/api/v1/runs/<id> or subscribe to via /api/v1/runs/<id>/events
(WebSocket). The <project_id> is the id returned from nyx-agent project list or POST /api/v1/projects. See docs/api.md
(forthcoming) for the full route reference.
#Read the results
CLI (one block per scanned project):
scan: project acme-app run 01J... finished in 12_345ms - 1 succeeded, 0 inconclusive, 0 failed
- acme-backend: Succeeded (diags: 7, 12_180ms)
SPA: the findings table shows every persisted finding with severity,
status, repo, path, and the cap label. Filters live on the toolbar.
Quarantine is its own tab; findings the AI surfaced but the
dynamic verifier did not yet confirm sit there until promoted.
CLI inspectors:
nyx-agent inspect quarantine # quarantined findings + AI candidates
nyx-agent traces # AI conversation traces (recent first)
nyx-agent traces --finding F... # traces scoped to one finding
#Recurring + remote triggers
Two no-touch paths kick a scan without the daemon's UI or CLI:
[[schedule]]cron entries fire from the in-process scheduler. Seedocs/triggers/cron.md.POST /webhook/gitaccepts HMAC-signed pushes from any self-hosted git server. Seedocs/triggers/webhook.md.
For CI gating, the shipped composite GitHub Action runs a scan and
posts a dedup'd PR comment. See docs/ci/github-actions.md.
#Common failure modes
#scan: no repositories selected; configure one in nyx-agent.toml
No [[project]] blocks define any repos, every entry has enabled = false, or --project NAME / --repo NAME did not match a
configured entry. Add one per the Attach a repo to the
project section.
#scan: --repo requires --project context (or use --project to scan whole projects)
--repo NAME was passed without a --project. Scoping is
intentionally explicit; add --project NAME (repeatable) to
narrow the search space first.
#Browser opens but the SPA shows "needs configuration"
nyx-agent.toml is missing. Either complete the wizard at /setup
or write the file by hand and reload. The daemon does not require
a restart.
#error: bind 127.0.0.1:8765: Address already in use
Another nyx-agent serve (or another process) holds the port. Stop
the other process, or pass --listen 127.0.0.1:0 to pick a free
port and read the actual address off the ready on ... line.
#Scan exits 1 with no diagnostics
The static-pass succeeded but at least one repo reported a failure
(ingest error, scanner panic, or Inconclusive outcome). The CLI
report lists per-repo status; the SPA's run detail view shows the
full failure path including nyx stderr.
#Related pages
docs/install.md: building from source and verifyingnyx-agent doctor.docs/triggers/cron.md: recurring scans via[[schedule]].docs/triggers/webhook.md: push-driven scans via signed HTTP.docs/ci/github-actions.md: PR-gate composite Action.