Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

Your agent tests. Red Clippy keeps the record.

Red Clippy is open-source pentest management built to be operated by an AI agent. Connect it to Claude Code, Codex, or any other client that supports MCP, and it runs the engagement alongside you: scope and assets, recon observations, methodology coverage, and findings with CVSS and evidence.

Everything it writes goes into a SQLite database and shows up in the web panel straight away, where you can edit or delete any of it. The panel, the REST API and the MCP server all work on the same data.

It ships as a single binary. No installer, no runtime to set up, and no database server to run.

Who this manual is for

Testers who want an agent’s speed without giving up the discipline of a real engagement: work that does not get repeated, findings that do not evaporate between sessions, and nothing reaching the report that was never proved. The agent is the intended operator, but nothing here requires one, and everything it writes you can write yourself.

Working with an agent comes first, because that is what Red Clippy is for. Using the panel then covers the browser side screen by screen, for the work you do yourself and for reviewing what the agent did. Administration holds the things you set up once: members, the config file, the command line. Developer reference documents the API, the MCP tools and the data model, for anyone building against Red Clippy rather than using it.

If you just want the server running, start with Install and first run.

Background

A coding agent with a shell is a genuinely capable tester. It runs the same tools you do and it covers ground quickly. What it does not have is a memory that outlives its context window. When the window fills, the engagement goes with it: the next session rescans hosts it already cleared, re-tests what it already ruled out, and cannot tell you which parts of the scope were ever touched. A confirmed SQL injection ends up somewhere in a transcript nobody kept.

Red Clippy gives an agent two things it does not have on its own.

Somewhere to put the work. Every asset, observation, check and finding is written to the database as testing happens. Coverage becomes something you query rather than something you remember: which assets exist, which checks are cleared on each, what has already been reported. Tomorrow’s session starts where the last one stopped.

Rules to work by. A Red Team Instructions document is handed to the agent during the MCP handshake, before it does anything: verify before reporting, prove every claim, take the minimum access needed to show impact, leave third-party systems alone. You can override that document per organization and per engagement, because house rules differ between teams and clients.

You stay in the loop throughout. Everything the agent writes is an ordinary row you can read, correct, reclassify or delete in the browser.

Warning

Authorised testing only. Red Clippy is a tool for testers working under an engagement. Test only systems you own or have explicit written permission to assess. Scope marking and the Red Team Instructions exist to keep an agent inside the rules of engagement, but they are guardrails, not authorisation. An agent acts on your authority, and you remain responsible for everything it does.

Disclaimer

The screenshots here come from a demonstration database. “Northwind Retail” is not a real customer. Its hostnames use the reserved .test and example.com domains (RFC 2606) and its addresses come from the ranges reserved for documentation, 192.0.2.0/24, 198.51.100.0/24 and 203.0.113.0/24 (RFC 5737). None of them is allocated to anyone or routable on the internet. Nothing in this documentation describes a real target, a real engagement, or a real vulnerability.

Install and first run

Red Clippy ships as one executable. Download it, run it, open the browser.

Get the binary

Take the archive for your platform from the latest release.

Linux

tar xzf red-clippy-*-x86_64-unknown-linux-musl.tar.gz
cd red-clippy-*-x86_64-unknown-linux-musl
./red-clippy serve

Windows

Unzip the archive, then from that folder in PowerShell:

.\red-clippy.exe serve

If Windows SmartScreen or a firewall prompt appears the first time, allow the app. It listens on localhost only, so nothing outside the machine can reach it unless you change the bind address yourself.

The server prints a line like this and then stays in the foreground:

INFO red_clippy::web: red-clippy listening addr=127.0.0.1:7337 storage=./red-clippy-storage

Leave that window open. Closing it stops the server; Ctrl+C stops it on purpose.

Files created on first run

Red Clippy writes into whatever directory you launch it from:

PathWhat it is
red-clippy.dbThe database. Everything is in here.
red-clippy-storage/Uploaded evidence and per-pentest files.
red-clippy.tomlOptional config file, read if present.

Keep the binary and these together, and cd into that folder before starting the server. Both paths can be moved, see Configuration.

Create the first account

Open http://127.0.0.1:7337. Because the database is empty there is nothing to log into yet, so the browser shows a setup wizard instead of a login form.

The first-run setup wizard, with the organization slug, display name, username, optional email and password filled in

It asks for:

  • an organization slug and display name, which become your first workspace
  • a username, and optionally an email
  • a password, at least 8 characters, typed twice

Submitting creates the organization and your account, and signs you in. The wizard only appears while the database has no users, so it cannot be used later to add a second account.

The slug is permanent because it names the organization’s folder on disk (red-clippy-storage/org_<slug>/). The display name can be changed whenever you like. Use something short and lowercase for the slug: letters, digits, hyphen and underscore only.

Signing in

The sign-in form, with username, optional organization and password

From then on you sign in with your username and password. The Organization field is optional: leave it blank to land in your default organization, or type a slug to go straight to that one. If you belong to several organizations, you can also switch between them after signing in from the avatar menu.

If nobody can sign in, reset a password from the command line on the server:

red-clippy reset-password --username alice --password 'new-one'

That signs the user out everywhere and sets the new password immediately.

Next steps

How the data is organized

Five records carry most of Red Clippy: organizations, pentests, assets, observations and findings. This page explains what each one holds and where the boundary between them sits.

Organizations

An organization owns pentests, and each pentest owns the assets, findings and evidence for that engagement. One organization can see nothing belonging to another, so two clients’ engagements never mix.

If you test for one company, a single organization is all you need. If you consult for several clients, give each client its own.

An organization has two names:

  • the display name (“Northwind Retail”), shown in the panel, changeable at any time
  • the slug (northwind), a short lowercase identifier that cannot be changed once set, and that names the organization’s folder on disk

You can belong to several organizations and switch between them without signing out. In each one you are either an owner, who can add and remove people and rename or delete the organization, or a member, who works the engagements. See Organizations and members.

API keys are org-scoped: a key issued in Northwind can only ever read or write Northwind data. This is how one client’s data stays out of another client’s agent session.

Pentests

A pentest is one round of testing: a quarterly assessment, an annual review, a retest of last round’s findings. A client you test on a cycle accumulates one pentest per round, each with its own scope, dates and findings.

It carries a code, a name, a free-text scope description, start and end dates, a status and a notes field.

The code (PT-2026-Q3-NORTHWIND) identifies the engagement and cannot be changed once created. Leave it blank and the server generates one from the name and today’s date.

Status is one of planned, in_progress, completed or archived.

Creating a pentest seeds two things automatically:

  • five engagement phases (scoping, recon, testing, reporting, retest), which track the pulse of the engagement as a whole
  • the check catalogue, 135 built-in checks. Each asset gets its own copy of the ones that apply to it, the first time you open its methodology tab

Assets and observations

An asset is a unit you actually test. It has one of five kinds:

KindExample
domainshop.northwind.test
ip198.51.100.11
urlhttps://shop.northwind.test/checkout
apihttps://api.northwind.test/v2
mobilecom.northwind.shopper

Each asset carries its own methodology checklist, its own file area, an in-scope or out-of-scope flag, and a reachability marking (up, down, blocked, or unmarked).

An observation is something you noticed about an asset: a discovered path, a technology fingerprint, a DNS record, a certificate, a leaked credential, an open port, a wayback URL, an employee name. Observations attach to an asset rather than joining the scope list, and their kind is free text. The panel offers the conventional ones (port, service, dns_record, tech, cert, whois, email, employee, credential_leak, secret, screenshot, asn, takeover_candidate, default_cred_portal, wayback_url, js_endpoint, other) and accepts anything else you type.

The split matters because of volume. A single content-discovery run produces thousands of URLs. If each one became an asset, the asset list would be worthless and every one of them would drag in its own 100-plus item checklist. As observations they sit under the asset they came from, searchable and out of the way, and you promote the handful that deserve their own testing surface into real assets.

Two more things live alongside assets:

  • IP ranges: the CIDRs the customer declared, marked in-scope or out-of-scope. Marking a range fans the decision out to every IP asset inside it. See The network view.
  • API endpoints: for an api asset, a structured list of operations (method, path, headers, request and response bodies) with copy-as-cURL and OpenAPI export.

An asset can also be an alias of another. Mark www.northwind.test as an alias of northwind.test and the second one becomes the canonical: the row that carries the testing. The alias inherits its scope and reachability, and testing the canonical covers both.

Coverage: phases and checks

Coverage is tracked at two levels.

Engagement phases are the five seeded steps, plus any you add. They answer “where are we in this engagement”, and there are only a handful of them so the answer stays readable. Each has a status (pending, in_progress, done, na, blocked) and a notes field.

Checks are per asset. The catalogue holds 135 entries drawn from the OWASP Web Security Testing Guide (95 of them), the OWASP API Security Top 10, CIS quick wins and PTES, grouped into categories such as recon, config, input, auth, authz, session, crypto, client, business_logic and cloud. Each entry lists which asset kinds it applies to, so a mobile asset does not inherit web-only checks.

A check on an asset is not_tested, in_progress, tested, not_applicable or blocked, and carries its own notes. Marking not_applicable counts as resolved for coverage purposes. “We looked and it does not apply here” is a real answer, and it should not sit in the untested pile forever.

Findings

A finding is something you are prepared to put in the report. It has a title, severity, status, the affected asset and URL, a CVSS 3.1 vector and score, CWE and CVE identifiers, and four bodies of text: details, impact, proof of concept, and remediation.

Severity is critical, high, medium, low or info. Status is open, confirmed, remediated, accepted or false_positive.

Findings also carry tags, a comma-separated list. Two conventions matter because the panel filters on them:

  • asset:<value> ties the finding to an asset, so it appears on that asset’s Findings tab
  • check:<code> ties it to a catalogue check, so it appears under that check in the workspace

Files attach to findings as evidence: screenshots render inline, everything else downloads.

Supporting records

  • Files: each pentest has a file area, and so does each asset. Ordinary folders and uploads, for scan output and anything else you want kept with the engagement.
  • Notes: a free-text scratchpad on the pentest, plus notes on every phase, check and asset.
  • Attack graph: any two nodes (asset, observation or finding) can be linked with a free-form relation. You can then walk neighbours or compute the shortest path between two nodes.
  • Scans: an optional built-in runner for subfinder, nmap, masscan and naabu, with resumable per-target coverage. See Ports and the scan engine.
  • Red Team Instructions: the operating doctrine handed to an agent. It resolves most-specific-first, from a per-engagement override, to an organization default, to the document built into the binary. See Red Team Instructions.

Connecting an agent over MCP

This is the main way Red Clippy is meant to be used. red-clippy mcp exposes the whole application over the Model Context Protocol: scope and assets, observations, methodology coverage, findings, evidence, the attack graph, the scan engine and tool-output ingestion. A connected agent works the engagement rather than answering questions about it.

You need a running server and an organization to connect to. The setup wizard creates both on first run, so if you have signed in once you already have everything this chapter needs. See Install and first run.

Architecture

Three processes, on one machine:

  1. red-clippy serve holds the database and serves the panel on 127.0.0.1:7337.
  2. Your AI client (Claude Code, Codex, anything that speaks MCP) launches red-clippy mcp as a child process and talks to it over stdin and stdout.
  3. red-clippy mcp is a thin proxy. It calls the REST API of the running server using an API key.

