# PAJIN
PAJIN is a policy-governed multi-agent AI red-team and security validation platform.
The current implementation is a CLI-first backend approaching MVP. It validates typed campaign and
Mode Pack manifests, dynamically creates a bounded Supervisor/Planner/Specialist/Semantic
Validator/Reporter team, evaluates every tool request through the Tool Gateway, executes registered
mock, HTTP, or MCP tools through a simulated or isolated Docker Worker, admits Candidates, reviews
them through separate semantic and objective evidence gates, and writes audit evidence plus
structured JSON and Markdown reports. An optional FastAPI/PostgreSQL Control Plane and lease-aware
Worker daemon provide the first durable execution path without replacing the local CLI.
## Current implementation status
The implementation baseline as of 2026-07-22 is:
| Area | Current scope |
| --- | --- |
| Core engine | Typed Campaigns, policy and capability enforcement, dynamic Specialists, budgets, retries, cancellation, Candidate admission, semantic evidence review, versioned replay contracts, a deterministic Replay Compiler, single-use execution tickets, a local SQLite replay-ticket ledger, stateless and registered fresh-session Restricted Reproducer paths, receipt-reloading confirmation/retest gates, and tamper-evident evidence seals |
| AI Red Team | KISA catalog for 19 threat classes and 52 checklist items; executable A01, A02, A04, M03, and M06 scenarios; exact M03, M06, and A04 fresh-session replay through `kisa-run` and an explicit Local path; Candidate-bound replay-evidence projections; and baseline-bound negative replay that remains inconclusive without external remediation attestation |
| Bug Bounty | Program-policy review, canonical scope compilation, conservative duplicate triage, local report drafts, and one fixed Boolean SQL injection lab |
| CTF | Typed local Web backup and offline single-byte XOR challenges, plus a bounded Web + Crypto Suite |
| Control Plane | Optional authenticated FastAPI API, PostgreSQL Job queue, approval checkpoints, fenced cooperative cancellation, leases and crash recovery, a same-origin Web Console preview, owner-controlled managed Artifacts, opaque Operator Replay source/batch admission with role-scoped batch/item/ticket/finalization/projection reads, durable exact-KISA Replay finalization, fresh-identity retry issuance, and a dedicated `kisa-exact-v1` Replay Worker. Schema v11 publishes CAS-fenced multi-item projections; schema v12 binds a confirmed baseline to one parent Retest Artifact and publishes a server-reverified `kisa-retest.json` projection from negative replay receipts plus normal-function regression. Defensive responses remain `inconclusive` without independent remediation attestation. |
| Primary gaps | Multi-host/object-store Artifact transfer, non-KISA Local replay orchestration, portable/off-host replay proof, Finding/report review UI, distributed Workers, external integrations, and independently anchored production evidence |
The primary operator interface remains CLI + YAML. Generic public-target attack automation,
external Bug Bounty or CTF submission, and production multi-tenant deployment are not implemented.
## Current safety boundary
- Network access is denied by default and cannot be granted by a Tool Adapter.
- A network-enabled tool receives a campaign-derived egress policy only from the Tool Gateway.
- Each network execution gets a private internal Docker network and a dedicated allowlist proxy.
- Public destinations are the default; loopback, link-local, private, reserved, multicast, and
unspecified addresses are rejected. Private-network Mode Pack exceptions are limited to fixed
synthetic labs: Bug Bounty uses its `local-lab` profile, while the CTF Web slice permits only
`host.docker.internal:8780/backup/config.json.bak`.
- The CTF Crypto slice has no egress policy. It accepts only a content-addressed inline artifact of
at most 4 KiB and evaluates exactly 256 single-byte XOR keys inside the no-network Worker.
- MCP process commands are kept in the Worker catalog. Agents can submit only registered server
IDs, tool names, and typed arguments.
- Planner-provided agent identities are ignored; the Supervisor binds each request to the assigned
Specialist and issues an attenuated, task-specific Capability Grant.
- A child tool call consumes both its grant and every ancestor grant, preventing sibling agents from
multiplying the campaign call budget.
- Agent count, spawn depth, tool calls, elapsed time, cost, low-risk retries, and cancellation are
controlled by the PAJIN runtime rather than model instructions.
- Explicit deny scope takes precedence over allow scope.
- Authorization, capability, risk tier, method, and call budgets are checked before execution.
- Optional tool-category allowlists, recurring IANA-timezone testing windows, and per-campaign
request rates are enforced by the Policy Engine and Tool Gateway.
- Unregistered tools are rejected before Worker dispatch.
- Provider endpoints, model IDs, function-tool allowlists, and credential references are fixed by
trusted registration; an Agent cannot override them in a chat request.
- Provider credentials are materialized through audience-bound, single-use Secret Leases and enter
the Worker only through its stdin envelope, never Docker arguments, environment variables, Job
metadata, events, or evidence.
- Docker images are allowlisted and are never pulled implicitly during a campaign.
- Product-level confirmation requires the objective gate, Candidate-bound replay, and an
independently verifiable execution/target attestation. The repository does not currently have
that last authority, so Worker-only evidence cannot exceed `needs-review`.
- A KISA `fixed` determination additionally requires independently verifiable remediation
attestation. Exact bindings, successful repetitions, a negative transcript, Worker flags, and
local receipts are necessary consistency checks but are not sufficient proof; current negative
replay remains `inconclusive`.
- `ReplayIntent` is a strict, non-executable schema: raw Tool requests, commands, arbitrary URLs,
Capability Grants, and undeclared executable fields are rejected. Versioned replay artifacts bind
Candidate, Run, original and replay request, Mode, scenario, Tool, target, and threat identities
before the deterministic compiler can issue a candidate-bound, non-delegable replay Grant and
opaque single-use execution ticket. The Restricted Reproducer rechecks the Campaign, Tool,
scenario fingerprint, shared budget/rate ledger, fresh evidence JSON, sealed artifact digest, and
finalized ticket receipt before a Mode Oracle can support the claim. Replay dispatch and Oracle
evaluation share deadline/cancellation bounds, and the Tool Adapter cannot request new Secret
Leases. The exact KISA M03, M06, and A04 `ai.chat-probe` contracts may materialize only a fresh
per-attempt `session_id`; every other catalog argument remains compiler-bound. Unregistered
session-bearing contracts fail closed.
- Local KISA replay ticket state is stored in a stable SQLite ledger outside the sealed replay Run.
The ledger uses atomic single-use state transitions and a read-only verifier, but it is trusted as
a local database under the host OS account/ACL boundary. It is not a portable signed proof, an
off-host attestation, or the PostgreSQL Control Plane replay authority. Consequently it is not
product-level Confirmed/FIXED authority either.
- The explicit Local KISA coordinator is limited to one process and one writer, and only the exact
M03, M06, and A04 `ai.chat-probe` contracts are allowlisted. It is not a generic structural replay
predicate or a distributed lock. Accepted ADR 0029 governs Control Plane replay artifact handoff,
lease fencing, PostgreSQL ticket/batch/item state, and durable budget/rate state. The implemented
M6-07B-2B foundation includes the versioned Replay aggregate and burn-on-claim lifecycle, an
owner-controlled managed filesystem repository, immutable `cp_artifacts` metadata, and
server-owned admission of completed sealed sources. Producer Control Plane Run identity remains
distinct from the sealed Run identity. Consumers provide only the exact opaque
`(artifact_id, repository_version)` locator, and the server resolves it and re-verifies content
and seals. As of 2026-07-18, batch creation accepts only that locator and an idempotency key; the
Control Plane rereads the managed sealed AI Red Team source, derives eligible exact M03, M06, and
A04 confirmation Candidates and contracts, runs the trusted Replay Compiler, and persists the
canonical `ReplayCompilation` plus its `ReplayCapabilityGrant` as an append-only planned/pending,
non-dispatchable derivation record and proof in PostgreSQL. Caller-authored Candidate, contract,
policy, digest, target, and arguments are not authority inputs. Schema v4 extends the forward
v1→v2→v3→v4 path with canonical,
non-dispatchable compilation derivation records. Each append-only row has its own
`compilation_id`, Replay Run identity, compilation digest, and Grant digest; its non-unique
`item_id` and Candidate/contract plan-identity foreign key allow later attempt/version rows for
the same item. M6-07B-2C durable issuance is also implemented as of 2026-07-18. The internal,
idempotent `ControlPlaneService.issue_replay_batch(batch_id, actor=...)` path resolves and
re-verifies the managed source again, then uses schema v5 `cp_replay_budget_accounts`,
`cp_replay_budget_reservations`, `cp_replay_rate_accounts`, and
`cp_replay_rate_reservations` authority. It conservatively binds the sealed budget and request-rate
snapshot, reserves the complete first-attempt Tool-call/request-unit requirement, recompiles every
pending item with a fresh Replay Run identity and five-minute Grant, appends a new canonical
`ReplayCompilation`, and atomically creates exactly one internal Job and `issued` ticket per item.
The Job payload and ticket are bound by foreign keys and strict models to the exact
`compilation_id`, `budget_reservation_id`, `rate_reservation_id`, attempt, Replay Run, compilation
digest, and Grant digest. The original planned compilation remains a non-dispatchable proof and is
never promoted or reused. Only a response-loss retry against the current active exact authority
graph reconstructs that issuance: the ticket/Job pair must still be `issued`/`queued` immediately
after issuance, or `claimed`/`running` after claim. A terminal or otherwise changed graph must
fail closed. M6-07B-2D internal service-only per-call permit ledger and issuance is also implemented
as of 2026-07-18. Schema v6 extends the forward v1→v2→v3→v4→v5→v6 path with append-only
`cp_replay_tool_permits`. The strict `ReplayToolPermitRequest` accepts only the executor profile,
lease token, ticket ID, fencing value, and 1-based call ordinal. The idempotent
`ControlPlaneService.issue_replay_tool_permit(job_id, request, actor=...)` service rechecks the
authenticated principal and registered executor profile; the exact Job/ticket lease token and
fence; active Run, batch, item, and ticket state; canonical compilation and Grant; exact
reservation counters; and rolling request-rate admission. With a configured cap, admission counts
the current sealed baseline, post-admission unconsumed units in still-live reservations, active permit units in
their 60-second windows, and the new trusted request cost. With no cap, rate rejection is skipped
but exact reservation counters are still consumed. A canonical permit binds that authority graph,
source and original request, Tool and version, target, method, 1-based ordinal, one Tool-call unit, and the
trusted request-unit cost. Its TTL is at most 30 seconds and never exceeds the lease, compiled
spec, or Grant deadline; rate-reservation expiry is not a permit-TTL cap. The unique ticket/ordinal
key plus persisted permit digest and request ID makes an exact response-loss duplicate return the same row without consuming counters or
appending an event twice. First issuance atomically moves its reserved budget and rate units to
consumed and appends the audit event. An issued permit remains consumed if execution is uncertain;
cancellation or abandonment releases only the definitely unissued remainder. Stale, mismatched,
cancelled, expired, finalized, ordinal-gap, and over-limit requests fail closed. M6-07B-2E now
exposes that existing service authority only through dedicated WORKER-role HTTP endpoints for
Replay claim, heartbeat, and Tool-permit issuance, plus matching async client methods. The strict
JSON `PAJIN_CP_REPLAY_EXECUTOR_PROFILES` subject-to-executor-profile-array allowlist accepts only
authenticated Worker subjects; when unset it is empty and fails closed. For example,
`{"replay-worker-service":["kisa-exact-v1"]}` grants that one profile only to the
separately authenticated Replay Worker subject. Route authorization is symmetric: that Replay
subject is rejected from every generic Worker route, while the generic Worker and every
non-allowlisted subject are rejected from all Replay routes. Claim and heartbeat return a
`ReplayExecutionClaimView` containing the exact server-validated canonical `ReplayCompilation`,
and the envelope rechecks its canonical compilation, Candidate, contract, Grant, Campaign, Mode,
Candidate Run, and Replay Run bindings. The permit remains a non-bearer proof whose issuance has
already consumed the durable units; M6-07B-2E adds no separate redeem mutation. M6-07B-2F now
creates one append-only schema-v7 `cp_replay_execution_contexts` row for every fresh compilation
during issuance. Its canonical `ReplayExecutionContext` binds the exact typed Campaign, exact KISA
Scenario, and canonical `AIChatProbeTool.spec`, stores a digest for each component and the complete
context, fixes `required_executor_profile` to `kisa-exact-v1`, forbids Secret Leases, and allocates
only an opaque `stage_
` output slot rather than a Worker path. The Job payload repeats the
context ID/digest; claim and heartbeat return the same server-validated context; profile checks and
every permit issuance revalidate the transitive compilation/context/ticket binding. A v6→v7
migration advances only non-dispatchable v6 state with an empty context table and fails closed if
tickets, permits, internal Replay Jobs, durable reservations, or advanced batch/item state already
exist, because those exact historical context bytes cannot be backfilled. The dedicated
`kisa-exact-v1` daemon now claims only that profile, heartbeats its fenced lease, obtains one
durable server permit immediately before every Tool dispatch, and seals output into the exact
opaque staging slot. It submits no path, ArtifactRef, result, digest, or verdict. Schema v9
finalization imports the slot into the server-owned repository, independently reopens the seals,
checks compilation/ticket/source/permit lineage, derives the common Gate decision, and atomically
finalizes the output Artifact, ticket, Job, item, batch, Run, and audit state after revalidating
the permit-backed authority whose budget/rate units were already consumed at issuance. Once
any permit exists, execution failure is terminal and automatic same-ticket dispatch retry is
forbidden. Exact response-loss retries of the identical ordinal-bound permit request and the
identical server finalization request are idempotent; neither retries a Tool dispatch.
Opaque public source/batch admission and role-scoped state-read APIs are implemented. When Replay
claim polling finds no issued Job, the Control Plane may issue a pending retry only after rereading
the immutable source, proving an unchanged Candidate/contract plan, finding no permit, confirming
fully released budget/rate reservations, removing an empty prior staging capability, and checking
the item remains below its maximum attempts. It preserves the abandoned Job/ticket/Run as history
and appends a fresh Replay Run, compilation, execution context, reservations, one-shot Job, ticket,
staging capability, attempt, and fence. Any permit, staged output, missing capability, authority
mismatch, or exhausted attempt count fails closed; the same Job or ticket is never redispatched.
Schema v11/v12 aggregate projection now covers both confirmation and exact dual-source negative
Control Plane retest; portable/off-host proof remains incomplete.
- Audit Events form a sequence-checked SHA-256 chain, and completed Run artifacts are captured in
append-only integrity seals. Mode Pack outputs extend the previous root instead of overwriting it.
## Development setup
Python 3.12 or newer is supported. The repository `.python-version` selects Python 3.12 as the
portable contributor and CI baseline.
The checked-in root `uv.lock` is the canonical dependency lock for the application, development
tools, and optional Control Plane. Create an exact environment from a clean clone with:
uv sync --locked --extra dev --extra control-plane
Use `uv lock` after an intentional dependency constraint change and review the resulting lockfile
diff. Use `uv lock --upgrade-package ` for a targeted upgrade rather than refreshing every
package implicitly. The Docker Worker remains a separate execution boundary and continues to use
`containers/worker/requirements.lock`.
For environments where `uv` is unavailable, the editable pip install remains a supported
bootstrap path, but it resolves within the declared version ranges and is not the reproducible
quality-gate environment:
python -m venv .venv
.venv\Scripts\python -m pip install -e ".[dev,control-plane]"
## Supported command surface
| Group | Commands |
| --- | --- |
| Core | `validate`, `run`, `multi-run`, `multi-cancel-check` |
| Provider and agent loop | `provider-check`, `provider-agent-run`, `tool-loop-run`, `tool-loop-approval-check` |
| KISA AI Red Team | `kisa-run`, `kisa-plan-remediation`, `kisa-retest` |
| Bug Bounty | `bug-bounty-review`, `bug-bounty-compile`, `bug-bounty-report`, `bug-bounty-run` |
| CTF | `ctf-run`, `ctf-web-run` (compatibility alias), `ctf-suite-run` |
| Evidence and infrastructure | `evidence-verify`, `replay-verify`, `worker-check`, `egress-check`, `mcp-check` |
The optional server processes are installed as `pajin-control-plane`, `pajin-worker-daemon`, and
`pajin-replay-worker-daemon`.
Run `pajin --help` or `pajin --help` for the authoritative option list.
## Run the vertical slice
.venv\Scripts\pajin validate examples\ai-redteam.yaml
.venv\Scripts\pajin run examples\ai-redteam.yaml
# Explicit development/test-only execution
.venv\Scripts\pajin run examples\ai-redteam.yaml --worker simulated
`run` and `multi-run` default to the Docker Worker. The simulated backend must be selected
explicitly and exists only for deterministic development and unit tests; it is not an isolation
boundary and does not produce real-target evidence. Every Local and Multi-Agent Run seals the
actual backend identity in `execution-context.json`, duplicates it in `run.json` and the start
event, and renders it in the report. Simulated CLI output and reports carry an explicit
`SIMULATED / NOT REAL TARGET EVIDENCE` warning.
## Bug Bounty Scope Parser
Bug Bounty execution starts from a typed program-policy snapshot, not an agent's interpretation of
free-form scope. The first command normalizes the source policy and emits `program.normalized.json`,
`scope-review.json`, and an operator-facing `scope-review.md`:
.venv\Scripts\pajin bug-bounty-review examples\bug-bounty-program.yaml
The review prints a SHA-256 scope digest over canonical policy JSON, including the original policy
text. After comparing the review with the authoritative program page, compile only that exact digest:
.venv\Scripts\pajin bug-bounty-compile examples\bug-bounty-program.yaml `
--scope-digest `
--approved-by `
--approved-at 2026-07-13T10:00:00+09:00 `
--expires-at 2026-07-20T10:00:00+09:00 `
--evidence
.venv\Scripts\pajin validate .pajin\campaigns\example-bug-bounty-lab.yaml
Any change to the raw policy, assets, methods, tool categories, limits, or time windows invalidates
the digest. The compiler caps this MVP at T2, injects mandatory prohibitions for denial of service,
social engineering, persistence, credential stuffing, real-user-data access, and exfiltration, and
requires concrete entry points that match an allow rule without matching a deny rule. The runtime
then enforces allow/deny scope, method and category allowlists, weekly test windows, and a sliding
one-minute request limit before Worker dispatch.
Compilation also requires the approval to be active at compilation time. A concrete asset using
the generic `generic-http` profile remains review-only until PAJIN implements a bounded probe
profile for it; mixed manifests fail instead of silently skipping that target. Review and Campaign
artifacts are atomically replaced only when the destination is absent or a regular file, and any
symbolic-link parent or leaf is rejected.
Evidence retention remains an explicit manual control. Duplicate triage can consume a typed local
snapshot, but synchronizing that snapshot with a platform or issue tracker remains manual.
### Finding triage and submission drafts
After a completed Bug Bounty Campaign has a sealed validation snapshot, compare its reportable
Candidates and Findings with a program-specific known-finding index and generate local review
drafts:
.venv\Scripts\pajin bug-bounty-report `
examples\bug-bounty-program.yaml `
`
--known-findings examples\bug-bounty-known-findings.yaml
The reporter loads the exact sealed Candidate/Decision snapshot, including a complete versioned
projection when one exists. It rechecks that the Run used the current program digest and exact
compiled scope policy, accepts only declared targets, and requires every cited evidence file to be
sealed under that Run's `evidence/` directory. A partial, substituted, or authority-mismatched
snapshot fails closed. The reporter writes an immutable-input report set under:
bug-bounty-reports//
bug-bounty-triage.json
bug-bounty-report.md
submissions/.md
Exact fingerprints use the program, normalized target path and query-parameter names,
vulnerability class, affected component, and normalized root cause. Only exact matches against an
unresolved known Finding or the same Run are automatically suppressed. A resolved known Finding or
the same cause on a different endpoint becomes `needs-review`, preserving possible regressions and
multi-endpoint impact. Missing impact, remediation, component, or root-cause data produces a draft
with explicit TODOs instead of an automatic submission.
When the program declares `duplicateCheckRequired: true`, omitting `--known-findings` is not treated
as an authoritative empty index: affected items receive `duplicate-check-not-performed`, remain
`needs-review`, and are not submission-eligible. Supplying a typed index with `findings: []` records
that the check was performed and found no known matches. A finding whose concrete target belongs
only to an asset with `eligibleForBounty: false` likewise remains `needs-review` regardless of its
other fields.
A Candidate whose exact Decision records successful objective and semantic checks but lacks
independent reproduction is retained as `semantic-review-only`. It receives
`independent-reproduction-not-confirmed`, remains `needs-review` with
`submissionEligible: false`, and may produce a clearly marked operator-review draft. Unsupported,
inconclusive, rejected, or authority-mismatched Candidate claims do not become drafts. Only a
Finding from a sealed `verified-independent-replay` projection can become `ready` and
submission-eligible; Worker-only replay evidence without independently verifiable target-execution
attestation remains review-only.
The generated Markdown is a local draft only. PAJIN does not submit to a Bug Bounty platform or
claim that local evidence has production-grade external attestation.
### Automated local Bug Bounty lab
The executable Bug Bounty slice is intentionally narrower than the general scope parser. It runs
only the compiled `boolean-sqli-lab` profile against the synthetic loopback-bound target. The
Planner can select only `bug-bounty.boolean-sqli-probe`; the Tool accepts no agent-authored attack
payload and the trusted Worker performs exactly one baseline, one negative control, and one boolean
comparison. The Validator ignores the Worker's claimed conclusion and recomputes the signal from
the three bounded observations. This protects the evidence-review boundary but reuses the original
execution and is not independent reproduction. One Tool call reserves three request-rate units.
Build the Worker and egress proxy, then start the vulnerable lab:
docker build --tag pajin-worker:dev containers\worker
docker build --tag pajin-egress-proxy:dev containers\egress-proxy
docker compose -f containers\compose.bug-bounty-lab.yaml up --build --detach
.venv\Scripts\pajin bug-bounty-review `
examples\bug-bounty-lab-program.yaml `
--output .pajin\bug-bounty-lab-review
Inspect the generated review and copy its printed digest into the approval command:
.venv\Scripts\pajin bug-bounty-compile `
examples\bug-bounty-lab-program.yaml `
--scope-digest `
--approved-by `
--approved-at `
--expires-at `
--evidence `
--output .pajin\campaigns
.venv\Scripts\pajin bug-bounty-run `
examples\bug-bounty-lab-program.yaml `
.pajin\campaigns\local-bug-bounty-sqli-lab.yaml
The vulnerable profile currently produces one legacy validation draft. Recreate the target with
the hardened override and run the same digest-approved Campaign again; the fixed probe should then
produce zero findings:
docker compose `
-f containers\compose.bug-bounty-lab.yaml `
-f containers\compose.bug-bounty-lab.hardened.yaml `
up --build --detach --force-recreate
.venv\Scripts\pajin bug-bounty-run `
examples\bug-bounty-lab-program.yaml `
.pajin\campaigns\local-bug-bounty-sqli-lab.yaml
docker compose -f containers\compose.bug-bounty-lab.yaml down
`bug-bounty-run` always uses the Docker Worker, creates local evidence and triage drafts, and never
submits a report externally. Generic public Bug Bounty assets remain reviewable, but compilation
rejects them until a separately bounded executable probe profile is implemented.
## Local CTF Mode
CTF Mode accepts a typed `CTFChallenge` manifest and runs the existing five-role team as Triage
Planner, category Specialist, independent flag Validator, and Reporter under the Supervisor. The
Triage Planner currently recognizes two separately bounded scenarios:
- `web.exposed-backup-config` routes to the fixed Web Specialist;
- `crypto.single-byte-xor` routes to the no-network Crypto Specialist.
Both manifests keep the expected flag as SHA-256 rather than plaintext and cannot select a Docker
image, command, executable, or scoreboard destination. `ctf-run` is the category-aware entry point;
`ctf-web-run` remains a backward-compatible alias that rejects non-Web manifests.
### Web challenge
Build the Worker and egress proxy, then start the vulnerable loopback-bound challenge target:
docker build --tag pajin-worker:dev containers\worker
docker build --tag pajin-egress-proxy:dev containers\egress-proxy
docker compose -f containers\compose.ctf-web-lab.yaml up --build --detach
.venv\Scripts\pajin ctf-run examples\ctf-web-backup-lab.yaml
The Triage Planner can create only one `ctf.web-backup-probe` step. The Tool and trusted Worker both
enforce one GET to `http://host.docker.internal:8780/backup/config.json.bak`; the Gateway injects
the private-network egress policy from the compiled Campaign. The Specialist never receives the
expected digest. The Mode-specific digest Validator hashes the candidate and produces a verified
solve result only on a constant-time digest match.
The vulnerable profile should produce `solved` plus `ctf-result.json` and `ctf-writeup.md`. Recreate
the same target with the hardened override to confirm the backup artifact is absent and the command
returns an `unsolved` non-zero result:
docker compose `
-f containers\compose.ctf-web-lab.yaml `
-f containers\compose.ctf-web-lab.hardened.yaml `
up --build --detach --force-recreate
.venv\Scripts\pajin ctf-run examples\ctf-web-backup-lab.yaml
docker compose -f containers\compose.ctf-web-lab.yaml down
### Crypto challenge
The Crypto manifest carries one bounded inline artifact as lowercase hex plus the SHA-256 of its
decoded bytes. The compiler derives a logical `artifact.invalid` content address; the manifest does
not supply any network target or filesystem path. The Tool rechecks the digest before creating a
Worker Job, and the Worker rechecks it again before evaluating all 256 single-byte XOR keys. The
Tool declares T0 risk, receives no egress policy, invokes no external process, and returns at most
one `PAJIN{...}` candidate.
Build the updated Worker and run the synthetic artifact without starting any target service:
docker build --tag pajin-worker:dev containers\worker
.venv\Scripts\pajin ctf-run examples\ctf-crypto-xor-lab.yaml
The Crypto Specialist never receives the expected flag digest. The Mode-specific digest Validator
binds the candidate to same-run evidence and compares its SHA-256 with the sealed Campaign value.
The write-up records category routing, offline analysis, and the final digest decision.
Core execution creates the first evidence-integrity seal; CTF result and write-up finalization
verifies that root and appends a second seal. `ctf-run` is Docker-only and has no scoreboard
credential, API client, or external submission path. Additional categories require a separate
typed scenario, Tool grammar, isolated fixture, independent verification rule, and safety review.
### Web + Crypto Suite
`ctf-suite-run` compiles exactly one Web manifest and one Crypto manifest into one Campaign. The
two manifests must have distinct challenge IDs, the same approving authority, and overlapping
authorization windows. The compiled Campaign uses only their approval-window intersection, binds
both member contracts into authorization evidence, derives a six-agent budget, and permits exactly
two Tool calls.
Start the local Web fixture, then run both typed challenges together:
docker compose -f containers\compose.ctf-web-lab.yaml up --build --detach
.venv\Scripts\pajin ctf-suite-run `
web-crypto-suite `
examples\ctf-web-backup-lab.yaml `
examples\ctf-crypto-xor-lab.yaml
docker compose -f containers\compose.ctf-web-lab.yaml down
The deterministic Triage Planner creates one `ctf-web-specialist` and one
`ctf-crypto-specialist`. Each receives a separate Capability Grant restricted to its own target and
Tool. Both fixed Tools opt in to `parallelSafe`, so the generic runner executes them in the same
bounded local wave and restores their results to deterministic plan order. The aggregate
`ctf-suite-result.json` retains `solved`, `unsolved`, or `invalid-flag` for each challenge, while
`ctf-suite-writeup.md` records only independently digest-validated flags. Any non-solved member
makes the CLI return non-zero after still sealing the complete aggregate evidence. No scoreboard
submission is available.
## KISA AI Red Team Mode Pack
Run the KISA-aligned indirect prompt-injection and unauthorized tool-use scenario with two
independent repetitions:
.venv\Scripts\pajin kisa-run examples\kisa-ai-redteam.yaml --worker simulated --repetitions 2
.venv\Scripts\pajin kisa-run examples\kisa-ai-redteam.yaml --worker docker --repetitions 2
For the exact KISA AI Chat contracts, the ordinary Local runner can opt into the same
Candidate-to-replay-to-Gate boundary explicitly:
.venv\Scripts\pajin run examples\kisa-ai-chat-lab.yaml --worker docker --kisa-replay --repetitions 2
Without `--kisa-replay`, `pajin run` remains the normal Local execution path and does not create
replay tickets or invoke the confirmation Gate. The opt-in path is limited to AI Red Team Campaigns
and the exact M03, M06, and A04 allowlist; unsupported or missing contracts stay unconfirmed rather
than being selected by a generic predicate.
The Mode Pack maps the 19 threat classes in the KISA AI Security Red Teaming Guide to a typed
catalog, selects target-compatible scenarios, executes each scenario through separate Specialist
agents, and deduplicates Candidate and legacy validation findings after same-Run evidence checks.
Trusted M03, M06, and A04 Candidates can receive a sealed `verified-replay-evidence` projection
through separate replay Runs and the common Gate, but remain `needs-review` without independent
execution attestation. Other requested threats remain a
coverage gap or `needs-review` until an executable target-linked scenario and explicit replay
contract are added.
In addition to the standard run artifacts, `kisa-run` writes:
kisa-results.json
kisa-checklist.json
kisa-test-plan.json
kisa-completion-report.json
kisa-execution-log.json
kisa-report.md
Checklist values distinguish `yes`, `no`, `not-applicable`, and `needs-review`. Legal, ethical,
personnel, business-impact, remediation, and lifecycle-governance questions are not inferred from
technical execution evidence. The generated report supports an assessment; it is not a compliance
certification.
### Provider-neutral AI Chat/RAG lab
PAJIN defines a fixed, provider-neutral chat contract for authorized AI application targets. The
registered `ai.chat-probe` Tool can send only bounded POST conversations selected from the KISA
scenario catalog; it cannot inject arbitrary process commands or grant itself network access. The
Tool Gateway derives egress from Campaign Scope, and the Semantic Validator rechecks the raw
transcript instead of trusting the Tool's vulnerability flag. This is semantic and deterministic
evidence review over the original execution, not a second reproduction request.
Build and start the intentionally vulnerable local target, then run the M03, M06, and A04 campaign:
docker build --tag pajin-worker:dev containers/worker
docker compose -f containers/compose.ai-lab.yaml up --build --detach
.venv\Scripts\pajin kisa-run examples\kisa-ai-chat-lab.yaml --worker docker --repetitions 2
docker compose -f containers/compose.ai-lab.yaml down
The six Specialist Tasks use unique session IDs and cover system-prompt disclosure, jailbreak
policy bypass, and persistent memory poisoning. The lab binds only to `127.0.0.1:8765`, runs as a
non-root user with a read-only filesystem and no Linux capabilities, and is not a production AI
service.
A completed `kisa-run` additionally reproduces eligible trusted M03, M06, and A04 Candidates in
separate replay Runs. Each attempt uses a session distinct from the source execution and every other
attempt. The live KISA Oracle recomputes the exact catalog checks from the raw transcript, and the
source/replay link is written to `kisa-replay-index.json`. The current Worker-only path keeps
`confirmationMutationApplied` at `false`. The common gate reloads the receipts and appends a sealed
`validation/v1alpha1` Decision/evidence/report projection with
`verified-replay-evidence` semantics; the original flat artifacts remain the immutable pre-replay
snapshot and no product Finding is added.
The local positive replay-ticket ledger is stored at
`