Your first engagement

From an empty account to a report and a sealed evidence locker.

This page goes from an empty account to a report and a sealed evidence locker in seven steps.

Keys, assets and the locker export use the API

The console covers steps 3 through 6 and the share link in step 7: the new-engagement wizard, the live run, the findings, the report and share links. It has no screen for registering an asset, proving ownership, managing signing keys or exporting the evidence locker; those have HTTP endpoints, called with the same session you are signed in with. The sidebar shows Targets, Evidence and Signing keys greyed out.

1. Create an organization and a signing key

Register and sign in. You do not create an organization by hand: the first time you make an authenticated request, Pentr provisions a solo organization named after your email address and makes you its owner. The owner role is the one that can attest to asset ownership in step 2.

Your organization has at most one active Ed25519 signing key, and a sealed run is signed with it. If none exists when a run is first sealed, one is generated then. To create it beforehand, or to register a key you already hold:

  • POST /api/keys generates a fresh key.
  • POST /api/keys/upload registers an Ed25519 private key you already hold, in PKCS#8 form.

Either call rotates: the current active key, if there is one, is retired and the new one becomes active. No route deletes a key, so a seal signed by a retired key stays verifiable against it. Private key material is stored wrapped and is never returned by the API; the public half is. Pentr holds the private key and signs with it — see Key custody.

2. Add a target and complete ownership verification

A launch is refused unless every target and every in-scope entry is covered by an asset whose ownership proof holds at that launch. That check covers the identifiers the engagement names. What the agent can reach during the run is bounded by the scope guard, whose limits are set out on Scope and Rules of engagement.

Register the asset with POST /api/assets, giving a kind of domain, ip, cidr or url and its value. The response includes a verification token that stays the same for the asset's lifetime. Every launch fetches the proof behind a verified asset again, so the record or file has to stay published.

For a domain, prove it either way:

  • Publish a TXT record at _pentr-verify.<your-domain> containing the token; or
  • Serve the token at https://<your-domain>/.well-known/pentr-verification.txt.

Then call POST /api/assets/{asset_id}/verify. A pass sets the asset to verified. A fail returns the reason — for example, no TXT record at the name, or a file that does not contain the token — and you can fix it and call again. Ownership verification lists the checks each method makes.

For an IP address, a CIDR range or a URL, no automated check ships. The only route is written attestation: an organization owner calls POST /api/assets/{asset_id}/attest with a justification, which sets the asset to attested and records who claimed it and why. An attestation expires after 90 days. A verified status has no expiry, but its proof is fetched again at every launch.

Only verified and attested assets can clear the launch gate, and the status alone is not enough: an expired attestation, or a verified asset whose proof fails the re-check at launch, blocks the launch.

3. Start an engagement

In the console, choose New engagement. The wizard has five steps:

  1. Targets — the hosts to test, each entered as an IP address or a domain.
  2. Scope — the in-scope and out-of-scope lists. An out-of-scope name is compared as an exact string and excludes only that name. An out-of-scope address inside an in-scope range is an overlap, and an overlap aborts the run in its first phase; see Scope.
  3. Rules — a maximum duration in minutes, and which of scan, enumerate, exploit and post_exploit are allowed. The duration field carries min 30 and max 480 as attributes of the HTML input; neither the server nor the agent validates the number, and nothing enforces the duration (step 4).
  4. Framework — one of the six. This decides the control identifiers on the report; it selects no tool and no check.
  5. Review — check the summary and press Create engagement.

Creating is not starting. Create engagement saves the engagement at status pending and opens its detail page, where nothing is running yet. Press Start pentest there to launch it. That button appears only while the status is pending, so once the run is under way it is gone.

Starting re-verifies ownership for every target and in-scope entry. If any of them cannot be re-proven, the launch is refused, the engagement is marked blocked, and the response, an HTTP 409, names each identifier and the reason. The console displays no message for a refused launch: the page keeps showing pending and Start pentest until it is reloaded, and the reasons are in the API response only. Publish what is missing, or renew the attestation, and submit the same engagement again with POST /api/engagements/{engagement_id}/submit — blocked is retryable, and you do not have to rebuild the engagement. The console offers no button for this, because Start pentest is shown only on a pending engagement.

This creates one engagement: a single run. To repeat it, open Schedules and press New schedule. The schedule wizard collects the targets, scope, rules and framework again in its own steps, then a cadence; it does not copy an existing engagement. A schedule stores those fields as a template and creates a fresh engagement at each occurrence it does not skip. An occurrence whose ownership check fails creates an engagement at blocked and is also recorded as a skip. That recurring series is what these docs call a campaign. Managing a schedule describes the wizard.