The panel cannot start or stop the MCP process; your client owns it. To cut off access, revoke the key.

Because step 3 goes over HTTP to 127.0.0.1, the server and the agent are on the same machine in the normal setup. If your client runs elsewhere, point RED_CLIPPY_URL at the machine running the server, and be aware that the API key is then travelling over the network.

Issue an API key

Open Settings from the avatar menu.

The Settings page with the API keys table

Type a label (claude-code, codex, ci-runner) and press Issue key. The token appears once, in a banner, and is never shown again. Copy it now.

Tokens look like rcl_<prefix>_<secret>. The table keeps the prefix so you can tell keys apart later, along with when each was created and last used. Revoke disables a key immediately. Anything using it fails on the next call.

Keys are org-scoped. A key issued in Northwind can only read and write Northwind data, regardless of which organization your browser session is currently viewing.

To issue one from the command line instead:

red-clippy create-api-key --username alice --org northwind --name laptop
# -> rcl_<prefix>_<secret>   (shown once, store it now)

Copy the connection snippet

Scroll down to Connect MCP on the same page.

The Connect MCP section, with ready-to-paste snippets for Claude Code, Codex and a shell

The snippets are filled in with this server’s URL and the path to the binary that is running it, so there is nothing to substitute by hand. If you issued a key a moment ago, the token is already in them; otherwise they carry a placeholder to replace.

Pin to pentest is optional. Put an engagement’s id or code in it and the snippets gain RED_CLIPPY_PENTEST, which binds the session to that engagement. Write tools then accept calls without a pentest_id and the server fills it in, so you can say “log this finding” without naming the engagement every time.

Claude Code

claude mcp add red-clippy \
  --env RED_CLIPPY_URL=http://127.0.0.1:7337 \
  --env RED_CLIPPY_API_KEY=rcl_xxxx_yyyy \
  -- /path/to/red-clippy mcp

The name after add is only the label Claude Code shows. What matters is the command after --, which is what actually gets launched. Use the binary’s full path: Claude Code will not necessarily find red-clippy on its PATH. If you copy the command from Connect MCP, the right path is already there.

Check it worked with claude mcp list; red-clippy should show as connected.

One folder per organization

An API key belongs to one organization, so the cleanest arrangement is one working folder per client, with the MCP server added inside it:

mkdir northwind && cd northwind
claude mcp add red-clippy \
  --env RED_CLIPPY_URL=http://127.0.0.1:7337 \
  --env RED_CLIPPY_API_KEY=rcl_xxxx_yyyy \
  -- /path/to/red-clippy mcp

Claude Code launched from the Northwind folder sees only Northwind’s data; launched from the Contoso folder, only Contoso’s. That mirrors the tenant separation on the server, so one client’s findings cannot surface in another client’s session.

Use the default scope for this. Do not add --scope user, which would make one organization’s key active everywhere.

Your first organization came from the setup wizard. To add one per client, see Organizations and members.

The in-app Help page walks through the same setup with copyable commands.

The in-app Help page, showing the Claude MCP integration tutorial

OpenAI Codex CLI

In ~/.codex/config.toml:

[mcp_servers.red-clippy]
command = "/path/to/red-clippy"
args = ["mcp"]
env = { RED_CLIPPY_URL = "http://127.0.0.1:7337", RED_CLIPPY_API_KEY = "rcl_xxxx_yyyy" }

z.ai GLM

z.ai serves an Anthropic-compatible endpoint, so Claude Code can drive Red Clippy with GLM instead of an Anthropic model.

Add this to Claude Code’s settings: .claude/settings.local.json inside your working folder to scope it to one engagement, or ~/.claude/settings.json to apply it everywhere.

{
  "env": {
    "ANTHROPIC_BASE_URL": "https://api.z.ai/api/anthropic",
    "ANTHROPIC_AUTH_TOKEN": "your-z-ai-api-key",
    "ANTHROPIC_API_KEY": "",
    "API_TIMEOUT_MS": "3000000",
    "ANTHROPIC_DEFAULT_OPUS_MODEL": "glm-5.3",
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "glm-5.3",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "glm-5.3",
    "ANTHROPIC_SMALL_FAST_MODEL": "glm-5.3",
    "CLAUDE_CODE_SUBAGENT_MODEL": "glm-5.3"
  }
}

Two things to substitute:

  • your-z-ai-api-key with your own key. Prefer the project-local .claude/settings.local.json, which Claude Code keeps out of git, so the key does not end up committed.
  • glm-5.3 with whichever model z.ai currently offers. Model names change, so check z.ai’s Claude Code guide for the current one and set every entry to it.

Then add the MCP server exactly as above. Red Clippy is unaffected by the choice of model: MCP runs client-side, so the tools behave identically whatever is behind the agent.

Any other MCP client

red-clippy mcp is a plain JSON-RPC 2.0 stdio server. It needs two things in its environment and takes an optional engagement pin:

RED_CLIPPY_URL=http://127.0.0.1:7337 \
RED_CLIPPY_API_KEY=rcl_xxxx_yyyy \
/path/to/red-clippy mcp --pentest PT-2026-Q3-NORTHWIND
VariableFlagMeaning
RED_CLIPPY_URL--urlBase URL of the running server. Defaults to http://127.0.0.1:7337.
RED_CLIPPY_API_KEY--api-keyThe org-scoped token. Required.
RED_CLIPPY_PENTEST--pentestNumeric id or exact code of an engagement to pin the session to. Optional.

Protocol versions 2024-11-05, 2025-03-26 and 2025-06-18 are all accepted.

Run one MCP entry per active engagement and switch engagements by switching servers.

Verifying the connection

Ask the agent directly: “can you access the Red Clippy MCP?” A connected client will confirm it, and can usually name the tools it now has.

Follow up with “list my pentests”. If it comes back with the engagements from your organization, the URL, the key and the tool surface are all working.

Running an engagement with an agent

An engagement runs in three parts. You set it up, the agent tests and records, and you supervise and correct. This page describes that loop.

The handshake

When your client starts the MCP server, three things reach the agent before its first tool call:

  1. A statement that Red Clippy is the system of record for this engagement, and that assets, observations, checks and findings belong in it rather than in the transcript.
  2. If the session is pinned, which engagement it is pinned to, so tools taking a pentest_id default to it.
  3. The Red Team Instructions document in full, resolved for this engagement.

The third item matters most. An agent that has to ask for the doctrine will often not bother, so it arrives in the handshake instead. get_instructions re-reads it on demand.

Setup

Create the pentest. Code, name, dates, and the scope text from the engagement letter. Do this in the browser: it takes a minute, and the scope text is what both you and the agent read before touching anything. See Pentests.

Declare the scope. Add the domains, hosts and IP ranges you are authorised to test, and mark anything excluded as out of scope. Type them in, or paste the customer’s list to the agent and have it enter them. Either way, an agent checks these markings before touching anything. See Scope and assets and The network view.

Connect the agent, pinned to this engagement. See Connecting an agent over MCP.

Testing

From there the agent runs the tools it would run anyway, from its own shell, and posts the results back. In practice that means:

Recon. Subdomain enumeration, DNS, certificate transparency, content discovery. Raw output goes in with ingest_tool_output, one call per scan file regardless of size. Anything it worked out itself goes in with create_asset or bulk_observations.

Triage. Real scope units become assets with create_asset or promote_observation. Everything else stays an observation. This step decides whether your asset list is still usable in a week, so say in your first message what you consider asset-worthy.

Scope decisions. set_asset_marking and bulk_set_asset_marking for in and out of scope and reachability; add_ip_range and apply_ip_range for whole CIDRs.

Coverage. list_checks returns the catalogue for an asset with its current status. As the agent clears ground it calls mark_check or bulk_mark_checks with notes, so coverage is recorded as it happens rather than reconstructed afterwards.

Findings. create_finding with severity, CVSS vector, CWE, details, impact, proof of concept and remediation, tagged asset:<value> and check:<code>. attach_evidence uploads the screenshot or the raw output that proves it.

Wrap-up. pentest_coverage and get_pentest_summary say what is left.

Supervision

Watch it land in the browser, live. Everything the agent writes is an ordinary row you can edit, reclassify or delete, and it will see your edits on its next read.

Three habits are worth building:

Check the coverage view. The engagement Overview percentage and the per asset check counts show what has actually been tested. An agent that has filed six findings but cleared nine checks has not tested the target. It has skimmed it. See Workspace and coverage.

Read the findings, not the summary. The agent’s account of its own work is a summary of a summary. The finding rows are the deliverable, and each one has a proof of concept field that either proves the claim or does not. See Findings.

Correct the classification. Severity, status, and whether something is a finding at all are judgement calls. Reclassifying an agent’s high to medium, or its finding to a false positive, takes ten seconds in the browser and is exactly the kind of decision a human should be making.

Multiple engagements

An MCP server is pinned to at most one engagement and an API key belongs to exactly one organization. Run one MCP entry per active engagement, in one working folder per client, and switch engagements by switching servers. Nothing stops you pointing two clients at the same server. The separation exists so that you do not have to rely on an agent keeping two clients’ data apart by itself.

Working without an agent

Everything above goes through the same REST API the panel uses. Anything an agent records you can enter, correct or delete yourself; anything you enter it can read. Run an engagement entirely by hand, entirely through the agent, or switch between the two mid-session.

Red Team Instructions

The Red Team Instructions are a single markdown document describing how to operate during testing: mindset, working discipline, ethics and scope. It is not a methodology; the check catalogue already owns “what to test”. This document is about conduct.

It matters because it reaches an agent in the MCP handshake, before the first tool call, rather than waiting to be asked for.

Resolution order

Resolution is most-specific-first:

  1. a per-engagement override, edited on that pentest’s PT Settings page
  2. an organization default, edited under Settings
  3. the built-in document, compiled into the binary

There is always an answer. A fresh install with nothing customised serves the built-in document to every agent that connects.

The built-in document

It opens by saying it is a reference, not a checklist, and that treating it as a complete list of things to test is a mistake. Then, in sections:

Mindset. Follow the application, not a list. Every response is information. Weird things deserve attention. Do not stop at confirming a vulnerability exists; work out what an attacker can actually do with it, because that is what determines severity.

Working discipline. Say what you are about to do before doing it, especially for anything that writes or modifies. Verify everything before writing it up. A finding that cannot be confirmed on demand is not a finding. Prove every claim, or use “probable” and “not confirmed during testing” and mean it.

Ethics and scope. Take the minimum access needed to demonstrate impact: a few real records with identifying fields visible proves a data exposure, a full dump proves nothing extra and creates a problem. Do not log in with discovered credentials unless told to. Leave third-party systems alone; document why you found them and move on. Keep regulatory and legal advice out of findings.

The rest is practical reference: recon, target assessment, JavaScript bundle analysis, API testing, error triggering, response headers, email security, takeover, exposed development artefacts, authentication flows, storage and file access, third-party services, CAPTCHA, management interfaces, then finding triage, severity calibration and how to write findings and replication steps.

Read it in full at Settings > Red Team Instructions, or ask a connected agent for get_instructions.

Editing the document

