Skip to main content

Create a scan

A scan is one run of a workflow against a target. It bundles the target, the model and harness, and the skills, post-scripts, and rankers you want applied, then the engine schedules it and runs every step.

open·kritt new scan form
The new-scan form, section by section.

The form

Open Scans → New scan, then choose one of two starting points:

  • Blank scan opens the default form for a new configuration.
  • Duplicate an existing scan lets you select a previous scan, copies only its editable inputs, and opens them for review. Results, logs, status, attempts, and timestamps are not copied.

Then fill in or review each section:

  1. Workflow

    The blueprint to run. Its steps execute in depth order.

  2. Target

    A remote or local repository, an optional commit_sha, and a repo_scope describing what to focus on.

  3. Dependencies

    Optional dependency repos checked out alongside the target.

  4. Configuration

    A JSON configuration blob the agents can read.

  5. Extra

    Values for any extra keys the workflow requires.

  6. Model and harness

    The model, provider, harness, and thinking effort to run with.

  7. Skills, post-scripts, rankers

    Attach skills, post-scripts, and rankers for this run.

  8. Launch

    If another scan is active, choose Start immediately to run concurrently or Queue to wait until all active scans finish.

HTTP API

The UI is the guided creation path. Automation can queue the same scan with POST /api/scans; ./kritt configures and starts the stack but does not have a scan subcommand.

Obtain workflow, post-script, skill, and ranker content from their list endpoints first:

curl --fail http://127.0.0.1:3002/api/workflows
curl --fail http://127.0.0.1:3002/api/post-scripts
curl --fail http://127.0.0.1:3002/api/agent-skills
curl --fail http://127.0.0.1:3002/api/severity-rankers

The create body accepts these fields:

FieldRequiredShape and behavior
workflowIdYesExisting workflow ID.
postScriptIdYesExisting primary post-script ID. Put additional IDs in configuration.post_script_ids.
agentSkillIdsNoArray of existing numeric skill IDs.
launchPolicyWhen another scan is activeimmediate starts concurrently; queue waits for all active scans to finish.
repo_kindNoremote (default) or local.
repo_fullYesGitHub owner/repo/URL for remote; one immediate folder name under /local_repos for local.
commit_shaRemote onlyCommit, tag, or branch to check out; defaults to HEAD. Local snapshots ignore it.
repo_scopeNoFocus description exposed as {{repo_scope}}; defaults to full repository.
dependenciesNoArray of { "kind", "repo_full", "commit_sha"? } references.
configurationNoJSON object exposed as {{configuration}}; repeat_runs and additional post-script IDs also live here.
modelYesProvider model ID or supported alias.
model_providerYescodex, claude, or openrouter.
harnessYesSupported values are codex and claude-code; it must be compatible with the provider.
thinking_effortNoModel/harness-specific: default, low, medium, high, xhigh, max, or ultra; defaults to medium.
jobLimitNoPositive whole-number cap on logical workflow and post-processing model jobs. Internal retries do not consume another job. Omit or use null for unlimited.
severity_rankerYesNon-empty combined Markdown ranking rules, not a ranker ID.
extraAs required by selected promptsObject containing every {{extra.<key>}} referenced by the selected workflow or post-scripts. Unknown keys are discarded.

Provider/harness combinations are codex + codex, claude + claude-code, and openrouter + claude-code or advanced codex. The API returns 422 with field-specific errors when IDs, required scan extras, local folders, configured providers, models, or combinations are invalid.

If launchPolicy is omitted while another scan is active, the API returns 409 with code: "scan_launch_policy_required" and does not create the scan. Resubmit the same body with immediate or queue.

Remote scan example

Replace the IDs and model with values available in your installation. This example pins the target commit, adds a remote dependency, attaches a skill and two post-scripts, and uses OpenRouter through Claude Code:

curl --fail-with-body \
-X POST http://127.0.0.1:3002/api/scans \
-H 'Content-Type: application/json' \
--data-binary @- <<'JSON'
{
"workflowId": 1,
"postScriptId": 1,
"agentSkillIds": [1],
"repo_kind": "remote",
"repo_full": "octocat/Hello-World",
"commit_sha": "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d",
"repo_scope": "authentication and untrusted input paths",
"dependencies": [
{
"kind": "remote",
"repo_full": "octocat/Spoon-Knife",
"commit_sha": "HEAD"
}
],
"configuration": {
"repeat_runs": 1,
"post_script_ids": [1, 2]
},
"model": "z-ai/glm-5.2",
"model_provider": "openrouter",
"harness": "claude-code",
"thinking_effort": "low",
"severity_ranker": "Rank exploitable, remotely reachable findings first.",
"extra": {}
}
JSON

Local scan example

Create the host folder before submitting. With the default Compose mapping, ./local_repos/qa-smoke-target on the host appears as /local_repos/qa-smoke-target in the backend and engine. The request contains only the immediate folder name, not either absolute path:

mkdir -p ./local_repos/qa-smoke-target
# Add the source files to ./local_repos/qa-smoke-target, then:

curl --fail-with-body \
-X POST http://127.0.0.1:3002/api/scans \
-H 'Content-Type: application/json' \
--data-binary @- <<'JSON'
{
"workflowId": 1,
"postScriptId": 1,
"repo_kind": "local",
"repo_full": "qa-smoke-target",
"repo_scope": "full repository",
"dependencies": [],
"configuration": {},
"model": "sonnet",
"model_provider": "claude",
"harness": "claude-code",
"thinking_effort": "medium",
"severity_ranker": "Rank exploitable findings first.",
"extra": {}
}
JSON

Local source contents, including modified and untracked files, are copied into a fixed scan snapshot. Review them for secrets before queueing the request.

Lifecycle

An immediately started scan normally follows:

pending → prewarming_cache (optional) → running → post_processing → completed

A queued scan stays queued until no active scans remain, then moves to running. prewarming_cache may be skipped when no cache preparation is needed.

A temporary provider limit moves active work to rate_limited. The engine preserves completed work and returns the scan to running when its automatic retry is due. An active scan can also move to paused; stopped and failed require an operator to resume them. Reaching jobLimit also stops a scan after already-started jobs finish; raise or remove the limit before resuming it.

The following pages break down each part of the form.