4. Watch the run

The engagement page streams progress while the status is queued or running. A six-stage pipeline shows which phase the agent is in — intel, threat model, vuln scan, exploit, post-exploit, report. Below it, a terminal shows phase changes, the text of each command the agent runs, refused commands, errors and each finding as it is recorded. Command output is not streamed; it is stored in the artifact. The run happens in the container, so closing the tab does not affect it. Running an engagement describes what reopening the page shows.

There is no interactive control over a running engagement, and no pause or abort button. A run that ends with a clean exit and a results payload is marked completed: the agent finished its last phase, reached the 500-step ceiling, or aborted after five consecutive failing steps. A run is marked failed when the container exits non-zero, exits without a results payload, or the scheduler raises while processing the job. When a run fails describes each outcome.

The maximum duration set in the wizard is not enforced. It is written into the rules-of-engagement document, which is stored with the run and not sent to the model, and no timer stops the run.

5. Read the findings

Each finding carries a title, a description, a severity from critical to info, the affected target, the remediation advice, and a status. The agent sets the status during the run, and you can change it afterwards. The agent's prompts ask it to use the four values this way:

  • verified — for an exploit it demonstrated.
  • unverified — the default, for a finding it has not proven.
  • not_exploitable and false_positive — for findings that did not hold up.

Nothing checks the status against the evidence. Review each finding and change its status where it is wrong: expand the finding on the engagement's page or on the Findings page (which lists the findings of every engagement), then pick a value under Triage status. The console, the CSV export and the SARIF export show the status; the PDF does not. Anatomy of a finding describes the control and what a change affects.

6. Generate and download the report

Opening a completed engagement generates its report. If generation fails, the Report panel shows a Generate report button that tries again. The panel then offers three downloads:

  • PDF — the human-readable report.
  • SARIF — for code-scanning and ticketing pipelines.
  • CSV — for a spreadsheet.

The PDF is rendered once, when the report is generated. SARIF and CSV are rebuilt at every download, so a change to the framework file in between can give them different control ids from the PDF, and they can list findings in a different order. Findings & reports describes the three formats.

7. Export the locker and verify it yourself

The report is the human-readable output. The evidence locker holds the evidence: a zip containing the manifest, the signature block that carries the manifest's hash, the signature over that block, the organization's public key, an unsigned summary header, a README, and every captured artifact named by its SHA-256 hash. The report is not in it. Exporting a locker lists each file.

Download it from GET /api/engagements/{engagement_id}/locker, authenticated as the owning organization's user. It arrives as pentr-locker-<engagement-id>.zip. Fetch the matching public key from GET /api/keys/{key_id}/public and save the public_key_pem field as org-public-key.pem. Then, with pentr-verify installed:

ENGAGEMENT_ID=...   # the engagement you exported
pentr-verify "pentr-locker-$ENGAGEMENT_ID.zip" --key org-public-key.pem

It recomputes every artifact hash and rebuilds the entire chain offline — no network, no database, no Pentr credentials. Exit code 0 means the locker verified. Exit 1 means it did not; the output lists each failed check by its code, with the artifact or file it concerns where there is one. Exit 2 means the command could not begin verifying — for example, the file is not a locker, or the key passed to --key could not be read or parsed. Exit 2 is not a verification result. Verifying a locker lists every case.

Run without --key, it verifies against the public key packaged inside the zip. When the locker passes in that mode, the text output ends with an UNPINNED warning; a failure and --json output carry no such warning. A pass in that mode shows that the locker is internally consistent, and nothing about who signed it. A pass with a pinned key shows that the locker was signed with the key you pinned. What verification does not prove sets out both cases.

What the recipient of a share link sees

An auditor who receives a share link downloads the locker and runs the same command. Each export rebuilds the zip, and seal.json records a fresh exported_at, so two exports of one run are not byte-identical; the manifest, the signature block, the signature and the artifacts are the same in both. The share page shows the seal values recorded for the run and a download button. It does not verify the locker and shows no verification result.

To hand a run over, create a share link from the engagement page: give it a label naming the recipient and an expiry of 1 to 365 days, and send it. The recipient needs no account. Revoking the link stops further access through it; it does not affect a copy already downloaded. A link is not tied to one person: anyone who has it has the same access. Share links describes them.

Last updated 2026-10-11