The Red Team Instructions editor on the Settings page, showing the built-in default

The editor is the same at both levels. A badge tells you where the text you are looking at came from:

BadgeMeaning
Using built-in defaultNothing is customised at this level or above.
Using organization defaultOn a pentest page, when the organization has an override but this engagement does not.
Custom for this organization / Custom for this pentestAn override exists here, and Reset can remove it.

Edit and Preview toggle between the markdown source and the rendered version. Save writes an override at the level you are editing, whatever text is in the box. Note what that means: saving on a pentest page while the built-in default is loaded copies the built-in text into a per-engagement override. If you only meant to look, do not press Save.

Ctrl+S saves. Reset deletes the override at this level and falls back to the next one down, after a confirmation.

When to override

At organization level, for house rules that apply to every client: your reporting conventions, your evidence standards, the things your team has learned the hard way.

The per-engagement Red Team Instructions override on the PT Settings page

At engagement level, for this client’s rules of engagement: a test window, a system that must not be touched even though it resolves inside an in-scope range, a customer who wants no automated scanning at all, a requirement to notify before anything intrusive.

Keep the machine-enforceable parts of scope in the actual scope markings, on assets and IP ranges. The instructions are for the parts a rule cannot express.

Over MCP

Three tools, all of which take a scope argument of org or pentest:

ToolWhat it does
get_instructionsReturns the resolved document and which level it came from.
set_instructionsWrites an override at the given level.
reset_instructionsDeletes the override at the given level.

An agent gets the document automatically on connect. get_instructions is for re-reading it mid-session, which is worth asking for when a long session has pushed the handshake out of context.

set_instructions exists so an agent can help you write the document, not so it can rewrite its own rules unprompted. The handshake text tells it to change the document only with your agreement, which is a norm rather than a control. If that matters to you, watch the Settings page: an override that appeared without you asking for it is visible there.

Getting around

The panel is a single-page app served by the same binary that serves the API. Everything in it is reached from the top bar.

The engagement dashboard: severity counts across the organization and the recent pentests list

The top bar

Five links, left to right after the logo:

LinkWhat it shows
OverviewThe dashboard above: severity counts across the organization and your recent engagements.
PentestsEvery engagement in this organization, searchable and filterable by status.
FindingsEvery finding across every engagement, in one list.
ReconEvery asset across every engagement, in one list.
HelpThe in-app tutorial for connecting an agent.

Under the logo is the display name of the organization you are currently working in. If it says the wrong client, you are in the wrong tenant. Switch from the avatar menu.

On the right:

  • a Search button that opens the command palette
  • the avatar menu, holding Organizations, Settings, the theme picker and Sign out

On a narrow screen the five links collapse into a drawer behind the menu button.

The dashboard

The severity tiles count findings across your 25 most recent engagements, not just the active one. A total that looks too high usually means an old engagement is still being counted.

The command palette

Press Ctrl+K (or Cmd+K on macOS) anywhere in the panel.

The command palette open over the dashboard, showing pentest, asset, observation and finding matches for a search term

It searches across pentests, findings, assets, observations, phases and attachments in the current organization at once. Each row says which kind of record it is and which engagement it belongs to. Arrow keys move, Enter opens, Escape closes.

Paste a hostname, an IP, a partial path, a username or a CVE identifier. When there are more matches than the palette can show, See all results opens the full search page, described in Searching across engagements.

Inside an engagement

Opening a pentest replaces the page with the engagement shell: a breadcrumb and title at the top, a row of tabs under it, and three buttons on the right.

The tab strip inside an engagement, with Overview, Workspace, Network, Ports, Assets, Findings, Notes and Files

The tabs are:

TabPage
OverviewProgress, severity breakdown, next phases, recent findings, engagement details.
WorkspaceEngagement phases, and per-asset methodology when an asset is focused.
NetworkDeclared CIDR ranges and discovered IP clusters.
PortsEvery open port found in this engagement, grouped or tabulated.
AssetsThe scope list: in scope, flagged, out of scope.
FindingsThe findings for this engagement.
NotesA full-page scratchpad.
FilesThe engagement’s file area.

The three buttons on the right stay put on every tab:

  • Scanner opens the built-in scan engine
  • Recon opens a side drawer listing this engagement’s observations, with a quick add form
  • PT Settings opens engagement details and the Red Team Instructions override

Counts next to a tab are live. Assets counts in-scope, reachable assets only, since that is the remaining workload rather than the total inventory.

Theme

The theme picker in the avatar menu has three settings: System, Light and Dark. System follows the operating system and keeps following it. The other two pin the panel regardless of what the OS reports, which is useful when you are capturing screenshots for a report.

Pentests

A pentest is one round of testing against one client. That might be a quarterly external assessment, an annual full-scope review, or a retest after the client fixes what you reported last time. Client work is rarely a one-off, so each round gets its own pentest record rather than being added to the last one.

Every asset, observation, finding, check and file you record belongs to exactly one pentest. That keeps each round’s results self-contained, while the organization-wide Findings and Recon pages read across all of them: what you reported last time, whether a host has been tested before, what is still open.

The list

Pentests in the top bar lists every engagement in the current organization, newest first.

The pentests list with a search box, a status filter and one row per engagement

The search box matches on code, name, scope text and notes. The dropdown next to it filters by status. Both live in the URL, so a link to a filtered view sends someone else to the same filtered view, and the browser back button does what you expect.

Creating a pentest

New pentest opens a form with a single field.

The new pentest form, with an optional name field

The name is optional. Leave it blank and press Enter for a pentest named after today’s date, which suits a recurring internal test. Type a name and the code is derived from it: “Northwind Q3 retest” becomes something like PT-2026-08-27-northwind-q3-retest.

Creating the pentest seeds the five engagement phases and wires up the check catalogue. The remaining details (code, scope, dates, status) are edited afterwards on the PT Settings page.

The engagement overview

Opening a pentest lands on Overview. This is the “where are we” page.

The engagement overview: progress, severity breakdown, next phases, recent findings, recon counts and engagement details

Findings by severity counts this engagement only, unlike the tiles on the main dashboard.

Next engagement phases lists what is still unresolved. Clicking one jumps into the workspace with that phase selected.

Engagement details shows the scope, dates and notes as entered on PT Settings.

PT Settings

The PT Settings button sits top right on every tab inside an engagement.

Pentest settings: engagement details with code, name, dates, status, scope and notes

The top card is the engagement record itself:

FieldNotes
CodeShown for reference. The code is fixed at creation because the on-disk storage path is derived from it, so the server ignores edits to this field.
NameFree text. Required.
Start / EndYYYY-MM-DD.
Statusplanned, in progress, completed, archived.
ScopeFree text. Shown at the top of the Network page and under Engagement details.
NotesAnything else about the engagement. Shared with the Notes tab.

Put the real rules of engagement in Scope: what is in, what is out, the test window, and who to call. Nothing enforces the text, but it is what a human reads before testing and what an agent sees when it reads the engagement. Machine-enforced scope is separate, set with the in-scope and out-of-scope markings on assets and IP ranges.

Below that is the Red Team Instructions override for this engagement, covered in Red Team Instructions.

Closing an engagement

Set the status to completed when the report goes out, and archived when you are done with it entirely. Archived pentests stay in the list and stay searchable; the status is a marker, not a filter that hides them.

A pentest cannot be deleted, from the panel or the API or MCP. Assets, observations and findings can each be removed individually, but the engagement record is permanent once created, so a mis-typed code or a throwaway test engagement stays in the list. Archive it.

The one thing that removes a pentest is deleting the organization it belongs to, which takes every other engagement with it. See Organizations and members.

Scope and assets

The Assets tab is the scope list: everything you are testing, everything you have set aside, and everything the customer told you to leave alone.

The assets page: quick add box, kind filter chips, and the in-scope table

Adding assets

The box at the top takes one value. The kind is inferred from its shape, so shop.northwind.test becomes a domain, 198.51.100.11 an IP, https://shop.northwind.test/checkout a URL. If the shape is not recognised it defaults to a domain, which you can correct on the asset’s Details tab.

For anything larger than a handful of values, you have better options than typing:

Filtering

The filter box matches on the asset value or its label. The chips beside it narrow to one kind: All, Domain, IP, URL, API, Mobile.

Filters live in the URL, so the current view is always linkable.

The three tables

The page splits assets into three tables.

In scope is the working list: assets that are in scope and either marked reachable or not marked at all. This is the remaining workload, and it is the number shown on the Assets tab in the header.

Host down / blocked holds in-scope assets you have flagged as unreachable or WAF-blocked. They are still in scope, just not the next thing to work on. Clear the marking and they move back up.

Out of scope, do not test holds everything excluded. Keeping excluded assets visible means there is always an answer to “why is this not in my list”, instead of the asset having quietly disappeared a week ago.

The down/blocked and out-of-scope tables below the main list

Each table paginates independently, so paging through out-of-scope entries does not disturb your position in the main list.

The columns

ColumnMeaning
AssetThe value, plus badges: reachability, label chip, alias link, API count.
ProviderWho hosts it (Cloudflare, AWS, Akamai). Useful for spotting third-party infrastructure at a glance.
OriginThe asset kind.
ProgressChecks done over checks total for this asset. 0/0 means you have not opened its Methodology tab yet, which is what creates the checklist.

Three controls sit at the end of each row. Workspace opens the asset, the round button opens the marking picker, and the red button deletes.

Deleting an asset removes it, its observations and its check results. Findings tagged asset:<value> are kept, so an accidental delete does not take your report with it. You delete those separately from the Findings page.

Marking scope and reachability

The round button on each row opens a picker with the available markings:

MarkingWhen
ReachableThe host answers.
Host downProbes do not connect at all.
Getting blockedA CDN or WAF is short-circuiting your requests. Different problem, different fix.
UnknownClears the marking.
Out of scopeExcludes the asset from testing and moves it to the bottom table.

Marking something out of scope also clears its reachability, because it is not going to be probed either way.

The same fields, plus a note, are on the asset’s Details tab, where you can record the evidence for the marking (Cloudflare 1020, ECONNREFUSED 80/443). The note stops the same host being re-probed by three different people over a week.

Labels

A label is a short tag of your own choosing: primary, revisit, client-confirmed. It renders as a coloured chip in the table and beside the asset title.

Clicking a chip filters the whole page to everything sharing that label, and clicking it again clears the filter. Labels are free text with a 40-character limit, set on the asset’s Details tab.

Asset details

Open an asset and choose Details.

The asset Details tab, showing resolved IPs, aliases, label, provider and scope controls

Four things live here.

Related IPs / hostnames shows what this asset resolves to, read from asset attributes that scanner imports populate. A domain lists the IPs behind it; an IP lists the hostnames pointing at it. Rows imported without that metadata do not appear.

Aliases record that two names are the same target. Link www.northwind.test as an alias of northwind.test and it inherits the canonical’s scope and reachability, and no longer needs testing of its own. The alias row still exists and is still searchable, but testing the canonical now covers both.

Label, Provider and Organization are free-text metadata. Set Organization only when the asset is owned by someone other than the engagement’s customer. That is usually the sign of a third-party host which should probably be out of scope.

Reachability and the Out of scope checkbox are the same markings as the row picker, with room for the note.

The Recon drawer

The Recon button in the engagement header opens a side drawer over whatever page you are on.

It lists the engagement’s observations grouped by kind, with a search box and a kind filter, and a small add form at the top. The add form is kind-aware: pick one of the five asset kinds and it creates an asset, pick anything else and it creates an observation attached to the matching asset.

The drawer shows at most 200 rows, and says so when it has cut the list short. For anything bigger, use the Assets tab or the asset’s own Observations tab. Both paginate properly.

The network view

The Network tab answers two questions at once: what perimeter the customer declared, and what the address space you have actually found looks like.

The network page: scope description, bubble map, and the scope map tree

Scope description

The card at the top is the free-text scope from PT Settings, reproduced here because this is the page where you decide what is in and what is out. Edit it with the PT Settings button in the header.

The network map

Every bubble is a cluster of IP addresses, with area proportional to how many addresses it holds, biggest first. Clicking one scrolls the tree below to the matching row and highlights it.

ColourMeaning
GreenA declared in-scope range.
BlueA discovered cluster: addresses you have found that fall in no declared range. These still need a scope decision.
IndigoA loose /16: many /24 blocks each holding one or two addresses, which is the shape a CDN makes.
GreyThe singletons bucket.

Declared out-of-scope ranges are deliberately not drawn. The map is for finding targets, and exclusions are not targets. They still appear in the tree below for the record.

The scope map

Below the map is the tree, in three sections.

The scope map tree, with declared ranges expanded to show the IP assets nested under each

Declared ranges are the CIDRs from the engagement letter, each marked IN or OUT, with the addresses you have discovered nested underneath. Expand a row to see each member IP with its reachability and check progress, which explains the “4 IPs, 3 up” summary on the row itself. A wide range such as a /16 gets an extra /24 fold, so the customer’s perimeter and your actual coverage line up visually.

Add range opens a dialog for a CIDR, its in or out marking, and optional ASN, organization and notes. This is where the engagement letter gets typed in.

Discovered clusters group addresses that fall in no declared range, by /24, biggest first. Each has In and Out buttons. Pressing one records the /24 as a declared range and applies that scope decision to every member address at the same time. This is the quickest way to deal with a recon sweep that turned up several unfamiliar blocks.

Singletons are /24 blocks holding a single address. They sit in a collapsed bucket at the bottom, where they cannot crowd out the larger clusters.

Deleting a range

Deleting a declared range removes the range record only. No asset rows are touched, and any out-of-scope markings already applied to member addresses stay where they are. If you want those cleared too, clear them per address on the Assets page.

This is deliberate. Removing a line from the engagement letter should not silently pull a host back into testing.

Workspace and coverage

The Workspace tab is where testing is actually recorded. It has two modes. With no asset selected it tracks the engagement as a whole. Focus an asset and it becomes that asset’s testing surface.

Engagement phases

The workspace in engagement mode: the phase list on the left, the selected phase on the right

The left column lists the phases as a vertical timeline. Five are seeded with every pentest:

PhaseCategoryWhat it covers
Scope & rules of engagementscopingIn-scope assets, exclusions, test windows, escalation contacts, retest policy.
Recon & asset discoveryreconSubdomains, DNS records, ASN and IP ranges, certificate transparency, cloud assets, leaked secrets.
Mapping & active testingtestingPort and service scans, control fingerprinting, then per-asset coverage.
Report & evidence packagereportingWriteups with CVSS, impact, repro steps and evidence.
Retest & closureretestRetest fixed findings, close out, archive per the retention policy.

The counter in the header (2/5) counts phases that are done or na. Add phase at the bottom adds your own with a title and optional category.

The right pane is the selected phase: its status dropdown, a notes box, and two buttons. Mark done & next resolves the phase and jumps to the next unresolved one. Skip moves on without changing anything.

Notes save when the box loses focus, or on Ctrl+S. There are only five phases and they last the whole engagement, so this is where the durable notes belong: “client confirmed staging is behind the same WAF as production”.

Phases are deliberately coarse. Anything finer belongs in the per-asset checklist, where it can be tracked per target instead of once for the whole engagement.

Focusing an asset

Open an asset from the Assets page (Workspace on its row), from a finding’s affected-asset link, or from the command palette. The engagement tab strip is replaced by the asset’s own.

An asset in focus, on the Methodology tab, with the check list and the selected check

The title line shows the asset value, its kind, its scope state, its label if it has one, and its check progress. Tabs vary by kind:

TabShown forWhat it holds
MethodologyallThe check catalogue for this asset.
URLsdomain, urlURLs discovered under this asset.
EndpointsapiStructured API operations.
PortsipOpen ports on this host.
ObservationsallEverything else found here.
FindingsallFindings tagged asset:<value>.
NotesallA notes field for this asset.
FilesallThis asset’s slice of the file area.
Importip onlyCommand hints and an upload box for scanner output about this host.
DetailsallAliases, resolved addresses, label, provider, scope markings.

Methodology

The coverage strip along the top counts the catalogue for this asset kind, then breaks it down: tested, n/a, active, blocked, untested.

The left column groups checks by category (recon, config, input, auth, authz, session, crypto, client, business logic, cloud) with a progress count on each. Only the group holding the selected check is expanded, which keeps the list short on a domain with 114 checks. Above it sit a text filter and a status filter, with a counter showing how many checks match.

Selecting a check fills the right pane with everything needed to work through it:

  • the check code, category, standard (WSTG, API Top 10, CIS, PTES), CWE and suggested severity
  • the description and the reference links, which go to the source methodology
  • a status dropdown, plus Tested and N/A buttons for the two common answers
  • a notes and evidence box, saved on blur or Ctrl+S
  • the findings already tagged for this check, and a one-line form to log another

Next untested at the bottom moves to the next unresolved check in the current filter, so the page can be worked as a queue.

Marking a check not_applicable counts as resolved. Use it. “We looked and there is no file upload here” is a real testing outcome, and it belongs in the record rather than in the untested pile forever.

Quick-logging a finding from a check

The form at the bottom of the check pane takes a title and a severity and creates a finding tagged with both asset:<value> and check:<code>. Severity is pre-filled from the check’s suggested severity and the check’s CWE is copied over. You are then taken to the finding detail page to fill in the rest.

Those two tags are why the finding then appears under this check and on the asset’s Findings tab.

Observations

The Observations tab for an asset, showing certificate, JavaScript endpoint, secret and technology entries

This tab holds everything discovered about the asset that is not a URL, a port or a finding. Each row shows its kind, its value and the source you recorded, and can be edited or deleted in place. Add observation takes a kind, a value and an optional source.

Content discovery output, technology fingerprints, certificates, DNS records, leaked secrets and takeover candidates all belong here. Keeping them out of the asset list is why one ffuf run does not make the scope list unusable.

URLs

For a domain or URL asset, the URLs tab lists paths discovered under it, with search, pagination, bulk import and export.

Additions are validated against the asset they are being added to. A URL whose host does not match, or whose path falls outside a path-scoped asset’s prefix, is rejected with a message telling you where it does belong. Without that check, URLs land under an asset they will never be attributed to and quietly disappear.

Import from previous PTs pulls the URLs recorded for this same host in the organization’s earlier engagements, grouped by the engagement they came from and excluding anything already present here. On a repeat client this saves re-running content discovery to find the paths you already know about, and it shows you at once whether a path you reported last round is still there.

Endpoints

An api asset gets a structured catalogue of operations instead of a flat URL list.

The Endpoints tab for an API asset, listing REST operations with method, path and summary

Each row carries protocol (REST, GraphQL, SOAP, gRPC), method, operation path, summary and source, and can hold query parameters, headers, and request and response bodies. The icons at the end of a row copy the endpoint as a cURL command or delete it.

Export OpenAPI produces an OpenAPI document for the whole asset, which is useful for feeding the API into another tool. Add endpoint adds one by hand. The REST API and MCP both have a bulk call for importing a whole specification at once.

Ports, Notes, Files, Details

Ports appears on IP assets and lists what is open on that host. Adding or editing a port matches on host, port and protocol, so re-importing a scan updates the existing rows instead of duplicating them.

Notes is a per-asset text area. Files is the asset’s slice of the engagement file area, with the same browser as the Files tab. Details is covered in Scope and assets.

Findings

A finding is something you are prepared to put in the report. Anything you cannot yet defend belongs in an observation or a note until you can.

The list

The findings list for an engagement, with severity chips, a search box and a status filter

Findings are sorted by severity first, then newest first, so critical work is always at the top. Filters live in the URL, so a filtered view is linkable and the back button works.

The search box matches title, URL, affected asset and tags. Clicking the active severity chip clears it.

The Affected column links to that asset’s workspace, the quickest way from a finding back to the rest of what you know about the host.

Export Excel downloads the current filter, not the whole list.

The finding form

The top of a finding: title, URL, affected asset, severity, status, CVSS and identifiers

The first card is the classification:

FieldNotes
TitleRequired. Describe the vulnerability class and the behaviour. Keep the host, path and product name out of it; they have their own fields, and a title that names one host is useless when the same bug turns up on three.
URLWhere it manifests, when there is a specific one.
Affected assetHostname, IP or component. This is what the Affected column links on.
Severitycritical, high, medium, low, info.
Statusopen, confirmed, remediated, accepted, false positive.
CVSS score0.0 to 10.0. Fill it in by hand or let the calculator do it.
CVSS 3.1 vectorThe vector string.
CWE / CVECWE-89, CVE-2024-12345.
TagsFree-form chips. Type and press Enter, or type a comma.

Compute score & severity parses the vector, writes the base score into the CVSS score field, and sets the severity to the matching band. Paste a vector from any source, press the button, and both derived fields match the vector instead of whatever was typed before.

Two tag conventions the panel understands:

  • asset:<value> puts the finding on that asset’s Findings tab
  • check:<code> puts it under that catalogue check in the workspace

Anything else is yours to use as you like. retest-passed, unverified and manual are all reasonable.

The four text fields

The details, impact, proof of concept and remediation fields

Four text areas, and they are not interchangeable:

Details is what is broken and where it shows up, in language a non-expert can follow. Not the exploit, not the fix, not a definition of the vulnerability class.

Impact is the concrete consequence: what data is exposed, what privilege is gained, what control is bypassed. Business impact, not exploit mechanics.

Proof of concept is the technical evidence. The request, the response excerpt, the payload, the stack trace. One self-contained request that proves the bug is worth more than five that circle it. Use triple backticks for blocks.

Remediation is what needs to change and what the end state looks like. Name the broken control. Resist writing the patch itself. Specific commands, config snippets and library versions are wrong often enough to cost you credibility, and the engineers reading the report know their own stack.

Evidence

The evidence section of a finding, with an inline screenshot and a text attachment

Below the body are the files attached to this finding. Images render inline and open full size in a new tab. Everything else shows as a file row and downloads.

Pick a file, optionally add a caption, and upload. The default size limit is 25 MiB per file, configurable with max_upload_bytes.

Evidence files are stored under the engagement’s directory with server-generated names, so the original filename never touches disk. See Files on disk.

Exporting findings

Two Excel exports, both plain .xlsx:

  • Export Excel on an engagement’s findings list exports that engagement, honouring the current search, severity and status filters
  • Export Excel on the cross-engagement Findings page exports everything in the organization, with two extra leading columns for the pentest code and name

Columns are ID, Title, Severity, Status, CVSS Score, CVSS Vector, CWE, CVE, URL, Affected Asset, Tags, Details, Impact, PoC, Remediation, Created, Updated.

Deleting a finding

Delete on the finding form removes it after a confirmation. Deleting an asset does not delete findings tagged against it, so an over-enthusiastic scope cleanup cannot take your report with it.

Searching across engagements

Three pages work across the whole organization rather than one engagement: Findings, Recon, and Search. The panel labels the first two Library, which is what the eyebrow above the heading says on each.

Findings across engagements

Findings in the top bar is every finding the organization has ever logged.

The organization-wide Findings page, with severity tiles, filters and one row per finding

Five severity tiles across the top double as filters: click one to narrow to it, click it again to clear. Below them are a search box that matches everything (title, asset, CWE, CVE, tags, and the details, impact, proof of concept and remediation bodies), a status filter, and a filter for one engagement.

The counter next to the search box always reads “n of m”, so you can tell an over-narrow filter from an empty database at a glance.

The page has three main uses:

  • Retest. Filter to last quarter’s engagement and work down the list.
  • Reuse. You have written this issue up before. Find that writeup rather than writing it again.
  • Repeat issues. The same missing header across four clients is worth raising differently from the same missing header once.

Export Excel downloads the whole organization’s findings, with the pentest code and name as the first two columns.

Filtering here is done in the browser over one fetch, which keeps it instant. The page is a knowledge base rather than a feed, so it is not paginated.

Recon across engagements

Recon in the top bar is the same idea for assets.

The organization-wide Recon page, showing assets from several engagements with reachability and repeat-engagement badges

The main use is the “have I touched this before” lookup: paste a hostname or an IP and see every engagement that already has it. When the same value appears in more than one engagement, the row gets an “n engagements” badge. That usually means a repeat client or shared infrastructure.

The filters are in-scope, out-of-scope or both; reachability (up, down, blocked, unmarked); one engagement; and chips per asset kind with counts.

Watch the reachability column across engagements. The same host blocked in one and up in another is nearly always a difference in source address or VPN, not a change at the target.

Clicking a value opens that asset in its own engagement’s workspace.

The command palette (Ctrl+K) covers most lookups. See all results, or /search?q=..., opens the full page.

The search page, with source filters and highlighted matches across pentests, findings and observations

It covers pentests, findings, assets, observations, files and phases, with a count per source across the top and matches highlighted in context. Matching is plain substring, so partial hostnames, URL fragments, usernames, paths and CVE identifiers all work, and you do not have to think about tokenisation.

Every result says which engagement it came from, so a hostname that appears in three of them stays distinguishable.

Files and notes

Files

Every engagement has a file area, and so does every asset. Both use the same browser and the same storage, showing a different slice of the tree.

The Files tab, inside a folder, listing uploaded scan output

The breadcrumb at the top navigates back up. New folder creates a subfolder, Upload adds files to the folder you are in, and the bin icon on a row deletes.

Images, PDFs and text-like files (json, txt, log, csv, md, xml) open in a new tab. Everything else downloads.

This is the right place for raw scan output, screenshots that are not attached to a specific finding, client-supplied documents, and any working file you want to survive the session. See Files on disk for where they end up on the server.

Reveal in file manager

The external-link icon asks the server to open the folder in the desktop file manager of the machine running red-clippy serve. It is off unless local_open = true is set in the config file.

Turn it on only when the server is your own desktop in a logged-in graphical session. Under systemd, or on a machine you are reaching over the network, there is no session for it to open into and it will either fail or do something you did not intend.

Agents and files

The asset file area is reachable over MCP with list_asset_files, read_asset_file, write_asset_file, create_asset_folder and delete_asset_file. An agent can therefore keep its working notes and its raw output in the same place you would, and you can read them in the browser without asking it for a summary.

Notes

The engagement notes page, a full-height scratchpad

The Notes tab is a full-page scratchpad for the engagement. Commands tried, payloads to revisit, half-formed observations, the thing the client said on the phone. Markdown, code blocks, or nothing in particular.

Ctrl+S saves, or use the Save button. The header says “unsaved changes” while your draft differs from what is stored, so a closed tab does not quietly lose an hour of thinking.

There are four separate note fields in the app, and they are separate on purpose:

Notes onWhereGood for
The engagementNotes tab, and the Notes field on PT SettingsAnything about the engagement as a whole.
A phaseWorkspace, engagement modeWhat happened during scoping, recon, testing.
A checkWorkspace, asset methodologyEvidence, repro steps, payloads tried, responses seen.
An assetAsset workspace, Notes tabWhat this host is, how it behaves, what to come back to.

The engagement Notes tab and the PT Settings notes field are the same underlying field, shown twice: once in a form with the rest of the engagement record, and once on a page with room to actually write.

Importing scanner output

Red Clippy parses the export formats of seven tools and writes the results straight into an engagement: assets, observations, findings and the raw scan record, from a single upload.

The import page

The full import page lives at /pentests/<id>/import. It is not on the tab strip, so type the URL or use the per-asset Import tab, which appears on IP assets and does the same thing scoped to that host.

The import page, with the nmap card expanded showing recommended commands and an upload box

Each tool gets a card. Expanding one shows what the parser expects, the commands that produce it, and an upload box. The copy button next to a command puts it on the clipboard.

Upload a file and Red Clippy tells you exactly what it did:

Ingested nmap from northwind-perimeter.xml: 7 assets, 8 observations,
0 findings, 1 nmap scans.

The bottom of the page lists the recent nmap scans for this engagement, with the target and the command line each one was run with.

Supported formats

ToolFormatWhat you get
NmapXML (-oX)IP assets, hostname assets from PTR records, and a port observation per open port with service, product and version. The raw scan is kept with its command line.
Burp SuiteIssues XMLOne finding per issue, with the request and response as proof of concept, plus Burp’s background, remediation and references.
Nessus.nessus v2One finding per report item, carrying the CVSS vector, score and CVE. Items marked informational with no CVSS score are skipped. Each host also produces host metadata and port observations.
OpenVAS / GVMReport XMLFindings from host-bound NVT results, with the threat level mapped to a severity, plus host and port observations and the CVE and OID.
MasscanJSON (-oJ or -oD)IP assets and port observations. Masscan reports open ports only, never services.
NaabuJSON linesIP assets, port observations, and a domain asset when the record’s host differs from its ip.
SubfinderOne host per line (-silent)A domain asset per line.

The format is detected from the content on upload, so you do not have to say which tool produced a file. Subfinder is the exception: a bare list of hostnames is too generic to auto-detect without hijacking other uploads, so it is only parsed when the format is named explicitly. The scan engine always does that; a manual upload should use the Subfinder card.

Nmap output formats

Use the XML output. It is the canonical machine-readable format and it carries service, version and NSE script results. Normal output (-oN) and grepable output (-oG) are not consumable.

nmap -sV -oX scan.xml <target>            # service and version detection, top 1000 ports
nmap -sC -sV -p- -oX scan.xml <target>    # all ports plus default scripts
nmap -Pn -sV -oX scan.xml <target>        # skip host discovery, for filtered ICMP

Port merging

Re-importing the same host after a service scan updates the existing port rows with the new detail instead of stacking a second copy. Ports are matched on host, port and protocol.

This is why the masscan-then-nmap pattern works: sweep wide for open ports, re-scan the interesting ones for services, and the two results fold together.

Other import paths

The same parser is reachable two other ways.

From a script, post to the ingest endpoint:

curl -X POST http://127.0.0.1:7337/api/v1/pentests/1/ingest \
  -H "Authorization: Bearer rcl_xxxx_yyyy" \
  -H "Content-Type: application/json" \
  -d "{\"format\":\"nmap\",\"filename\":\"scan.xml\",
       \"content_base64\":\"$(base64 -w0 scan.xml)\"}"

format is optional. Omit it and the server detects the format itself.

From an agent, the ingest_tool_output MCP tool takes the same payload. This is the main way results move out of an agent’s shell and into the record, and it is a single call regardless of how much the scan found. See Running an engagement with an agent.

Ports and the scan engine

Two related pages. Ports shows what is open. Scanner is an optional built-in runner for going and finding out.

Ports

The ports page: filters, common port chips, and services grouped by port

This page lists every open port in the engagement, taken from port observations. Those arrive from a scanner import, from the built-in scan engine, or from typing them in on an asset’s Ports tab. It does not matter which; they all land in the same place.

The filter row narrows by free text (host, service, product, version, banner), by port number or range, by protocol, by service name and by product or version. The chips below are shortcuts for the ports you ask about most: 21 FTP, 22 SSH, 23 Telnet, 25 SMTP, 53 DNS, 80 HTTP, 110 POP3, 139 NetBIOS, 143 IMAP, 161 SNMP, 389 LDAP, 443 HTTPS, 445 SMB, 1433 MSSQL, 3306 MySQL, 3389 RDP, 5432 Postgres, 5900 VNC, 6379 Redis, 8080 HTTP-alt, 27017 Mongo.

In-scope only and Reachable only hide everything you have already excluded or marked as down.

Overview groups by service and port, with a host count on each; expanding a group lists the hosts. Table is one row per host and port, which is the view to use before copying the results somewhere else.

Copy hosts and Copy host:port put the current filtered set on the clipboard, in the format your next tool wants. The line under the filters spells out what that set currently is: “8 open ports across 4 hosts, copy acts on all 8 shown”. Check it before you copy, or you may take a filtered view for the whole list.

The scan engine

The Scanner button in the engagement header opens the built-in runner.

The scan engine: the funnel with subdomain, host discovery, port and service stages, plus per-range coverage

The scan engine is a convenience for working by hand. It shells out to tools installed on the machine running red-clippy serve and ingests their output through the same parsers as a manual import. It is not part of the agent-driven workflow, where the agent runs its own tooling and posts the results back.

The funnel

Four stages, in order:

StageToolWhat it does
SubdomainssubfinderPassive subdomain enumeration for a domain.
Live hostsnmap host sweepHost discovery across a range. Probes ICMP plus common TCP and UDP ports including 135, 139, 445 and 3389, so it finds Windows hosts that block ping and serve nothing on 80 or 443. Works unprivileged.
Portsnmap, naabu, masscanPort scanning. Top 1000, all 65535, a custom list, naabu’s fast connect scan, or masscan’s SYN scan.
Servicesnmap service scanService, version and default-script detection on hosts that already have an open port.

Each row shows what has been found, with a progress bar for how much of the available surface that stage has covered. The button on the right launches the stage with sensible defaults. If a tool is not installed, the panel says so and gives you the install command instead of failing at run time.

masscan needs root or cap_net_raw. Everything else runs unprivileged.

Targets

A scan takes its targets from one of four sources:

SourceTargets
Type targetsIPs, CIDRs or domains you type in.
In-scope rangesEvery declared in-scope range.
Live hostsIP assets already discovered.
Open-port hostsHosts that already have at least one open port.

Coverage and resumption

By range breaks coverage down per declared range: how many hosts, how many ports, and how many targets remain unscanned. Launching a scan skips targets already covered by an equivalent run, so re-running a stage after adding a range scans the new addresses only.

Scan ports and Custom ports on a range row launch that range directly.

You can also mark coverage by hand, for when you scanned something outside Red Clippy and want the funnel to stop asking for it.

Chaining

The custom scan drawer can queue follow-up stages: run host discovery, then automatically port-scan whatever came back alive, then service-scan whatever had a port open. The preview line at the bottom of the drawer spells out exactly what will run and against what before you commit to it.

Runs

Each run appears in the Runs list with its stage, status (running, done, failed, canceled), targets and results. Expand one to see per-target tasks, grouped by declared range. Running scans can be cancelled, individual tasks skipped, and finished runs cleared in bulk.

The page polls while anything is running, so results appear without a refresh.

Organizations and members

Organizations in the avatar menu is the in-app replacement for the create-org, create-user and add-member CLI commands. Anything on this page can also be done from the command line, and vice versa.

The Organizations page: your organizations, and the members of the selected one

Your organizations

The table lists every organization you belong to, with your role in each, and its pentest and member counts. The one you are currently working in is badged current.

Switch to changes the active organization and reloads the panel. Everything cached is scoped to the previous tenant, so a full reload is the honest way to do it.

Create org takes a slug and a display name. The slug is permanent because it names the organization’s directory on disk (<storage_root>/org_<slug>/); use lowercase letters, digits, hyphen and underscore. The display name can change any time with Rename.

Any signed-in user can create an organization, and becomes its owner.

Members

Selecting a row loads that organization’s members below.

The member list for an organization, with the add-member form

Add member behaves differently depending on whether the username already exists:

  • an existing username joins with the password they already have, so leave the password field blank
  • a new username creates the account with the password you type, so you have to tell them what it is

Roles are owner or member. Owners can add and remove members, change roles, rename the organization and delete it. Members work the engagements and issue their own API keys.

Only owners see the add form and the role dropdowns. The server enforces the same rules regardless of what the browser shows, so a hidden button is a convenience, not the control.

Removing someone from an organization keeps their account and their API keys for other organizations. It only removes this membership.

One organization per client

One organization per client is about separation, not tidiness. An organization cannot see another’s data, and an API key belongs to exactly one organization. Give each client its own and connect one MCP server per organization. An agent working for one client then has no path to another’s findings.

Your password

The bottom card changes your own password. It asks for the current one, and changing it signs out your other sessions.

There is deliberately no “reset this member’s password” button. Any signed-in user can create an organization and invite any username into it, so an in-app reset would be an account takeover primitive. Forgotten passwords are reset from the CLI on the server:

red-clippy reset-password --username bob --password 'new-one'

Deleting an organization

Owners can delete, and it asks you to type the slug rather than clicking yes. Deletion removes every pentest, finding, asset, observation, scan and evidence record inside the organization, for every member.

Files on disk under <storage_root>/org_<slug>/ are deliberately left in place. Remove them yourself if you want them gone. A mis-click therefore costs you the index, not the artefacts.

Configuration

Settings come from a TOML file. red-clippy serve reads ./red-clippy.toml if it exists, and otherwise runs entirely on built-in defaults. Copy the example and edit:

cp red-clippy.toml.example red-clippy.toml
bind             = "127.0.0.1:7337"
database_url     = "sqlite://red-clippy.db?mode=rwc"
storage_root     = "./red-clippy-storage"   # uploaded evidence lives here
max_upload_bytes = 26_214_400               # 25 MiB

Settings

SettingDefaultMeaning
bind127.0.0.1:7337Address and port for the HTTP server.
database_urlsqlite://red-clippy.db?mode=rwcSQLx URL. SQLite is the supported backend.
storage_root./red-clippy-storageDirectory for evidence and per-pentest files. Created at startup if missing.
max_upload_bytes26214400Cap on a single upload, in bytes.
frontend_distunsetOverride for the built SPA. File-only.
local_openfalseAllow the Files page to open folders on the server host. File-only.

Relative paths are resolved against the working directory the server was started in. Always cd into the folder holding the binary before launching it.

Precedence

Per field: CLI flag > environment variable > TOML > built-in default.

SettingCLI flagEnvironment variable
bind--bind, -bRED_CLIPPY_BIND
database_url--database-urlRED_CLIPPY_DATABASE_URL
storage_root--storage-rootRED_CLIPPY_STORAGE_ROOT
max_upload_bytes--max-upload-bytesRED_CLIPPY_MAX_UPLOAD_BYTES
config file path-c, --configRED_CLIPPY_CONFIG

The database and storage flags are global, so red-clippy init-db and the other CLI helpers honour them too.

An unrecognised key in the TOML file stops startup with an error instead of being ignored, so a typo like bnid fails loudly rather than leaving the server quietly on a default.

File-only settings

frontend_dist points at a directory holding a built SPA. Release binaries embed frontend/dist at compile time, so this is normally unset and there is no sidecar directory to ship. Set it to serve a patched bundle without a Rust toolchain; the server then reads <frontend_dist>/index.html and <frontend_dist>/assets/.

local_open lets the Files page’s “reveal in file manager” button shell out to xdg-open, open or explorer.exe on the machine running the server. It ships off. Turn it on only when the server is your own desktop in a logged-in graphical session. Under systemd, or when you are reaching the panel across a network, there is no session for it to open into.

Logging

Log filtering uses RUST_LOG, defaulting to info,sqlx=warn. Logs go to stderr.

RUST_LOG=debug ./red-clippy serve         # verbose
RUST_LOG=warn ./red-clippy serve          # quiet
RUST_LOG=info,sqlx=info ./red-clippy serve  # include SQL

Network exposure

Red Clippy binds to 127.0.0.1 and is meant to run on your own machine, next to the agent driving it. It is not built to be exposed to a network or the internet: there is no TLS, no rate limiting on the login form, and API keys grant full access to everything in their organization.

If you do change bind, put it behind TLS and treat the API keys like the passwords they are.

Command line

The same binary that serves the panel also carries a handful of subcommands. Most of the time you only need serve. The rest exist for the things the browser deliberately cannot do, such as resetting a forgotten password, and for scripting the initial set-up of a machine.

red-clippy [OPTIONS] <COMMAND>
CommandWhat it does
serveRun the web UI and REST API.
mcpRun the MCP stdio server, proxying to a running instance.
init-dbApply migrations against the configured database.
create-orgCreate an organization.
create-userCreate a user.
add-memberAdd a user to an organization.
reset-passwordBreak-glass password reset.
create-api-keyIssue an API key.

Four options are global and apply to every subcommand: -c / --config, --database-url, --storage-root and --max-upload-bytes. That matters for the helper commands: without it, running create-user with RED_CLIPPY_DATABASE_URL set would create the user in the default database rather than the one you meant.

Add --help to any subcommand for its own flags.

serve

red-clippy serve [-b HOST:PORT]

Runs migrations if needed, then serves the panel and the API. Stays in the foreground; Ctrl+C stops it.

mcp

red-clippy mcp --url http://127.0.0.1:7337 --api-key rcl_xxxx_yyyy [--pentest CODE]
FlagEnvironment variableMeaning
--urlRED_CLIPPY_URLBase URL of the running server. Defaults to http://127.0.0.1:7337.
--api-keyRED_CLIPPY_API_KEYOrg-scoped token. Required.
--pentestRED_CLIPPY_PENTESTNumeric id or exact code of an engagement to pin the session to.

This speaks JSON-RPC 2.0 on stdin and stdout. Your MCP client launches it; you do not normally run it by hand except to check that the URL and key work. See Connecting an agent over MCP.

init-db

red-clippy init-db

Applies migrations and prints ok. serve does this on startup, so this is for pre-creating a database or checking that a migration applies cleanly.

create-org

red-clippy create-org --slug northwind --name "Northwind Retail"

Fails if the slug is taken. The slug names the organization’s directory on disk and cannot be changed afterwards.

create-user

red-clippy create-user --username bob --password 's3cret-pass' --email bob@example.com

--email is optional. This creates the account only; it does not join any organization.

add-member

red-clippy add-member --username bob --org northwind --role owner

--role is owner or member, defaulting to member. Run it again with a different --org to give one person access to several tenants.

reset-password

red-clippy reset-password --username alice --password 'new-one'

Sets the password and signs the user out everywhere. This is the only way to reset a password you do not know, and it needs shell access on the server host. See Organizations and members for why.

create-api-key

red-clippy create-api-key --username alice --org northwind --name laptop

Prints the token once:

created api key id=1 name=laptop
token (store this now - it will not be shown again):
    rcl_1a2b3c4d_5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f7081

The user must already be a member of the organization. --name defaults to default; give it something that will still mean something in six months.

Only the prefix and a hash are stored, so a lost token cannot be recovered. Issue a new one and revoke the old.

Troubleshooting

Each heading below is a symptom. Find the one that matches what you are seeing and work from there.

The browser shows a login form on a fresh install

You cannot get past it, because no account exists yet. Normally the panel detects that and shows the setup wizard instead; if it shows the login form, the detection request failed.

The form says so when that happens: it warns that GET /api/v1/bootstrap could not be reached. Two causes:

  • the server binary is older than the SPA it is serving, so the endpoint does not exist. Rebuild, or use a matching release.
  • a proxy in front of the panel is not forwarding /api.

Check it directly:

curl -s http://127.0.0.1:7337/api/v1/bootstrap
# {"needs_setup":true}

The setup wizard never appears

It only appears while the database has no users, and it refuses to run once one exists. If you have an account but cannot get in, reset the password:

red-clippy reset-password --username alice --password 'new-one'

Run that on the server host, in the directory holding the database, or with --database-url pointing at it.

Nobody can sign in

Sign-in takes a username, a password, and an optional organization slug. The slug is only needed to pick a specific organization; leave it blank for your default. Getting the slug wrong is a common cause of a login that looks broken.

If you are sure the credentials are right, check you are talking to the database you think you are. The server prints its storage path at startup, and the database path comes from the same config.

The panel loads but every page is empty

You are probably in the wrong organization. The display name under the logo says which one. Switch from avatar menu > Organizations.

The MCP server is not connecting

Work through it in order:

  1. Is red-clippy serve running? The MCP process is a proxy to it, not a replacement for it.
    curl -s http://127.0.0.1:7337/api/v1/health
    
  2. Is the key valid? A revoked key fails on every call. Check its state in Settings > API keys.
  3. Is the command path right? Your client launches the binary by path. red-clippy on its own only works if the binary is on the client’s PATH, which it usually is not. Copy the command from Connect MCP, where the path is filled in.
  4. Run the Shell (manual test) snippet from that same section. It should sit and wait for input. If it exits immediately, the error it prints names the problem.

For Claude Code specifically, claude mcp list shows whether the server is connected.

An agent writes to the wrong engagement

Either the session is not pinned, or it is pinned to the wrong one. Pin it with --pentest or RED_CLIPPY_PENTEST, using the numeric id or the exact code. A code has to match exactly, including case.

Without a pin, write tools require an explicit pentest_id on every call, and an agent that guesses will guess wrong eventually.

An agent writes to the wrong client

An API key belongs to one organization, so this means the wrong key. Use one working folder per client with the MCP server added inside it, and launch the agent from that folder. See Connecting an agent over MCP.

A scan says the tool is not installed

The scan engine shells out to tools on the machine running red-clippy serve, not on your machine and not inside the agent’s environment. The Scanner page shows availability per tool and gives the install command:

ToolInstall
nmapapt install nmap
masscanapt install masscan, and the server needs root or cap_net_raw
naabugo install github.com/projectdiscovery/naabu/v2/cmd/naabu@latest
subfindergo install github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest

Availability for the display is probed once at startup and cached, so a tool installed while the server is running still shows as missing until you restart. Launching a scan re-checks for real, so if you know it is there you can start the run anyway.

An import says the format is not recognised

The parser sniffs the content. Two things trip it up:

  • nmap text or grepable output. Only XML (-oX) is consumable, because it is the only format carrying service, version and script results.
  • subfinder output. A bare list of hostnames is too generic to detect safely, so it is only parsed when the format is named. Use the Subfinder card, or pass "format": "subfinder" to the API.

Ports appear twice

They should not: ports are matched on host, port and protocol, so a re-import updates rather than duplicates. If you are seeing two rows, check whether they differ in protocol or in host spelling. shop.northwind.test and the IP it resolves to count as different hosts.

Coverage went down after a recon run

Working as intended. The percentage counts every check across every asset you have opened, so opening new assets adds checks to the total.

“Reveal in file manager” does nothing

It is off unless local_open = true is set in the config file, and even then it opens a folder on the machine running the server. On a headless server there is no graphical session for it to open into. See Configuration.

An upload is rejected as too large

The default cap is 25 MiB per file. Raise max_upload_bytes in the config, or put the artefact in the Files area instead, which is the better home for a 120 MiB packet capture anyway.

Startup fails complaining about a config key

An unrecognised key is an error rather than a warning, so a typo cannot leave you quietly on a default. The message names the key. The valid ones are in Configuration.

Increasing log verbosity

RUST_LOG=debug ./red-clippy serve
RUST_LOG=info,sqlx=info ./red-clippy serve   # include SQL

Logs go to stderr.

Reporting a bug

Bug reports go to the issue tracker. Security issues should be reported privately instead, following SECURITY.md.

REST API

Red Clippy serves a REST API at /api/v1, on the same address and from the same process as the web panel. The panel is built on this API, so every operation available in the browser is available to a script or an agent through the same endpoints.

Authentication

Authorization: Bearer rcl_<prefix>_<secret>

Keys are organization-scoped, so no endpoint takes an organization in its path. Issue one under Settings > API keys or with red-clippy create-api-key.

The API also accepts the session cookie, so the same-origin SPA can call it without a key. Cookie sessions are re-checked against the membership table on every request, so access ends when the membership does rather than when the cookie expires.

Three endpoints need no authentication, because they run before there is anyone to authenticate: GET /health, GET /cvss/score, and the bootstrap pair. The bootstrap endpoints both refuse once any user exists.

Request and response format

Lists are paginated:

{ "items": [ ... ], "total": 137, "page": 1, "page_size": 50 }

page starts at 1 and page_size defaults to 50 with a ceiling of 500.

Errors are JSON with a single field:

{ "error": "invalid asset kind `host` - use one of: domain, ip, url, api, mobile" }
StatusWhen
400Bad request. The message names the problem and, for closed vocabularies, the accepted values.
401Missing, malformed, revoked or expired credentials.
403Authenticated but not permitted, typically an owner-only action.
404Not found, or belongs to another organization. The response does not distinguish the two.
409Conflict, such as a duplicate slug or code.
500Server error. The detail goes to the log, not the response.

Endpoints

System

Method and pathPurpose
GET /healthLiveness.
GET /cvss/score?vector=...Score a CVSS 3.1 vector.
GET /bootstrapWhether first-run setup is still available.
POST /bootstrapCreate the first organization and administrator.
GET /meThe current identity, organization, and the server’s own binary path.

Pentests

Method and pathPurpose
GET /pentestsList, filterable by q and status.
POST /pentestsCreate.
GET /pentests/{id}Fetch.
PATCH /pentests/{id}Update.
GET /pentests/{id}/summaryCounts, recent items and phase progress in one call.
GET /pentests/{id}/severitySeverity counts.
GET /pentests/{id}/coverageCheck coverage rollup.

Phases and checks

Method and pathPurpose
GET, POST /pentests/{id}/phasesList and create.
PATCH, DELETE /pentests/{id}/phases/{phase_id}Update status and notes, or remove.
GET /pentests/{id}/checks?asset_id=NThe catalogue for one asset. Materialises it on first call.
PATCH /pentests/{id}/checks/{check_id}Set status and notes.
POST /pentests/{id}/assets/{asset_id}/checks/bulkBulk update on one asset.

Assets

Method and pathPurpose
GET, POST /pentests/{id}/assetsList and create.
GET /assetsEvery asset in the organization, with its pentest.
GET, PATCH, DELETE /assets/{id}Fetch, update, delete.
DELETE /pentests/{id}/assets/by-valueDelete by value.
POST /pentests/{id}/assets/scopeSet marking by value.
POST /pentests/{id}/assets/scope/bulkSet the same marking on many values.
GET, POST /assets/{id}/aliasesList and add aliases.
DELETE /assets/{id}/aliases/{alias_id}Detach an alias.
POST /pentests/{id}/assets/promotePromote an observation to an asset.
POST /assets/{id}/demoteDemote an asset back to an observation.
GET /assets/{id}/observationsObservations attached to one asset.
GET /pentests/{id}/assets/urlsURLs under an asset.
GET /pentests/{id}/assets/urls/from-previousURLs seen for this asset in earlier engagements.
GET /pentests/{id}/assets/relatedRelated IPs and hostnames.
GET /pentests/{id}/assets/rangesDeclared ranges containing an address.
GET /pentests/{id}/assets/portsPorts for one asset.

Observations and ports

Method and pathPurpose
GET, POST /pentests/{id}/observationsList and create.
POST /pentests/{id}/observations/bulkCreate many in one transaction.
GET, PATCH, DELETE /observations/{id}Fetch, update, delete.
GET /observationsEvery observation in the organization.
GET /pentests/{id}/portsEngagement-wide port and service search.

IP ranges

Method and pathPurpose
GET, POST /pentests/{id}/ip-rangesList and declare.
PATCH, DELETE /pentests/{id}/ip-ranges/{range_id}Update or remove.
POST /pentests/{id}/ip-ranges/applyDeclare a range and fan the scope decision out to member addresses.
GET /pentests/{id}/range-mapThe unified scope tree behind the Network page.

API endpoints

Method and pathPurpose
GET, POST /assets/{id}/api-endpointsList and add.
POST /assets/{id}/api-endpoints/bulkAdd many.
GET /assets/{id}/api-endpoints/openapiExport an OpenAPI 3.1 document.
GET, PATCH, DELETE /api-endpoints/{id}Fetch, update, delete.
GET /api-endpoints/{id}/curlExport as a cURL command.

Findings and evidence

Method and pathPurpose
GET, POST /pentests/{id}/findingsList and create.
GET /pentests/{id}/findings.xlsxExcel export for one engagement, honouring the same filters.
GET /findingsEvery finding in the organization.
GET /findings.xlsxExcel export for the organization.
GET, PATCH, DELETE /findings/{id}Fetch, update, delete.
GET, POST /findings/{id}/attachmentsList and upload evidence.
GET, DELETE /findings/{id}/attachments/{attachment_id}Download or delete one.

Files

Method and pathPurpose
GET, DELETE /pentests/{id}/filesList a folder, or delete a path.
POST /pentests/{id}/files/foldersCreate a folder.
POST /pentests/{id}/files/uploadUpload, as multipart with file, optional dir and optional name.
GET /pentests/{id}/files/downloadDownload.
POST /pentests/{id}/files/revealOpen the folder on the server host, if local_open is on.
/assets/{id}/files/...The same set, scoped to one asset, plus read and write JSON variants.

Tool output and scans

Method and pathPurpose
POST /pentests/{id}/ingestImport a scanner report.
GET, POST /pentests/{id}/nmapList and attach raw nmap scans.
GET /nmap/{id}One scan with its raw output.
GET /pentests/{id}/scan-toolsThe tool registry with availability.
GET /pentests/{id}/scan-coverageFunnel progress.
GET /pentests/{id}/scan-coverage/by-rangeThe same, per declared range.
POST /pentests/{id}/scan-coverage/markMark targets as already covered.
GET, POST, DELETE /pentests/{id}/scansList, launch, bulk-delete finished.
GET, DELETE /pentests/{id}/scans/{scan_id}Fetch one with its tasks, or delete.
POST /pentests/{id}/scans/{scan_id}/cancelCancel.
POST /pentests/{id}/scans/{scan_id}/tasks/{task_id}/skipSkip one target.
Method and pathPurpose
POST /edgesCreate an edge.
DELETE /edges/{id}Delete an edge.
GET /graph/neighborsNodes reachable from one node.
GET /graph/pathsPaths between two nodes.
GET /searchOrganization-wide keyword search.

Instructions

Method and pathPurpose
GET, PUT, DELETE /org/rt-instructionsRead, override, reset at the organization level.
GET, PUT, DELETE /pentests/{id}/rt-instructionsThe same for one engagement.

Organizations, members and keys

Method and pathPurpose
GET, POST /orgsList your memberships, or create an organization.
PATCH, DELETE /orgs/{id}Rename or delete. Owner only.
GET, POST /orgs/{id}/membersList and add.
PATCH, DELETE /orgs/{id}/members/{user_id}Change role or remove.
POST /session/orgSwitch the active organization for a cookie session.
POST /account/passwordChange your own password.
GET, POST /api-keysList and issue.
DELETE /api-keys/{id}Revoke.

Examples

KEY=rcl_1a2b3c4d_5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f7081
BASE=http://127.0.0.1:7337/api/v1

# List engagements
curl -s -H "Authorization: Bearer $KEY" "$BASE/pentests"

# Add an asset
curl -s -X POST -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"kind":"domain","value":"shop.northwind.test","reachability":"up"}' \
  "$BASE/pentests/1/assets"

# File a finding
curl -s -X POST -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"title":"Reflected cross-site scripting in the search results page",
       "severity":"high","affected_asset":"shop.northwind.test",
       "cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N",
       "tags":"asset:shop.northwind.test,check:WSTG-INPV-01"}' \
  "$BASE/pentests/1/findings"

MCP tool reference

red-clippy mcp exposes 82 tools. This page groups them by area so you can see what is covered. The authoritative descriptions and JSON schemas come from tools/list on the running server, which is what your agent actually reads.

Every tool is scoped to the organization the API key belongs to. Tools taking a pentest_id accept the session pin instead when the server was started with --pentest or RED_CLIPPY_PENTEST.

Engagements

ToolWhat it does
list_pentestsList pentests, most recent first, paginated. Filters on free text and status.
create_pentestCreate an engagement. The code is immutable afterwards.
get_pentest_summaryOne snapshot: severity counts, totals for findings, recon, assets and nmap scans, recon counts by kind, the testable in-scope asset count, five recent findings and scans, and phase progress.
pentest_coverageCheck coverage rollup across every asset opened so far.

Engagement phases

ToolWhat it does
list_phasesList the phases for a pentest.
add_phaseAdd a custom phase.
mark_phaseSet a phase’s status.
delete_phaseRemove a phase.

Red Team Instructions

ToolWhat it does
get_instructionsReturn the resolved document and the level it came from.
set_instructionsWrite an override at the org or pentest level.
reset_instructionsDelete the override at a level, falling back to the next one down.

Assets

ToolWhat it does
list_assetsList scope units, with filters.
create_assetCreate one.
get_assetFetch one by id, including its attributes JSON.
update_assetPatch one by id.
delete_assetDelete by id.
delete_asset_by_valueDelete by value. Cascades observations and check runs, and reports how many findings still tag the value.
set_asset_markingSet scope and reachability by value.
bulk_set_asset_markingApply the same marking to many values in one transaction.
list_asset_aliasesList aliases pointing at a canonical asset.
set_asset_aliasMark one asset as an alias of another.
clear_asset_aliasDetach an alias, making it a standalone asset again.
promote_observationTurn an observation into its own asset.
demote_assetThe inverse: turn an asset back into an observation under its parent.

Observations and ports

ToolWhat it does
list_observationsList observations for a pentest, paginated.
create_observationAdd one.
bulk_observationsAdd many in one transaction. This is the one to use after a recon run.
get_observationFetch one by id.
update_observationPatch one by id.
delete_observationDelete one by id.
observations_for_assetEverything attached to one asset, unpaginated.
list_portsEngagement-wide open port and service search.

IP ranges

ToolWhat it does
list_ip_rangesThe customer-declared perimeter, which is not the same thing as asset rows.
add_ip_rangeDeclare a range.
update_ip_rangeReplace the editable fields of one.
apply_ip_rangeDeclare a range and fan its scope decision out to every IP asset inside it.
delete_ip_rangeRemove a declared range.
asset_rangesWhich declared ranges contain a given address.
get_range_mapThe unified scope tree: declared ranges, discovered clusters, loose blocks, singletons.

API endpoints

ToolWhat it does
list_api_endpointsList endpoints on an api asset.
add_api_endpointAttach one.
add_api_endpoints_bulkAttach many in one call, for importing a specification.
get_api_endpointFetch one with its JSON fields parsed.
update_api_endpointPatch one.
delete_api_endpointDelete one.
export_api_endpoint_curlBuild a copy-pastable cURL command using the parent asset as the base URL.
export_api_asset_openapiSynthesise an OpenAPI 3.1 document from every REST endpoint on the asset.

Methodology checks

ToolWhat it does
list_checksThe catalogue for one asset, with current status per check.
mark_checkSet one check’s status and notes.
bulk_mark_checksFlip many checks on a single asset to the same status, atomically.

Findings and evidence

ToolWhat it does
create_findingRecord a finding.
list_findingsList findings for a pentest, paginated and filterable.
get_findingFetch one in full: details, impact, proof of concept, remediation, CVSS, tags.
update_findingPatch one.
delete_findingDelete one, and its attachments.
attach_evidenceUpload a file against a finding.
list_evidenceList a finding’s attachments. Metadata only, no bytes.
delete_evidenceDelete an attachment by id.

create_finding’s description carries writing guidance the agent will follow: confirm with the operator before recording a finding, keep details, impact and remediation to a paragraph each, keep scope identifiers out of the title, and describe what needs to change in remediation rather than emitting commands, config snippets or library versions that may be wrong for the target’s stack.

Asset working files

ToolWhat it does
list_asset_filesList files and folders under an asset’s directory.
read_asset_fileRead one file.
write_asset_fileCreate or overwrite one.
create_asset_folderCreate an empty folder.
delete_asset_fileDelete a file, or a folder recursively.

Tool output

ToolWhat it does
ingest_tool_outputImport a scanner report. Auto-detects the format, writes assets, observations, findings and the raw scan in one transaction.
add_nmapAttach raw nmap output as a scan record.
get_nmap_scanRetrieve one scan by id, including its raw output.

The scan engine

ToolWhat it does
list_scan_toolsThe tool registry by stage, with availability on this host.
scan_coverageFunnel progress, plus how many hosts are waiting for the next stage.
scan_coverage_by_rangeThe same, broken down per declared in-scope range.
mark_coveredRecord targets as already scanned so future launches skip them.
enqueue_scanLaunch a run.
list_scansRecent runs with status and progress counts.
get_scanOne run with its per-target tasks.
skip_scan_taskSkip one target inside a run, killing it if mid-scan.
cancel_scanCancel a running scan.
delete_scanDelete one finished run.
clear_scansBulk-delete finished runs.

Attack graph

ToolWhat it does
linkCreate a directed edge between two nodes (observation, asset or finding) with a free-form relation.
unlinkDelete an edge by id.
neighborsEverything reachable from a node up to depth edges (default 1, max 5), in a given direction.
attack_pathsPaths from one node to another, up to max_depth hops (default 5, max 8).

Search

ToolWhat it does
org_searchKeyword search across every source table in the organization.

Data model and vocabularies

Several fields are closed vocabularies. The strings below are the ones stored in the database and accepted by the API, and they are a contract: they will not be renamed under you.

Vocabularies

FieldValues
Membership roleowner, member
Pentest statusplanned, in_progress, completed, archived
Finding severitycritical, high, medium, low, info
Finding statusopen, confirmed, remediated, accepted, false_positive
Asset kinddomain, ip, url, api, mobile
Asset reachabilityup, down, blocked, or absent for not yet determined
IP range kindin, out
API protocolrest, graphql, soap, grpc
Phase statuspending, in_progress, done, na, blocked
Check statusnot_tested, in_progress, tested, not_applicable, blocked
Graph node typeobservation, asset, finding

Parsing is case-insensitive and trims whitespace, so Up and OWNER are accepted. Anything outside the list is a 400 whose message lists the valid values.

Reachability has no “unknown” member on purpose: that state is the absence of a value, not a value.

Observation kinds

Unlike the vocabularies above, an observation’s kind is free text, lowercased on write. The panel offers these:

port, service, dns_record, tech, cert, whois, email, employee, credential_leak, secret, screenshot, asn, takeover_candidate, default_cred_portal, wayback_url, js_endpoint, other

Sticking to them keeps the Recon drawer’s grouping and the cross-engagement views useful, but nothing stops you inventing one when you genuinely need it.

port is special: observations of that kind dedupe on <host>:<port>/<proto>, so re-importing a host after a service scan merges into the existing row rather than stacking a second one.

The check catalogue

The catalogue holds 135 checks, materialised per asset the first time you open that asset’s methodology tab. Each catalogue entry declares which asset kinds it applies to, which is why a domain gets 114 checks and an API asset gets 67.

The catalogue is re-synced from the binary on every startup, so upgrading picks up new and revised checks. Codes that have been withdrawn are deactivated rather than deleted, which leaves the check results you already recorded against them intact.

By standard:

StandardChecks
OWASP Web Security Testing Guide95
CIS quick wins10
OWASP API Security Top 1010
PTES3
No external standardthe remainder

By category:

CategoryChecks
config24
input21
recon20
auth18
client14
business_logic10
session9
authz7
crypto4
cloud4
others (scanner, network, osint_paid, and the phase categories)the remainder

Each entry carries a code, title, description, category, standard and reference, an optional CWE, an optional suggested severity, and reference links.

Tag conventions

Finding tags are a comma-separated string. Two prefixes are understood by the panel:

TagEffect
asset:<value>The finding appears on that asset’s Findings tab.
check:<code>The finding appears under that check in the workspace.

Tag matching is exact on the comma-separated token, so asset:foo does not match asset:foo.bar.

Everything else is free. manual, unverified, retest-passed and client-confirmed are all reasonable, and they are searchable from the findings list and the organization-wide Findings page.

Identifiers

Organization slug: lowercase letters, digits, hyphen and underscore. Fixed after creation because it names a directory.

Pentest code: 2 to 64 characters, starting with an alphanumeric, then letters, digits, hyphen and underscore. Fixed after creation for the same reason. Auto-generated as PT-YYYY-MM-DD-<slug> from the name, with a numeric suffix if that is taken.

API key: rcl_<prefix>_<secret>. Only the prefix and a hash of the secret are stored.

The attack graph

An edge links two nodes, each identified by a type (observation, asset or finding) and an id, with a free-form relation string. Direction matters.

neighbors walks out from a node up to a depth (default 1, maximum 5) in a direction (out, in or both). attack_paths finds paths between two nodes up to a maximum depth (default 5, maximum 8).

Relations are free text, so resolves_to, hosts, credential_for, pivots_to and same_owner_as are all valid without registering them anywhere.

Stored representation

Vocabulary values are stored as their text spelling, not as integers, so a database opened with any SQLite tool reads as critical rather than 0. A stored value outside the vocabulary fails loudly on read rather than being coerced to something plausible.

Files on disk

Red Clippy keeps two things outside the process: a SQLite database and a storage directory. Everything else is in the binary.

The database

The database is red-clippy.db in the working directory by default, or wherever database_url points.

SQLite runs in WAL mode, so you will see three files:

red-clippy.db
red-clippy.db-wal
red-clippy.db-shm

All three belong together. Copying only the .db while the server is running gives you a database missing the most recent writes.

The storage directory

The storage directory is ./red-clippy-storage by default, or wherever storage_root points. It is created at startup if missing.

red-clippy-storage/
  org_northwind/
    pentest_PT-2026-Q3-NORTHWIND/
      findings/
        2/
          9a9344b81c8e1bfb44740ab09b23d933
          aa26c30965cbc9533cf47261680b8c4e
      data/
        notes/checkout-flow.md
        scans/northwind-perimeter.xml
        scans/subfinder-northwind.txt
        screenshots/evidence.png

Each engagement has two buckets, and they name files differently.

findings/<finding_id>/ holds evidence attachments. Names are server-generated random hex, so the original filename never touches disk and a storage key lifted from an old database dump cannot be replayed against a running server. The real filename, MIME type and caption live in the database.

data/ is the operator file area, the one behind the Files tab. It keeps the names you gave things, so the Files tab is a faithful view of what is on disk. Every path segment is validated, and nothing user-supplied can resolve outside the engagement’s own data/ root.

Asset file areas are a slice of the same data/ tree, so a file written through an asset’s Files tab is visible from the engagement’s Files tab too.

Backing up

With the server stopped, copy the database file (all three parts) and the storage directory. That is the whole state.

With the server running, use SQLite’s own backup so you get a consistent snapshot:

sqlite3 red-clippy.db ".backup 'backup/red-clippy.db'"
cp -a red-clippy-storage backup/

Deletion behaviour

Deleting a record and deleting its files are deliberately separate actions.

Deleting an organization in the panel removes every database row inside it and leaves the files under org_<slug>/ alone. Deleting an asset removes its observations and check results but keeps findings tagged against it.

In both cases a destructive action removes the index, not the artefacts. If you want the files gone, remove the directory yourself.

Path derivation

org_<slug> and pentest_<code> are derived from the slug and the code, which is why both are immutable after creation. Both are re-validated every time a path is built, so even a tampered database row cannot produce a path that escapes the storage root.

This means one engagement’s artefacts are one directory. Archiving an engagement is tar czf. Handing over a client’s files is copying org_<slug>/.