{"owner":"KeygraphHQ","repo":"shannon","hasSkills":true,"totalSkillsCount":6,"totalTokensCount":24819,"categories":["root-instruction","command-prompt","claude-rule"],"hasMcp":false,"mcpConfig":null,"found":["llms.txt",".claude/commands/pr.md",".claude/commands/review.md",".claude/commands/debug.md","CLAUDE.md","llms-full.txt"],"skills":{"llms.txt":"# Shannon\n\n> Shannon is an autonomous AI pentesting project by Keygraph. This repository contains Shannon, the AGPL-3.0 open-source white-box pentesting CLI. Keygraph Pro and Keygraph Enterprise are Keygraph's commercial continuous pentesting and AppSec editions.\n\nUse this file as the concise entry point for AI agents and LLMs reading this repository. For a single combined context file, use [llms-full.txt](llms-full.txt).\n\n## Start Here\n\n- [README](README.md): Main project overview, editions, quick start, Shannon capabilities, CI/CD integrations, common questions, safety notes, licensing, and support links.\n\n## Shannon\n\n- [Development](docs/development.md): Source-build workflow, common CLI commands, repository paths, and output locations.\n- [Configuration](docs/configuration.md): Authenticated testing, login flows, rules of engagement (including rate-limit guidance), report filters, and credential precedence.\n- [AI Providers](docs/ai-providers.md): Anthropic, OpenAI, xAI, AWS Bedrock, any other Pi-supported provider, custom LLM gateway setup, custom model configuration for models not yet in the Pi catalogue, and local self-hosted runtimes (Ollama, LM Studio, vLLM).\n- [Platforms and Networking](docs/platforms.md): Windows/WSL2, Linux, macOS, Docker networking, local applications, and custom hostnames.\n- [Workspaces and Resuming](docs/workspaces.md): Workspace storage, naming, resuming interrupted scans, and examples.\n- [Safety and Limitations](docs/safety.md): Authorized-use requirements, non-production guidance, mutative effects, model caveats, scope limits, cost, and performance.\n- [Coverage and Roadmap](docs/coverage-roadmap.md): Current Shannon coverage and roadmap direction.\n\n## Keygraph Pro and Enterprise\n\n- [Pro and Enterprise](docs/keygraph-platform.md): Keygraph's commercial continuous pentesting and AppSec editions, compared edition by edition with Shannon. The Community Program, Pro, and Enterprise all include black-box and white-box pentesting, parsed-code SAST, SCA and secrets scanning, findings management, fix pull requests and retests, Jira sync, and SSO, RBAC, and audit logs. Pro and the Community Program are cloud-hosted; Enterprise is self-hosted or air-gapped. Shannon Lite is now just Shannon. Shannon Pro is now Keygraph Pro.\n\n## External Links\n\n- [Keygraph website](https://keygraph.io): Company and commercial product information.\n- [Keygraph pricing](https://keygraph.io/pricing): Prices and the feature table for Shannon, the Community Program, Pro, and Enterprise.\n- [Keygraph demo](https://cal.com/team/keygraph/keygraph-technical-demo): Technical demo booking.\n- [Community Discord](https://discord.gg/cmctpMBXwE): Community support and discussion.\n",".claude/commands/pr.md":"---\ndescription: Create a PR to main branch using conventional commit style for the title\n---\n\nCreate a pull request from the current branch to the `main` branch.\n\n## Arguments\n\nThe user may provide issue numbers that this PR fixes: `$ARGUMENTS`\n\n- If provided (e.g., `123` or `123,456`), use these issue numbers\n- If not provided, check the branch name for issue numbers (e.g., `fix/123-bug` or `issue-456-feature` → extract `123` or `456`)\n- If no issues are found, omit the \"Closes\" section\n\n## Steps\n\nFirst, analyze the current branch to understand what changes have been made:\n1. Run `git log --oneline -10` to see recent commit history and understand commit style\n2. Run `git log main..HEAD --oneline` to see all commits on this branch that will be included in the PR\n3. Run `git diff main...HEAD --stat` to see a summary of file changes\n4. Run `git branch --show-current` to get the branch name for issue detection (if no explicit issues provided)\n\nThen generate a PR title that:\n- Follows conventional commit format (e.g., `fix:`, `feat:`, `chore:`, `refactor:`)\n- Is concise and accurately describes the changes\n- Matches the style of recent commits in the repository\n\nGenerate a PR body with:\n- A `## Summary` section using rich bullets with bold action leads\n- A `Closes #X` line for each issue number (if any were provided or detected from branch name)\n\nEach Summary bullet must follow this format:\n- **Bold action phrase** (imperative verb: \"Add X\", \"Replace Y\", \"Fix Z\") — followed by em dash and a 1-2 sentence conceptual description of what changed and why\n- Keep descriptions conceptual — no inline code references (no backticks for function/file names). The diff shows the code\n- Use 2-5 bullets, scaling with PR size. Group related changes into single bullets rather than listing every file touched\n\nExample:\n```\n## Summary\n\n- **Add preflight validation** — validates repo path, config, and credentials before agent execution. Fails fast with actionable errors\n- **Replace error strings** — pipe-delimited segments rendered as multi-line blocks with phase context, type, message, and remediation hint\n- **Add error classification** — new error codes for repo, auth, and billing failures with proper retry classification\n```\n\nFinally, create the PR using the gh CLI:\n```\ngh pr create --base main --title \"<generated title>\" --body \"$(cat <<'EOF'\n## Summary\n<rich bullets>\n\nCloses #<issue1>\nCloses #<issue2>\nEOF\n)\"\n```\n\nNote: Omit the \"Closes\" lines entirely if no issues are associated with this PR.\n\nIMPORTANT:\n- Do NOT include any Claude Code attribution in the PR\n- Use the conventional commit prefix that best matches the changes (fix, feat, chore, refactor, docs, etc.)\n- The `Closes #X` syntax will automatically close the referenced issues when the PR is merged\n",".claude/commands/review.md":"---\ndescription: Review code changes for Shannon-specific patterns, security, and common mistakes\n---\n\nReview the current changes (staged or working directory) with focus on Shannon-specific patterns and common mistakes.\n\n## Step 1: Gather Changes\nRun these commands to understand the scope:\n```bash\ngit diff --stat HEAD\ngit diff HEAD\n```\n\n## Step 2: Check Shannon-Specific Patterns\n\n### Error Handling (CRITICAL)\n- [ ] **All errors use PentestError** - Never use raw `Error`. Use `new PentestError(message, type, retryable, context)`\n- [ ] **Error type is appropriate** - Use correct type: 'config', 'network', 'tool', 'prompt', 'filesystem', 'validation', 'billing', 'unknown'\n- [ ] **Retryable flag matches behavior** - If error will be retried, set `retryable: true`\n- [ ] **Context includes debugging info** - Add relevant paths, tool names, error codes to context object\n- [ ] **Never swallow errors silently** - Always log or propagate errors\n- [ ] **Use ErrorCode enum** - Prefer `ErrorCode.CONFIG_INVALID` over string matching for classification\n- [ ] **Result<T,E> for service returns** - Services return `Result`, not throw\n\n### Audit System & Concurrency (CRITICAL)\n- [ ] **Mutex protection for parallel operations** - Use `sessionMutex.lock()` when updating `session.json` during parallel agent execution\n- [ ] **Reload before modify** - Always call `this.metricsTracker.reload()` before updating metrics in mutex block\n- [ ] **Atomic writes for session.json** - Use `atomicWrite()` for session metadata, never `fs.writeFile()` directly\n- [ ] **Stream drain handling** - Log writes must wait for buffer drain before resolving\n- [ ] **Semaphore release in finally** - Git semaphore must be released in `finally` block\n\n### Claude SDK Integration (CRITICAL)\n- [ ] **MCP server configuration** - Verify Playwright MCP uses `--isolated` and unique `--user-data-dir`\n- [ ] **Prompt variable interpolation** - Check all `{{VARIABLE}}` placeholders are replaced\n- [ ] **Turn counting** - Increment `turnCount` on assistant messages, not tool calls\n- [ ] **Cost tracking** - Extract cost from final `result` message, track even on failure\n- [ ] **API error detection** - Check for \"session limit reached\" (fatal) vs other errors\n\n### Configuration & Validation (CRITICAL)\n- [ ] **FAILSAFE_SCHEMA for YAML** - Never use default schema (prevents code execution)\n- [ ] **Security pattern detection** - Check for path traversal (`../`), HTML injection (`<>`), JavaScript URLs\n- [ ] **Rule conflict detection** - Rules cannot appear in both `avoid` AND `focus`\n- [ ] **Duplicate rule detection** - Same `type:url_path` cannot appear twice\n- [ ] **JSON Schema validation before use** - Config must pass AJV validation\n\n### Services Layer & DI Container (CRITICAL)\n- [ ] **Business logic in services, not activities** — Activities: heartbeat loop, error classification, container calls only. Domain logic → `src/services/`\n- [ ] **Services accept ActivityLogger** — Never import `@temporalio/*` in services. Use `ActivityLogger` interface from `src/types/`\n- [ ] **Result type for fallible operations** — Service methods return `Result<T, PentestError>`, unwrap with `isOk()`/`isErr()`. Activities call `executeOrThrow()` at the boundary\n- [ ] **Container lifecycle** — `getOrCreateContainer()` at activity start, `removeContainer()` only in workflow cleanup\n- [ ] **AuditSession not in container** — Must be passed per-agent call (parallel safety)\n\n### Session & Agent Management (CRITICAL)\n- [ ] **Deliverable dependencies respected** - Exploitation agents only run if vulnerability queue exists AND has items\n- [ ] **Queue validation before exploitation** - Use `safeValidateQueueAndDeliverable()` to check eligibility\n- [ ] **Git checkpoint before agent run** - Create checkpoint for rollback on failure\n- [ ] **Git rollback on retry** - Call `rollbackGitWorkspace()` before each retry attempt\n- [ ] **Agent prerequisites checked** - Verify prerequisite agents completed before running dependent agent\n\n### Parallel Execution\n- [ ] **Promise.allSettled for parallel agents** - Never use `Promise.all` (partial failures should not crash batch)\n- [ ] **Staggered startup** - 2-second delay between parallel agent starts to prevent API throttle\n- [ ] **Individual retry loops** - Each agent retries independently (3 attempts max)\n- [ ] **Results aggregated correctly** - Handle both 'fulfilled' and 'rejected' results from `Promise.allSettled`\n\n## Step 3: TypeScript Safety\n\n### Type Assertions (WARNING)\n- [ ] **No double casting** - Never use `as unknown as SomeType` (bypasses type safety)\n- [ ] **Validate before casting** - JSON parsed data should be validated (JSON Schema) before `as Type`\n- [ ] **Prefer type guards** - Use `instanceof` or property checks instead of assertions where possible\n\n### Null/Undefined Handling\n- [ ] **Explicit null checks** - Use `if (x === null || x === undefined)` not truthy checks for critical paths\n- [ ] **Nullish coalescing** - Use `??` for null/undefined, not `||` which also catches empty string/0\n- [ ] **Optional chaining** - Use `?.` for nested property access on potentially undefined objects\n\n### Imports & Types\n- [ ] **Type imports** - Use `import type { ... }` for type-only imports\n- [ ] **No implicit any** - All function parameters and returns must have explicit types\n- [ ] **Readonly for constants** - Use `Object.freeze()` and `Readonly<>` for immutable data\n\n## Step 4: Security Review\n\n### Defensive Tool Security\n- [ ] **No credentials in logs** - Check that passwords, tokens, TOTP secrets are not logged to audit files\n- [ ] **Config file size limit** - Ensure 1MB max for config files (DoS prevention)\n- [ ] **Safe shell execution** - Command arguments must be escaped/sanitized\n\n### Code Injection Prevention\n- [ ] **YAML safe parsing** - FAILSAFE_SCHEMA only\n- [ ] **No eval/Function** - Never use dynamic code evaluation\n- [ ] **Input validation at boundaries** - URLs, paths validated before use\n\n## Step 5: Common Mistakes to Avoid\n\n### Anti-Patterns Found in Codebase\n- [ ] **Catch + re-throw without context** - Don't just `throw error`, wrap with additional context\n- [ ] **Silent failures in session loading** - Corrupted session files should warn user, not silently reset\n- [ ] **Duplicate retry logic** - Don't implement retry at both caller and callee level\n- [ ] **Hardcoded error message matching** - Prefer error codes over regex on error.message\n- [ ] **Missing timeout on long operations** - Git operations and API calls should have timeouts\n- [ ] **Console.log in services** — Use `ActivityLogger`. Only CLI display code (`client.ts`, `worker.ts`, `output-formatters.ts`) uses console.log\n- [ ] **Temporal imports in services** — Services must stay Temporal-agnostic. If you need Temporal APIs, it belongs in activities\n\n### Code Quality\n- [ ] **No dead code added** - Remove unused imports, functions, variables\n- [ ] **No over-engineering** - Don't add abstractions for single-use operations\n- [ ] **Comments only where needed** - Self-documenting code preferred over excessive comments\n- [ ] **Consistent file naming** - kebab-case for files (e.g., `queue-validation.ts`)\n\n## Step 6: Provide Feedback\n\nFor each issue found:\n1. **Location**: File and line number\n2. **Issue**: What's wrong and why it matters\n3. **Fix**: How to correct it (with code example if helpful)\n4. **Severity**: Critical / Warning / Suggestion\n\n### Severity Definitions\n- **Critical**: Will cause bugs, crashes, data loss, or security issues\n- **Warning**: Code smell, inconsistent pattern, or potential future issue\n- **Suggestion**: Style improvement or minor enhancement\n\nSummarize with:\n- Total issues by severity\n- Overall assessment (Ready to commit / Needs fixes / Needs discussion)\n\n---\n\nNow review the current changes.\n",".claude/commands/debug.md":"---\ndescription: Systematically debug errors using context analysis and structured recovery\n---\n\nYou are debugging an issue. Follow this structured approach to avoid spinning in circles.\n\n## Step 1: Capture Error Context\n- Read the full error message and stack trace\n- Identify the layer where the error originated:\n  - **CLI/Args** - Input validation, path resolution\n  - **Config Parsing** - YAML parsing, JSON Schema validation (`src/config-parser.ts`)\n  - **Session Management** - Agent definitions (`src/session-manager.ts`), mutex (`src/utils/concurrency.ts`)\n  - **DI Container** - Container initialization/lookup (`src/services/container.ts`)\n  - **Services** - AgentExecutionService, ConfigLoaderService, ExploitationCheckerService, error-handling (`src/services/`)\n  - **Audit System** - Logging, metrics tracking, atomic writes (`src/audit/`)\n  - **Claude SDK** - Agent execution, MCP servers, turn handling (`src/ai/claude-executor.ts`)\n  - **Git Operations** - Checkpoints, rollback, commit (`src/services/git-manager.ts`)\n  - **Validation** - Deliverable checks, queue validation (`src/services/queue-validation.ts`)\n\n## Step 2: Check Relevant Logs\n\n**Session audit logs:**\n```bash\n# Find most recent session\nls -lt workspaces/ | head -5\n\n# Check session metrics and errors\ncat workspaces/<session>/session.json | jq '.errors, .agentMetrics'\n\n# Check agent execution logs\nls -lt workspaces/<session>/agents/\ncat workspaces/<session>/agents/<latest>.log\n```\n\n## Step 3: Trace the Call Path\n\nFor Shannon, trace through these layers:\n\n1. **Worker + Client** → `src/temporal/worker.ts` - Combined worker + workflow submission\n2. **Workflow** → `src/temporal/workflows.ts` - Pipeline orchestration\n3. **Activities** → `src/temporal/activities.ts` - Thin wrappers: heartbeat, error classification\n4. **Container** → `src/services/container.ts` - Per-workflow DI\n5. **Services** → `src/services/agent-execution.ts` - Agent lifecycle\n6. **Config** → `src/config-parser.ts` via `src/services/config-loader.ts`\n7. **Prompts** → `src/services/prompt-manager.ts`\n8. **Audit** → `src/audit/audit-session.ts` - Logging facade, metrics tracking\n9. **Executor** → `src/ai/claude-executor.ts` - SDK calls, MCP setup, retry logic\n10. **Validation** → `src/services/queue-validation.ts` - Deliverable checks\n\n## Step 4: Identify Root Cause\n\n**Common Shannon-specific issues:**\n\n| Symptom | Likely Cause | Fix |\n|---------|--------------|-----|\n| Agent hangs indefinitely | MCP server crashed, Playwright timeout | Check Playwright logs in `/tmp/playwright-*` |\n| \"Validation failed: Missing deliverable\" | Agent didn't create expected file | Check `deliverables/` dir, review prompt |\n| Git checkpoint fails | Uncommitted changes, git lock | Run `git status`, remove `.git/index.lock` |\n| \"Session limit reached\" | Claude API billing limit | Not retryable - check API usage |\n| Parallel agents all fail | Shared resource contention | Check mutex usage, stagger startup timing |\n| Cost/timing not tracked | Metrics not reloaded before update | Add `metricsTracker.reload()` before updates |\n| session.json corrupted | Partial write during crash | Delete and restart, or restore from backup |\n| YAML config rejected | Invalid schema or unsafe content | Run through AJV validator manually |\n| Prompt variable not replaced | Missing `{{VARIABLE}}` in context | Check `src/services/prompt-manager.ts` interpolation |\n| Service returns Err result | Check `ErrorCode` in Result | Trace through `classifyErrorForTemporal()` in `src/services/error-handling.ts` |\n| Container not found | `getOrCreateContainer()` not called | Check activity setup code in `src/temporal/activities.ts` |\n| ActivityLogger undefined | `createActivityLogger()` not called | Must be called at top of each activity function |\n\n**MCP Server Issues:**\n```bash\n# Check if Playwright browsers are installed\nnpx playwright install chromium\n\n# Check MCP server startup (look for connection errors)\ngrep -i \"mcp\\|playwright\" workspaces/<session>/agents/*.log\n```\n\n**Git State Issues:**\n```bash\n# Check for uncommitted changes\ngit status\n\n# Check for git locks\nls -la .git/*.lock\n\n# View recent git operations from Shannon\ngit reflog | head -10\n```\n\n## Step 5: Apply Fix with Retry Limit\n\n- **CRITICAL**: Track consecutive failed attempts\n- After **3 consecutive failures** on the same issue, STOP and:\n  - Summarize what was tried\n  - Explain what's blocking progress\n  - Ask the user for guidance or additional context\n- After a successful fix, reset the failure counter\n\n## Step 6: Validate the Fix\n\n**For code changes:**\n```bash\n# Compile TypeScript\nnpx tsc --noEmit\n\n# Quick validation run\nshannon <URL> <REPO> --pipeline-testing\n```\n\n**For audit/session issues:**\n- Verify `session.json` is valid JSON after fix\n- Check that atomic writes complete without errors\n- Confirm mutex release in `finally` blocks\n\n**For agent issues:**\n- Verify deliverable files are created in correct location\n- Check that validation functions return expected results\n- Confirm retry logic triggers on appropriate errors\n\n## Anti-Patterns to Avoid\n\n- Don't delete `session.json` without checking if session is active\n- Don't modify git state while an agent is running\n- Don't retry billing/quota errors (they're not retryable)\n- Don't ignore PentestError type - it indicates the error category\n- Don't make random changes hoping something works\n- Don't fix symptoms without understanding root cause\n- Don't bypass mutex protection for \"quick fixes\"\n\n## Quick Reference: Error Types\n\n`ErrorCode` enum in `src/types/errors.ts` provides finer-grained classification used by `classifyErrorForTemporal()` in `src/services/error-handling.ts`.\n\n| PentestError Type | Meaning | Retryable? |\n|-------------------|---------|------------|\n| `config` | Configuration file issues | No |\n| `network` | Connection/timeout issues | Yes |\n| `prompt` | Claude SDK/API issues | Sometimes |\n| `filesystem` | File read/write errors | Sometimes |\n| `validation` | Deliverable validation failed | Yes (via retry) |\n| `billing` | API quota/billing limit | No |\n| `unknown` | Unexpected error | Depends |\n\n---\n\nNow analyze the error and begin debugging systematically.\n","CLAUDE.md":"# CLAUDE.md\n\nAI-powered penetration testing agent for defensive security analysis. Automates vulnerability assessment by combining reconnaissance tools with AI-powered code analysis.\n\n## Commands\n\n**Prerequisites:** Docker, AI provider credentials (`.env` for local, `npx @keygraph/shannon setup` or env vars for npx)\n\n### Dual CLI\n\nShannon supports two CLI modes, auto-detected based on the current working directory:\n\n| | **npx** (`npx @keygraph/shannon`) | **Local** (`./shannon`) |\n|---|---|---|\n| **Install** | Zero-install via npm | Clone the repo |\n| **Image** | Pulled from Docker Hub (`keygraph/shannon:latest`) | Built locally (`shannon-worker`) |\n| **State** | `~/.shannon/` | Project directory |\n| **Credentials** | `~/.shannon/config.toml` (via `npx @keygraph/shannon setup`) or env vars | `./.env` |\n| **Config** | `~/.shannon/config.toml` (via `npx @keygraph/shannon setup`) | N/A |\n| **Prompts** | Bundled in Docker image | Mounted from `./apps/worker/prompts/` (live-editable) |\n\nMode auto-detection: local mode activates when env var `SHANNON_LOCAL=1` is set by the `./shannon` entry point (`apps/cli/src/mode.ts`). Otherwise npx mode.\n\n### npx Quick Start\n\n```bash\n# Configure credentials (interactive wizard)\nnpx @keygraph/shannon setup\n\n# Or export env vars directly (non-interactive / CI)\nexport ANTHROPIC_API_KEY=your-key\n\n# Run\nnpx @keygraph/shannon start -u <url> -r /path/to/repo\n```\n\n### Local (Development) Quick Start\n\n```bash\n# Setup\necho \"ANTHROPIC_API_KEY=your-key\" > .env\n\n# Build (auto-runs if image missing)\n./shannon build\n\n# Run\n./shannon start -u <url> -r ./my-repo\n./shannon start -u <url> -r ./my-repo -c ./apps/worker/configs/my-config.yaml\n./shannon start -u <url> -r /any/path/to/repo\n```\n\n### Common Commands\n\n```bash\n# Setup (npx mode only — one-time credential configuration)\nnpx @keygraph/shannon setup\n\n# Workspaces & Resume\n./shannon start -u <url> -r ./my-repo -w my-audit    # New named workspace\n./shannon start -u <url> -r ./my-repo -w my-audit    # Resume (same command)\n\n# Monitor\n./shannon scans                       # List running and completed scans, with each report's path\n./shannon logs [<workspace>]          # Show a scan's live log (default: the single running scan, else the most recent)\n./shannon logs [<workspace>] --agent <name>   # Tail one agent's own log (from .shannon/agents/)\n./shannon logs [<workspace>] --list-agents    # List the agents that have their own log\n./shannon status [<workspace>]        # Live phase/agent progress of one scan, read from Temporal (redraws, then exits; same default target)\n# Dashboard: http://localhost:8233\n\n# Stop\n./shannon stop [<workspace>]          # Stop one scan (default: the single running scan; confirms first; --yes/-y to skip)\n./shannon stop --all                  # Stop all running scans (Temporal stays up; confirms first)\n./shannon reset                       # Stop everything and wipe all Temporal data + volumes (type 'confirm' to proceed; cannot be skipped)\n\n# Version\n./shannon version                     # npx: package version; local: git SHA\n\n# Image management\n./shannon build [--no-cache]          # Local mode: build worker image\n\n# Build TypeScript (development)\npnpm run build                       # Build all packages via Turborepo\npnpm run check                       # Type-check all packages\npnpm biome                           # Biome lint + format + import sorting check\npnpm biome:fix                       # Auto-fix lint, format, and import sorting\n```\n\n**Monorepo tooling:** pnpm workspaces, Turborepo for task orchestration, Biome for linting/formatting. TypeScript compiler options shared via `tsconfig.base.json` at the root. All packages extend it, overriding only `rootDir` and `outDir`. Shared devDependencies (`typescript`, `@types/node`, `turbo`, `@biomejs/biome`) are hoisted to the root workspace.\n\n**Options:** `-c <file>` (YAML config), `--models-config <file>` (pi `models.json` defining models pi's catalogue lacks), `-o <path>` (output directory), `-w <name>` (named workspace; auto-resumes if exists), `--validate-auth` (run preflight and auth validation only, then stop; no pentest or report; requires a fresh workspace and an `authentication` block in the config), `--validate-model` (run the preflight model checks only — credential/registry resolution plus the cyber-access verification for cyber-gated providers — then stop; no pentest or report; requires a fresh workspace; needs no config; mutually exclusive with `--validate-auth`), `--pipeline-testing` (minimal prompts, 10s retries), `--keep-container` (preserve worker container after exit for log inspection), `--yes`/`-y` (skip the confirmation prompt on `stop`; required for non-interactive use; `reset` requires a typed `confirm` and cannot be skipped)\n\n## Architecture\n\n### Monorepo Layout\n\n```\napps/cli/        — @keygraph/shannon (published to npm, bundled with tsdown)\napps/worker/     — @shannon/worker (private, Temporal worker + pipeline logic)\n```\n\n### CLI Package (`apps/cli/`)\nPublished as `@keygraph/shannon` on npm. Contains Docker orchestration and a direct `@temporalio/client` integration for read-only status plus bounded workflow lifecycle operations; no worker/pipeline business logic or prompts. Bundled with tsdown for single-file ESM output (deps stay external).\n\n- `apps/cli/src/index.ts` — CLI dispatcher (`setup`, `start`, `stop`, `reset`, `logs`, `status`, `scans`, `build`, `version`)\n- `apps/cli/src/temporal-client.ts` — `@temporalio/client` integration: connects to the frontend on `127.0.0.1:7233` (published by compose), provides `describeScan` (status + `pendingActivities` → running agents), `queryProgress` (live `getProgress` query → `PipelineState`), `getTerminalOutcome` (workflow `result()`), and bounded lifecycle RPCs for `stop`. `stop` requests cancellation first, waits up to 10 seconds, then requests termination only when necessary and verifies closure within a bounded window. No worker of its own; scans are visible within Temporal's retention window, which `ensureInfra` (`apps/cli/src/docker.ts`) converges to `168h` (7 days) on every successful `shannon start`; override with `SHANNON_TEMPORAL_RETENTION` (a positive whole-hour value like `72h`)\n- `apps/cli/src/scan/` — `status` rendering: `pipeline.ts` (static phase/agent plan + `run*Agent` activity-type→agent map + mirrored `PipelineState`/`AgentMetrics` types; keep in sync with the worker), `derive.ts` (pure phase/agent state derivation shared by the tree and `--json`), `render.ts` (one renderer for both the live query state and the terminal result). The tree shows model work only: every row is an agent, an Agentic SAST stage, or a report step that is currently running or failed. Reconciliation is model work owned by a class, so its wall time renders as a trailing `+ duration` on that class's exploitation row (its analysis row when `exploit: false`) rather than as a row of its own; deterministic bookkeeping stages (`report:*` renumber/assemble/finalize/surface) never appear once they complete. `DerivedPhase.children` (renders sub-rows) and `DerivedPhase.meta` (`duration` vs a `k/N done` tally) are independent — Agentic SAST lists stages under a duration, exploitation lists classes under a tally\n- `apps/cli/src/mode.ts` — Auto-detection: local mode if `SHANNON_LOCAL=1` env var is set\n- `apps/cli/src/docker.ts` — Compose lifecycle, image pull/build, and ephemeral `docker run` worker spawning. Each worker carries workspace, task-queue, and preselected workflow-ID labels so stop can correlate the local worker with its Temporal execution before `session.json` exists. Before `docker run`, start fsyncs that exact candidate under the workspace's hidden internals and clears it only when `session.json` registers the same ID; stop reconciles any candidate left by an interrupted launch. Start also checks the image's workflow-ID protocol label and refuses a stale worker that would ignore the preselected ID\n- `apps/cli/src/home.ts` — State directory management (`~/.shannon/` for npx, `./` for local)\n- `apps/cli/src/env.ts` — `.env` loading, TOML fallback (npx only) via `apps/cli/src/config/resolver.ts`, credential validation, provider-scoped env flag building\n- `apps/cli/src/model-spec.ts` — `SHANNON_AI_MODEL` (`<provider>:<model-id>`) parsing; mirrors `apps/worker/src/ai/models.ts`\n- `apps/cli/src/config/resolver.ts` — Cascading config (npx only): env vars → `~/.shannon/config.toml` (parsed with `smol-toml`)\n- `apps/cli/src/config/writer.ts` — TOML serialization and secure file persistence (0o600)\n- `apps/cli/src/commands/setup.ts` — Interactive TUI wizard (`@clack/prompts`) for provider credential setup (npx only)\n- `apps/cli/src/paths.ts` — Repo/config/models-config path resolution (any absolute or relative path). `MODELS_CONFIG_CONTAINER_PATH` is fixed at `/app/models.json` because the worker names it to pi rather than discovering it\n- `apps/cli/src/version.ts` — Version reporting (npx: `package.json` version; local: `git-<sha>`)\n- `apps/cli/src/tty.ts` — Terminal capability detection: `requireInteractive` guard (fails fast off-TTY instead of hanging on a prompt), `supportsColor` color gating (`NO_COLOR`/`FORCE_COLOR`), and `stdoutIsTerminal` for spinner/cursor output\n- `apps/cli/src/commands/` — Command handlers\n- `apps/cli/infra/compose.yml` — Bundled Temporal compose file for npx mode\n- `apps/cli/tsdown.config.ts` — tsdown bundler config\n- `shannon` — Node.js entry point (`#!/usr/bin/env node`) that delegates to `apps/cli/dist/index.mjs`\n\n### Docker Architecture\nInfra (Temporal) runs via `docker-compose.yml`. Workers are ephemeral `docker run --rm` containers, one per scan, each with a unique task queue, preselected workflow ID, matching identity labels, and isolated volume mounts. `shannon stop --all` takes the union of labeled running workers and Temporal-running workflows, so an orphaned workflow is still stopped after its worker has disappeared.\n\n- `docker-compose.yml` — Infra only: `shannon-temporal` (port 7233/8233). Network: `shannon-net`\n- `Dockerfile` — 2-stage build (builder + Chainguard Wolfi runtime). Uses pnpm. Entrypoint: `CMD [\"node\", \"apps/worker/dist/temporal/worker.js\"]`\n- No `docker-compose.docker.yml` — host gateway handled via `--add-host` flag in CLI\n- `/etc/hosts` forwarding — at worker spawn, `forwardEtcHostsFlags` in `apps/cli/src/docker.ts` reads the host's `/etc/hosts` and emits one `--add-host` flag per valid user-added entry. Loopback IPs (`127.x`, `::1`) are rewritten to `host-gateway`; IPv6 addresses are bracketed. Disable per-scan via `SHANNON_FORWARD_HOSTS=false`. Native Windows is refused at startup (`blockNativeWindows` in `apps/cli/src/index.ts`, pointing to the WSL2 guide in `docs/platforms.md`); WSL2 reads its own `/etc/hosts` via the Linux path.\n\n### Worker Package (`apps/worker/`)\n- `apps/worker/src/paths.ts` — Centralized path constants (`PROMPTS_DIR`, `CONFIGS_DIR`, `WORKSPACES_DIR`)\n- `apps/worker/src/session-manager.ts` — Agent definitions (`AGENTS` record). Agent types in `apps/worker/src/types/agents.ts`\n- `apps/worker/src/config-parser.ts` — YAML config parsing with JSON Schema validation\n- `apps/worker/src/ai/pi/pi-executor.ts` — pi harness integration (agent-level retry disabled so Temporal owns restarts; provider-level retry on, see `apps/worker/src/ai/pi/retry-settings.ts`)\n- `apps/worker/src/services/` — Business logic layer (Temporal-agnostic). Activities delegate here. Key: `agent-execution.ts`, `error-handling.ts`, `container.ts`\n- `apps/worker/src/types/` — Consolidated types: `Result<T,E>`, `ErrorCode`, `AgentName`, `ActivityLogger`, etc.\n- `apps/worker/src/utils/` — Shared utilities (file I/O, formatting, concurrency)\n\n### Temporal Orchestration\nDurable workflow orchestration with crash recovery, queryable progress, intelligent retry, and parallel execution (5 concurrent agents in vuln/exploit phases).\n\n- `apps/worker/src/temporal/workflows.ts` — Main workflow (`pentestPipelineWorkflow`)\n- `apps/worker/src/temporal/activities.ts` — Thin wrappers — heartbeat loop, error classification, container lifecycle. Business logic delegated to `apps/worker/src/services/`\n- `apps/worker/src/temporal/activity-logger.ts` — `TemporalActivityLogger` implementation of `ActivityLogger` interface\n- `apps/worker/src/temporal/summary-mapper.ts` — Maps `PipelineSummary` to `WorkflowSummary`\n- `apps/worker/src/temporal/worker.ts` — Combined worker + client entry point (per-invocation task queue, submits workflow, waits for result)\n- `apps/worker/src/temporal/shared.ts` — Types, interfaces, query definitions\n### Five-Phase Pipeline\n\n1. **Pre-Recon** (`pre-recon`) — Source code analysis to build the architectural baseline\n2. **Recon** (`recon`) — Attack surface mapping from initial findings\n3. **Vulnerability Analysis** (5 parallel agents) — injection, xss, auth, authz, ssrf\n4. **Exploitation** (5 parallel agents, conditional) — Exploits confirmed vulnerabilities\n5. **Reporting** (`report`) — Executive-level security report\n\nAround those phases:\n\n- Optional agentic static analysis runs before the pentest when `agentic_sast.enabled` is `\"true\"`, as a child workflow.\n- After each class's analysis, reconciliation groups its findings into exploitation tasks.\n- Findings outside the five classes form an internal `miscellaneous` class with its own exploitation agent (`miscellaneous-exploit`).\n- A scan can finish `completed`, `partial`, `failed`, or `cancelled`; `partial` carries an ordered set of reasons.\n\n### Supporting Systems\n- **Configuration** — YAML configs in `apps/worker/configs/` use the closed JSON Schema in `config-schema.json`. Every fresh scan runs the fixed five analysis classes; there is no public class selector. `agentic_sast.enabled` is the only public agentic-SAST setting. Finding reconciliation runs on every scan and has no public setting of its own. Config also supports authentication (MFA/TOTP), URL/code rule scoping (`rules.avoid`/`rules.focus`), `exploit`, free-form `rules_of_engagement`, and post-hoc `report` options (`min_severity`, `min_confidence`, `guidance`, and exploit-only `sarif` output via `apps/worker/src/services/sarif-renderer.ts`, on by default for exploit runs and opt out with `report.sarif: \"false\"`). `code_path` avoid rules are enforced via the `@gotgenes/pi-permission-system` extension: `apps/worker/src/temporal/activities.ts:syncCodePathDenyRules` writes a global `path` deny config once per workflow (`apps/worker/src/ai/pi/permission-system.ts:syncPermissionSystemConfig`), and the executor loads the extension when that config is present (`apps/worker/src/ai/pi/pi-executor.ts`), so denies fire across every tool and child `task` session. Credential resolution — local mode: env vars → `./.env`; npx mode: env vars → `~/.shannon/config.toml` (via `npx @keygraph/shannon setup`)\n- **Agentic SAST progress** — Capella runs as a child workflow, so its activities are absent from the parent's `pendingActivities` and invisible to the CLI. The child signals each stage boundary up via `capellaStageProgress` (`apps/worker/src/temporal/shared.ts`); the parent's handler validates the payload and writes the child-supplied `startedAt` and `durationMs` directly to `operationalStages['agentic-sast:<stage>']`, so both the live `getProgress` query and the terminal result carry per-stage rows. Signalling is best-effort and every failure is swallowed — a closed or unreachable parent must never fail a SAST run. `CAPELLA_STAGE_LABELS` in `apps/worker/src/ai/sast/types.ts` is the one label table, shared by the scan log and the status tree; `CAPELLA_PROGRESS_STAGES` omits `export`, which runs no model and so never becomes a row. Scans predating the signal keep the aggregate `agentic-sast` span and render as a bare phase line\n- **Prompts** — Per-phase templates in `apps/worker/prompts/` with variable substitution (`{{TARGET_URL}}`, `{{CONFIG_CONTEXT}}`). Shared partials in `apps/worker/prompts/shared/` via `apps/worker/src/services/prompt-manager.ts`, including `_code-path-rules.txt` (focus/avoid `[FILE]`/`[GLOB]` routing) and `_rules-of-engagement.txt` (free-text engagement rules). When `exploit: false`, `apps/worker/src/services/findings-renderer.ts` deterministically converts each `*_exploitation_queue.json` into a `*_findings.md` for report assembly — no LLM in the loop\n- **Agent Harness (pi)** — Uses the **pi harness** (`@earendil-works/pi-coding-agent`, requires Node ≥ 22.19) via `apps/worker/src/ai/pi/pi-executor.ts` (`runPiPrompt` → `createAgentSession`). Retry is split in `apps/worker/src/ai/pi/retry-settings.ts`: pi's agent-level loop is off so Temporal owns agent restarts, while `provider.maxRetries` stays on — pi reads the `provider` block independently of the `enabled` flag — so transport faults are absorbed in-session rather than costing a full agent re-run. `maxRetryDelayMs` is left at pi's 60s default. One model runs every phase, named by `SHANNON_AI_MODEL=<provider>:<model-id>` (default `anthropic:claude-sonnet-4-6`). `apps/worker/src/ai/models.ts` parses the spec — splitting on the **first** colon only, so Bedrock IDs keep theirs — and resolves it through pi's `ModelRuntime`. pi ships the `CredentialStore` interface but no in-memory implementation (its own reads `auth.json` from disk), so `RuntimeCredentialStore` in that file supplies one: credentials arrive as env vars in an ephemeral container and must never touch disk. `createModelRuntime(providerId, apiKey)` builds the runtime with `allowModelNetwork: true`, so `ModelRuntime.create()` refreshes the model catalogue over the network at scan start and a freshly released model resolves without a `--models-config` file. The fetch is bounded (10s) and falls back to the static catalogue on timeout, so an unreachable catalogue endpoint cannot hang the scan. The refresh does not override a `--models-config`: pi reloads and re-applies that file as a config overlay on every refresh (it reloads `this.config` at the top of `refresh()`), so custom definitions still win over the fetched catalogue; the merge semantics below are unchanged, just layered over a fresher base. `resolveModelSelection()` is **async** because `ModelRuntime.create()` is. Any pi-ai provider id is accepted — `parseModelSpec` no longer rejects against a hardcoded list, so pi's registry is the authority (an unknown provider/model surfaces as a clear \"not found in pi registry\" error at preflight, which points to the browsable catalogue at `pi.dev/models` — `PI_CATALOG_URL` in `apps/worker/src/ai/models.ts`, appended to the not-found errors and shown in the setup wizard's \"Other provider\" hint). Four providers are **curated** (`CURATED_PROVIDERS`: `anthropic`, `openai`, `xai`, `amazon-bedrock`) with their own credential variables, config sections, and setup flows; each provider's API key env var is declared once in `PROVIDER_API_KEY_ENV` — Shannon uses each vendor's own variable name (`OPENAI_API_KEY`, `XAI_API_KEY`, …), never an invented one; Bedrock's entry is `AWS_BEARER_TOKEN_BEDROCK`, paired with `AWS_REGION`, which preflight requires separately as provider config rather than a credential. Any other provider uses the **generic** credential path: `SHANNON_AI_API_KEY` (`GENERIC_API_KEY_ENV`) supplies the key for any provider whose credential is a plain API key. Curated providers' own variables take precedence over it, and it also works as a fallback for them — Bedrock is the sole exception (it authenticates through its AWS_ variables, so the generic key never stands in for it). The CLI forwards `SHANNON_AI_API_KEY` in `COMMON_FORWARD_VARS` (it is provider-neutral, binding to whatever `SHANNON_AI_MODEL` names, so the \"only one provider configured\" guard counts only named credentials), and stores it under a generic `[provider]` config.toml section (`provider.api_key`). `npx @keygraph/shannon setup` exposes this as the \"Other provider\" option: free-text provider id + model id + key (a curated provider id is rejected there, since it has its own option). A model pi's catalogue does not carry, such as a self-hosted model, is reachable without an SDK bump: `--models-config <file>` mounts a pi `models.json` read-only at `/app/models.json`. The mount is the entire CLI→worker protocol: nothing is forwarded through the environment, and `modelsConfigPath()` detects the file at that fixed path, exactly as `piAuthPresent()` detects the pi auth mount whose flag is likewise not forwarded (`MODELS_CONFIG_CONTAINER_PATH` in the CLI and `MODELS_CONFIG_PATH` in `apps/worker/src/paths.ts` must stay in sync). `createModelRuntime` always names `modelsPath` explicitly — the mounted path, or **`null` when no config was supplied**, which switches models.json off outright. It is never left to pi's default of `<agent dir>/models.json`, because that dir is shared with the pi auth mount, so a file landing there must not silently contribute model definitions to a scan that did not ask for one. `modelsStorePath` is pinned to the agent dir alongside it, since pi otherwise derives it from `dirname(modelsPath)` and would try to write beside a read-only mount. Custom definitions merge over the built-in catalogue: a matching model id replaces the built-in entry, a new id is added alongside, and `modelOverrides` adjusts a built-in without replacing the provider's list. Omitted fields take pi's defaults (`contextWindow` 128000, `maxTokens` 16384), so a large-context model needs them stated. Credentials are unaffected: `RuntimeCredentialStore` outranks any `apiKey` the file carries, so a snippet pasted from `pi.dev/models` can keep its placeholder key while the real secret stays in the environment — and pi's `!command` config-value form is never reached through `apiKey`. Preflight reports `modelRuntime.getError()` **before** the \"model not found\" check, because a file that fails to parse, fails schema validation, or composes badly otherwise leaves pi with an empty or fallback provider and surfaces as a model-id error that blames `SHANNON_AI_MODEL`. `SHANNON_AI_BASE_URL` overrides the endpoint for any provider (proxies/gateways); the credential and API dialect are unchanged. It is applied after model resolution, so it also wins over a `baseUrl` set in a model config. A base URL only changes the address — `resolveModel` (`apps/worker/src/ai/models.ts`) carries it onto the model descriptor and nothing else, and it grants **no exemption from registry validation**: the model id must resolve for every provider and endpoint alike. A gateway serving a model under its own name is described in a `--models-config` file, which puts a real descriptor in the registry rather than guessing one. pi's builtin `openai` provider serves a single API (Responses) and dispatches on the provider rather than `model.api`, so an `openai:` gateway run always speaks Responses; a Chat-Completions-only gateway must be reached through a completions-native provider such as `openrouter` instead. `buildEnvFlags` forwards only the selected provider's credential into the worker container. The CLI mirrors the parse rule and the provider/credential tables in `apps/cli/src/model-spec.ts` (it cannot import from the worker package); the two must stay in sync. pi ships no JSON-schema output or `Task`/`TodoWrite` built-ins, so structured queues are captured via a `submit_exploitation_queue` custom tool (`apps/worker/src/ai/queue-schemas.ts`), and `task` (child sessions scoped to `read`, `grep`, `find`, `ls`, `write`, and `bash` — no nested `task` or collector tools; `CHILD_TOOLS` in `apps/worker/src/ai/pi/task-tool.ts`) + `todo_write` (`apps/worker/src/ai/pi/session-tools.ts`) are provided as custom tools; the per-phase collectors are pi custom tools (TypeBox `defineTool` in `apps/worker/src/collectors/`). Every agent `createAgentSession` call passes `thinkingLevel: PI_THINKING_LEVEL` (`'high'`, `apps/worker/src/ai/pi/thinking-level.ts`) for deeper analysis than pi's default (`medium`). The preflight credential probe (`apps/worker/src/services/preflight.ts`) passes none and runs at pi's default. The reconciliation-enrichment `completeSimple` call (`apps/worker/src/ai/pi/structured-generation.ts`) passes no `reasoning`, so it runs with thinking off. There is no public thinking setting. Browser automation via `playwright-cli` with session isolation (`-s=<session>`). TOTP generation via `generate-totp` CLI tool. Login flow template at `apps/worker/prompts/shared/login-instructions.txt` supports form, SSO, API, and basic auth. On authenticated whitebox scans, the `validate-authentication` preflight performs the single real login and saves the browser session to `auth-state.json` in the per-session audit directory (path from `authStateFile()` in `apps/worker/src/audit/utils.ts`, derived from `generateAuditPath()`). The validation activity (`apps/worker/src/services/validate-authentication.ts`) removes any stale file from a prior run before the agent runs and verifies the file parses and contains cookies or storage before the preflight is marked complete; `logWorkflowComplete` deletes it when the workflow ends so authenticated cookies don't sit on disk between scans. Agent prompts opt in to session reuse by `@include(shared/_shared-session.txt)` before their `<login_instructions>` block — the partial restores the session and falls through to the full login flow if verification fails. `vuln-auth`/`exploit-auth` omit the include and own their own login\n- **Pi Credential Reuse** — `SHANNON_USE_PI_AUTH=1` opts into reusing the host's Pi login, including an `openai-codex` ChatGPT Plus/Pro subscription (`SHANNON_AI_MODEL=openai-codex:<model-id>`) or an `xai` Grok subscription (`SHANNON_AI_MODEL=xai:<model-id>`); the mechanism is provider-agnostic and works for any Pi login. `apps/cli/src/env.ts` requires `~/.pi/agent/auth.json`; `start.ts` passes its path to `spawnWorker`, which mounts only that file read-write at `/tmp/.pi/agent/auth.json`. The flag itself is not forwarded: the worker detects the file with `piAuthPresent()` and passes its path to `ModelRuntime.create`. CLI and worker API-key presence checks are skipped on this path, but the normal preflight model probe still validates the credential. The image and UID-remapping entrypoint keep `/tmp/.pi/agent` owned by `pentest` so adjacent Pi/Shannon configuration remains writable. Refreshed OAuth state is persisted to the host for subsequent scans.\n- **Audit System** — Crash-safe append-only logging in `workspaces/{hostname}_{sessionId}/`. The run directory's top level holds the human-facing report in both formats (`Security-Assessment-Report.pdf` and `Security-Assessment-Report.md`, `FINAL_REPORT_PDF_FILENAME`/`FINAL_REPORT_MD_FILENAME` in `apps/worker/src/paths.ts`); everything else — deliverables, per-agent logs, prompts, `session.json`, `workflow.log`, and browser artifacts — is nested under a hidden `.shannon/` internals dir (`INTERNAL_DIR`) so a customer sees only the report. Audit path helpers route through `generateInternalPath` (`apps/worker/src/audit/utils.ts`); the CLI nests the overlay backing dirs under the same `.shannon/` (`apps/cli/src/docker.ts`, `start.ts`). `session.json`/`workflow.log` reads use dual-read resolvers (`resolveSessionJsonPath`, `resolveRunFile`) that prefer `.shannon/` and fall back to the legacy run-root layout, so pre-restructure workspaces stay listable (`scans`/`logs`) without migration. A pre-restructure workspace cannot be resumed: `classifyWorkspaceLaunch` (`apps/cli/src/commands/start.ts`) requires `.shannon/launch.json`, and its absence fails the launch as \"created by an earlier version of Shannon\" before anything on disk is touched. There is no in-place migration — the workspace's files and report are left untouched, and the operator starts a new scan under a different `-w` name. The report agent writes structured findings to `report.json`, from which `report-renderer.ts` renders the assembled markdown and `report-json-adapter.ts` produces the Typst-shaped JSON that `pdf-renderer.ts` compiles into `comprehensive_security_assessment_report.pdf` using the bundled `apps/worker/templates/typst/report.typ` template (the `typst` binary is installed in the worker image). `copyReportToRunRoot` (`apps/worker/src/services/reporting.ts`) surfaces both the PDF and the markdown to the run root as `Security-Assessment-Report.pdf` and `Security-Assessment-Report.md`; the deliverables-dir copies remain as the git-checkpointed sources. PDF compilation is best-effort — a failure is logged and the run still completes. WorkflowLogger (`apps/worker/src/audit/workflow-logger.ts`) provides unified human-readable per-workflow logs, backed by LogStream (`apps/worker/src/audit/log-stream.ts`) shared stream primitive. Every combined-log line is also projected into a per-agent file under `.shannon/agents/<slug>.log` (one per pipeline agent, one per Capella stage; subagents fold into the parent's file, and a stage's concurrent sessions share its file with an inline session label). The projection boundary is `apps/worker/src/audit/actor-projection.ts` (`projectActor` maps a `TraceActor` to its combined prefix and owning file slug — slugs come only from closed fields); fan-out is best-effort and never blocks the canonical combined log. A lifecycle owner holds a `LogStream` lease per agent file (the pipeline agent's `logAgent` span, or a Capella stage activity's `try/finally`) so per-line writes ride the reference count; `CapellaStageTrace.drain()` flushes a stage's trace queue before its activity returns. The CLI tails one file with `shannon logs --agent <name>` (`--list-agents` to enumerate); the default `shannon logs` path is unchanged\n- **Deliverables** — Saved to `.shannon/deliverables/` in the target repo via the `save-deliverable` CLI script (`apps/worker/src/scripts/save-deliverable.ts`)\n- **Workspaces & Resume** — Named workspaces via `-w <name>` or auto-named from URL+timestamp. Resume detects completed agents via `session.json`. `loadResumeState()` in `apps/worker/src/temporal/activities.ts` validates deliverable existence, restores git checkpoints, and cleans up incomplete deliverables\n\n## Development Notes\n\n### Adding a New Agent\n1. Define agent in `apps/worker/src/session-manager.ts` (add to `AGENTS` record). `ALL_AGENTS`/`AgentName` types live in `apps/worker/src/types/agents.ts`\n2. Create prompt template in `apps/worker/prompts/` (e.g., `vuln-newtype.txt`)\n3. Two-layer pattern: add a thin activity wrapper in `apps/worker/src/temporal/activities.ts` (heartbeat + error classification). `AgentExecutionService` in `apps/worker/src/services/agent-execution.ts` handles the agent lifecycle automatically via the `AGENTS` registry\n4. Register activity in `apps/worker/src/temporal/workflows.ts` within the appropriate phase\n\n### Modifying Prompts\n- Variable substitution: `{{TARGET_URL}}`, `{{CONFIG_CONTEXT}}`, `{{LOGIN_INSTRUCTIONS}}`\n- Shared partials in `apps/worker/prompts/shared/` included via `apps/worker/src/services/prompt-manager.ts`\n- Test with `--pipeline-testing` for fast iteration\n\n### Key Design Patterns\n- **Configuration-Driven** — YAML configs with JSON Schema validation\n- **Progressive Analysis** — Each phase builds on previous results\n- **Harness-First** — the pi harness (`@earendil-works/pi-coding-agent`) handles autonomous analysis\n- **Modular Error Handling** — `ErrorCode` enum, `Result<T,E>` for explicit error propagation, automatic retry (3 attempts per agent)\n- **Services Boundary** — Activities are thin Temporal wrappers; `apps/worker/src/services/` owns business logic, accepts `ActivityLogger`, returns `Result<T,E>`. No Temporal imports in services\n- **DI Container** — Per-workflow in `apps/worker/src/services/container.ts`. `AuditSession` excluded (parallel safety)\n- **Ephemeral Workers** — Each scan runs in its own `docker run --rm` container with a per-invocation task queue. Temporal routes activities by queue name, so per-scan queues ensure activities never land on a worker with the wrong repo mounted\n\n### Security\nDefensive security tool only. Use only on systems you own or have explicit permission to test.\n\n## Code Style Guidelines\n\n### Formatting\nBiome handles formatting and linting. Run `pnpm biome:fix` to auto-fix. Config in `biome.json`: single quotes, semicolons, trailing commas, 2-space indent, 120 char line width.\n\n### Clarity Over Brevity\n- Optimize for readability, not line count — three clear lines beat one dense expression\n- Use descriptive names that convey intent\n- Prefer explicit logic over clever one-liners\n\n### Structure\n- Keep functions focused on a single responsibility\n- Use early returns and guard clauses instead of deep nesting\n- Never use nested ternary operators — use if/else or switch\n- Extract complex conditions into well-named boolean variables\n\n### TypeScript Conventions\n- Use `function` keyword for top-level functions (not arrow functions)\n- Explicit return type annotations on exported/top-level functions\n- Prefer `readonly` for data that shouldn't be mutated\n- `exactOptionalPropertyTypes` is enabled — use spread for optional props, not direct `undefined` assignment\n\n### Avoid\n- Combining multiple concerns into a single function to \"save lines\"\n- Dense callback chains when sequential logic is clearer\n- Sacrificing readability for DRY — some repetition is fine if clearer\n- Abstractions for one-time operations\n- Backwards-compatibility shims, deprecated wrappers, or re-exports for removed code — delete the old code, don't preserve it\n\n### Comments\nComments must be **timeless** — no references to this conversation, refactoring history, or the AI.\n\n**Patterns used in this codebase:**\n- `/** JSDoc */` — file headers (after license) and exported functions/interfaces\n- `// N. Description` — numbered sequential steps inside function bodies. Use when a\n  function has 3+ distinct phases where at least one isn't immediately obvious from the\n  code. Each step marks the start of a logical phase. Reference: `AgentExecutionService.execute`\n  (steps 1-9) and `injectModelIntoReport` (steps 1-5)\n- `// === Section ===` — high-level dividers between groups of functions in long files,\n  or to label major branching/classification blocks (e.g., `// === SPENDING CAP SAFEGUARD ===`).\n  Not for sequential steps inside function bodies — use numbered steps for that\n- `// NOTE:` / `// WARNING:` / `// IMPORTANT:` — gotchas and constraints\n\n**Never:** obvious comments, conversation references (\"as discussed\"), history (\"moved from X\")\n\n## Key Files\n\n**CLI:** `shannon` (entry point), `apps/cli/src/index.ts` (dispatcher), `apps/cli/src/docker.ts` (orchestration), `apps/cli/src/mode.ts` (auto-detection)\n\n**Entry Points:** `apps/worker/src/temporal/workflows.ts`, `apps/worker/src/temporal/activities.ts`, `apps/worker/src/temporal/worker.ts`\n\n**Core Logic:** `apps/worker/src/session-manager.ts`, `apps/worker/src/ai/pi/pi-executor.ts`, `apps/worker/src/ai/pi/permission-system.ts` (writes `code_path` deny rules to the `@gotgenes/pi-permission-system` global config), `apps/worker/src/config-parser.ts`, `apps/worker/src/services/` (incl. `preflight.ts`, `findings-renderer.ts`, `reporting.ts`), `apps/worker/src/audit/`\n\n**Config:** `docker-compose.yml`, `apps/cli/infra/compose.yml`, `apps/worker/configs/`, `apps/worker/prompts/`, `tsconfig.base.json` (shared compiler options), `turbo.json`, `biome.json`\n\n**CI/CD:** `.github/workflows/release.yml` (Docker Hub push + npm publish + GitHub release, manual dispatch)\n\n## Package Installation\n\nPackage managers are configured with a minimum release age (7 days). Requires pnpm >= 10.16.0. If `pnpm install` fails due to a package being too new, **do not attempt to bypass it** — report the blocked package to the user and stop.\n\n## Troubleshooting\n\n- **\"Repository not found\"** — Pass a path to the target repo (`-r /path/to/repo` or `-r ./my-repo`)\n- **\"Temporal not ready\"** — Wait for health check or `docker compose logs temporal`\n- **Worker not processing** — Check `docker ps --filter \"name=shannon-worker-\"`\n- **Reset state** — `./shannon reset`\n- **Local apps unreachable** — Use `host.docker.internal` instead of `localhost`\n- **Container permissions** — On Linux, may need `sudo` for docker commands\n","llms-full.txt":"# Shannon Full LLM Context\n\n> Combined README and documentation for AI agents and LLMs, generated verbatim from the local files referenced in llms.txt. For the concise index, see [llms.txt](llms.txt).\n\n> Retired names: Shannon Lite is now just Shannon. Shannon Pro is now Keygraph Pro.\n\n---\n\n# File: README.md\n\n> [!NOTE]\n> **[Cyber verification for Anthropic and OpenAI models](https://github.com/KeygraphHQ/shannon/discussions/483):** complete your provider's cyber verification program to prevent model failures and refusals during cyber workloads.\n\n<div align=\"center\">\n\n<picture>\n<source media=\"(prefers-color-scheme: dark)\" srcset=\"./assets/github-banner-dark.png\">\n<source media=\"(prefers-color-scheme: light)\" srcset=\"./assets/github-banner-light.png\">\n<img src=\"./assets/github-banner-light.png\" alt=\"Shannon, AI Pentester for Web Apps and APIs, by Keygraph\" width=\"100%\">\n</picture>\n\n<a href=\"https://trendshift.io/repositories/15604\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/15604\" alt=\"KeygraphHQ%2Fshannon | Trendshift\" style=\"width: 250px; height: 55px;\" width=\"250\" height=\"55\"/></a>\n\n### Shannon is an autonomous, AI pentester for web applications and APIs.\n\nIt analyzes your source code, identifies attack paths, and executes real exploits to prove vulnerabilities before they reach production. **No exploit, no report.**\n\n**This repository is Shannon: the full open-source agent, run locally from your command line.**\n\n<p><strong>Launch Shannon</strong></p>\n\n```bash\nnpx @keygraph/shannon@latest\n```\n\n<sub>The interactive launcher will guide you through setup and your first pentest.</sub>\n\n---\n\n<a href=\"https://discord.gg/9ZqQPuhJB7\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"./assets/discord_button_dark.png\"><source media=\"(prefers-color-scheme: light)\" srcset=\"./assets/discord_button_light.png\"><img src=\"./assets/discord_button_light.png\" height=\"40\" alt=\"Join Discord\"></picture></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"https://keygraph.io/\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"./assets/keygraph_button_dark.png\"><source media=\"(prefers-color-scheme: light)\" srcset=\"./assets/keygraph_button_light.png\"><img src=\"./assets/keygraph_button_light.png\" height=\"40\" alt=\"Visit Keygraph.io\"></picture></a>\n\n---\n\n</div>\n\n> [!TIP]\n> **AI agents and LLMs:** start with [llms.txt](llms.txt) for a concise map of this repository, or use [llms-full.txt](llms-full.txt) for the README and docs combined into one file.\n\n\n\n## Table of Contents\n\n- [Table of Contents](#table-of-contents)\n- [What is Shannon?](#what-is-shannon)\n  - [Why Shannon Exists](#why-shannon-exists)\n  - [Why \"Shannon\"?](#why-shannon)\n  - [Not a replacement for human pentesters](#not-a-replacement-for-human-pentesters)\n- [Shannon in Action](#shannon-in-action)\n- [Quick Start](#quick-start)\n  - [Prerequisites](#prerequisites)\n  - [Run Shannon](#run-shannon)\n- [Key Capabilities](#key-capabilities)\n- [CI/CD Integrations](#cicd-integrations)\n  - [GitHub Actions](#github-actions)\n- [Editions](#editions)\n- [Architecture](#architecture)\n- [Documentation](#documentation)\n- [Safety, Scope, and Limitations](#safety-scope-and-limitations)\n- [License](#license)\n- [Acknowledgements](#acknowledgements)\n- [About Keygraph](#about-keygraph)\n- [Community and Support](#community-and-support)\n- [Common Questions](#common-questions)\n  - [Can I self-host Shannon?](#can-i-self-host-shannon)\n  - [Does Shannon support bring your own key (BYOK)?](#does-shannon-support-bring-your-own-key-byok)\n  - [Does Shannon output SARIF?](#does-shannon-output-sarif)\n  - [Which AI providers does Shannon support?](#which-ai-providers-does-shannon-support)\n  - [Can I run Shannon on a local or self-hosted model?](#can-i-run-shannon-on-a-local-or-self-hosted-model)\n  - [Does Shannon actually exploit vulnerabilities, or just scan?](#does-shannon-actually-exploit-vulnerabilities-or-just-scan)\n\n\n\n## What is Shannon?\n\nShannon is an autonomous AI pentester developed by [Keygraph](https://keygraph.io). It performs security testing of web applications and their underlying APIs by combining source-code analysis with live exploitation.\n\nShannon analyzes your web application's source code to identify potential attack vectors, then uses browser automation and command-line tools to execute real exploits against the running application and its APIs. Only vulnerabilities with a working proof-of-concept are included in the final report.\n\nShannon is the agent. This repository is Shannon, the open-source pentester you run yourself. The same Shannon also powers Keygraph's commercial editions, [Pro and Enterprise](https://keygraph.io/pricing). See [Editions](#editions) for how Shannon compares with Pro, Enterprise, and the Community Program.\n\n<a id=\"why-shannon-exists\"></a>\n<details>\n<summary><strong>Why Shannon Exists</strong></summary>\n\nThanks to tools like Claude Code and Cursor, your team ships code non-stop. But your penetration test? That happens once a year. This creates a massive security gap. For the other 364 days, you could be unknowingly shipping vulnerabilities to production.\n\nShannon closes that gap by providing on-demand, automated penetration testing that can run against every build or release.\n\n</details>\n\n<a id=\"why-shannon\"></a>\n<details>\n<summary><strong>Why \"Shannon\"?</strong></summary>\n\nIt's named after Claude Shannon, the father of information theory. At its core, pentesting is an information problem: every probe reduces uncertainty about a system's state. The best tools maximize the signal gained from every request, turning those bits of knowledge into an exploit path.\n\nAlso, we wanted you to be able to say, \"Hey Claude, run Shannon\" to find all the security flaws in your vibe-coded app.\n\n</details>\n\n<a id=\"not-a-replacement-for-human-pentesters\"></a>\n<details>\n<summary><strong>Not a replacement for human pentesters</strong></summary>\n\nShannon is built to work alongside expert pentesters and red teamers, not replace them. Great pentesters understand the business, chain attacks in ways nobody anticipated, and bring years of judgment that current models can't match.\n\nShannon solves a different problem: there is far more software to test than security teams have time to cover. Critical systems get periodic expert assessments, while the long tail of internal apps, APIs, and fast-moving services rarely gets tested at all.\n\nShannon shifts pentesting left into the software development lifecycle (SDLC). Use it to run exploitation-backed tests against staging environments and releases at the cadence they actually ship, and save expert human time for the risks that need someone who knows the organization.\n\n</details>\n\n## Shannon in Action\n\n![Shannon running an autonomous pentest](assets/Shannon3GIF.gif)\n\nThese reports are from Shannon OSS scans of Photoview 2.4.0, one of the applications in Doyensec's comparison of Aikido and XBOW. We ran Shannon against the same application version and evaluated its results separately. Read the [Doyensec study](https://doyensec.com/resources/ComparingAIApplicationSecurityTestingPlatforms_Doyensec.pdf) and our [Shannon follow-up comparison](docs/shannon-xbow-aikido-benchmark.md) for the methodology, limitations, costs, and results.\n\n\n| Model             | Report                                                                       | SARIF                                                            |\n| ----------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------- |\n| DeepSeek v4 Flash | [View report](benchmark/photoview-deepseek-v4-flash.pdf) | [SARIF](benchmark/photoview-deepseek-v4-flash.sarif) |\n| Grok 4.6          | [View report](benchmark/photoview-grok-4-6.pdf)          | [SARIF](benchmark/photoview-grok-4-6.sarif)          |\n| Claude Opus 5     | [View report](benchmark/photoview-opus-5.pdf)            | [SARIF](benchmark/photoview-opus-5.sarif)            |\n\n## Quick Start\n\n\n\n### Prerequisites\n\n- **Docker**: required for the worker container.\n- **Node.js 18+**: required for the recommended `npx` workflow.\n- **AI provider credentials**: Shannon runs on Anthropic, OpenAI, xAI, AWS Bedrock, and [any other provider](docs/ai-providers.md#any-other-provider) in the harness catalogue — each of which you can point at a proxy or LLM gateway through a [custom base URL](docs/ai-providers.md#custom-base-url), and a model the catalogue does not carry can be described with a [custom model configuration](docs/ai-providers.md#custom-model-configuration). You bring your own key, and Keygraph never proxies your model traffic. Shannon is provider-agnostic. See [AI providers](docs/ai-providers.md#suggested-models) for suggested model IDs.\n- **Cyber safeguards cleared with your provider**: Anthropic and OpenAI apply real-time safeguards to cyber-security workloads, which can interrupt a scan mid-run. Complete their guidance for legitimate security testers before your first run - see [AI providers](docs/ai-providers.md#cyber-safeguards-do-this-before-your-first-scan) and the [cyber verification announcement](https://github.com/KeygraphHQ/shannon/discussions/483).\n\n\n\n### Run Shannon\n\n> [!WARNING]\n> Shannon actively executes exploits. Run it only against applications and environments you own or have explicit written authorization to test. Do not run Shannon against production systems.\n\n```bash\n# Configure credentials with the interactive wizard.\nnpx @keygraph/shannon@latest setup\n\n# Run a pentest against a source-available target.\nnpx @keygraph/shannon@latest start \\\n  -u https://your-app.com \\\n  -r /path/to/your/repo\n```\n\nShannon pulls the worker image from Docker Hub, starts the required local infrastructure, mounts the target repository read-only inside an ephemeral worker container, and writes results to a local workspace.\n\nFor source builds, authenticated scans, provider-specific setup, and platform notes, see [Documentation](#documentation).\n\n> [!TIP]\n> **Prefer to use a subscription instead of API credits?**\n>\n> - **OpenAI Codex:** The latest version of Shannon supports ChatGPT Plus and Pro subscriptions. Follow the [OpenAI Codex subscription setup guide](docs/ai-providers.md#openai-codex-chatgpt-pluspro-subscription) to get started.\n> - **xAI (Grok):** The latest version of Shannon supports xAI subscriptions. Follow the [xAI subscription setup guide](docs/ai-providers.md#xai-grok-subscription) to get started.\n> - **Claude Code:** The latest version of Shannon does not support Claude Code subscriptions. Follow the [Claude Code subscription setup guide](docs/ai-providers.md#claude-code-subscription) to use version `1.9.0`, which is the final release built on the Claude Agent SDK.\n\n\n\n## Key Capabilities\n\n- **No exploit, no report**: Reports only vulnerabilities confirmed with a reproducible proof of concept, reducing speculative scanner noise.\n- **Advanced code analysis**: Maps architecture, trust boundaries, interfaces, data flows, and critical assets before sending credible attack paths to live pentesting agents.\n- **Autonomous execution**: Runs reconnaissance, analysis, exploitation, and reporting from a single command.\n- **Live terminal experience**: Simplifies scan setup and shows agent progress and results without exposing orchestration logs.\n- **Authenticated testing**: Supports credentials, login flows, TOTP, email authentication, focus areas, and rules of engagement through configuration.\n- **OWASP-focused coverage**: Tests for exploitable injection, XSS, SSRF, broken authentication, and broken authorization.\n- **Resumable workspaces**: Resumes interrupted scans without repeating completed work.\n- **Native CI/CD integrations**: Runs through the official GitHub Action or GitLab CI/CD component, preserves artifacts, publishes findings, and gates releases on proven vulnerabilities.\n- **Multi-format reports**: Produces evidence-rich PDF and Markdown reports plus JSON and SARIF 2.1.0. SARIF is enabled by default for exploit-mode scans.\n- **Provider agnostic and BYOK**: Supports Anthropic, OpenAI, xAI, AWS Bedrock, compatible APIs and LLM gateways, and local models served through Ollama, vLLM, or LM Studio.\n- **Private by design**: Runs in your infrastructure, stores results locally, and sends model requests directly to your chosen endpoint. A local endpoint keeps data inside your environment.\n\n\n\n## CI/CD Integrations\n\nShannon can run continuously against deployed staging and development environments through official integrations for [GitHub Actions](https://github.com/KeygraphHQ/shannon-action) and [GitLab CI/CD](https://gitlab.com/KeygraphHQ/shannon-ci).\n\nBoth integrations:\n\n- analyze the checked-out source repository while attacking a running target;\n- preserve PDF, Markdown, and SARIF reports as pipeline artifacts;\n- preserve scan and agent logs for debugging, including incomplete runs;\n- support pull-request, release, and scheduled pentests;\n- distinguish an incomplete assessment from a completed scan with no findings; and\n- optionally fail the pipeline when Shannon exploits a vulnerability at or above a configured severity threshold.\n\nA code-analysis hypothesis does not fail the pipeline. Severity gates count only findings with `status: exploited`.\n\n### GitHub Actions\n\n```yaml\nname: Shannon Pentest\n\non:\n  workflow_dispatch:\n\npermissions:\n  security-events: write\n\njobs:\n  pentest:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Run Shannon\n        uses: KeygraphHQ/shannon-action@v1\n        with:\n          url: https://staging.example.com\n          api-key: ${{ secrets.SHANNON_AI_API_KEY }}\n          fail-on-severity: high\n          upload-sarif: true\n```\n\nThe Action defaults `repo` to the checked-out GitHub workspace. It uploads one artifact containing the security assessment reports and SARIF, plus a separate run artifact containing scan and agent logs. Enabling `upload-sarif` publishes supported findings to GitHub code scanning.\n\nRequirements:\n\n- a private repository;\n- a runner with Docker and Docker Compose v2;\n- access to the running staging or development target; and\n- a model-provider credential stored as a GitHub Actions secret.\n\nSee the [Shannon GitHub Action documentation](https://github.com/KeygraphHQ/shannon-action) and [GitHub Marketplace listing](https://github.com/marketplace/actions/shannon-ai-pentester).\n\n## Editions\n\n| Edition | What it is | Price |\n| --- | --- | --- |\n| **Shannon** (open source) | This repository. A complete autonomous pentester you run yourself, locally or in CI/CD, against an application whose source code you have. Well suited to individual developers and small teams. | Open source under AGPL-3.0. You pay only your own model costs. |\n| **Community Program** | Keygraph Pro at no cost, for U.S.-based 501(c)(3) nonprofits and for seed or pre-Series-A startups with 20 or fewer active developers. | $0 in cloud service fees while you qualify. |\n| **Pro** | Keygraph's cloud-hosted edition, managed by Keygraph, with every module included. | $50 per active developer per month. |\n| **Enterprise** | Everything in Pro, self-hosted in your own environment or fully air-gapped. | Custom. |\n\nSee [keygraph.io/pricing](https://keygraph.io/pricing) for current prices and the full feature table.\n\n**Pro**, **Enterprise**, and the **Community Program** are powered by an enhanced, enterprise-grade version of the Shannon engine, and all three add:\n\n- **Black-box pentesting**: tests the running application from the outside, with no source code needed.\n- **Dependency (SCA) and secret checks**: SCA with reachability, and secrets scanning that includes repository history.\n- **Findings management**: one record per vulnerability per repository across scans and scanners, with status history, owners, SLAs, and dashboards.\n- **Fix pull requests and retests**: reviewable fix pull requests, with each fix verified by re-analysis and exploit replay, without a full rescan.\n- **Jira and access control**: two-way Jira sync, SSO, RBAC, and audit logs.\n\nNo source code? Use the [Blackbox Pentester](https://keygraph.io/agentic-blackbox-pentester).\n\n[Compare Shannon, Pro, and Enterprise in detail →](docs/keygraph-platform.md)\n\n## Architecture\n\nShannon combines multi-stage security code analysis with live reconnaissance and exploitation:\n\n```mermaid\nflowchart TD\n    S[\"Source code\"] --> EXISTING[\"Recon + vulnerability analysis\"]\n    S --> SAST[\"Agentic security code analysis\"]\n\n    EXISTING -- \"Pentest candidates\" --> REC[\"Finding reconciliation<br/>(merge + deduplicate)\"]\n    SAST -- \"SAST candidates\" --> REC\n\n    REC -- \"Reconciled exploitation queue\" --> EXP[\"Exploitation agents\"]\n    APP[\"Running application\"] --> EXP\n\n    EXP -- \"Exploit demonstrated\" --> REPORT[\"Reporting<br/>PDF · Markdown · SARIF\"]\n    EXP -- \"No exploit demonstrated\" --> DROP[\"Discard\"]\n\n    REPORT --> CICD[\"CI/CD gate\"]\n```\n\n\n\nStage by stage:\n\n1. **Recon and vulnerability analysis** explores the running application, ties runtime behavior back to the source, and runs specialized agents across Injection, XSS, SSRF, Authentication, and Authorization.\n2. **Agentic security code analysis** maps the application's architecture, trust boundaries, exposed interfaces, dependencies, data flows, and high-risk assets, then opens targeted investigations against them.\n3. **Finding reconciliation** merges both streams of candidates, deduplicates the overlap, and groups what remains into an exploitation queue.\n4. **Exploitation agents** attempt real proof-of-concept attacks against the running application.\n5. **Validation** throws out every candidate Shannon can't demonstrate.\n6. **Reporting** produces PDF and Markdown reports with the evidence attached, plus structured JSON and SARIF for downstream systems.\n\nOnly live-validated vulnerabilities become Shannon pentest findings or count toward CI/CD severity gates.\n\nEach scan runs in an ephemeral Docker container with an isolated workspace and per-invocation orchestration.\n\n## Documentation\n\nUse these guides for operational detail:\n\n\n| Guide                                                     | Use it for                                                                                                                                                                |\n| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [Source build and CLI commands](docs/development.md)      | Cloning, building, common commands, output paths, and local development.                                                                                                  |\n| [Configuration](docs/configuration.md)                    | Authenticated testing, login flows, rules of engagement, and report filters.                                                                                              |\n| [AI providers](docs/ai-providers.md)                      | Selecting the model, the supported providers (Anthropic, OpenAI, xAI, AWS Bedrock, and any other Pi-supported provider), and custom LLM gateways.                             |\n| [Platforms and networking](docs/platforms.md)             | Windows/WSL2, Linux, macOS, Docker networking, local apps, and custom hostnames.                                                                                          |\n| [Workspaces and resuming](docs/workspaces.md)             | Naming workspaces, resuming interrupted scans, and workspace storage.                                                                                                     |\n| [Safety and limitations](docs/safety.md)                  | Authorized-use requirements, non-production guidance, mutative effects, cost, and model caveats.                                                                          |\n| [Coverage and roadmap](docs/coverage-roadmap.md)          | Current vulnerability coverage and planned work.                                                                                                                          |\n| [Pro and Enterprise](docs/keygraph-platform.md)           | Shannon compared with Pro, Enterprise, and the Community Program: black-box pentesting, SCA and secrets, findings management, fix pull requests, retests, Jira, and deployment. |\n\n\n\n\n## Safety, Scope, and Limitations\n\nShannon is not a passive scanner. Its exploitation agents can create users, submit forms, mutate application state, trigger outbound requests, and otherwise affect the target system. Use sandboxed, staging, or local development environments with disposable data.\n\nYou are responsible for using Shannon legally and ethically. Do not point Shannon at systems, repositories, or applications you do not own or do not have explicit authorization to test.\n\nImportant limitations:\n\n- Shannon is tuned for fast, code-informed pentesting in everyday development and CI/CD. Exhaustive agentic SAST, broader scanner coverage, centralized governance, and full-lifecycle vulnerability management are delivered through Pro, Enterprise, and the Community Program.\n- Findings still require human review. LLM-generated reports can contain weakly supported or incorrect details.\n- Anthropic, OpenAI, xAI, and AWS Bedrock are built-in providers, and any other provider in the harness catalogue works too — each reachable through a custom base URL that points it at a proxy or LLM gateway. Model capability varies, and a model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker results.\n- A full run can take roughly 1 to 1.5 hours and may incur LLM API costs depending on model pricing and application complexity.\n- Do not scan untrusted or adversarial codebases. AI-powered tools that read source code can be exposed to prompt injection.\n\nRead the full [Safety and limitations](docs/safety.md) guide before running Shannon in a new environment.\n\n## License\n\nShannon is licensed under the [GNU Affero General Public License v3.0](LICENSE).\n\nCommercial and enterprise licensing is available for organizations that need different license terms, commercial support, private redistribution, managed-service use, or broader deployment options, including Keygraph Pro and Enterprise.\n\nFor commercial licensing, contact [shannon@keygraph.io](mailto:shannon@keygraph.io).\n\n## Acknowledgements\n\nThanks to [Pi](https://github.com/earendil-works/pi),\n[Playwright CLI](https://github.com/microsoft/playwright-cli),\nand [Mantis](https://github.com/google/mantis).\n\nSee [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md) for licensing and attribution details.\n\n## About Keygraph\n\n**Keygraph** is the company behind Shannon. It also builds **Keygraph Pro** and **Keygraph Enterprise**, the commercial editions that close the full AppSec lifecycle and run an enhanced build of Shannon as their pentesting engine.\n\n## Community and Support\n\n**Community office hours** are available for hands-on help with bugs, deployments, and configuration questions.\n\n- US/EU: Thursday, 10:00 AM PT\n- Asia: Thursday, 2:00 PM IST\n- [Book a slot](https://cal.com/george-flores-keygraph/shannon-community-office-hours)\n\n[Join Discord](https://discord.gg/cmctpMBXwE) to ask questions, share feedback, and connect with other Shannon users.\n\nAt this time, Keygraph is not accepting external code contributions. Issues are welcome for bug reports and feature requests:\n\n- [Report bugs](https://github.com/KeygraphHQ/shannon/issues)\n- [Suggest features](https://github.com/KeygraphHQ/shannon/discussions)\n\nStay connected:\n\n- [Keygraph website](https://keygraph.io)\n- [Twitter/X: @KeygraphHQ](https://twitter.com/KeygraphHQ)\n- [LinkedIn: Keygraph](https://linkedin.com/company/keygraph)\n\n\n\n## Common Questions\n\n\n\n### Can I self-host Shannon?\n\nYes. Shannon runs inside your infrastructure in an ephemeral worker container. It mounts the repository read-only and writes results to a local workspace.\n\nKeygraph never receives your source code and never proxies your model traffic. Your model requests go straight to the provider or endpoint you configure, and they carry source and application context with them. Point Shannon at a locally hosted endpoint and that traffic stays inside your environment too.\n\n### Does Shannon support bring your own key (BYOK)?\n\nYes, always. You provide the LLM credentials Shannon uses to run a pentest, in every deployment, open source and commercial. Keygraph never proxies your model traffic.\n\n### Does Shannon output SARIF?\n\nYes. Shannon emits SARIF 2.1.0, the OASIS standard format for static analysis results, alongside structured JSON. Any SARIF consumer reads it: code scanning services, vulnerability management platforms, security dashboards, and CI/CD pipelines. It is written by default on exploit-mode scans; set `report.sarif` to `\"false\"` in your configuration file to opt out.\n\n### Which AI providers does Shannon support?\n\nAnthropic, OpenAI, xAI, and AWS Bedrock are built in and configured directly by provider ID. Beyond those, Shannon runs on any provider in the Pi harness catalogue, named the same `<provider>:<model-id>` way. Any provider can be pointed at a proxy or LLM gateway through a custom base URL, which overrides only the endpoint and keeps that provider's API dialect. A model the catalogue does not carry, such as one a router or gateway serves under its own ID, or a self-hosted model, is described in a [custom model configuration](docs/ai-providers.md#custom-model-configuration) file and passed with `--models-config`. Shannon uses a single unified model setting throughout a pentest.\n\n### Can I run Shannon on a local or self-hosted model?\n\nShannon works with local models served through Ollama, vLLM, or LM Studio, which expose an OpenAI-compatible endpoint, as well as routers such as OpenRouter and LLM gateways such as LiteLLM. A model the harness catalogue does not carry, which most self-hosted models are, is described in a [custom model configuration](docs/ai-providers.md#custom-model-configuration) file passed with `--models-config`; routers and gateways can also be reached with a custom base URL. Capability varies, and a model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker pentests than a frontier model, so take this path only if you know how your chosen model behaves. See [Local and self-hosted models](docs/ai-providers.md#local-and-self-hosted-models).\n\n### Does Shannon actually exploit vulnerabilities, or just scan?\n\nShannon executes real exploits. It reports a finding only when it has produced a working proof-of-concept, and discards hypotheses it cannot prove. It is a pentester, not a passive scanner.\n\n**Built by [Keygraph](https://keygraph.io)**\n\n---\n\n# File: docs/development.md\n\n# Source Build and CLI Commands\n\nThis guide covers the source-build workflow, common CLI commands, repository paths, and output locations. For the fastest first run, use the `npx` workflow in the main README.\n\n## Prerequisites\n\n- Docker\n- Node.js 18+\n- pnpm\n- AI provider credentials\n\n## Clone and Build\n\nUse the source-build workflow if you want to run Shannon from a local clone, modify the open-source CLI, or keep the worker image built locally.\n\n```bash\n# 1. Clone Shannon.\ngit clone https://github.com/KeygraphHQ/shannon.git\ncd shannon\n\n# 2. Configure credentials.\ncp .env.example .env\n\n# 3. Install dependencies and build.\npnpm install\npnpm build\n\n# 4. Run a pentest.\n./shannon start -u https://your-app.com -r /path/to/your-repo\n```\n\nAt minimum, your `.env` file should include one supported AI provider credential, such as:\n\n```bash\nANTHROPIC_API_KEY=your-api-key\n```\n\nEnvironment variables can also be exported directly:\n\n```bash\nexport ANTHROPIC_API_KEY=\"your-api-key\"\n```\n\n## Prepare Your Repository\n\nShannon can scan any repository on your machine. Pass an absolute or relative path with `-r`.\n\n```bash\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo\n./shannon start -u https://example.com -r ./relative/path\n```\n\nThe target repository is mounted read-only inside the worker container.\n\n## Common Commands\n\nMonitor progress:\n\n```bash\nnpx @keygraph/shannon logs [<workspace>]     # defaults to the single running scan, else the most recent\nnpx @keygraph/shannon status [<workspace>]   # same default target; add --json for a machine-readable snapshot\nnpx @keygraph/shannon scans\nnpx @keygraph/shannon version\n```\n\nWith no workspace, `logs` and `status` follow the single running scan; when several are running, name one.\n\nSource-build equivalents:\n\n```bash\n./shannon logs [<workspace>]                 # the combined live log (unchanged default)\n./shannon logs [<workspace>] --agent <name>  # tail one agent's own log\n./shannon logs [<workspace>] --list-agents   # list the agents with their own log\n./shannon status [<workspace>]\n./shannon scans\n./shannon version\n```\n\nEvery scan writes one combined `.shannon/workflow.log` and a per-agent projection of it under\n`.shannon/agents/`: one file per pipeline agent (`recon.log`, `xss-vuln.log`, …) and one per Capella\nstage (`agentic-sast-research.log`, …). Delegated subagents fold into their parent's file, and a\nCapella stage's concurrent sessions share its file with an inline session label. The combined log\nstays canonical; the per-agent files are best-effort projections.\n\nOpen the Temporal Web UI for detailed monitoring:\n\n```bash\nopen http://localhost:8233\n```\n\nStop Shannon:\n\n```bash\nnpx @keygraph/shannon stop [<workspace>] # stop one scan (defaults to the single running scan; confirms first; add --yes/-y to skip)\nnpx @keygraph/shannon stop --all         # stop all scans (Temporal stays up)\nnpx @keygraph/shannon reset              # stop everything and wipe all Temporal data (type 'confirm' to proceed; cannot be skipped)\n```\n\nSource-build equivalents:\n\n```bash\n./shannon stop [<workspace>]             # stop one scan (defaults to the single running scan; confirms first; add --yes/-y to skip)\n./shannon stop --all                     # stop all scans (Temporal stays up)\n./shannon reset                          # stop everything and wipe all Temporal data (type 'confirm' to proceed; cannot be skipped)\n```\n\nUsage examples:\n\n```bash\n# Basic pentest.\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo\n\n# With a configuration file.\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -c /path/to/my-config.yaml\n\n# Custom output directory.\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -o ./my-reports\n\n# Named workspace.\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -w q1-audit\n\n# Stream the log until the scan finishes, then exit on its outcome (useful in CI).\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo --follow\n\n# Validate the configured login only, then stop (no pentest or report).\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -c /path/to/my-config.yaml --validate-auth\n\n# List running and completed scans.\nnpx @keygraph/shannon scans\n```\n\nSource-build examples:\n\n```bash\n./shannon start -u https://example.com -r /path/to/repo\n./shannon start -u https://example.com -r /path/to/repo -c /path/to/my-config.yaml\n./shannon start -u https://example.com -r /path/to/repo -o ./my-reports\n./shannon start -u https://example.com -r /path/to/repo -w q1-audit\n./shannon start -u https://example.com -r /path/to/repo --follow\n./shannon start -u https://example.com -r /path/to/repo -c /path/to/my-config.yaml --validate-auth\n./shannon scans\n\n# Rebuild the worker image.\n./shannon build --no-cache\n```\n\n## Output and Results\n\nResults are saved to the workspaces directory:\n\n- `./workspaces/` in source-build mode\n- `~/.shannon/workspaces/` in `npx` mode\n\nUse `-o <path>` to copy deliverables to a custom output directory after a run completes.\n\nOutput structure — the run directory's top level holds the final report, in PDF and Markdown; everything else is nested under a hidden `.shannon/` directory:\n\n```text\nworkspaces/{hostname}_{sessionId}/\n|-- Security-Assessment-Report.pdf  # the final report (PDF)\n|-- Security-Assessment-Report.md   # the final report (Markdown)\n`-- .shannon/                       # internals\n    |-- deliverables/               # report source, per-phase analysis, queues\n    |-- agents/                     # per-agent log projections, one file per agent/Capella stage\n    |-- prompts/                    # rendered prompts\n    |-- scratchpad/                 # screenshots, scripts\n    |-- session.json                # resume state\n    `-- workflow.log\n```\n\n---\n\n# File: docs/configuration.md\n\n# Configuration\n\nShannon can run without a configuration file, but configuration enables authenticated testing, scope guidance, rules of engagement, and report filtering.\n\n## Credential Precedence\n\nSource-build mode resolves credentials from:\n\n1. Environment variables, such as `export ANTHROPIC_API_KEY=...`\n2. `./.env`\n\n`npx` mode resolves credentials from:\n\n1. Environment variables\n2. `~/.shannon/config.toml`, created by `npx @keygraph/shannon setup`\n\nEnvironment variables always win, so you can override saved config for a single session without editing files.\n\n## Create a Configuration File\n\nCopy and modify the example configuration:\n\n```bash\ncp configs/example-config.yaml ./my-app-config.yaml\n```\n\nRun with:\n\n```bash\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -c ./my-app-config.yaml\n```\n\nSource-build equivalent:\n\n```bash\n./shannon start -u https://example.com -r /path/to/repo -c ./my-app-config.yaml\n```\n\n## Basic Configuration Structure\n\n```yaml\n# Describe your target environment.\ndescription: \"Next.js e-commerce app on PostgreSQL. Local dev environment; .env files contain local-only credentials.\"\n\n# Every scan runs all five vulnerability classes.\n\n# Agentic static analysis. `enabled` is its only setting.\n# agentic_sast:\n#   enabled: \"true\"\n\n# Skip the exploitation phase.\n# exploit: \"false\"\n\n# Free-form rules of engagement.\n# rules_of_engagement: |\n#   - No password brute-force; cap login attempts at 5 per account.\n#   - Throttle to under 5 requests per second per endpoint; back off 60s on any 429.\n#   - Use placeholders like [order_id] in deliverables; no real data values.\n\nauthentication:\n  login_type: form\n  login_url: \"https://your-app.com/login\"\n  credentials:\n    username: \"test@example.com\"\n    password: \"yourpassword\"\n    totp_secret: \"LB2E2RX7XFHSTGCK\"\n\n    # Optional mailbox credentials for magic-link or email-OTP flows.\n    # email_login:\n    #   address: \"inbox@example.com\"\n    #   password: \"mailbox-password\"\n    #   totp_secret: \"JBSWY3DPEHPK3PXP\"\n\n  login_flow:\n    - \"Type $username into the email field\"\n    - \"Type $password into the password field\"\n    - \"Click the 'Sign In' button\"\n\n  success_condition:\n    type: url_contains\n    value: \"/dashboard\"\n\nrules:\n  avoid:\n    - description: \"AI should avoid testing logout functionality\"\n      type: url_path\n      value: \"/logout\"\n\n    # code_path values are repo-relative file paths or globs.\n    # - description: \"Out-of-scope vendored libraries\"\n    #   type: code_path\n    #   value: \"src/vendor/**\"\n\n  focus:\n    - description: \"AI should emphasize testing API endpoints\"\n      type: url_path\n      value: \"/api\"\n\n# Report options applied when assembling the final report.\n# report:\n#   min_severity: low\n#   min_confidence: low\n#   guidance: |\n#     Drop findings about missing security headers and rate-limit gaps.\n#   sarif: \"false\"\n```\n\n## Analysis Scope and Agentic SAST\n\nEvery scan runs all five analysis classes: Injection, Cross-Site Scripting, Authentication, Authorization, and\nServer-Side Request Forgery. The class set is fixed and has no configuration selector.\n\nAgentic static analysis is opt-in:\n\n```yaml\nagentic_sast:\n  enabled: \"true\"\n```\n\n`enabled` is the only setting. Omitting the block, or setting `enabled: \"false\"`, turns agentic static analysis off;\n`\"true\"` turns it on. Either way, all five vulnerability classes still run.\n\nAgentic static analysis reads the repository for vulnerabilities before the pentest and passes what it finds into the\nexploitation phase. It adds model time and cost. If it fails, the pentest continues without its findings and the scan\nfinishes as \"partial\".\n\n## Report Options\n\n| Key | Effect |\n| --- | --- |\n| `min_severity` | Drops findings rated below this severity. Applies in both exploitative and analysis-only runs. |\n| `min_confidence` | Drops findings rated below this confidence. Applies only when `exploit` is `\"false\"`. |\n| `guidance` | Free-text instruction to the report agent, such as which topics to exclude. |\n| `sarif` | SARIF 2.1.0 log alongside the Markdown report. On by default for exploit runs; set `\"false\"` to opt out. Ignored when `exploit` is `\"false\"`. |\n\nEvery finding carries a severity, but it does not mean the same thing in each mode: an exploitative run measures severity from what the exploit demonstrated, while an analysis-only run assesses it from the class of flaw and the impact it would have. An analysis-only finding carries a confidence rating alongside its severity, since nothing was proven. Setting `min_confidence` on an exploitative run is ignored, and Shannon logs a warning naming the threshold to use instead.\n\n### SARIF Output\n\nOn exploit-mode runs Shannon writes `report.sarif` next to `Security-Assessment-Report.pdf` at the workspace root by default, for upload to GitHub code scanning or any other SARIF consumer. No configuration is needed; set `sarif: \"false\"` to opt out.\n\n```yaml\nreport:\n  sarif: \"false\"\n```\n\nEach finding becomes one SARIF result, filed under a rule per vulnerability class (`shannon/injection`, `shannon/xss`, `shannon/auth`, `shannon/authz`, `shannon/ssrf`, and `shannon/miscellaneous` for findings outside those classes) and tagged with its OWASP Top Ten 2025 category. Results are anchored to the code location the analysis phase recorded, falling back to the HTTP entry point when the finding names no file. Severity maps onto SARIF's three levels: `critical` and `high` become `error`, `medium` becomes `warning`, everything else becomes `note`.\n\nIf the SARIF log cannot be written, the JSON and Markdown reports are still produced and the scan finishes as \"partial\".\n\nThe log is written only for exploitative runs. `sarif` is ignored when `exploit` is `\"false\"`.\n\nSupported rule types include `url_path`, `subdomain`, `domain`, `method`, `header`, `parameter`, and `code_path`.\n\n## Writing Login Flow\n\nLog in once in a fresh private browser window. Write the steps in the same order you perform them:\n\n- When typing into a field, reference the field by its exact label or placeholder.\n- When clicking a button, reference the exact button text.\n\nSupported placeholders:\n\n- `$username`\n- `$password`\n- `$totp`\n- `$email_address`\n- `$email_password`\n- `$email_totp`\n\nAt runtime, Shannon replaces these placeholders with the credentials passed in the config.\n\n```yaml\nlogin_flow:\n  - \"Type $username in <exact email field label or placeholder>\"\n  - \"Click <exact button text>\"\n  - \"Type $password in <exact password field label or placeholder>\"\n  - \"Click <exact button text>\"\n  - \"If prompted for 2FA, type $totp in <exact code field label or placeholder>\"\n  - \"Click <exact button text>\"\n```\n\n### Validating Authentication Only\n\nTo confirm your login flow works before committing to a full scan, add `--validate-auth` to `start`:\n\n```bash\nnpx @keygraph/shannon start -u https://your-app.com -r /path/to/repo -c config.yaml --validate-auth\n```\n\nThe run performs preflight and the single real login, then stops. No pentest, reconciliation, or report\nis produced. It requires an `authentication` block in the config.\n\n---\n\n# File: docs/ai-providers.md\n\n# AI Providers\n\nOne model runs the entire scan — pre-recon, recon, vulnerability analysis, exploitation, and reporting. A single setting names both the provider and the model:\n\n```bash\nexport SHANNON_AI_MODEL=<provider>:<model-id>\n```\n\nThe provider half decides where the request goes, which credential is used, and which API dialect is spoken. You never configure those separately.\n\n## Supported providers\n\n| Provider | Value | Credential |\n| --- | --- | --- |\n| Anthropic | `anthropic` | `SHANNON_AI_API_KEY` (or `CLAUDE_CODE_OAUTH_TOKEN`) |\n| OpenAI | `openai` | `SHANNON_AI_API_KEY` |\n| xAI | `xai` | `SHANNON_AI_API_KEY` |\n| AWS Bedrock | `amazon-bedrock` | `AWS_REGION` and `AWS_BEARER_TOKEN_BEDROCK` |\n\n`SHANNON_AI_API_KEY` holds the key for whichever provider `SHANNON_AI_MODEL` names. Bedrock is the exception — it authenticates through its `AWS_` variables only. If `SHANNON_AI_MODEL` is unset, Shannon uses `anthropic:claude-sonnet-4-6`.\n\nAnthropic, OpenAI, and xAI also accept their native variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `XAI_API_KEY`); if one of those is set, it is used instead of `SHANNON_AI_API_KEY`.\n\nShannon forwards only the selected provider's credential into the scan container. Keys for other providers stay on your machine.\n\n### Any other provider\n\nShannon accepts any provider and model present in the Pi harness catalogue. Browse them at [pi.dev/models](https://pi.dev/models).\n\n```bash\nexport SHANNON_AI_API_KEY=your-api-key                     # the provider's key — or the gateway's when a base URL is set\nexport SHANNON_AI_MODEL=openrouter:moonshotai/kimi-k3      # <provider>:<model-id>\nexport SHANNON_AI_BASE_URL=https://llm-gateway.example.com # optional: route through a proxy or LLM gateway\n```\n\nThis path covers providers whose credential is a single API key. Providers that need more than that are not currently supported.\n\nA model the catalogue does not carry is reachable by describing it yourself. See [Custom model configuration](#custom-model-configuration).\n\n`npx @keygraph/shannon setup` exposes this as the **Other provider** option.\n\n> [!IMPORTANT]\n> Models are validated against the harness catalogue, but capability varies. A model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker pentests. Evaluate the model you choose against your own targets before depending on its results.\n\n## Cyber safeguards (do this before your first scan)\n\nAnthropic and OpenAI both apply real-time safeguards to cyber-security workloads. Shannon is exactly such a workload. If a safeguard engages mid-run, the model can refuse, and the scan fails partway through rather than at the start.\n\nReview each vendor's guidance and complete the verification or enrollment they ask of legitimate security testers before running Shannon:\n\n- Anthropic - [Real-time cyber safeguards on Claude Opus and Sonnet](https://support.claude.com/en/articles/14604842-real-time-cyber-safeguards-on-claude-opus-and-sonnet)\n- OpenAI - [Cyber](https://chatgpt.com/cyber)\n\nThis applies to the Anthropic and OpenAI providers, including when either is reached through an LLM gateway. Bedrock serves Claude models and is subject to Anthropic's safeguards as well.\n\nTo confirm your model is ready before committing to a full scan, add `--validate-model` to `start`:\n\n```bash\nnpx @keygraph/shannon start -u https://your-app.com -r /path/to/repo --validate-model\n```\n\nThe run performs the preflight model checks only — credential and registry resolution for any provider, plus a single cyber-access verification against Anthropic and OpenAI that trips the cyber safeguard if your account is not approved — then stops. No pentest or report is produced, and it needs no config. A decline fails the run with the vendor's enrollment link.\n\n## Suggested models\n\nThese are the models `npx @keygraph/shannon setup` offers, best-first. They are suggestions: the wizard also takes a typed model ID, and `SHANNON_AI_MODEL` accepts any model in the provider's catalogue.\n\n| Provider | Suggested model IDs |\n| --- | --- |\n| `anthropic` | `claude-sonnet-5`, `claude-opus-5`, `claude-sonnet-4-6`, `claude-opus-4-8`, `claude-opus-4-7`, `claude-haiku-4-5-20251001` |\n| `openai` | `gpt-6-sol`, `gpt-5.6-sol`, `gpt-5.5`, `gpt-5.4` |\n| `xai` | `grok-4.7` |\n| `amazon-bedrock` | `us.anthropic.claude-sonnet-4-6`, `us.anthropic.claude-opus-4-8`, `us.anthropic.claude-opus-4-7` |\n\nBedrock IDs are region-prefixed and must be enabled in your account, so the ID that works for you may differ from the one listed here.\n\n## Switching provider\n\nThe pattern is learned once: export the provider's key, name the model. Two lines change, nothing else.\n\nAnthropic (default):\n\n```bash\nexport SHANNON_AI_API_KEY=sk-ant-...\nexport SHANNON_AI_MODEL=anthropic:claude-sonnet-4-6\n```\n\nOpenAI:\n\n```bash\nexport SHANNON_AI_API_KEY=sk-...\nexport SHANNON_AI_MODEL=openai:gpt-6-sol\n```\n\nxAI:\n\n```bash\nexport SHANNON_AI_API_KEY=xai-...\nexport SHANNON_AI_MODEL=xai:grok-4.7\n```\n\nSource-build mode reads the same variables from a `.env` file.\n\n## AWS Bedrock\n\nRun `npx @keygraph/shannon setup` and select **AWS Bedrock**, or export directly:\n\n```bash\nexport AWS_REGION=us-east-1\nexport AWS_BEARER_TOKEN_BEDROCK=your-bearer-token\nexport SHANNON_AI_MODEL=amazon-bedrock:us.anthropic.claude-opus-4-8\n```\n\nBedrock uses bearer-token authentication only. IAM access keys, session tokens, assumed roles, and instance profiles are not supported. The model must be enabled in your region.\n\n## Custom base URL\n\n`SHANNON_AI_BASE_URL` routes model traffic through a proxy or LLM gateway instead of the provider's default endpoint — an LLM gateway such as LiteLLM, a regional endpoint, or any other host you choose. It is a plain endpoint override: it changes only *where* requests go. The provider half of `SHANNON_AI_MODEL` still decides which credential is sent and which API dialect is spoken, and that is unchanged by the base URL.\n\nThis works for **any** provider, curated or not, subject to two rules. A provider's dialect is fixed, so the endpoint you point at must speak that provider's dialect:\n\n| Provider prefix | Dialect the endpoint must speak |\n| --- | --- |\n| `anthropic:` | Anthropic Messages |\n| `openai:` | OpenAI Responses |\n\nAnd the model ID must still resolve in the harness catalogue. A base URL changes only the address; it grants no exemption from that check. A gateway serving a model under its own name needs that name described in a [custom model configuration](#custom-model-configuration) file.\n\nAnthropic Messages LLM gateway:\n\n```bash\nexport SHANNON_AI_API_KEY=sk-ant-...\nexport SHANNON_AI_MODEL=anthropic:claude-sonnet-4-6\nexport SHANNON_AI_BASE_URL=https://llm-gateway.example.com\n```\n\nOpenAI Responses LLM gateway:\n\n```bash\nexport SHANNON_AI_API_KEY=sk-...\nexport SHANNON_AI_MODEL=openai:gpt-6-sol\nexport SHANNON_AI_BASE_URL=https://llm-gateway.example.com/v1\n```\n\n`npx @keygraph/shannon setup` configures a base URL two ways: **Custom Base URL** covers the common Anthropic Messages and OpenAI Responses LLM gateways, and **Other provider** takes any provider ID plus an optional base URL of its own.\n\n## Custom model configuration\n\nA custom model configuration is a Pi `models.json` file that describes a model the harness catalogue does not carry: one a router or gateway serves under its own ID, or a local server (see [Local and self-hosted models](#local-and-self-hosted-models)). You pass it with `--models-config`, and Shannon merges its definitions over the catalogue so `SHANNON_AI_MODEL` can then name the model like any other:\n\n```bash\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo --models-config ./models.json\n```\n\n```bash\n./shannon start -u https://example.com -r ./my-repo --models-config ./models.json\n```\n\n[pi.dev/models](https://pi.dev/models) supplies the file contents. Find the model under the provider you want, since the same model has a different ID per provider, then open its page and expand **Show configuration** for a ready-to-paste snippet:\n\n```json\n{\n  \"providers\": {\n    \"openrouter\": {\n      \"apiKey\": \"YOUR_API_KEY\",\n      \"models\": [\n        {\n          \"id\": \"z-ai/glm-5.3\",\n          \"name\": \"Z.ai: GLM 5.3\",\n          \"reasoning\": true,\n          \"input\": [\n            \"text\"\n          ],\n          \"thinkingLevelMap\": {\n            \"off\": null,\n            \"minimal\": null,\n            \"low\": \"low\",\n            \"medium\": null,\n            \"high\": \"high\",\n            \"xhigh\": null,\n            \"max\": \"max\"\n          },\n          \"contextWindow\": 1048576,\n          \"maxTokens\": 943718,\n          \"cost\": {\n            \"input\": 1.4,\n            \"output\": 4.4,\n            \"cacheRead\": 0.26,\n            \"cacheWrite\": 0\n          },\n          \"compat\": {\n            \"supportsDeveloperRole\": false,\n            \"thinkingFormat\": \"openrouter\"\n          }\n        }\n      ],\n      \"api\": \"openai-completions\",\n      \"baseUrl\": \"https://openrouter.ai/api/v1\"\n    }\n  }\n}\n```\n\nThen name the model the usual way:\n\n```bash\nexport SHANNON_AI_API_KEY=your-api-key\nexport SHANNON_AI_MODEL=openrouter:z-ai/glm-5.3\n```\n\nLeave `YOUR_API_KEY` exactly as it is. Shannon sends the credential from your environment, and that takes precedence over anything the file declares, so the file describes the model and never has to hold a secret.\n\nPi's [models documentation](https://pi.dev/docs/latest/models) describes the full format, including provider routing preferences and compatibility flags.\n\n## Local and self-hosted models\n\nOllama, LM Studio, vLLM, and any other OpenAI-compatible server are reached through the same mechanism. Describe the server as a provider in a model config file, then name its model with `SHANNON_AI_MODEL`.\n\n> [!IMPORTANT]\n> Use `host.docker.internal`, not `localhost`. The scan runs inside a container, so `localhost` points at the container itself rather than at your machine.\n\nA `models.json` for Ollama:\n\n```json\n{\n  \"providers\": {\n    \"ollama\": {\n      \"baseUrl\": \"http://host.docker.internal:11434/v1\",\n      \"api\": \"openai-completions\",\n      \"apiKey\": \"ollama\",\n      \"models\": [\n        { \""},"files":{"llms.txt":"# Shannon\n\n> Shannon is an autonomous AI pentesting project by Keygraph. This repository contains Shannon, the AGPL-3.0 open-source white-box pentesting CLI. Keygraph Pro and Keygraph Enterprise are Keygraph's commercial continuous pentesting and AppSec editions.\n\nUse this file as the concise entry point for AI agents and LLMs reading this repository. For a single combined context file, use [llms-full.txt](llms-full.txt).\n\n## Start Here\n\n- [README](README.md): Main project overview, editions, quick start, Shannon capabilities, CI/CD integrations, common questions, safety notes, licensing, and support links.\n\n## Shannon\n\n- [Development](docs/development.md): Source-build workflow, common CLI commands, repository paths, and output locations.\n- [Configuration](docs/configuration.md): Authenticated testing, login flows, rules of engagement (including rate-limit guidance), report filters, and credential precedence.\n- [AI Providers](docs/ai-providers.md): Anthropic, OpenAI, xAI, AWS Bedrock, any other Pi-supported provider, custom LLM gateway setup, custom model configuration for models not yet in the Pi catalogue, and local self-hosted runtimes (Ollama, LM Studio, vLLM).\n- [Platforms and Networking](docs/platforms.md): Windows/WSL2, Linux, macOS, Docker networking, local applications, and custom hostnames.\n- [Workspaces and Resuming](docs/workspaces.md): Workspace storage, naming, resuming interrupted scans, and examples.\n- [Safety and Limitations](docs/safety.md): Authorized-use requirements, non-production guidance, mutative effects, model caveats, scope limits, cost, and performance.\n- [Coverage and Roadmap](docs/coverage-roadmap.md): Current Shannon coverage and roadmap direction.\n\n## Keygraph Pro and Enterprise\n\n- [Pro and Enterprise](docs/keygraph-platform.md): Keygraph's commercial continuous pentesting and AppSec editions, compared edition by edition with Shannon. The Community Program, Pro, and Enterprise all include black-box and white-box pentesting, parsed-code SAST, SCA and secrets scanning, findings management, fix pull requests and retests, Jira sync, and SSO, RBAC, and audit logs. Pro and the Community Program are cloud-hosted; Enterprise is self-hosted or air-gapped. Shannon Lite is now just Shannon. Shannon Pro is now Keygraph Pro.\n\n## External Links\n\n- [Keygraph website](https://keygraph.io): Company and commercial product information.\n- [Keygraph pricing](https://keygraph.io/pricing): Prices and the feature table for Shannon, the Community Program, Pro, and Enterprise.\n- [Keygraph demo](https://cal.com/team/keygraph/keygraph-technical-demo): Technical demo booking.\n- [Community Discord](https://discord.gg/cmctpMBXwE): Community support and discussion.\n",".claude/commands/pr.md":"---\ndescription: Create a PR to main branch using conventional commit style for the title\n---\n\nCreate a pull request from the current branch to the `main` branch.\n\n## Arguments\n\nThe user may provide issue numbers that this PR fixes: `$ARGUMENTS`\n\n- If provided (e.g., `123` or `123,456`), use these issue numbers\n- If not provided, check the branch name for issue numbers (e.g., `fix/123-bug` or `issue-456-feature` → extract `123` or `456`)\n- If no issues are found, omit the \"Closes\" section\n\n## Steps\n\nFirst, analyze the current branch to understand what changes have been made:\n1. Run `git log --oneline -10` to see recent commit history and understand commit style\n2. Run `git log main..HEAD --oneline` to see all commits on this branch that will be included in the PR\n3. Run `git diff main...HEAD --stat` to see a summary of file changes\n4. Run `git branch --show-current` to get the branch name for issue detection (if no explicit issues provided)\n\nThen generate a PR title that:\n- Follows conventional commit format (e.g., `fix:`, `feat:`, `chore:`, `refactor:`)\n- Is concise and accurately describes the changes\n- Matches the style of recent commits in the repository\n\nGenerate a PR body with:\n- A `## Summary` section using rich bullets with bold action leads\n- A `Closes #X` line for each issue number (if any were provided or detected from branch name)\n\nEach Summary bullet must follow this format:\n- **Bold action phrase** (imperative verb: \"Add X\", \"Replace Y\", \"Fix Z\") — followed by em dash and a 1-2 sentence conceptual description of what changed and why\n- Keep descriptions conceptual — no inline code references (no backticks for function/file names). The diff shows the code\n- Use 2-5 bullets, scaling with PR size. Group related changes into single bullets rather than listing every file touched\n\nExample:\n```\n## Summary\n\n- **Add preflight validation** — validates repo path, config, and credentials before agent execution. Fails fast with actionable errors\n- **Replace error strings** — pipe-delimited segments rendered as multi-line blocks with phase context, type, message, and remediation hint\n- **Add error classification** — new error codes for repo, auth, and billing failures with proper retry classification\n```\n\nFinally, create the PR using the gh CLI:\n```\ngh pr create --base main --title \"<generated title>\" --body \"$(cat <<'EOF'\n## Summary\n<rich bullets>\n\nCloses #<issue1>\nCloses #<issue2>\nEOF\n)\"\n```\n\nNote: Omit the \"Closes\" lines entirely if no issues are associated with this PR.\n\nIMPORTANT:\n- Do NOT include any Claude Code attribution in the PR\n- Use the conventional commit prefix that best matches the changes (fix, feat, chore, refactor, docs, etc.)\n- The `Closes #X` syntax will automatically close the referenced issues when the PR is merged\n",".claude/commands/review.md":"---\ndescription: Review code changes for Shannon-specific patterns, security, and common mistakes\n---\n\nReview the current changes (staged or working directory) with focus on Shannon-specific patterns and common mistakes.\n\n## Step 1: Gather Changes\nRun these commands to understand the scope:\n```bash\ngit diff --stat HEAD\ngit diff HEAD\n```\n\n## Step 2: Check Shannon-Specific Patterns\n\n### Error Handling (CRITICAL)\n- [ ] **All errors use PentestError** - Never use raw `Error`. Use `new PentestError(message, type, retryable, context)`\n- [ ] **Error type is appropriate** - Use correct type: 'config', 'network', 'tool', 'prompt', 'filesystem', 'validation', 'billing', 'unknown'\n- [ ] **Retryable flag matches behavior** - If error will be retried, set `retryable: true`\n- [ ] **Context includes debugging info** - Add relevant paths, tool names, error codes to context object\n- [ ] **Never swallow errors silently** - Always log or propagate errors\n- [ ] **Use ErrorCode enum** - Prefer `ErrorCode.CONFIG_INVALID` over string matching for classification\n- [ ] **Result<T,E> for service returns** - Services return `Result`, not throw\n\n### Audit System & Concurrency (CRITICAL)\n- [ ] **Mutex protection for parallel operations** - Use `sessionMutex.lock()` when updating `session.json` during parallel agent execution\n- [ ] **Reload before modify** - Always call `this.metricsTracker.reload()` before updating metrics in mutex block\n- [ ] **Atomic writes for session.json** - Use `atomicWrite()` for session metadata, never `fs.writeFile()` directly\n- [ ] **Stream drain handling** - Log writes must wait for buffer drain before resolving\n- [ ] **Semaphore release in finally** - Git semaphore must be released in `finally` block\n\n### Claude SDK Integration (CRITICAL)\n- [ ] **MCP server configuration** - Verify Playwright MCP uses `--isolated` and unique `--user-data-dir`\n- [ ] **Prompt variable interpolation** - Check all `{{VARIABLE}}` placeholders are replaced\n- [ ] **Turn counting** - Increment `turnCount` on assistant messages, not tool calls\n- [ ] **Cost tracking** - Extract cost from final `result` message, track even on failure\n- [ ] **API error detection** - Check for \"session limit reached\" (fatal) vs other errors\n\n### Configuration & Validation (CRITICAL)\n- [ ] **FAILSAFE_SCHEMA for YAML** - Never use default schema (prevents code execution)\n- [ ] **Security pattern detection** - Check for path traversal (`../`), HTML injection (`<>`), JavaScript URLs\n- [ ] **Rule conflict detection** - Rules cannot appear in both `avoid` AND `focus`\n- [ ] **Duplicate rule detection** - Same `type:url_path` cannot appear twice\n- [ ] **JSON Schema validation before use** - Config must pass AJV validation\n\n### Services Layer & DI Container (CRITICAL)\n- [ ] **Business logic in services, not activities** — Activities: heartbeat loop, error classification, container calls only. Domain logic → `src/services/`\n- [ ] **Services accept ActivityLogger** — Never import `@temporalio/*` in services. Use `ActivityLogger` interface from `src/types/`\n- [ ] **Result type for fallible operations** — Service methods return `Result<T, PentestError>`, unwrap with `isOk()`/`isErr()`. Activities call `executeOrThrow()` at the boundary\n- [ ] **Container lifecycle** — `getOrCreateContainer()` at activity start, `removeContainer()` only in workflow cleanup\n- [ ] **AuditSession not in container** — Must be passed per-agent call (parallel safety)\n\n### Session & Agent Management (CRITICAL)\n- [ ] **Deliverable dependencies respected** - Exploitation agents only run if vulnerability queue exists AND has items\n- [ ] **Queue validation before exploitation** - Use `safeValidateQueueAndDeliverable()` to check eligibility\n- [ ] **Git checkpoint before agent run** - Create checkpoint for rollback on failure\n- [ ] **Git rollback on retry** - Call `rollbackGitWorkspace()` before each retry attempt\n- [ ] **Agent prerequisites checked** - Verify prerequisite agents completed before running dependent agent\n\n### Parallel Execution\n- [ ] **Promise.allSettled for parallel agents** - Never use `Promise.all` (partial failures should not crash batch)\n- [ ] **Staggered startup** - 2-second delay between parallel agent starts to prevent API throttle\n- [ ] **Individual retry loops** - Each agent retries independently (3 attempts max)\n- [ ] **Results aggregated correctly** - Handle both 'fulfilled' and 'rejected' results from `Promise.allSettled`\n\n## Step 3: TypeScript Safety\n\n### Type Assertions (WARNING)\n- [ ] **No double casting** - Never use `as unknown as SomeType` (bypasses type safety)\n- [ ] **Validate before casting** - JSON parsed data should be validated (JSON Schema) before `as Type`\n- [ ] **Prefer type guards** - Use `instanceof` or property checks instead of assertions where possible\n\n### Null/Undefined Handling\n- [ ] **Explicit null checks** - Use `if (x === null || x === undefined)` not truthy checks for critical paths\n- [ ] **Nullish coalescing** - Use `??` for null/undefined, not `||` which also catches empty string/0\n- [ ] **Optional chaining** - Use `?.` for nested property access on potentially undefined objects\n\n### Imports & Types\n- [ ] **Type imports** - Use `import type { ... }` for type-only imports\n- [ ] **No implicit any** - All function parameters and returns must have explicit types\n- [ ] **Readonly for constants** - Use `Object.freeze()` and `Readonly<>` for immutable data\n\n## Step 4: Security Review\n\n### Defensive Tool Security\n- [ ] **No credentials in logs** - Check that passwords, tokens, TOTP secrets are not logged to audit files\n- [ ] **Config file size limit** - Ensure 1MB max for config files (DoS prevention)\n- [ ] **Safe shell execution** - Command arguments must be escaped/sanitized\n\n### Code Injection Prevention\n- [ ] **YAML safe parsing** - FAILSAFE_SCHEMA only\n- [ ] **No eval/Function** - Never use dynamic code evaluation\n- [ ] **Input validation at boundaries** - URLs, paths validated before use\n\n## Step 5: Common Mistakes to Avoid\n\n### Anti-Patterns Found in Codebase\n- [ ] **Catch + re-throw without context** - Don't just `throw error`, wrap with additional context\n- [ ] **Silent failures in session loading** - Corrupted session files should warn user, not silently reset\n- [ ] **Duplicate retry logic** - Don't implement retry at both caller and callee level\n- [ ] **Hardcoded error message matching** - Prefer error codes over regex on error.message\n- [ ] **Missing timeout on long operations** - Git operations and API calls should have timeouts\n- [ ] **Console.log in services** — Use `ActivityLogger`. Only CLI display code (`client.ts`, `worker.ts`, `output-formatters.ts`) uses console.log\n- [ ] **Temporal imports in services** — Services must stay Temporal-agnostic. If you need Temporal APIs, it belongs in activities\n\n### Code Quality\n- [ ] **No dead code added** - Remove unused imports, functions, variables\n- [ ] **No over-engineering** - Don't add abstractions for single-use operations\n- [ ] **Comments only where needed** - Self-documenting code preferred over excessive comments\n- [ ] **Consistent file naming** - kebab-case for files (e.g., `queue-validation.ts`)\n\n## Step 6: Provide Feedback\n\nFor each issue found:\n1. **Location**: File and line number\n2. **Issue**: What's wrong and why it matters\n3. **Fix**: How to correct it (with code example if helpful)\n4. **Severity**: Critical / Warning / Suggestion\n\n### Severity Definitions\n- **Critical**: Will cause bugs, crashes, data loss, or security issues\n- **Warning**: Code smell, inconsistent pattern, or potential future issue\n- **Suggestion**: Style improvement or minor enhancement\n\nSummarize with:\n- Total issues by severity\n- Overall assessment (Ready to commit / Needs fixes / Needs discussion)\n\n---\n\nNow review the current changes.\n",".claude/commands/debug.md":"---\ndescription: Systematically debug errors using context analysis and structured recovery\n---\n\nYou are debugging an issue. Follow this structured approach to avoid spinning in circles.\n\n## Step 1: Capture Error Context\n- Read the full error message and stack trace\n- Identify the layer where the error originated:\n  - **CLI/Args** - Input validation, path resolution\n  - **Config Parsing** - YAML parsing, JSON Schema validation (`src/config-parser.ts`)\n  - **Session Management** - Agent definitions (`src/session-manager.ts`), mutex (`src/utils/concurrency.ts`)\n  - **DI Container** - Container initialization/lookup (`src/services/container.ts`)\n  - **Services** - AgentExecutionService, ConfigLoaderService, ExploitationCheckerService, error-handling (`src/services/`)\n  - **Audit System** - Logging, metrics tracking, atomic writes (`src/audit/`)\n  - **Claude SDK** - Agent execution, MCP servers, turn handling (`src/ai/claude-executor.ts`)\n  - **Git Operations** - Checkpoints, rollback, commit (`src/services/git-manager.ts`)\n  - **Validation** - Deliverable checks, queue validation (`src/services/queue-validation.ts`)\n\n## Step 2: Check Relevant Logs\n\n**Session audit logs:**\n```bash\n# Find most recent session\nls -lt workspaces/ | head -5\n\n# Check session metrics and errors\ncat workspaces/<session>/session.json | jq '.errors, .agentMetrics'\n\n# Check agent execution logs\nls -lt workspaces/<session>/agents/\ncat workspaces/<session>/agents/<latest>.log\n```\n\n## Step 3: Trace the Call Path\n\nFor Shannon, trace through these layers:\n\n1. **Worker + Client** → `src/temporal/worker.ts` - Combined worker + workflow submission\n2. **Workflow** → `src/temporal/workflows.ts` - Pipeline orchestration\n3. **Activities** → `src/temporal/activities.ts` - Thin wrappers: heartbeat, error classification\n4. **Container** → `src/services/container.ts` - Per-workflow DI\n5. **Services** → `src/services/agent-execution.ts` - Agent lifecycle\n6. **Config** → `src/config-parser.ts` via `src/services/config-loader.ts`\n7. **Prompts** → `src/services/prompt-manager.ts`\n8. **Audit** → `src/audit/audit-session.ts` - Logging facade, metrics tracking\n9. **Executor** → `src/ai/claude-executor.ts` - SDK calls, MCP setup, retry logic\n10. **Validation** → `src/services/queue-validation.ts` - Deliverable checks\n\n## Step 4: Identify Root Cause\n\n**Common Shannon-specific issues:**\n\n| Symptom | Likely Cause | Fix |\n|---------|--------------|-----|\n| Agent hangs indefinitely | MCP server crashed, Playwright timeout | Check Playwright logs in `/tmp/playwright-*` |\n| \"Validation failed: Missing deliverable\" | Agent didn't create expected file | Check `deliverables/` dir, review prompt |\n| Git checkpoint fails | Uncommitted changes, git lock | Run `git status`, remove `.git/index.lock` |\n| \"Session limit reached\" | Claude API billing limit | Not retryable - check API usage |\n| Parallel agents all fail | Shared resource contention | Check mutex usage, stagger startup timing |\n| Cost/timing not tracked | Metrics not reloaded before update | Add `metricsTracker.reload()` before updates |\n| session.json corrupted | Partial write during crash | Delete and restart, or restore from backup |\n| YAML config rejected | Invalid schema or unsafe content | Run through AJV validator manually |\n| Prompt variable not replaced | Missing `{{VARIABLE}}` in context | Check `src/services/prompt-manager.ts` interpolation |\n| Service returns Err result | Check `ErrorCode` in Result | Trace through `classifyErrorForTemporal()` in `src/services/error-handling.ts` |\n| Container not found | `getOrCreateContainer()` not called | Check activity setup code in `src/temporal/activities.ts` |\n| ActivityLogger undefined | `createActivityLogger()` not called | Must be called at top of each activity function |\n\n**MCP Server Issues:**\n```bash\n# Check if Playwright browsers are installed\nnpx playwright install chromium\n\n# Check MCP server startup (look for connection errors)\ngrep -i \"mcp\\|playwright\" workspaces/<session>/agents/*.log\n```\n\n**Git State Issues:**\n```bash\n# Check for uncommitted changes\ngit status\n\n# Check for git locks\nls -la .git/*.lock\n\n# View recent git operations from Shannon\ngit reflog | head -10\n```\n\n## Step 5: Apply Fix with Retry Limit\n\n- **CRITICAL**: Track consecutive failed attempts\n- After **3 consecutive failures** on the same issue, STOP and:\n  - Summarize what was tried\n  - Explain what's blocking progress\n  - Ask the user for guidance or additional context\n- After a successful fix, reset the failure counter\n\n## Step 6: Validate the Fix\n\n**For code changes:**\n```bash\n# Compile TypeScript\nnpx tsc --noEmit\n\n# Quick validation run\nshannon <URL> <REPO> --pipeline-testing\n```\n\n**For audit/session issues:**\n- Verify `session.json` is valid JSON after fix\n- Check that atomic writes complete without errors\n- Confirm mutex release in `finally` blocks\n\n**For agent issues:**\n- Verify deliverable files are created in correct location\n- Check that validation functions return expected results\n- Confirm retry logic triggers on appropriate errors\n\n## Anti-Patterns to Avoid\n\n- Don't delete `session.json` without checking if session is active\n- Don't modify git state while an agent is running\n- Don't retry billing/quota errors (they're not retryable)\n- Don't ignore PentestError type - it indicates the error category\n- Don't make random changes hoping something works\n- Don't fix symptoms without understanding root cause\n- Don't bypass mutex protection for \"quick fixes\"\n\n## Quick Reference: Error Types\n\n`ErrorCode` enum in `src/types/errors.ts` provides finer-grained classification used by `classifyErrorForTemporal()` in `src/services/error-handling.ts`.\n\n| PentestError Type | Meaning | Retryable? |\n|-------------------|---------|------------|\n| `config` | Configuration file issues | No |\n| `network` | Connection/timeout issues | Yes |\n| `prompt` | Claude SDK/API issues | Sometimes |\n| `filesystem` | File read/write errors | Sometimes |\n| `validation` | Deliverable validation failed | Yes (via retry) |\n| `billing` | API quota/billing limit | No |\n| `unknown` | Unexpected error | Depends |\n\n---\n\nNow analyze the error and begin debugging systematically.\n","CLAUDE.md":"# CLAUDE.md\n\nAI-powered penetration testing agent for defensive security analysis. Automates vulnerability assessment by combining reconnaissance tools with AI-powered code analysis.\n\n## Commands\n\n**Prerequisites:** Docker, AI provider credentials (`.env` for local, `npx @keygraph/shannon setup` or env vars for npx)\n\n### Dual CLI\n\nShannon supports two CLI modes, auto-detected based on the current working directory:\n\n| | **npx** (`npx @keygraph/shannon`) | **Local** (`./shannon`) |\n|---|---|---|\n| **Install** | Zero-install via npm | Clone the repo |\n| **Image** | Pulled from Docker Hub (`keygraph/shannon:latest`) | Built locally (`shannon-worker`) |\n| **State** | `~/.shannon/` | Project directory |\n| **Credentials** | `~/.shannon/config.toml` (via `npx @keygraph/shannon setup`) or env vars | `./.env` |\n| **Config** | `~/.shannon/config.toml` (via `npx @keygraph/shannon setup`) | N/A |\n| **Prompts** | Bundled in Docker image | Mounted from `./apps/worker/prompts/` (live-editable) |\n\nMode auto-detection: local mode activates when env var `SHANNON_LOCAL=1` is set by the `./shannon` entry point (`apps/cli/src/mode.ts`). Otherwise npx mode.\n\n### npx Quick Start\n\n```bash\n# Configure credentials (interactive wizard)\nnpx @keygraph/shannon setup\n\n# Or export env vars directly (non-interactive / CI)\nexport ANTHROPIC_API_KEY=your-key\n\n# Run\nnpx @keygraph/shannon start -u <url> -r /path/to/repo\n```\n\n### Local (Development) Quick Start\n\n```bash\n# Setup\necho \"ANTHROPIC_API_KEY=your-key\" > .env\n\n# Build (auto-runs if image missing)\n./shannon build\n\n# Run\n./shannon start -u <url> -r ./my-repo\n./shannon start -u <url> -r ./my-repo -c ./apps/worker/configs/my-config.yaml\n./shannon start -u <url> -r /any/path/to/repo\n```\n\n### Common Commands\n\n```bash\n# Setup (npx mode only — one-time credential configuration)\nnpx @keygraph/shannon setup\n\n# Workspaces & Resume\n./shannon start -u <url> -r ./my-repo -w my-audit    # New named workspace\n./shannon start -u <url> -r ./my-repo -w my-audit    # Resume (same command)\n\n# Monitor\n./shannon scans                       # List running and completed scans, with each report's path\n./shannon logs [<workspace>]          # Show a scan's live log (default: the single running scan, else the most recent)\n./shannon logs [<workspace>] --agent <name>   # Tail one agent's own log (from .shannon/agents/)\n./shannon logs [<workspace>] --list-agents    # List the agents that have their own log\n./shannon status [<workspace>]        # Live phase/agent progress of one scan, read from Temporal (redraws, then exits; same default target)\n# Dashboard: http://localhost:8233\n\n# Stop\n./shannon stop [<workspace>]          # Stop one scan (default: the single running scan; confirms first; --yes/-y to skip)\n./shannon stop --all                  # Stop all running scans (Temporal stays up; confirms first)\n./shannon reset                       # Stop everything and wipe all Temporal data + volumes (type 'confirm' to proceed; cannot be skipped)\n\n# Version\n./shannon version                     # npx: package version; local: git SHA\n\n# Image management\n./shannon build [--no-cache]          # Local mode: build worker image\n\n# Build TypeScript (development)\npnpm run build                       # Build all packages via Turborepo\npnpm run check                       # Type-check all packages\npnpm biome                           # Biome lint + format + import sorting check\npnpm biome:fix                       # Auto-fix lint, format, and import sorting\n```\n\n**Monorepo tooling:** pnpm workspaces, Turborepo for task orchestration, Biome for linting/formatting. TypeScript compiler options shared via `tsconfig.base.json` at the root. All packages extend it, overriding only `rootDir` and `outDir`. Shared devDependencies (`typescript`, `@types/node`, `turbo`, `@biomejs/biome`) are hoisted to the root workspace.\n\n**Options:** `-c <file>` (YAML config), `--models-config <file>` (pi `models.json` defining models pi's catalogue lacks), `-o <path>` (output directory), `-w <name>` (named workspace; auto-resumes if exists), `--validate-auth` (run preflight and auth validation only, then stop; no pentest or report; requires a fresh workspace and an `authentication` block in the config), `--validate-model` (run the preflight model checks only — credential/registry resolution plus the cyber-access verification for cyber-gated providers — then stop; no pentest or report; requires a fresh workspace; needs no config; mutually exclusive with `--validate-auth`), `--pipeline-testing` (minimal prompts, 10s retries), `--keep-container` (preserve worker container after exit for log inspection), `--yes`/`-y` (skip the confirmation prompt on `stop`; required for non-interactive use; `reset` requires a typed `confirm` and cannot be skipped)\n\n## Architecture\n\n### Monorepo Layout\n\n```\napps/cli/        — @keygraph/shannon (published to npm, bundled with tsdown)\napps/worker/     — @shannon/worker (private, Temporal worker + pipeline logic)\n```\n\n### CLI Package (`apps/cli/`)\nPublished as `@keygraph/shannon` on npm. Contains Docker orchestration and a direct `@temporalio/client` integration for read-only status plus bounded workflow lifecycle operations; no worker/pipeline business logic or prompts. Bundled with tsdown for single-file ESM output (deps stay external).\n\n- `apps/cli/src/index.ts` — CLI dispatcher (`setup`, `start`, `stop`, `reset`, `logs`, `status`, `scans`, `build`, `version`)\n- `apps/cli/src/temporal-client.ts` — `@temporalio/client` integration: connects to the frontend on `127.0.0.1:7233` (published by compose), provides `describeScan` (status + `pendingActivities` → running agents), `queryProgress` (live `getProgress` query → `PipelineState`), `getTerminalOutcome` (workflow `result()`), and bounded lifecycle RPCs for `stop`. `stop` requests cancellation first, waits up to 10 seconds, then requests termination only when necessary and verifies closure within a bounded window. No worker of its own; scans are visible within Temporal's retention window, which `ensureInfra` (`apps/cli/src/docker.ts`) converges to `168h` (7 days) on every successful `shannon start`; override with `SHANNON_TEMPORAL_RETENTION` (a positive whole-hour value like `72h`)\n- `apps/cli/src/scan/` — `status` rendering: `pipeline.ts` (static phase/agent plan + `run*Agent` activity-type→agent map + mirrored `PipelineState`/`AgentMetrics` types; keep in sync with the worker), `derive.ts` (pure phase/agent state derivation shared by the tree and `--json`), `render.ts` (one renderer for both the live query state and the terminal result). The tree shows model work only: every row is an agent, an Agentic SAST stage, or a report step that is currently running or failed. Reconciliation is model work owned by a class, so its wall time renders as a trailing `+ duration` on that class's exploitation row (its analysis row when `exploit: false`) rather than as a row of its own; deterministic bookkeeping stages (`report:*` renumber/assemble/finalize/surface) never appear once they complete. `DerivedPhase.children` (renders sub-rows) and `DerivedPhase.meta` (`duration` vs a `k/N done` tally) are independent — Agentic SAST lists stages under a duration, exploitation lists classes under a tally\n- `apps/cli/src/mode.ts` — Auto-detection: local mode if `SHANNON_LOCAL=1` env var is set\n- `apps/cli/src/docker.ts` — Compose lifecycle, image pull/build, and ephemeral `docker run` worker spawning. Each worker carries workspace, task-queue, and preselected workflow-ID labels so stop can correlate the local worker with its Temporal execution before `session.json` exists. Before `docker run`, start fsyncs that exact candidate under the workspace's hidden internals and clears it only when `session.json` registers the same ID; stop reconciles any candidate left by an interrupted launch. Start also checks the image's workflow-ID protocol label and refuses a stale worker that would ignore the preselected ID\n- `apps/cli/src/home.ts` — State directory management (`~/.shannon/` for npx, `./` for local)\n- `apps/cli/src/env.ts` — `.env` loading, TOML fallback (npx only) via `apps/cli/src/config/resolver.ts`, credential validation, provider-scoped env flag building\n- `apps/cli/src/model-spec.ts` — `SHANNON_AI_MODEL` (`<provider>:<model-id>`) parsing; mirrors `apps/worker/src/ai/models.ts`\n- `apps/cli/src/config/resolver.ts` — Cascading config (npx only): env vars → `~/.shannon/config.toml` (parsed with `smol-toml`)\n- `apps/cli/src/config/writer.ts` — TOML serialization and secure file persistence (0o600)\n- `apps/cli/src/commands/setup.ts` — Interactive TUI wizard (`@clack/prompts`) for provider credential setup (npx only)\n- `apps/cli/src/paths.ts` — Repo/config/models-config path resolution (any absolute or relative path). `MODELS_CONFIG_CONTAINER_PATH` is fixed at `/app/models.json` because the worker names it to pi rather than discovering it\n- `apps/cli/src/version.ts` — Version reporting (npx: `package.json` version; local: `git-<sha>`)\n- `apps/cli/src/tty.ts` — Terminal capability detection: `requireInteractive` guard (fails fast off-TTY instead of hanging on a prompt), `supportsColor` color gating (`NO_COLOR`/`FORCE_COLOR`), and `stdoutIsTerminal` for spinner/cursor output\n- `apps/cli/src/commands/` — Command handlers\n- `apps/cli/infra/compose.yml` — Bundled Temporal compose file for npx mode\n- `apps/cli/tsdown.config.ts` — tsdown bundler config\n- `shannon` — Node.js entry point (`#!/usr/bin/env node`) that delegates to `apps/cli/dist/index.mjs`\n\n### Docker Architecture\nInfra (Temporal) runs via `docker-compose.yml`. Workers are ephemeral `docker run --rm` containers, one per scan, each with a unique task queue, preselected workflow ID, matching identity labels, and isolated volume mounts. `shannon stop --all` takes the union of labeled running workers and Temporal-running workflows, so an orphaned workflow is still stopped after its worker has disappeared.\n\n- `docker-compose.yml` — Infra only: `shannon-temporal` (port 7233/8233). Network: `shannon-net`\n- `Dockerfile` — 2-stage build (builder + Chainguard Wolfi runtime). Uses pnpm. Entrypoint: `CMD [\"node\", \"apps/worker/dist/temporal/worker.js\"]`\n- No `docker-compose.docker.yml` — host gateway handled via `--add-host` flag in CLI\n- `/etc/hosts` forwarding — at worker spawn, `forwardEtcHostsFlags` in `apps/cli/src/docker.ts` reads the host's `/etc/hosts` and emits one `--add-host` flag per valid user-added entry. Loopback IPs (`127.x`, `::1`) are rewritten to `host-gateway`; IPv6 addresses are bracketed. Disable per-scan via `SHANNON_FORWARD_HOSTS=false`. Native Windows is refused at startup (`blockNativeWindows` in `apps/cli/src/index.ts`, pointing to the WSL2 guide in `docs/platforms.md`); WSL2 reads its own `/etc/hosts` via the Linux path.\n\n### Worker Package (`apps/worker/`)\n- `apps/worker/src/paths.ts` — Centralized path constants (`PROMPTS_DIR`, `CONFIGS_DIR`, `WORKSPACES_DIR`)\n- `apps/worker/src/session-manager.ts` — Agent definitions (`AGENTS` record). Agent types in `apps/worker/src/types/agents.ts`\n- `apps/worker/src/config-parser.ts` — YAML config parsing with JSON Schema validation\n- `apps/worker/src/ai/pi/pi-executor.ts` — pi harness integration (agent-level retry disabled so Temporal owns restarts; provider-level retry on, see `apps/worker/src/ai/pi/retry-settings.ts`)\n- `apps/worker/src/services/` — Business logic layer (Temporal-agnostic). Activities delegate here. Key: `agent-execution.ts`, `error-handling.ts`, `container.ts`\n- `apps/worker/src/types/` — Consolidated types: `Result<T,E>`, `ErrorCode`, `AgentName`, `ActivityLogger`, etc.\n- `apps/worker/src/utils/` — Shared utilities (file I/O, formatting, concurrency)\n\n### Temporal Orchestration\nDurable workflow orchestration with crash recovery, queryable progress, intelligent retry, and parallel execution (5 concurrent agents in vuln/exploit phases).\n\n- `apps/worker/src/temporal/workflows.ts` — Main workflow (`pentestPipelineWorkflow`)\n- `apps/worker/src/temporal/activities.ts` — Thin wrappers — heartbeat loop, error classification, container lifecycle. Business logic delegated to `apps/worker/src/services/`\n- `apps/worker/src/temporal/activity-logger.ts` — `TemporalActivityLogger` implementation of `ActivityLogger` interface\n- `apps/worker/src/temporal/summary-mapper.ts` — Maps `PipelineSummary` to `WorkflowSummary`\n- `apps/worker/src/temporal/worker.ts` — Combined worker + client entry point (per-invocation task queue, submits workflow, waits for result)\n- `apps/worker/src/temporal/shared.ts` — Types, interfaces, query definitions\n### Five-Phase Pipeline\n\n1. **Pre-Recon** (`pre-recon`) — Source code analysis to build the architectural baseline\n2. **Recon** (`recon`) — Attack surface mapping from initial findings\n3. **Vulnerability Analysis** (5 parallel agents) — injection, xss, auth, authz, ssrf\n4. **Exploitation** (5 parallel agents, conditional) — Exploits confirmed vulnerabilities\n5. **Reporting** (`report`) — Executive-level security report\n\nAround those phases:\n\n- Optional agentic static analysis runs before the pentest when `agentic_sast.enabled` is `\"true\"`, as a child workflow.\n- After each class's analysis, reconciliation groups its findings into exploitation tasks.\n- Findings outside the five classes form an internal `miscellaneous` class with its own exploitation agent (`miscellaneous-exploit`).\n- A scan can finish `completed`, `partial`, `failed`, or `cancelled`; `partial` carries an ordered set of reasons.\n\n### Supporting Systems\n- **Configuration** — YAML configs in `apps/worker/configs/` use the closed JSON Schema in `config-schema.json`. Every fresh scan runs the fixed five analysis classes; there is no public class selector. `agentic_sast.enabled` is the only public agentic-SAST setting. Finding reconciliation runs on every scan and has no public setting of its own. Config also supports authentication (MFA/TOTP), URL/code rule scoping (`rules.avoid`/`rules.focus`), `exploit`, free-form `rules_of_engagement`, and post-hoc `report` options (`min_severity`, `min_confidence`, `guidance`, and exploit-only `sarif` output via `apps/worker/src/services/sarif-renderer.ts`, on by default for exploit runs and opt out with `report.sarif: \"false\"`). `code_path` avoid rules are enforced via the `@gotgenes/pi-permission-system` extension: `apps/worker/src/temporal/activities.ts:syncCodePathDenyRules` writes a global `path` deny config once per workflow (`apps/worker/src/ai/pi/permission-system.ts:syncPermissionSystemConfig`), and the executor loads the extension when that config is present (`apps/worker/src/ai/pi/pi-executor.ts`), so denies fire across every tool and child `task` session. Credential resolution — local mode: env vars → `./.env`; npx mode: env vars → `~/.shannon/config.toml` (via `npx @keygraph/shannon setup`)\n- **Agentic SAST progress** — Capella runs as a child workflow, so its activities are absent from the parent's `pendingActivities` and invisible to the CLI. The child signals each stage boundary up via `capellaStageProgress` (`apps/worker/src/temporal/shared.ts`); the parent's handler validates the payload and writes the child-supplied `startedAt` and `durationMs` directly to `operationalStages['agentic-sast:<stage>']`, so both the live `getProgress` query and the terminal result carry per-stage rows. Signalling is best-effort and every failure is swallowed — a closed or unreachable parent must never fail a SAST run. `CAPELLA_STAGE_LABELS` in `apps/worker/src/ai/sast/types.ts` is the one label table, shared by the scan log and the status tree; `CAPELLA_PROGRESS_STAGES` omits `export`, which runs no model and so never becomes a row. Scans predating the signal keep the aggregate `agentic-sast` span and render as a bare phase line\n- **Prompts** — Per-phase templates in `apps/worker/prompts/` with variable substitution (`{{TARGET_URL}}`, `{{CONFIG_CONTEXT}}`). Shared partials in `apps/worker/prompts/shared/` via `apps/worker/src/services/prompt-manager.ts`, including `_code-path-rules.txt` (focus/avoid `[FILE]`/`[GLOB]` routing) and `_rules-of-engagement.txt` (free-text engagement rules). When `exploit: false`, `apps/worker/src/services/findings-renderer.ts` deterministically converts each `*_exploitation_queue.json` into a `*_findings.md` for report assembly — no LLM in the loop\n- **Agent Harness (pi)** — Uses the **pi harness** (`@earendil-works/pi-coding-agent`, requires Node ≥ 22.19) via `apps/worker/src/ai/pi/pi-executor.ts` (`runPiPrompt` → `createAgentSession`). Retry is split in `apps/worker/src/ai/pi/retry-settings.ts`: pi's agent-level loop is off so Temporal owns agent restarts, while `provider.maxRetries` stays on — pi reads the `provider` block independently of the `enabled` flag — so transport faults are absorbed in-session rather than costing a full agent re-run. `maxRetryDelayMs` is left at pi's 60s default. One model runs every phase, named by `SHANNON_AI_MODEL=<provider>:<model-id>` (default `anthropic:claude-sonnet-4-6`). `apps/worker/src/ai/models.ts` parses the spec — splitting on the **first** colon only, so Bedrock IDs keep theirs — and resolves it through pi's `ModelRuntime`. pi ships the `CredentialStore` interface but no in-memory implementation (its own reads `auth.json` from disk), so `RuntimeCredentialStore` in that file supplies one: credentials arrive as env vars in an ephemeral container and must never touch disk. `createModelRuntime(providerId, apiKey)` builds the runtime with `allowModelNetwork: true`, so `ModelRuntime.create()` refreshes the model catalogue over the network at scan start and a freshly released model resolves without a `--models-config` file. The fetch is bounded (10s) and falls back to the static catalogue on timeout, so an unreachable catalogue endpoint cannot hang the scan. The refresh does not override a `--models-config`: pi reloads and re-applies that file as a config overlay on every refresh (it reloads `this.config` at the top of `refresh()`), so custom definitions still win over the fetched catalogue; the merge semantics below are unchanged, just layered over a fresher base. `resolveModelSelection()` is **async** because `ModelRuntime.create()` is. Any pi-ai provider id is accepted — `parseModelSpec` no longer rejects against a hardcoded list, so pi's registry is the authority (an unknown provider/model surfaces as a clear \"not found in pi registry\" error at preflight, which points to the browsable catalogue at `pi.dev/models` — `PI_CATALOG_URL` in `apps/worker/src/ai/models.ts`, appended to the not-found errors and shown in the setup wizard's \"Other provider\" hint). Four providers are **curated** (`CURATED_PROVIDERS`: `anthropic`, `openai`, `xai`, `amazon-bedrock`) with their own credential variables, config sections, and setup flows; each provider's API key env var is declared once in `PROVIDER_API_KEY_ENV` — Shannon uses each vendor's own variable name (`OPENAI_API_KEY`, `XAI_API_KEY`, …), never an invented one; Bedrock's entry is `AWS_BEARER_TOKEN_BEDROCK`, paired with `AWS_REGION`, which preflight requires separately as provider config rather than a credential. Any other provider uses the **generic** credential path: `SHANNON_AI_API_KEY` (`GENERIC_API_KEY_ENV`) supplies the key for any provider whose credential is a plain API key. Curated providers' own variables take precedence over it, and it also works as a fallback for them — Bedrock is the sole exception (it authenticates through its AWS_ variables, so the generic key never stands in for it). The CLI forwards `SHANNON_AI_API_KEY` in `COMMON_FORWARD_VARS` (it is provider-neutral, binding to whatever `SHANNON_AI_MODEL` names, so the \"only one provider configured\" guard counts only named credentials), and stores it under a generic `[provider]` config.toml section (`provider.api_key`). `npx @keygraph/shannon setup` exposes this as the \"Other provider\" option: free-text provider id + model id + key (a curated provider id is rejected there, since it has its own option). A model pi's catalogue does not carry, such as a self-hosted model, is reachable without an SDK bump: `--models-config <file>` mounts a pi `models.json` read-only at `/app/models.json`. The mount is the entire CLI→worker protocol: nothing is forwarded through the environment, and `modelsConfigPath()` detects the file at that fixed path, exactly as `piAuthPresent()` detects the pi auth mount whose flag is likewise not forwarded (`MODELS_CONFIG_CONTAINER_PATH` in the CLI and `MODELS_CONFIG_PATH` in `apps/worker/src/paths.ts` must stay in sync). `createModelRuntime` always names `modelsPath` explicitly — the mounted path, or **`null` when no config was supplied**, which switches models.json off outright. It is never left to pi's default of `<agent dir>/models.json`, because that dir is shared with the pi auth mount, so a file landing there must not silently contribute model definitions to a scan that did not ask for one. `modelsStorePath` is pinned to the agent dir alongside it, since pi otherwise derives it from `dirname(modelsPath)` and would try to write beside a read-only mount. Custom definitions merge over the built-in catalogue: a matching model id replaces the built-in entry, a new id is added alongside, and `modelOverrides` adjusts a built-in without replacing the provider's list. Omitted fields take pi's defaults (`contextWindow` 128000, `maxTokens` 16384), so a large-context model needs them stated. Credentials are unaffected: `RuntimeCredentialStore` outranks any `apiKey` the file carries, so a snippet pasted from `pi.dev/models` can keep its placeholder key while the real secret stays in the environment — and pi's `!command` config-value form is never reached through `apiKey`. Preflight reports `modelRuntime.getError()` **before** the \"model not found\" check, because a file that fails to parse, fails schema validation, or composes badly otherwise leaves pi with an empty or fallback provider and surfaces as a model-id error that blames `SHANNON_AI_MODEL`. `SHANNON_AI_BASE_URL` overrides the endpoint for any provider (proxies/gateways); the credential and API dialect are unchanged. It is applied after model resolution, so it also wins over a `baseUrl` set in a model config. A base URL only changes the address — `resolveModel` (`apps/worker/src/ai/models.ts`) carries it onto the model descriptor and nothing else, and it grants **no exemption from registry validation**: the model id must resolve for every provider and endpoint alike. A gateway serving a model under its own name is described in a `--models-config` file, which puts a real descriptor in the registry rather than guessing one. pi's builtin `openai` provider serves a single API (Responses) and dispatches on the provider rather than `model.api`, so an `openai:` gateway run always speaks Responses; a Chat-Completions-only gateway must be reached through a completions-native provider such as `openrouter` instead. `buildEnvFlags` forwards only the selected provider's credential into the worker container. The CLI mirrors the parse rule and the provider/credential tables in `apps/cli/src/model-spec.ts` (it cannot import from the worker package); the two must stay in sync. pi ships no JSON-schema output or `Task`/`TodoWrite` built-ins, so structured queues are captured via a `submit_exploitation_queue` custom tool (`apps/worker/src/ai/queue-schemas.ts`), and `task` (child sessions scoped to `read`, `grep`, `find`, `ls`, `write`, and `bash` — no nested `task` or collector tools; `CHILD_TOOLS` in `apps/worker/src/ai/pi/task-tool.ts`) + `todo_write` (`apps/worker/src/ai/pi/session-tools.ts`) are provided as custom tools; the per-phase collectors are pi custom tools (TypeBox `defineTool` in `apps/worker/src/collectors/`). Every agent `createAgentSession` call passes `thinkingLevel: PI_THINKING_LEVEL` (`'high'`, `apps/worker/src/ai/pi/thinking-level.ts`) for deeper analysis than pi's default (`medium`). The preflight credential probe (`apps/worker/src/services/preflight.ts`) passes none and runs at pi's default. The reconciliation-enrichment `completeSimple` call (`apps/worker/src/ai/pi/structured-generation.ts`) passes no `reasoning`, so it runs with thinking off. There is no public thinking setting. Browser automation via `playwright-cli` with session isolation (`-s=<session>`). TOTP generation via `generate-totp` CLI tool. Login flow template at `apps/worker/prompts/shared/login-instructions.txt` supports form, SSO, API, and basic auth. On authenticated whitebox scans, the `validate-authentication` preflight performs the single real login and saves the browser session to `auth-state.json` in the per-session audit directory (path from `authStateFile()` in `apps/worker/src/audit/utils.ts`, derived from `generateAuditPath()`). The validation activity (`apps/worker/src/services/validate-authentication.ts`) removes any stale file from a prior run before the agent runs and verifies the file parses and contains cookies or storage before the preflight is marked complete; `logWorkflowComplete` deletes it when the workflow ends so authenticated cookies don't sit on disk between scans. Agent prompts opt in to session reuse by `@include(shared/_shared-session.txt)` before their `<login_instructions>` block — the partial restores the session and falls through to the full login flow if verification fails. `vuln-auth`/`exploit-auth` omit the include and own their own login\n- **Pi Credential Reuse** — `SHANNON_USE_PI_AUTH=1` opts into reusing the host's Pi login, including an `openai-codex` ChatGPT Plus/Pro subscription (`SHANNON_AI_MODEL=openai-codex:<model-id>`) or an `xai` Grok subscription (`SHANNON_AI_MODEL=xai:<model-id>`); the mechanism is provider-agnostic and works for any Pi login. `apps/cli/src/env.ts` requires `~/.pi/agent/auth.json`; `start.ts` passes its path to `spawnWorker`, which mounts only that file read-write at `/tmp/.pi/agent/auth.json`. The flag itself is not forwarded: the worker detects the file with `piAuthPresent()` and passes its path to `ModelRuntime.create`. CLI and worker API-key presence checks are skipped on this path, but the normal preflight model probe still validates the credential. The image and UID-remapping entrypoint keep `/tmp/.pi/agent` owned by `pentest` so adjacent Pi/Shannon configuration remains writable. Refreshed OAuth state is persisted to the host for subsequent scans.\n- **Audit System** — Crash-safe append-only logging in `workspaces/{hostname}_{sessionId}/`. The run directory's top level holds the human-facing report in both formats (`Security-Assessment-Report.pdf` and `Security-Assessment-Report.md`, `FINAL_REPORT_PDF_FILENAME`/`FINAL_REPORT_MD_FILENAME` in `apps/worker/src/paths.ts`); everything else — deliverables, per-agent logs, prompts, `session.json`, `workflow.log`, and browser artifacts — is nested under a hidden `.shannon/` internals dir (`INTERNAL_DIR`) so a customer sees only the report. Audit path helpers route through `generateInternalPath` (`apps/worker/src/audit/utils.ts`); the CLI nests the overlay backing dirs under the same `.shannon/` (`apps/cli/src/docker.ts`, `start.ts`). `session.json`/`workflow.log` reads use dual-read resolvers (`resolveSessionJsonPath`, `resolveRunFile`) that prefer `.shannon/` and fall back to the legacy run-root layout, so pre-restructure workspaces stay listable (`scans`/`logs`) without migration. A pre-restructure workspace cannot be resumed: `classifyWorkspaceLaunch` (`apps/cli/src/commands/start.ts`) requires `.shannon/launch.json`, and its absence fails the launch as \"created by an earlier version of Shannon\" before anything on disk is touched. There is no in-place migration — the workspace's files and report are left untouched, and the operator starts a new scan under a different `-w` name. The report agent writes structured findings to `report.json`, from which `report-renderer.ts` renders the assembled markdown and `report-json-adapter.ts` produces the Typst-shaped JSON that `pdf-renderer.ts` compiles into `comprehensive_security_assessment_report.pdf` using the bundled `apps/worker/templates/typst/report.typ` template (the `typst` binary is installed in the worker image). `copyReportToRunRoot` (`apps/worker/src/services/reporting.ts`) surfaces both the PDF and the markdown to the run root as `Security-Assessment-Report.pdf` and `Security-Assessment-Report.md`; the deliverables-dir copies remain as the git-checkpointed sources. PDF compilation is best-effort — a failure is logged and the run still completes. WorkflowLogger (`apps/worker/src/audit/workflow-logger.ts`) provides unified human-readable per-workflow logs, backed by LogStream (`apps/worker/src/audit/log-stream.ts`) shared stream primitive. Every combined-log line is also projected into a per-agent file under `.shannon/agents/<slug>.log` (one per pipeline agent, one per Capella stage; subagents fold into the parent's file, and a stage's concurrent sessions share its file with an inline session label). The projection boundary is `apps/worker/src/audit/actor-projection.ts` (`projectActor` maps a `TraceActor` to its combined prefix and owning file slug — slugs come only from closed fields); fan-out is best-effort and never blocks the canonical combined log. A lifecycle owner holds a `LogStream` lease per agent file (the pipeline agent's `logAgent` span, or a Capella stage activity's `try/finally`) so per-line writes ride the reference count; `CapellaStageTrace.drain()` flushes a stage's trace queue before its activity returns. The CLI tails one file with `shannon logs --agent <name>` (`--list-agents` to enumerate); the default `shannon logs` path is unchanged\n- **Deliverables** — Saved to `.shannon/deliverables/` in the target repo via the `save-deliverable` CLI script (`apps/worker/src/scripts/save-deliverable.ts`)\n- **Workspaces & Resume** — Named workspaces via `-w <name>` or auto-named from URL+timestamp. Resume detects completed agents via `session.json`. `loadResumeState()` in `apps/worker/src/temporal/activities.ts` validates deliverable existence, restores git checkpoints, and cleans up incomplete deliverables\n\n## Development Notes\n\n### Adding a New Agent\n1. Define agent in `apps/worker/src/session-manager.ts` (add to `AGENTS` record). `ALL_AGENTS`/`AgentName` types live in `apps/worker/src/types/agents.ts`\n2. Create prompt template in `apps/worker/prompts/` (e.g., `vuln-newtype.txt`)\n3. Two-layer pattern: add a thin activity wrapper in `apps/worker/src/temporal/activities.ts` (heartbeat + error classification). `AgentExecutionService` in `apps/worker/src/services/agent-execution.ts` handles the agent lifecycle automatically via the `AGENTS` registry\n4. Register activity in `apps/worker/src/temporal/workflows.ts` within the appropriate phase\n\n### Modifying Prompts\n- Variable substitution: `{{TARGET_URL}}`, `{{CONFIG_CONTEXT}}`, `{{LOGIN_INSTRUCTIONS}}`\n- Shared partials in `apps/worker/prompts/shared/` included via `apps/worker/src/services/prompt-manager.ts`\n- Test with `--pipeline-testing` for fast iteration\n\n### Key Design Patterns\n- **Configuration-Driven** — YAML configs with JSON Schema validation\n- **Progressive Analysis** — Each phase builds on previous results\n- **Harness-First** — the pi harness (`@earendil-works/pi-coding-agent`) handles autonomous analysis\n- **Modular Error Handling** — `ErrorCode` enum, `Result<T,E>` for explicit error propagation, automatic retry (3 attempts per agent)\n- **Services Boundary** — Activities are thin Temporal wrappers; `apps/worker/src/services/` owns business logic, accepts `ActivityLogger`, returns `Result<T,E>`. No Temporal imports in services\n- **DI Container** — Per-workflow in `apps/worker/src/services/container.ts`. `AuditSession` excluded (parallel safety)\n- **Ephemeral Workers** — Each scan runs in its own `docker run --rm` container with a per-invocation task queue. Temporal routes activities by queue name, so per-scan queues ensure activities never land on a worker with the wrong repo mounted\n\n### Security\nDefensive security tool only. Use only on systems you own or have explicit permission to test.\n\n## Code Style Guidelines\n\n### Formatting\nBiome handles formatting and linting. Run `pnpm biome:fix` to auto-fix. Config in `biome.json`: single quotes, semicolons, trailing commas, 2-space indent, 120 char line width.\n\n### Clarity Over Brevity\n- Optimize for readability, not line count — three clear lines beat one dense expression\n- Use descriptive names that convey intent\n- Prefer explicit logic over clever one-liners\n\n### Structure\n- Keep functions focused on a single responsibility\n- Use early returns and guard clauses instead of deep nesting\n- Never use nested ternary operators — use if/else or switch\n- Extract complex conditions into well-named boolean variables\n\n### TypeScript Conventions\n- Use `function` keyword for top-level functions (not arrow functions)\n- Explicit return type annotations on exported/top-level functions\n- Prefer `readonly` for data that shouldn't be mutated\n- `exactOptionalPropertyTypes` is enabled — use spread for optional props, not direct `undefined` assignment\n\n### Avoid\n- Combining multiple concerns into a single function to \"save lines\"\n- Dense callback chains when sequential logic is clearer\n- Sacrificing readability for DRY — some repetition is fine if clearer\n- Abstractions for one-time operations\n- Backwards-compatibility shims, deprecated wrappers, or re-exports for removed code — delete the old code, don't preserve it\n\n### Comments\nComments must be **timeless** — no references to this conversation, refactoring history, or the AI.\n\n**Patterns used in this codebase:**\n- `/** JSDoc */` — file headers (after license) and exported functions/interfaces\n- `// N. Description` — numbered sequential steps inside function bodies. Use when a\n  function has 3+ distinct phases where at least one isn't immediately obvious from the\n  code. Each step marks the start of a logical phase. Reference: `AgentExecutionService.execute`\n  (steps 1-9) and `injectModelIntoReport` (steps 1-5)\n- `// === Section ===` — high-level dividers between groups of functions in long files,\n  or to label major branching/classification blocks (e.g., `// === SPENDING CAP SAFEGUARD ===`).\n  Not for sequential steps inside function bodies — use numbered steps for that\n- `// NOTE:` / `// WARNING:` / `// IMPORTANT:` — gotchas and constraints\n\n**Never:** obvious comments, conversation references (\"as discussed\"), history (\"moved from X\")\n\n## Key Files\n\n**CLI:** `shannon` (entry point), `apps/cli/src/index.ts` (dispatcher), `apps/cli/src/docker.ts` (orchestration), `apps/cli/src/mode.ts` (auto-detection)\n\n**Entry Points:** `apps/worker/src/temporal/workflows.ts`, `apps/worker/src/temporal/activities.ts`, `apps/worker/src/temporal/worker.ts`\n\n**Core Logic:** `apps/worker/src/session-manager.ts`, `apps/worker/src/ai/pi/pi-executor.ts`, `apps/worker/src/ai/pi/permission-system.ts` (writes `code_path` deny rules to the `@gotgenes/pi-permission-system` global config), `apps/worker/src/config-parser.ts`, `apps/worker/src/services/` (incl. `preflight.ts`, `findings-renderer.ts`, `reporting.ts`), `apps/worker/src/audit/`\n\n**Config:** `docker-compose.yml`, `apps/cli/infra/compose.yml`, `apps/worker/configs/`, `apps/worker/prompts/`, `tsconfig.base.json` (shared compiler options), `turbo.json`, `biome.json`\n\n**CI/CD:** `.github/workflows/release.yml` (Docker Hub push + npm publish + GitHub release, manual dispatch)\n\n## Package Installation\n\nPackage managers are configured with a minimum release age (7 days). Requires pnpm >= 10.16.0. If `pnpm install` fails due to a package being too new, **do not attempt to bypass it** — report the blocked package to the user and stop.\n\n## Troubleshooting\n\n- **\"Repository not found\"** — Pass a path to the target repo (`-r /path/to/repo` or `-r ./my-repo`)\n- **\"Temporal not ready\"** — Wait for health check or `docker compose logs temporal`\n- **Worker not processing** — Check `docker ps --filter \"name=shannon-worker-\"`\n- **Reset state** — `./shannon reset`\n- **Local apps unreachable** — Use `host.docker.internal` instead of `localhost`\n- **Container permissions** — On Linux, may need `sudo` for docker commands\n","llms-full.txt":"# Shannon Full LLM Context\n\n> Combined README and documentation for AI agents and LLMs, generated verbatim from the local files referenced in llms.txt. For the concise index, see [llms.txt](llms.txt).\n\n> Retired names: Shannon Lite is now just Shannon. Shannon Pro is now Keygraph Pro.\n\n---\n\n# File: README.md\n\n> [!NOTE]\n> **[Cyber verification for Anthropic and OpenAI models](https://github.com/KeygraphHQ/shannon/discussions/483):** complete your provider's cyber verification program to prevent model failures and refusals during cyber workloads.\n\n<div align=\"center\">\n\n<picture>\n<source media=\"(prefers-color-scheme: dark)\" srcset=\"./assets/github-banner-dark.png\">\n<source media=\"(prefers-color-scheme: light)\" srcset=\"./assets/github-banner-light.png\">\n<img src=\"./assets/github-banner-light.png\" alt=\"Shannon, AI Pentester for Web Apps and APIs, by Keygraph\" width=\"100%\">\n</picture>\n\n<a href=\"https://trendshift.io/repositories/15604\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/15604\" alt=\"KeygraphHQ%2Fshannon | Trendshift\" style=\"width: 250px; height: 55px;\" width=\"250\" height=\"55\"/></a>\n\n### Shannon is an autonomous, AI pentester for web applications and APIs.\n\nIt analyzes your source code, identifies attack paths, and executes real exploits to prove vulnerabilities before they reach production. **No exploit, no report.**\n\n**This repository is Shannon: the full open-source agent, run locally from your command line.**\n\n<p><strong>Launch Shannon</strong></p>\n\n```bash\nnpx @keygraph/shannon@latest\n```\n\n<sub>The interactive launcher will guide you through setup and your first pentest.</sub>\n\n---\n\n<a href=\"https://discord.gg/9ZqQPuhJB7\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"./assets/discord_button_dark.png\"><source media=\"(prefers-color-scheme: light)\" srcset=\"./assets/discord_button_light.png\"><img src=\"./assets/discord_button_light.png\" height=\"40\" alt=\"Join Discord\"></picture></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"https://keygraph.io/\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"./assets/keygraph_button_dark.png\"><source media=\"(prefers-color-scheme: light)\" srcset=\"./assets/keygraph_button_light.png\"><img src=\"./assets/keygraph_button_light.png\" height=\"40\" alt=\"Visit Keygraph.io\"></picture></a>\n\n---\n\n</div>\n\n> [!TIP]\n> **AI agents and LLMs:** start with [llms.txt](llms.txt) for a concise map of this repository, or use [llms-full.txt](llms-full.txt) for the README and docs combined into one file.\n\n\n\n## Table of Contents\n\n- [Table of Contents](#table-of-contents)\n- [What is Shannon?](#what-is-shannon)\n  - [Why Shannon Exists](#why-shannon-exists)\n  - [Why \"Shannon\"?](#why-shannon)\n  - [Not a replacement for human pentesters](#not-a-replacement-for-human-pentesters)\n- [Shannon in Action](#shannon-in-action)\n- [Quick Start](#quick-start)\n  - [Prerequisites](#prerequisites)\n  - [Run Shannon](#run-shannon)\n- [Key Capabilities](#key-capabilities)\n- [CI/CD Integrations](#cicd-integrations)\n  - [GitHub Actions](#github-actions)\n- [Editions](#editions)\n- [Architecture](#architecture)\n- [Documentation](#documentation)\n- [Safety, Scope, and Limitations](#safety-scope-and-limitations)\n- [License](#license)\n- [Acknowledgements](#acknowledgements)\n- [About Keygraph](#about-keygraph)\n- [Community and Support](#community-and-support)\n- [Common Questions](#common-questions)\n  - [Can I self-host Shannon?](#can-i-self-host-shannon)\n  - [Does Shannon support bring your own key (BYOK)?](#does-shannon-support-bring-your-own-key-byok)\n  - [Does Shannon output SARIF?](#does-shannon-output-sarif)\n  - [Which AI providers does Shannon support?](#which-ai-providers-does-shannon-support)\n  - [Can I run Shannon on a local or self-hosted model?](#can-i-run-shannon-on-a-local-or-self-hosted-model)\n  - [Does Shannon actually exploit vulnerabilities, or just scan?](#does-shannon-actually-exploit-vulnerabilities-or-just-scan)\n\n\n\n## What is Shannon?\n\nShannon is an autonomous AI pentester developed by [Keygraph](https://keygraph.io). It performs security testing of web applications and their underlying APIs by combining source-code analysis with live exploitation.\n\nShannon analyzes your web application's source code to identify potential attack vectors, then uses browser automation and command-line tools to execute real exploits against the running application and its APIs. Only vulnerabilities with a working proof-of-concept are included in the final report.\n\nShannon is the agent. This repository is Shannon, the open-source pentester you run yourself. The same Shannon also powers Keygraph's commercial editions, [Pro and Enterprise](https://keygraph.io/pricing). See [Editions](#editions) for how Shannon compares with Pro, Enterprise, and the Community Program.\n\n<a id=\"why-shannon-exists\"></a>\n<details>\n<summary><strong>Why Shannon Exists</strong></summary>\n\nThanks to tools like Claude Code and Cursor, your team ships code non-stop. But your penetration test? That happens once a year. This creates a massive security gap. For the other 364 days, you could be unknowingly shipping vulnerabilities to production.\n\nShannon closes that gap by providing on-demand, automated penetration testing that can run against every build or release.\n\n</details>\n\n<a id=\"why-shannon\"></a>\n<details>\n<summary><strong>Why \"Shannon\"?</strong></summary>\n\nIt's named after Claude Shannon, the father of information theory. At its core, pentesting is an information problem: every probe reduces uncertainty about a system's state. The best tools maximize the signal gained from every request, turning those bits of knowledge into an exploit path.\n\nAlso, we wanted you to be able to say, \"Hey Claude, run Shannon\" to find all the security flaws in your vibe-coded app.\n\n</details>\n\n<a id=\"not-a-replacement-for-human-pentesters\"></a>\n<details>\n<summary><strong>Not a replacement for human pentesters</strong></summary>\n\nShannon is built to work alongside expert pentesters and red teamers, not replace them. Great pentesters understand the business, chain attacks in ways nobody anticipated, and bring years of judgment that current models can't match.\n\nShannon solves a different problem: there is far more software to test than security teams have time to cover. Critical systems get periodic expert assessments, while the long tail of internal apps, APIs, and fast-moving services rarely gets tested at all.\n\nShannon shifts pentesting left into the software development lifecycle (SDLC). Use it to run exploitation-backed tests against staging environments and releases at the cadence they actually ship, and save expert human time for the risks that need someone who knows the organization.\n\n</details>\n\n## Shannon in Action\n\n![Shannon running an autonomous pentest](assets/Shannon3GIF.gif)\n\nThese reports are from Shannon OSS scans of Photoview 2.4.0, one of the applications in Doyensec's comparison of Aikido and XBOW. We ran Shannon against the same application version and evaluated its results separately. Read the [Doyensec study](https://doyensec.com/resources/ComparingAIApplicationSecurityTestingPlatforms_Doyensec.pdf) and our [Shannon follow-up comparison](docs/shannon-xbow-aikido-benchmark.md) for the methodology, limitations, costs, and results.\n\n\n| Model             | Report                                                                       | SARIF                                                            |\n| ----------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------- |\n| DeepSeek v4 Flash | [View report](benchmark/photoview-deepseek-v4-flash.pdf) | [SARIF](benchmark/photoview-deepseek-v4-flash.sarif) |\n| Grok 4.6          | [View report](benchmark/photoview-grok-4-6.pdf)          | [SARIF](benchmark/photoview-grok-4-6.sarif)          |\n| Claude Opus 5     | [View report](benchmark/photoview-opus-5.pdf)            | [SARIF](benchmark/photoview-opus-5.sarif)            |\n\n## Quick Start\n\n\n\n### Prerequisites\n\n- **Docker**: required for the worker container.\n- **Node.js 18+**: required for the recommended `npx` workflow.\n- **AI provider credentials**: Shannon runs on Anthropic, OpenAI, xAI, AWS Bedrock, and [any other provider](docs/ai-providers.md#any-other-provider) in the harness catalogue — each of which you can point at a proxy or LLM gateway through a [custom base URL](docs/ai-providers.md#custom-base-url), and a model the catalogue does not carry can be described with a [custom model configuration](docs/ai-providers.md#custom-model-configuration). You bring your own key, and Keygraph never proxies your model traffic. Shannon is provider-agnostic. See [AI providers](docs/ai-providers.md#suggested-models) for suggested model IDs.\n- **Cyber safeguards cleared with your provider**: Anthropic and OpenAI apply real-time safeguards to cyber-security workloads, which can interrupt a scan mid-run. Complete their guidance for legitimate security testers before your first run - see [AI providers](docs/ai-providers.md#cyber-safeguards-do-this-before-your-first-scan) and the [cyber verification announcement](https://github.com/KeygraphHQ/shannon/discussions/483).\n\n\n\n### Run Shannon\n\n> [!WARNING]\n> Shannon actively executes exploits. Run it only against applications and environments you own or have explicit written authorization to test. Do not run Shannon against production systems.\n\n```bash\n# Configure credentials with the interactive wizard.\nnpx @keygraph/shannon@latest setup\n\n# Run a pentest against a source-available target.\nnpx @keygraph/shannon@latest start \\\n  -u https://your-app.com \\\n  -r /path/to/your/repo\n```\n\nShannon pulls the worker image from Docker Hub, starts the required local infrastructure, mounts the target repository read-only inside an ephemeral worker container, and writes results to a local workspace.\n\nFor source builds, authenticated scans, provider-specific setup, and platform notes, see [Documentation](#documentation).\n\n> [!TIP]\n> **Prefer to use a subscription instead of API credits?**\n>\n> - **OpenAI Codex:** The latest version of Shannon supports ChatGPT Plus and Pro subscriptions. Follow the [OpenAI Codex subscription setup guide](docs/ai-providers.md#openai-codex-chatgpt-pluspro-subscription) to get started.\n> - **xAI (Grok):** The latest version of Shannon supports xAI subscriptions. Follow the [xAI subscription setup guide](docs/ai-providers.md#xai-grok-subscription) to get started.\n> - **Claude Code:** The latest version of Shannon does not support Claude Code subscriptions. Follow the [Claude Code subscription setup guide](docs/ai-providers.md#claude-code-subscription) to use version `1.9.0`, which is the final release built on the Claude Agent SDK.\n\n\n\n## Key Capabilities\n\n- **No exploit, no report**: Reports only vulnerabilities confirmed with a reproducible proof of concept, reducing speculative scanner noise.\n- **Advanced code analysis**: Maps architecture, trust boundaries, interfaces, data flows, and critical assets before sending credible attack paths to live pentesting agents.\n- **Autonomous execution**: Runs reconnaissance, analysis, exploitation, and reporting from a single command.\n- **Live terminal experience**: Simplifies scan setup and shows agent progress and results without exposing orchestration logs.\n- **Authenticated testing**: Supports credentials, login flows, TOTP, email authentication, focus areas, and rules of engagement through configuration.\n- **OWASP-focused coverage**: Tests for exploitable injection, XSS, SSRF, broken authentication, and broken authorization.\n- **Resumable workspaces**: Resumes interrupted scans without repeating completed work.\n- **Native CI/CD integrations**: Runs through the official GitHub Action or GitLab CI/CD component, preserves artifacts, publishes findings, and gates releases on proven vulnerabilities.\n- **Multi-format reports**: Produces evidence-rich PDF and Markdown reports plus JSON and SARIF 2.1.0. SARIF is enabled by default for exploit-mode scans.\n- **Provider agnostic and BYOK**: Supports Anthropic, OpenAI, xAI, AWS Bedrock, compatible APIs and LLM gateways, and local models served through Ollama, vLLM, or LM Studio.\n- **Private by design**: Runs in your infrastructure, stores results locally, and sends model requests directly to your chosen endpoint. A local endpoint keeps data inside your environment.\n\n\n\n## CI/CD Integrations\n\nShannon can run continuously against deployed staging and development environments through official integrations for [GitHub Actions](https://github.com/KeygraphHQ/shannon-action) and [GitLab CI/CD](https://gitlab.com/KeygraphHQ/shannon-ci).\n\nBoth integrations:\n\n- analyze the checked-out source repository while attacking a running target;\n- preserve PDF, Markdown, and SARIF reports as pipeline artifacts;\n- preserve scan and agent logs for debugging, including incomplete runs;\n- support pull-request, release, and scheduled pentests;\n- distinguish an incomplete assessment from a completed scan with no findings; and\n- optionally fail the pipeline when Shannon exploits a vulnerability at or above a configured severity threshold.\n\nA code-analysis hypothesis does not fail the pipeline. Severity gates count only findings with `status: exploited`.\n\n### GitHub Actions\n\n```yaml\nname: Shannon Pentest\n\non:\n  workflow_dispatch:\n\npermissions:\n  security-events: write\n\njobs:\n  pentest:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Run Shannon\n        uses: KeygraphHQ/shannon-action@v1\n        with:\n          url: https://staging.example.com\n          api-key: ${{ secrets.SHANNON_AI_API_KEY }}\n          fail-on-severity: high\n          upload-sarif: true\n```\n\nThe Action defaults `repo` to the checked-out GitHub workspace. It uploads one artifact containing the security assessment reports and SARIF, plus a separate run artifact containing scan and agent logs. Enabling `upload-sarif` publishes supported findings to GitHub code scanning.\n\nRequirements:\n\n- a private repository;\n- a runner with Docker and Docker Compose v2;\n- access to the running staging or development target; and\n- a model-provider credential stored as a GitHub Actions secret.\n\nSee the [Shannon GitHub Action documentation](https://github.com/KeygraphHQ/shannon-action) and [GitHub Marketplace listing](https://github.com/marketplace/actions/shannon-ai-pentester).\n\n## Editions\n\n| Edition | What it is | Price |\n| --- | --- | --- |\n| **Shannon** (open source) | This repository. A complete autonomous pentester you run yourself, locally or in CI/CD, against an application whose source code you have. Well suited to individual developers and small teams. | Open source under AGPL-3.0. You pay only your own model costs. |\n| **Community Program** | Keygraph Pro at no cost, for U.S.-based 501(c)(3) nonprofits and for seed or pre-Series-A startups with 20 or fewer active developers. | $0 in cloud service fees while you qualify. |\n| **Pro** | Keygraph's cloud-hosted edition, managed by Keygraph, with every module included. | $50 per active developer per month. |\n| **Enterprise** | Everything in Pro, self-hosted in your own environment or fully air-gapped. | Custom. |\n\nSee [keygraph.io/pricing](https://keygraph.io/pricing) for current prices and the full feature table.\n\n**Pro**, **Enterprise**, and the **Community Program** are powered by an enhanced, enterprise-grade version of the Shannon engine, and all three add:\n\n- **Black-box pentesting**: tests the running application from the outside, with no source code needed.\n- **Dependency (SCA) and secret checks**: SCA with reachability, and secrets scanning that includes repository history.\n- **Findings management**: one record per vulnerability per repository across scans and scanners, with status history, owners, SLAs, and dashboards.\n- **Fix pull requests and retests**: reviewable fix pull requests, with each fix verified by re-analysis and exploit replay, without a full rescan.\n- **Jira and access control**: two-way Jira sync, SSO, RBAC, and audit logs.\n\nNo source code? Use the [Blackbox Pentester](https://keygraph.io/agentic-blackbox-pentester).\n\n[Compare Shannon, Pro, and Enterprise in detail →](docs/keygraph-platform.md)\n\n## Architecture\n\nShannon combines multi-stage security code analysis with live reconnaissance and exploitation:\n\n```mermaid\nflowchart TD\n    S[\"Source code\"] --> EXISTING[\"Recon + vulnerability analysis\"]\n    S --> SAST[\"Agentic security code analysis\"]\n\n    EXISTING -- \"Pentest candidates\" --> REC[\"Finding reconciliation<br/>(merge + deduplicate)\"]\n    SAST -- \"SAST candidates\" --> REC\n\n    REC -- \"Reconciled exploitation queue\" --> EXP[\"Exploitation agents\"]\n    APP[\"Running application\"] --> EXP\n\n    EXP -- \"Exploit demonstrated\" --> REPORT[\"Reporting<br/>PDF · Markdown · SARIF\"]\n    EXP -- \"No exploit demonstrated\" --> DROP[\"Discard\"]\n\n    REPORT --> CICD[\"CI/CD gate\"]\n```\n\n\n\nStage by stage:\n\n1. **Recon and vulnerability analysis** explores the running application, ties runtime behavior back to the source, and runs specialized agents across Injection, XSS, SSRF, Authentication, and Authorization.\n2. **Agentic security code analysis** maps the application's architecture, trust boundaries, exposed interfaces, dependencies, data flows, and high-risk assets, then opens targeted investigations against them.\n3. **Finding reconciliation** merges both streams of candidates, deduplicates the overlap, and groups what remains into an exploitation queue.\n4. **Exploitation agents** attempt real proof-of-concept attacks against the running application.\n5. **Validation** throws out every candidate Shannon can't demonstrate.\n6. **Reporting** produces PDF and Markdown reports with the evidence attached, plus structured JSON and SARIF for downstream systems.\n\nOnly live-validated vulnerabilities become Shannon pentest findings or count toward CI/CD severity gates.\n\nEach scan runs in an ephemeral Docker container with an isolated workspace and per-invocation orchestration.\n\n## Documentation\n\nUse these guides for operational detail:\n\n\n| Guide                                                     | Use it for                                                                                                                                                                |\n| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [Source build and CLI commands](docs/development.md)      | Cloning, building, common commands, output paths, and local development.                                                                                                  |\n| [Configuration](docs/configuration.md)                    | Authenticated testing, login flows, rules of engagement, and report filters.                                                                                              |\n| [AI providers](docs/ai-providers.md)                      | Selecting the model, the supported providers (Anthropic, OpenAI, xAI, AWS Bedrock, and any other Pi-supported provider), and custom LLM gateways.                             |\n| [Platforms and networking](docs/platforms.md)             | Windows/WSL2, Linux, macOS, Docker networking, local apps, and custom hostnames.                                                                                          |\n| [Workspaces and resuming](docs/workspaces.md)             | Naming workspaces, resuming interrupted scans, and workspace storage.                                                                                                     |\n| [Safety and limitations](docs/safety.md)                  | Authorized-use requirements, non-production guidance, mutative effects, cost, and model caveats.                                                                          |\n| [Coverage and roadmap](docs/coverage-roadmap.md)          | Current vulnerability coverage and planned work.                                                                                                                          |\n| [Pro and Enterprise](docs/keygraph-platform.md)           | Shannon compared with Pro, Enterprise, and the Community Program: black-box pentesting, SCA and secrets, findings management, fix pull requests, retests, Jira, and deployment. |\n\n\n\n\n## Safety, Scope, and Limitations\n\nShannon is not a passive scanner. Its exploitation agents can create users, submit forms, mutate application state, trigger outbound requests, and otherwise affect the target system. Use sandboxed, staging, or local development environments with disposable data.\n\nYou are responsible for using Shannon legally and ethically. Do not point Shannon at systems, repositories, or applications you do not own or do not have explicit authorization to test.\n\nImportant limitations:\n\n- Shannon is tuned for fast, code-informed pentesting in everyday development and CI/CD. Exhaustive agentic SAST, broader scanner coverage, centralized governance, and full-lifecycle vulnerability management are delivered through Pro, Enterprise, and the Community Program.\n- Findings still require human review. LLM-generated reports can contain weakly supported or incorrect details.\n- Anthropic, OpenAI, xAI, and AWS Bedrock are built-in providers, and any other provider in the harness catalogue works too — each reachable through a custom base URL that points it at a proxy or LLM gateway. Model capability varies, and a model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker results.\n- A full run can take roughly 1 to 1.5 hours and may incur LLM API costs depending on model pricing and application complexity.\n- Do not scan untrusted or adversarial codebases. AI-powered tools that read source code can be exposed to prompt injection.\n\nRead the full [Safety and limitations](docs/safety.md) guide before running Shannon in a new environment.\n\n## License\n\nShannon is licensed under the [GNU Affero General Public License v3.0](LICENSE).\n\nCommercial and enterprise licensing is available for organizations that need different license terms, commercial support, private redistribution, managed-service use, or broader deployment options, including Keygraph Pro and Enterprise.\n\nFor commercial licensing, contact [shannon@keygraph.io](mailto:shannon@keygraph.io).\n\n## Acknowledgements\n\nThanks to [Pi](https://github.com/earendil-works/pi),\n[Playwright CLI](https://github.com/microsoft/playwright-cli),\nand [Mantis](https://github.com/google/mantis).\n\nSee [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md) for licensing and attribution details.\n\n## About Keygraph\n\n**Keygraph** is the company behind Shannon. It also builds **Keygraph Pro** and **Keygraph Enterprise**, the commercial editions that close the full AppSec lifecycle and run an enhanced build of Shannon as their pentesting engine.\n\n## Community and Support\n\n**Community office hours** are available for hands-on help with bugs, deployments, and configuration questions.\n\n- US/EU: Thursday, 10:00 AM PT\n- Asia: Thursday, 2:00 PM IST\n- [Book a slot](https://cal.com/george-flores-keygraph/shannon-community-office-hours)\n\n[Join Discord](https://discord.gg/cmctpMBXwE) to ask questions, share feedback, and connect with other Shannon users.\n\nAt this time, Keygraph is not accepting external code contributions. Issues are welcome for bug reports and feature requests:\n\n- [Report bugs](https://github.com/KeygraphHQ/shannon/issues)\n- [Suggest features](https://github.com/KeygraphHQ/shannon/discussions)\n\nStay connected:\n\n- [Keygraph website](https://keygraph.io)\n- [Twitter/X: @KeygraphHQ](https://twitter.com/KeygraphHQ)\n- [LinkedIn: Keygraph](https://linkedin.com/company/keygraph)\n\n\n\n## Common Questions\n\n\n\n### Can I self-host Shannon?\n\nYes. Shannon runs inside your infrastructure in an ephemeral worker container. It mounts the repository read-only and writes results to a local workspace.\n\nKeygraph never receives your source code and never proxies your model traffic. Your model requests go straight to the provider or endpoint you configure, and they carry source and application context with them. Point Shannon at a locally hosted endpoint and that traffic stays inside your environment too.\n\n### Does Shannon support bring your own key (BYOK)?\n\nYes, always. You provide the LLM credentials Shannon uses to run a pentest, in every deployment, open source and commercial. Keygraph never proxies your model traffic.\n\n### Does Shannon output SARIF?\n\nYes. Shannon emits SARIF 2.1.0, the OASIS standard format for static analysis results, alongside structured JSON. Any SARIF consumer reads it: code scanning services, vulnerability management platforms, security dashboards, and CI/CD pipelines. It is written by default on exploit-mode scans; set `report.sarif` to `\"false\"` in your configuration file to opt out.\n\n### Which AI providers does Shannon support?\n\nAnthropic, OpenAI, xAI, and AWS Bedrock are built in and configured directly by provider ID. Beyond those, Shannon runs on any provider in the Pi harness catalogue, named the same `<provider>:<model-id>` way. Any provider can be pointed at a proxy or LLM gateway through a custom base URL, which overrides only the endpoint and keeps that provider's API dialect. A model the catalogue does not carry, such as one a router or gateway serves under its own ID, or a self-hosted model, is described in a [custom model configuration](docs/ai-providers.md#custom-model-configuration) file and passed with `--models-config`. Shannon uses a single unified model setting throughout a pentest.\n\n### Can I run Shannon on a local or self-hosted model?\n\nShannon works with local models served through Ollama, vLLM, or LM Studio, which expose an OpenAI-compatible endpoint, as well as routers such as OpenRouter and LLM gateways such as LiteLLM. A model the harness catalogue does not carry, which most self-hosted models are, is described in a [custom model configuration](docs/ai-providers.md#custom-model-configuration) file passed with `--models-config`; routers and gateways can also be reached with a custom base URL. Capability varies, and a model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker pentests than a frontier model, so take this path only if you know how your chosen model behaves. See [Local and self-hosted models](docs/ai-providers.md#local-and-self-hosted-models).\n\n### Does Shannon actually exploit vulnerabilities, or just scan?\n\nShannon executes real exploits. It reports a finding only when it has produced a working proof-of-concept, and discards hypotheses it cannot prove. It is a pentester, not a passive scanner.\n\n**Built by [Keygraph](https://keygraph.io)**\n\n---\n\n# File: docs/development.md\n\n# Source Build and CLI Commands\n\nThis guide covers the source-build workflow, common CLI commands, repository paths, and output locations. For the fastest first run, use the `npx` workflow in the main README.\n\n## Prerequisites\n\n- Docker\n- Node.js 18+\n- pnpm\n- AI provider credentials\n\n## Clone and Build\n\nUse the source-build workflow if you want to run Shannon from a local clone, modify the open-source CLI, or keep the worker image built locally.\n\n```bash\n# 1. Clone Shannon.\ngit clone https://github.com/KeygraphHQ/shannon.git\ncd shannon\n\n# 2. Configure credentials.\ncp .env.example .env\n\n# 3. Install dependencies and build.\npnpm install\npnpm build\n\n# 4. Run a pentest.\n./shannon start -u https://your-app.com -r /path/to/your-repo\n```\n\nAt minimum, your `.env` file should include one supported AI provider credential, such as:\n\n```bash\nANTHROPIC_API_KEY=your-api-key\n```\n\nEnvironment variables can also be exported directly:\n\n```bash\nexport ANTHROPIC_API_KEY=\"your-api-key\"\n```\n\n## Prepare Your Repository\n\nShannon can scan any repository on your machine. Pass an absolute or relative path with `-r`.\n\n```bash\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo\n./shannon start -u https://example.com -r ./relative/path\n```\n\nThe target repository is mounted read-only inside the worker container.\n\n## Common Commands\n\nMonitor progress:\n\n```bash\nnpx @keygraph/shannon logs [<workspace>]     # defaults to the single running scan, else the most recent\nnpx @keygraph/shannon status [<workspace>]   # same default target; add --json for a machine-readable snapshot\nnpx @keygraph/shannon scans\nnpx @keygraph/shannon version\n```\n\nWith no workspace, `logs` and `status` follow the single running scan; when several are running, name one.\n\nSource-build equivalents:\n\n```bash\n./shannon logs [<workspace>]                 # the combined live log (unchanged default)\n./shannon logs [<workspace>] --agent <name>  # tail one agent's own log\n./shannon logs [<workspace>] --list-agents   # list the agents with their own log\n./shannon status [<workspace>]\n./shannon scans\n./shannon version\n```\n\nEvery scan writes one combined `.shannon/workflow.log` and a per-agent projection of it under\n`.shannon/agents/`: one file per pipeline agent (`recon.log`, `xss-vuln.log`, …) and one per Capella\nstage (`agentic-sast-research.log`, …). Delegated subagents fold into their parent's file, and a\nCapella stage's concurrent sessions share its file with an inline session label. The combined log\nstays canonical; the per-agent files are best-effort projections.\n\nOpen the Temporal Web UI for detailed monitoring:\n\n```bash\nopen http://localhost:8233\n```\n\nStop Shannon:\n\n```bash\nnpx @keygraph/shannon stop [<workspace>] # stop one scan (defaults to the single running scan; confirms first; add --yes/-y to skip)\nnpx @keygraph/shannon stop --all         # stop all scans (Temporal stays up)\nnpx @keygraph/shannon reset              # stop everything and wipe all Temporal data (type 'confirm' to proceed; cannot be skipped)\n```\n\nSource-build equivalents:\n\n```bash\n./shannon stop [<workspace>]             # stop one scan (defaults to the single running scan; confirms first; add --yes/-y to skip)\n./shannon stop --all                     # stop all scans (Temporal stays up)\n./shannon reset                          # stop everything and wipe all Temporal data (type 'confirm' to proceed; cannot be skipped)\n```\n\nUsage examples:\n\n```bash\n# Basic pentest.\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo\n\n# With a configuration file.\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -c /path/to/my-config.yaml\n\n# Custom output directory.\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -o ./my-reports\n\n# Named workspace.\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -w q1-audit\n\n# Stream the log until the scan finishes, then exit on its outcome (useful in CI).\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo --follow\n\n# Validate the configured login only, then stop (no pentest or report).\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -c /path/to/my-config.yaml --validate-auth\n\n# List running and completed scans.\nnpx @keygraph/shannon scans\n```\n\nSource-build examples:\n\n```bash\n./shannon start -u https://example.com -r /path/to/repo\n./shannon start -u https://example.com -r /path/to/repo -c /path/to/my-config.yaml\n./shannon start -u https://example.com -r /path/to/repo -o ./my-reports\n./shannon start -u https://example.com -r /path/to/repo -w q1-audit\n./shannon start -u https://example.com -r /path/to/repo --follow\n./shannon start -u https://example.com -r /path/to/repo -c /path/to/my-config.yaml --validate-auth\n./shannon scans\n\n# Rebuild the worker image.\n./shannon build --no-cache\n```\n\n## Output and Results\n\nResults are saved to the workspaces directory:\n\n- `./workspaces/` in source-build mode\n- `~/.shannon/workspaces/` in `npx` mode\n\nUse `-o <path>` to copy deliverables to a custom output directory after a run completes.\n\nOutput structure — the run directory's top level holds the final report, in PDF and Markdown; everything else is nested under a hidden `.shannon/` directory:\n\n```text\nworkspaces/{hostname}_{sessionId}/\n|-- Security-Assessment-Report.pdf  # the final report (PDF)\n|-- Security-Assessment-Report.md   # the final report (Markdown)\n`-- .shannon/                       # internals\n    |-- deliverables/               # report source, per-phase analysis, queues\n    |-- agents/                     # per-agent log projections, one file per agent/Capella stage\n    |-- prompts/                    # rendered prompts\n    |-- scratchpad/                 # screenshots, scripts\n    |-- session.json                # resume state\n    `-- workflow.log\n```\n\n---\n\n# File: docs/configuration.md\n\n# Configuration\n\nShannon can run without a configuration file, but configuration enables authenticated testing, scope guidance, rules of engagement, and report filtering.\n\n## Credential Precedence\n\nSource-build mode resolves credentials from:\n\n1. Environment variables, such as `export ANTHROPIC_API_KEY=...`\n2. `./.env`\n\n`npx` mode resolves credentials from:\n\n1. Environment variables\n2. `~/.shannon/config.toml`, created by `npx @keygraph/shannon setup`\n\nEnvironment variables always win, so you can override saved config for a single session without editing files.\n\n## Create a Configuration File\n\nCopy and modify the example configuration:\n\n```bash\ncp configs/example-config.yaml ./my-app-config.yaml\n```\n\nRun with:\n\n```bash\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -c ./my-app-config.yaml\n```\n\nSource-build equivalent:\n\n```bash\n./shannon start -u https://example.com -r /path/to/repo -c ./my-app-config.yaml\n```\n\n## Basic Configuration Structure\n\n```yaml\n# Describe your target environment.\ndescription: \"Next.js e-commerce app on PostgreSQL. Local dev environment; .env files contain local-only credentials.\"\n\n# Every scan runs all five vulnerability classes.\n\n# Agentic static analysis. `enabled` is its only setting.\n# agentic_sast:\n#   enabled: \"true\"\n\n# Skip the exploitation phase.\n# exploit: \"false\"\n\n# Free-form rules of engagement.\n# rules_of_engagement: |\n#   - No password brute-force; cap login attempts at 5 per account.\n#   - Throttle to under 5 requests per second per endpoint; back off 60s on any 429.\n#   - Use placeholders like [order_id] in deliverables; no real data values.\n\nauthentication:\n  login_type: form\n  login_url: \"https://your-app.com/login\"\n  credentials:\n    username: \"test@example.com\"\n    password: \"yourpassword\"\n    totp_secret: \"LB2E2RX7XFHSTGCK\"\n\n    # Optional mailbox credentials for magic-link or email-OTP flows.\n    # email_login:\n    #   address: \"inbox@example.com\"\n    #   password: \"mailbox-password\"\n    #   totp_secret: \"JBSWY3DPEHPK3PXP\"\n\n  login_flow:\n    - \"Type $username into the email field\"\n    - \"Type $password into the password field\"\n    - \"Click the 'Sign In' button\"\n\n  success_condition:\n    type: url_contains\n    value: \"/dashboard\"\n\nrules:\n  avoid:\n    - description: \"AI should avoid testing logout functionality\"\n      type: url_path\n      value: \"/logout\"\n\n    # code_path values are repo-relative file paths or globs.\n    # - description: \"Out-of-scope vendored libraries\"\n    #   type: code_path\n    #   value: \"src/vendor/**\"\n\n  focus:\n    - description: \"AI should emphasize testing API endpoints\"\n      type: url_path\n      value: \"/api\"\n\n# Report options applied when assembling the final report.\n# report:\n#   min_severity: low\n#   min_confidence: low\n#   guidance: |\n#     Drop findings about missing security headers and rate-limit gaps.\n#   sarif: \"false\"\n```\n\n## Analysis Scope and Agentic SAST\n\nEvery scan runs all five analysis classes: Injection, Cross-Site Scripting, Authentication, Authorization, and\nServer-Side Request Forgery. The class set is fixed and has no configuration selector.\n\nAgentic static analysis is opt-in:\n\n```yaml\nagentic_sast:\n  enabled: \"true\"\n```\n\n`enabled` is the only setting. Omitting the block, or setting `enabled: \"false\"`, turns agentic static analysis off;\n`\"true\"` turns it on. Either way, all five vulnerability classes still run.\n\nAgentic static analysis reads the repository for vulnerabilities before the pentest and passes what it finds into the\nexploitation phase. It adds model time and cost. If it fails, the pentest continues without its findings and the scan\nfinishes as \"partial\".\n\n## Report Options\n\n| Key | Effect |\n| --- | --- |\n| `min_severity` | Drops findings rated below this severity. Applies in both exploitative and analysis-only runs. |\n| `min_confidence` | Drops findings rated below this confidence. Applies only when `exploit` is `\"false\"`. |\n| `guidance` | Free-text instruction to the report agent, such as which topics to exclude. |\n| `sarif` | SARIF 2.1.0 log alongside the Markdown report. On by default for exploit runs; set `\"false\"` to opt out. Ignored when `exploit` is `\"false\"`. |\n\nEvery finding carries a severity, but it does not mean the same thing in each mode: an exploitative run measures severity from what the exploit demonstrated, while an analysis-only run assesses it from the class of flaw and the impact it would have. An analysis-only finding carries a confidence rating alongside its severity, since nothing was proven. Setting `min_confidence` on an exploitative run is ignored, and Shannon logs a warning naming the threshold to use instead.\n\n### SARIF Output\n\nOn exploit-mode runs Shannon writes `report.sarif` next to `Security-Assessment-Report.pdf` at the workspace root by default, for upload to GitHub code scanning or any other SARIF consumer. No configuration is needed; set `sarif: \"false\"` to opt out.\n\n```yaml\nreport:\n  sarif: \"false\"\n```\n\nEach finding becomes one SARIF result, filed under a rule per vulnerability class (`shannon/injection`, `shannon/xss`, `shannon/auth`, `shannon/authz`, `shannon/ssrf`, and `shannon/miscellaneous` for findings outside those classes) and tagged with its OWASP Top Ten 2025 category. Results are anchored to the code location the analysis phase recorded, falling back to the HTTP entry point when the finding names no file. Severity maps onto SARIF's three levels: `critical` and `high` become `error`, `medium` becomes `warning`, everything else becomes `note`.\n\nIf the SARIF log cannot be written, the JSON and Markdown reports are still produced and the scan finishes as \"partial\".\n\nThe log is written only for exploitative runs. `sarif` is ignored when `exploit` is `\"false\"`.\n\nSupported rule types include `url_path`, `subdomain`, `domain`, `method`, `header`, `parameter`, and `code_path`.\n\n## Writing Login Flow\n\nLog in once in a fresh private browser window. Write the steps in the same order you perform them:\n\n- When typing into a field, reference the field by its exact label or placeholder.\n- When clicking a button, reference the exact button text.\n\nSupported placeholders:\n\n- `$username`\n- `$password`\n- `$totp`\n- `$email_address`\n- `$email_password`\n- `$email_totp`\n\nAt runtime, Shannon replaces these placeholders with the credentials passed in the config.\n\n```yaml\nlogin_flow:\n  - \"Type $username in <exact email field label or placeholder>\"\n  - \"Click <exact button text>\"\n  - \"Type $password in <exact password field label or placeholder>\"\n  - \"Click <exact button text>\"\n  - \"If prompted for 2FA, type $totp in <exact code field label or placeholder>\"\n  - \"Click <exact button text>\"\n```\n\n### Validating Authentication Only\n\nTo confirm your login flow works before committing to a full scan, add `--validate-auth` to `start`:\n\n```bash\nnpx @keygraph/shannon start -u https://your-app.com -r /path/to/repo -c config.yaml --validate-auth\n```\n\nThe run performs preflight and the single real login, then stops. No pentest, reconciliation, or report\nis produced. It requires an `authentication` block in the config.\n\n---\n\n# File: docs/ai-providers.md\n\n# AI Providers\n\nOne model runs the entire scan — pre-recon, recon, vulnerability analysis, exploitation, and reporting. A single setting names both the provider and the model:\n\n```bash\nexport SHANNON_AI_MODEL=<provider>:<model-id>\n```\n\nThe provider half decides where the request goes, which credential is used, and which API dialect is spoken. You never configure those separately.\n\n## Supported providers\n\n| Provider | Value | Credential |\n| --- | --- | --- |\n| Anthropic | `anthropic` | `SHANNON_AI_API_KEY` (or `CLAUDE_CODE_OAUTH_TOKEN`) |\n| OpenAI | `openai` | `SHANNON_AI_API_KEY` |\n| xAI | `xai` | `SHANNON_AI_API_KEY` |\n| AWS Bedrock | `amazon-bedrock` | `AWS_REGION` and `AWS_BEARER_TOKEN_BEDROCK` |\n\n`SHANNON_AI_API_KEY` holds the key for whichever provider `SHANNON_AI_MODEL` names. Bedrock is the exception — it authenticates through its `AWS_` variables only. If `SHANNON_AI_MODEL` is unset, Shannon uses `anthropic:claude-sonnet-4-6`.\n\nAnthropic, OpenAI, and xAI also accept their native variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `XAI_API_KEY`); if one of those is set, it is used instead of `SHANNON_AI_API_KEY`.\n\nShannon forwards only the selected provider's credential into the scan container. Keys for other providers stay on your machine.\n\n### Any other provider\n\nShannon accepts any provider and model present in the Pi harness catalogue. Browse them at [pi.dev/models](https://pi.dev/models).\n\n```bash\nexport SHANNON_AI_API_KEY=your-api-key                     # the provider's key — or the gateway's when a base URL is set\nexport SHANNON_AI_MODEL=openrouter:moonshotai/kimi-k3      # <provider>:<model-id>\nexport SHANNON_AI_BASE_URL=https://llm-gateway.example.com # optional: route through a proxy or LLM gateway\n```\n\nThis path covers providers whose credential is a single API key. Providers that need more than that are not currently supported.\n\nA model the catalogue does not carry is reachable by describing it yourself. See [Custom model configuration](#custom-model-configuration).\n\n`npx @keygraph/shannon setup` exposes this as the **Other provider** option.\n\n> [!IMPORTANT]\n> Models are validated against the harness catalogue, but capability varies. A model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker pentests. Evaluate the model you choose against your own targets before depending on its results.\n\n## Cyber safeguards (do this before your first scan)\n\nAnthropic and OpenAI both apply real-time safeguards to cyber-security workloads. Shannon is exactly such a workload. If a safeguard engages mid-run, the model can refuse, and the scan fails partway through rather than at the start.\n\nReview each vendor's guidance and complete the verification or enrollment they ask of legitimate security testers before running Shannon:\n\n- Anthropic - [Real-time cyber safeguards on Claude Opus and Sonnet](https://support.claude.com/en/articles/14604842-real-time-cyber-safeguards-on-claude-opus-and-sonnet)\n- OpenAI - [Cyber](https://chatgpt.com/cyber)\n\nThis applies to the Anthropic and OpenAI providers, including when either is reached through an LLM gateway. Bedrock serves Claude models and is subject to Anthropic's safeguards as well.\n\nTo confirm your model is ready before committing to a full scan, add `--validate-model` to `start`:\n\n```bash\nnpx @keygraph/shannon start -u https://your-app.com -r /path/to/repo --validate-model\n```\n\nThe run performs the preflight model checks only — credential and registry resolution for any provider, plus a single cyber-access verification against Anthropic and OpenAI that trips the cyber safeguard if your account is not approved — then stops. No pentest or report is produced, and it needs no config. A decline fails the run with the vendor's enrollment link.\n\n## Suggested models\n\nThese are the models `npx @keygraph/shannon setup` offers, best-first. They are suggestions: the wizard also takes a typed model ID, and `SHANNON_AI_MODEL` accepts any model in the provider's catalogue.\n\n| Provider | Suggested model IDs |\n| --- | --- |\n| `anthropic` | `claude-sonnet-5`, `claude-opus-5`, `claude-sonnet-4-6`, `claude-opus-4-8`, `claude-opus-4-7`, `claude-haiku-4-5-20251001` |\n| `openai` | `gpt-6-sol`, `gpt-5.6-sol`, `gpt-5.5`, `gpt-5.4` |\n| `xai` | `grok-4.7` |\n| `amazon-bedrock` | `us.anthropic.claude-sonnet-4-6`, `us.anthropic.claude-opus-4-8`, `us.anthropic.claude-opus-4-7` |\n\nBedrock IDs are region-prefixed and must be enabled in your account, so the ID that works for you may differ from the one listed here.\n\n## Switching provider\n\nThe pattern is learned once: export the provider's key, name the model. Two lines change, nothing else.\n\nAnthropic (default):\n\n```bash\nexport SHANNON_AI_API_KEY=sk-ant-...\nexport SHANNON_AI_MODEL=anthropic:claude-sonnet-4-6\n```\n\nOpenAI:\n\n```bash\nexport SHANNON_AI_API_KEY=sk-...\nexport SHANNON_AI_MODEL=openai:gpt-6-sol\n```\n\nxAI:\n\n```bash\nexport SHANNON_AI_API_KEY=xai-...\nexport SHANNON_AI_MODEL=xai:grok-4.7\n```\n\nSource-build mode reads the same variables from a `.env` file.\n\n## AWS Bedrock\n\nRun `npx @keygraph/shannon setup` and select **AWS Bedrock**, or export directly:\n\n```bash\nexport AWS_REGION=us-east-1\nexport AWS_BEARER_TOKEN_BEDROCK=your-bearer-token\nexport SHANNON_AI_MODEL=amazon-bedrock:us.anthropic.claude-opus-4-8\n```\n\nBedrock uses bearer-token authentication only. IAM access keys, session tokens, assumed roles, and instance profiles are not supported. The model must be enabled in your region.\n\n## Custom base URL\n\n`SHANNON_AI_BASE_URL` routes model traffic through a proxy or LLM gateway instead of the provider's default endpoint — an LLM gateway such as LiteLLM, a regional endpoint, or any other host you choose. It is a plain endpoint override: it changes only *where* requests go. The provider half of `SHANNON_AI_MODEL` still decides which credential is sent and which API dialect is spoken, and that is unchanged by the base URL.\n\nThis works for **any** provider, curated or not, subject to two rules. A provider's dialect is fixed, so the endpoint you point at must speak that provider's dialect:\n\n| Provider prefix | Dialect the endpoint must speak |\n| --- | --- |\n| `anthropic:` | Anthropic Messages |\n| `openai:` | OpenAI Responses |\n\nAnd the model ID must still resolve in the harness catalogue. A base URL changes only the address; it grants no exemption from that check. A gateway serving a model under its own name needs that name described in a [custom model configuration](#custom-model-configuration) file.\n\nAnthropic Messages LLM gateway:\n\n```bash\nexport SHANNON_AI_API_KEY=sk-ant-...\nexport SHANNON_AI_MODEL=anthropic:claude-sonnet-4-6\nexport SHANNON_AI_BASE_URL=https://llm-gateway.example.com\n```\n\nOpenAI Responses LLM gateway:\n\n```bash\nexport SHANNON_AI_API_KEY=sk-...\nexport SHANNON_AI_MODEL=openai:gpt-6-sol\nexport SHANNON_AI_BASE_URL=https://llm-gateway.example.com/v1\n```\n\n`npx @keygraph/shannon setup` configures a base URL two ways: **Custom Base URL** covers the common Anthropic Messages and OpenAI Responses LLM gateways, and **Other provider** takes any provider ID plus an optional base URL of its own.\n\n## Custom model configuration\n\nA custom model configuration is a Pi `models.json` file that describes a model the harness catalogue does not carry: one a router or gateway serves under its own ID, or a local server (see [Local and self-hosted models](#local-and-self-hosted-models)). You pass it with `--models-config`, and Shannon merges its definitions over the catalogue so `SHANNON_AI_MODEL` can then name the model like any other:\n\n```bash\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo --models-config ./models.json\n```\n\n```bash\n./shannon start -u https://example.com -r ./my-repo --models-config ./models.json\n```\n\n[pi.dev/models](https://pi.dev/models) supplies the file contents. Find the model under the provider you want, since the same model has a different ID per provider, then open its page and expand **Show configuration** for a ready-to-paste snippet:\n\n```json\n{\n  \"providers\": {\n    \"openrouter\": {\n      \"apiKey\": \"YOUR_API_KEY\",\n      \"models\": [\n        {\n          \"id\": \"z-ai/glm-5.3\",\n          \"name\": \"Z.ai: GLM 5.3\",\n          \"reasoning\": true,\n          \"input\": [\n            \"text\"\n          ],\n          \"thinkingLevelMap\": {\n            \"off\": null,\n            \"minimal\": null,\n            \"low\": \"low\",\n            \"medium\": null,\n            \"high\": \"high\",\n            \"xhigh\": null,\n            \"max\": \"max\"\n          },\n          \"contextWindow\": 1048576,\n          \"maxTokens\": 943718,\n          \"cost\": {\n            \"input\": 1.4,\n            \"output\": 4.4,\n            \"cacheRead\": 0.26,\n            \"cacheWrite\": 0\n          },\n          \"compat\": {\n            \"supportsDeveloperRole\": false,\n            \"thinkingFormat\": \"openrouter\"\n          }\n        }\n      ],\n      \"api\": \"openai-completions\",\n      \"baseUrl\": \"https://openrouter.ai/api/v1\"\n    }\n  }\n}\n```\n\nThen name the model the usual way:\n\n```bash\nexport SHANNON_AI_API_KEY=your-api-key\nexport SHANNON_AI_MODEL=openrouter:z-ai/glm-5.3\n```\n\nLeave `YOUR_API_KEY` exactly as it is. Shannon sends the credential from your environment, and that takes precedence over anything the file declares, so the file describes the model and never has to hold a secret.\n\nPi's [models documentation](https://pi.dev/docs/latest/models) describes the full format, including provider routing preferences and compatibility flags.\n\n## Local and self-hosted models\n\nOllama, LM Studio, vLLM, and any other OpenAI-compatible server are reached through the same mechanism. Describe the server as a provider in a model config file, then name its model with `SHANNON_AI_MODEL`.\n\n> [!IMPORTANT]\n> Use `host.docker.internal`, not `localhost`. The scan runs inside a container, so `localhost` points at the container itself rather than at your machine.\n\nA `models.json` for Ollama:\n\n```json\n{\n  \"providers\": {\n    \"ollama\": {\n      \"baseUrl\": \"http://host.docker.internal:11434/v1\",\n      \"api\": \"openai-completions\",\n      \"apiKey\": \"ollama\",\n      \"models\": [\n        { \""},"items":[{"name":"llms-full.txt","path":"llms-full.txt","rawUrl":"https://raw.githubusercontent.com/KeygraphHQ/shannon/HEAD/llms-full.txt","title":"LLM Full Documentation Context","category":"root-instruction","format":"text","content":"# Shannon Full LLM Context\n\n> Combined README and documentation for AI agents and LLMs, generated verbatim from the local files referenced in llms.txt. For the concise index, see [llms.txt](llms.txt).\n\n> Retired names: Shannon Lite is now just Shannon. Shannon Pro is now Keygraph Pro.\n\n---\n\n# File: README.md\n\n> [!NOTE]\n> **[Cyber verification for Anthropic and OpenAI models](https://github.com/KeygraphHQ/shannon/discussions/483):** complete your provider's cyber verification program to prevent model failures and refusals during cyber workloads.\n\n<div align=\"center\">\n\n<picture>\n<source media=\"(prefers-color-scheme: dark)\" srcset=\"./assets/github-banner-dark.png\">\n<source media=\"(prefers-color-scheme: light)\" srcset=\"./assets/github-banner-light.png\">\n<img src=\"./assets/github-banner-light.png\" alt=\"Shannon, AI Pentester for Web Apps and APIs, by Keygraph\" width=\"100%\">\n</picture>\n\n<a href=\"https://trendshift.io/repositories/15604\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/15604\" alt=\"KeygraphHQ%2Fshannon | Trendshift\" style=\"width: 250px; height: 55px;\" width=\"250\" height=\"55\"/></a>\n\n### Shannon is an autonomous, AI pentester for web applications and APIs.\n\nIt analyzes your source code, identifies attack paths, and executes real exploits to prove vulnerabilities before they reach production. **No exploit, no report.**\n\n**This repository is Shannon: the full open-source agent, run locally from your command line.**\n\n<p><strong>Launch Shannon</strong></p>\n\n```bash\nnpx @keygraph/shannon@latest\n```\n\n<sub>The interactive launcher will guide you through setup and your first pentest.</sub>\n\n---\n\n<a href=\"https://discord.gg/9ZqQPuhJB7\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"./assets/discord_button_dark.png\"><source media=\"(prefers-color-scheme: light)\" srcset=\"./assets/discord_button_light.png\"><img src=\"./assets/discord_button_light.png\" height=\"40\" alt=\"Join Discord\"></picture></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"https://keygraph.io/\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"./assets/keygraph_button_dark.png\"><source media=\"(prefers-color-scheme: light)\" srcset=\"./assets/keygraph_button_light.png\"><img src=\"./assets/keygraph_button_light.png\" height=\"40\" alt=\"Visit Keygraph.io\"></picture></a>\n\n---\n\n</div>\n\n> [!TIP]\n> **AI agents and LLMs:** start with [llms.txt](llms.txt) for a concise map of this repository, or use [llms-full.txt](llms-full.txt) for the README and docs combined into one file.\n\n\n\n## Table of Contents\n\n- [Table of Contents](#table-of-contents)\n- [What is Shannon?](#what-is-shannon)\n  - [Why Shannon Exists](#why-shannon-exists)\n  - [Why \"Shannon\"?](#why-shannon)\n  - [Not a replacement for human pentesters](#not-a-replacement-for-human-pentesters)\n- [Shannon in Action](#shannon-in-action)\n- [Quick Start](#quick-start)\n  - [Prerequisites](#prerequisites)\n  - [Run Shannon](#run-shannon)\n- [Key Capabilities](#key-capabilities)\n- [CI/CD Integrations](#cicd-integrations)\n  - [GitHub Actions](#github-actions)\n- [Editions](#editions)\n- [Architecture](#architecture)\n- [Documentation](#documentation)\n- [Safety, Scope, and Limitations](#safety-scope-and-limitations)\n- [License](#license)\n- [Acknowledgements](#acknowledgements)\n- [About Keygraph](#about-keygraph)\n- [Community and Support](#community-and-support)\n- [Common Questions](#common-questions)\n  - [Can I self-host Shannon?](#can-i-self-host-shannon)\n  - [Does Shannon support bring your own key (BYOK)?](#does-shannon-support-bring-your-own-key-byok)\n  - [Does Shannon output SARIF?](#does-shannon-output-sarif)\n  - [Which AI providers does Shannon support?](#which-ai-providers-does-shannon-support)\n  - [Can I run Shannon on a local or self-hosted model?](#can-i-run-shannon-on-a-local-or-self-hosted-model)\n  - [Does Shannon actually exploit vulnerabilities, or just scan?](#does-shannon-actually-exploit-vulnerabilities-or-just-scan)\n\n\n\n## What is Shannon?\n\nShannon is an autonomous AI pentester developed by [Keygraph](https://keygraph.io). It performs security testing of web applications and their underlying APIs by combining source-code analysis with live exploitation.\n\nShannon analyzes your web application's source code to identify potential attack vectors, then uses browser automation and command-line tools to execute real exploits against the running application and its APIs. Only vulnerabilities with a working proof-of-concept are included in the final report.\n\nShannon is the agent. This repository is Shannon, the open-source pentester you run yourself. The same Shannon also powers Keygraph's commercial editions, [Pro and Enterprise](https://keygraph.io/pricing). See [Editions](#editions) for how Shannon compares with Pro, Enterprise, and the Community Program.\n\n<a id=\"why-shannon-exists\"></a>\n<details>\n<summary><strong>Why Shannon Exists</strong></summary>\n\nThanks to tools like Claude Code and Cursor, your team ships code non-stop. But your penetration test? That happens once a year. This creates a massive security gap. For the other 364 days, you could be unknowingly shipping vulnerabilities to production.\n\nShannon closes that gap by providing on-demand, automated penetration testing that can run against every build or release.\n\n</details>\n\n<a id=\"why-shannon\"></a>\n<details>\n<summary><strong>Why \"Shannon\"?</strong></summary>\n\nIt's named after Claude Shannon, the father of information theory. At its core, pentesting is an information problem: every probe reduces uncertainty about a system's state. The best tools maximize the signal gained from every request, turning those bits of knowledge into an exploit path.\n\nAlso, we wanted you to be able to say, \"Hey Claude, run Shannon\" to find all the security flaws in your vibe-coded app.\n\n</details>\n\n<a id=\"not-a-replacement-for-human-pentesters\"></a>\n<details>\n<summary><strong>Not a replacement for human pentesters</strong></summary>\n\nShannon is built to work alongside expert pentesters and red teamers, not replace them. Great pentesters understand the business, chain attacks in ways nobody anticipated, and bring years of judgment that current models can't match.\n\nShannon solves a different problem: there is far more software to test than security teams have time to cover. Critical systems get periodic expert assessments, while the long tail of internal apps, APIs, and fast-moving services rarely gets tested at all.\n\nShannon shifts pentesting left into the software development lifecycle (SDLC). Use it to run exploitation-backed tests against staging environments and releases at the cadence they actually ship, and save expert human time for the risks that need someone who knows the organization.\n\n</details>\n\n## Shannon in Action\n\n![Shannon running an autonomous pentest](assets/Shannon3GIF.gif)\n\nThese reports are from Shannon OSS scans of Photoview 2.4.0, one of the applications in Doyensec's comparison of Aikido and XBOW. We ran Shannon against the same application version and evaluated its results separately. Read the [Doyensec study](https://doyensec.com/resources/ComparingAIApplicationSecurityTestingPlatforms_Doyensec.pdf) and our [Shannon follow-up comparison](docs/shannon-xbow-aikido-benchmark.md) for the methodology, limitations, costs, and results.\n\n\n| Model             | Report                                                                       | SARIF                                                            |\n| ----------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------- |\n| DeepSeek v4 Flash | [View report](benchmark/photoview-deepseek-v4-flash.pdf) | [SARIF](benchmark/photoview-deepseek-v4-flash.sarif) |\n| Grok 4.6          | [View report](benchmark/photoview-grok-4-6.pdf)          | [SARIF](benchmark/photoview-grok-4-6.sarif)          |\n| Claude Opus 5     | [View report](benchmark/photoview-opus-5.pdf)            | [SARIF](benchmark/photoview-opus-5.sarif)            |\n\n## Quick Start\n\n\n\n### Prerequisites\n\n- **Docker**: required for the worker container.\n- **Node.js 18+**: required for the recommended `npx` workflow.\n- **AI provider credentials**: Shannon runs on Anthropic, OpenAI, xAI, AWS Bedrock, and [any other provider](docs/ai-providers.md#any-other-provider) in the harness catalogue — each of which you can point at a proxy or LLM gateway through a [custom base URL](docs/ai-providers.md#custom-base-url), and a model the catalogue does not carry can be described with a [custom model configuration](docs/ai-providers.md#custom-model-configuration). You bring your own key, and Keygraph never proxies your model traffic. Shannon is provider-agnostic. See [AI providers](docs/ai-providers.md#suggested-models) for suggested model IDs.\n- **Cyber safeguards cleared with your provider**: Anthropic and OpenAI apply real-time safeguards to cyber-security workloads, which can interrupt a scan mid-run. Complete their guidance for legitimate security testers before your first run - see [AI providers](docs/ai-providers.md#cyber-safeguards-do-this-before-your-first-scan) and the [cyber verification announcement](https://github.com/KeygraphHQ/shannon/discussions/483).\n\n\n\n### Run Shannon\n\n> [!WARNING]\n> Shannon actively executes exploits. Run it only against applications and environments you own or have explicit written authorization to test. Do not run Shannon against production systems.\n\n```bash\n# Configure credentials with the interactive wizard.\nnpx @keygraph/shannon@latest setup\n\n# Run a pentest against a source-available target.\nnpx @keygraph/shannon@latest start \\\n  -u https://your-app.com \\\n  -r /path/to/your/repo\n```\n\nShannon pulls the worker image from Docker Hub, starts the required local infrastructure, mounts the target repository read-only inside an ephemeral worker container, and writes results to a local workspace.\n\nFor source builds, authenticated scans, provider-specific setup, and platform notes, see [Documentation](#documentation).\n\n> [!TIP]\n> **Prefer to use a subscription instead of API credits?**\n>\n> - **OpenAI Codex:** The latest version of Shannon supports ChatGPT Plus and Pro subscriptions. Follow the [OpenAI Codex subscription setup guide](docs/ai-providers.md#openai-codex-chatgpt-pluspro-subscription) to get started.\n> - **xAI (Grok):** The latest version of Shannon supports xAI subscriptions. Follow the [xAI subscription setup guide](docs/ai-providers.md#xai-grok-subscription) to get started.\n> - **Claude Code:** The latest version of Shannon does not support Claude Code subscriptions. Follow the [Claude Code subscription setup guide](docs/ai-providers.md#claude-code-subscription) to use version `1.9.0`, which is the final release built on the Claude Agent SDK.\n\n\n\n## Key Capabilities\n\n- **No exploit, no report**: Reports only vulnerabilities confirmed with a reproducible proof of concept, reducing speculative scanner noise.\n- **Advanced code analysis**: Maps architecture, trust boundaries, interfaces, data flows, and critical assets before sending credible attack paths to live pentesting agents.\n- **Autonomous execution**: Runs reconnaissance, analysis, exploitation, and reporting from a single command.\n- **Live terminal experience**: Simplifies scan setup and shows agent progress and results without exposing orchestration logs.\n- **Authenticated testing**: Supports credentials, login flows, TOTP, email authentication, focus areas, and rules of engagement through configuration.\n- **OWASP-focused coverage**: Tests for exploitable injection, XSS, SSRF, broken authentication, and broken authorization.\n- **Resumable workspaces**: Resumes interrupted scans without repeating completed work.\n- **Native CI/CD integrations**: Runs through the official GitHub Action or GitLab CI/CD component, preserves artifacts, publishes findings, and gates releases on proven vulnerabilities.\n- **Multi-format reports**: Produces evidence-rich PDF and Markdown reports plus JSON and SARIF 2.1.0. SARIF is enabled by default for exploit-mode scans.\n- **Provider agnostic and BYOK**: Supports Anthropic, OpenAI, xAI, AWS Bedrock, compatible APIs and LLM gateways, and local models served through Ollama, vLLM, or LM Studio.\n- **Private by design**: Runs in your infrastructure, stores results locally, and sends model requests directly to your chosen endpoint. A local endpoint keeps data inside your environment.\n\n\n\n## CI/CD Integrations\n\nShannon can run continuously against deployed staging and development environments through official integrations for [GitHub Actions](https://github.com/KeygraphHQ/shannon-action) and [GitLab CI/CD](https://gitlab.com/KeygraphHQ/shannon-ci).\n\nBoth integrations:\n\n- analyze the checked-out source repository while attacking a running target;\n- preserve PDF, Markdown, and SARIF reports as pipeline artifacts;\n- preserve scan and agent logs for debugging, including incomplete runs;\n- support pull-request, release, and scheduled pentests;\n- distinguish an incomplete assessment from a completed scan with no findings; and\n- optionally fail the pipeline when Shannon exploits a vulnerability at or above a configured severity threshold.\n\nA code-analysis hypothesis does not fail the pipeline. Severity gates count only findings with `status: exploited`.\n\n### GitHub Actions\n\n```yaml\nname: Shannon Pentest\n\non:\n  workflow_dispatch:\n\npermissions:\n  security-events: write\n\njobs:\n  pentest:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Run Shannon\n        uses: KeygraphHQ/shannon-action@v1\n        with:\n          url: https://staging.example.com\n          api-key: ${{ secrets.SHANNON_AI_API_KEY }}\n          fail-on-severity: high\n          upload-sarif: true\n```\n\nThe Action defaults `repo` to the checked-out GitHub workspace. It uploads one artifact containing the security assessment reports and SARIF, plus a separate run artifact containing scan and agent logs. Enabling `upload-sarif` publishes supported findings to GitHub code scanning.\n\nRequirements:\n\n- a private repository;\n- a runner with Docker and Docker Compose v2;\n- access to the running staging or development target; and\n- a model-provider credential stored as a GitHub Actions secret.\n\nSee the [Shannon GitHub Action documentation](https://github.com/KeygraphHQ/shannon-action) and [GitHub Marketplace listing](https://github.com/marketplace/actions/shannon-ai-pentester).\n\n## Editions\n\n| Edition | What it is | Price |\n| --- | --- | --- |\n| **Shannon** (open source) | This repository. A complete autonomous pentester you run yourself, locally or in CI/CD, against an application whose source code you have. Well suited to individual developers and small teams. | Open source under AGPL-3.0. You pay only your own model costs. |\n| **Community Program** | Keygraph Pro at no cost, for U.S.-based 501(c)(3) nonprofits and for seed or pre-Series-A startups with 20 or fewer active developers. | $0 in cloud service fees while you qualify. |\n| **Pro** | Keygraph's cloud-hosted edition, managed by Keygraph, with every module included. | $50 per active developer per month. |\n| **Enterprise** | Everything in Pro, self-hosted in your own environment or fully air-gapped. | Custom. |\n\nSee [keygraph.io/pricing](https://keygraph.io/pricing) for current prices and the full feature table.\n\n**Pro**, **Enterprise**, and the **Community Program** are powered by an enhanced, enterprise-grade version of the Shannon engine, and all three add:\n\n- **Black-box pentesting**: tests the running application from the outside, with no source code needed.\n- **Dependency (SCA) and secret checks**: SCA with reachability, and secrets scanning that includes repository history.\n- **Findings management**: one record per vulnerability per repository across scans and scanners, with status history, owners, SLAs, and dashboards.\n- **Fix pull requests and retests**: reviewable fix pull requests, with each fix verified by re-analysis and exploit replay, without a full rescan.\n- **Jira and access control**: two-way Jira sync, SSO, RBAC, and audit logs.\n\nNo source code? Use the [Blackbox Pentester](https://keygraph.io/agentic-blackbox-pentester).\n\n[Compare Shannon, Pro, and Enterprise in detail →](docs/keygraph-platform.md)\n\n## Architecture\n\nShannon combines multi-stage security code analysis with live reconnaissance and exploitation:\n\n```mermaid\nflowchart TD\n    S[\"Source code\"] --> EXISTING[\"Recon + vulnerability analysis\"]\n    S --> SAST[\"Agentic security code analysis\"]\n\n    EXISTING -- \"Pentest candidates\" --> REC[\"Finding reconciliation<br/>(merge + deduplicate)\"]\n    SAST -- \"SAST candidates\" --> REC\n\n    REC -- \"Reconciled exploitation queue\" --> EXP[\"Exploitation agents\"]\n    APP[\"Running application\"] --> EXP\n\n    EXP -- \"Exploit demonstrated\" --> REPORT[\"Reporting<br/>PDF · Markdown · SARIF\"]\n    EXP -- \"No exploit demonstrated\" --> DROP[\"Discard\"]\n\n    REPORT --> CICD[\"CI/CD gate\"]\n```\n\n\n\nStage by stage:\n\n1. **Recon and vulnerability analysis** explores the running application, ties runtime behavior back to the source, and runs specialized agents across Injection, XSS, SSRF, Authentication, and Authorization.\n2. **Agentic security code analysis** maps the application's architecture, trust boundaries, exposed interfaces, dependencies, data flows, and high-risk assets, then opens targeted investigations against them.\n3. **Finding reconciliation** merges both streams of candidates, deduplicates the overlap, and groups what remains into an exploitation queue.\n4. **Exploitation agents** attempt real proof-of-concept attacks against the running application.\n5. **Validation** throws out every candidate Shannon can't demonstrate.\n6. **Reporting** produces PDF and Markdown reports with the evidence attached, plus structured JSON and SARIF for downstream systems.\n\nOnly live-validated vulnerabilities become Shannon pentest findings or count toward CI/CD severity gates.\n\nEach scan runs in an ephemeral Docker container with an isolated workspace and per-invocation orchestration.\n\n## Documentation\n\nUse these guides for operational detail:\n\n\n| Guide                                                     | Use it for                                                                                                                                                                |\n| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [Source build and CLI commands](docs/development.md)      | Cloning, building, common commands, output paths, and local development.                                                                                                  |\n| [Configuration](docs/configuration.md)                    | Authenticated testing, login flows, rules of engagement, and report filters.                                                                                              |\n| [AI providers](docs/ai-providers.md)                      | Selecting the model, the supported providers (Anthropic, OpenAI, xAI, AWS Bedrock, and any other Pi-supported provider), and custom LLM gateways.                             |\n| [Platforms and networking](docs/platforms.md)             | Windows/WSL2, Linux, macOS, Docker networking, local apps, and custom hostnames.                                                                                          |\n| [Workspaces and resuming](docs/workspaces.md)             | Naming workspaces, resuming interrupted scans, and workspace storage.                                                                                                     |\n| [Safety and limitations](docs/safety.md)                  | Authorized-use requirements, non-production guidance, mutative effects, cost, and model caveats.                                                                          |\n| [Coverage and roadmap](docs/coverage-roadmap.md)          | Current vulnerability coverage and planned work.                                                                                                                          |\n| [Pro and Enterprise](docs/keygraph-platform.md)           | Shannon compared with Pro, Enterprise, and the Community Program: black-box pentesting, SCA and secrets, findings management, fix pull requests, retests, Jira, and deployment. |\n\n\n\n\n## Safety, Scope, and Limitations\n\nShannon is not a passive scanner. Its exploitation agents can create users, submit forms, mutate application state, trigger outbound requests, and otherwise affect the target system. Use sandboxed, staging, or local development environments with disposable data.\n\nYou are responsible for using Shannon legally and ethically. Do not point Shannon at systems, repositories, or applications you do not own or do not have explicit authorization to test.\n\nImportant limitations:\n\n- Shannon is tuned for fast, code-informed pentesting in everyday development and CI/CD. Exhaustive agentic SAST, broader scanner coverage, centralized governance, and full-lifecycle vulnerability management are delivered through Pro, Enterprise, and the Community Program.\n- Findings still require human review. LLM-generated reports can contain weakly supported or incorrect details.\n- Anthropic, OpenAI, xAI, and AWS Bedrock are built-in providers, and any other provider in the harness catalogue works too — each reachable through a custom base URL that points it at a proxy or LLM gateway. Model capability varies, and a model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker results.\n- A full run can take roughly 1 to 1.5 hours and may incur LLM API costs depending on model pricing and application complexity.\n- Do not scan untrusted or adversarial codebases. AI-powered tools that read source code can be exposed to prompt injection.\n\nRead the full [Safety and limitations](docs/safety.md) guide before running Shannon in a new environment.\n\n## License\n\nShannon is licensed under the [GNU Affero General Public License v3.0](LICENSE).\n\nCommercial and enterprise licensing is available for organizations that need different license terms, commercial support, private redistribution, managed-service use, or broader deployment options, including Keygraph Pro and Enterprise.\n\nFor commercial licensing, contact [shannon@keygraph.io](mailto:shannon@keygraph.io).\n\n## Acknowledgements\n\nThanks to [Pi](https://github.com/earendil-works/pi),\n[Playwright CLI](https://github.com/microsoft/playwright-cli),\nand [Mantis](https://github.com/google/mantis).\n\nSee [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md) for licensing and attribution details.\n\n## About Keygraph\n\n**Keygraph** is the company behind Shannon. It also builds **Keygraph Pro** and **Keygraph Enterprise**, the commercial editions that close the full AppSec lifecycle and run an enhanced build of Shannon as their pentesting engine.\n\n## Community and Support\n\n**Community office hours** are available for hands-on help with bugs, deployments, and configuration questions.\n\n- US/EU: Thursday, 10:00 AM PT\n- Asia: Thursday, 2:00 PM IST\n- [Book a slot](https://cal.com/george-flores-keygraph/shannon-community-office-hours)\n\n[Join Discord](https://discord.gg/cmctpMBXwE) to ask questions, share feedback, and connect with other Shannon users.\n\nAt this time, Keygraph is not accepting external code contributions. Issues are welcome for bug reports and feature requests:\n\n- [Report bugs](https://github.com/KeygraphHQ/shannon/issues)\n- [Suggest features](https://github.com/KeygraphHQ/shannon/discussions)\n\nStay connected:\n\n- [Keygraph website](https://keygraph.io)\n- [Twitter/X: @KeygraphHQ](https://twitter.com/KeygraphHQ)\n- [LinkedIn: Keygraph](https://linkedin.com/company/keygraph)\n\n\n\n## Common Questions\n\n\n\n### Can I self-host Shannon?\n\nYes. Shannon runs inside your infrastructure in an ephemeral worker container. It mounts the repository read-only and writes results to a local workspace.\n\nKeygraph never receives your source code and never proxies your model traffic. Your model requests go straight to the provider or endpoint you configure, and they carry source and application context with them. Point Shannon at a locally hosted endpoint and that traffic stays inside your environment too.\n\n### Does Shannon support bring your own key (BYOK)?\n\nYes, always. You provide the LLM credentials Shannon uses to run a pentest, in every deployment, open source and commercial. Keygraph never proxies your model traffic.\n\n### Does Shannon output SARIF?\n\nYes. Shannon emits SARIF 2.1.0, the OASIS standard format for static analysis results, alongside structured JSON. Any SARIF consumer reads it: code scanning services, vulnerability management platforms, security dashboards, and CI/CD pipelines. It is written by default on exploit-mode scans; set `report.sarif` to `\"false\"` in your configuration file to opt out.\n\n### Which AI providers does Shannon support?\n\nAnthropic, OpenAI, xAI, and AWS Bedrock are built in and configured directly by provider ID. Beyond those, Shannon runs on any provider in the Pi harness catalogue, named the same `<provider>:<model-id>` way. Any provider can be pointed at a proxy or LLM gateway through a custom base URL, which overrides only the endpoint and keeps that provider's API dialect. A model the catalogue does not carry, such as one a router or gateway serves under its own ID, or a self-hosted model, is described in a [custom model configuration](docs/ai-providers.md#custom-model-configuration) file and passed with `--models-config`. Shannon uses a single unified model setting throughout a pentest.\n\n### Can I run Shannon on a local or self-hosted model?\n\nShannon works with local models served through Ollama, vLLM, or LM Studio, which expose an OpenAI-compatible endpoint, as well as routers such as OpenRouter and LLM gateways such as LiteLLM. A model the harness catalogue does not carry, which most self-hosted models are, is described in a [custom model configuration](docs/ai-providers.md#custom-model-configuration) file passed with `--models-config`; routers and gateways can also be reached with a custom base URL. Capability varies, and a model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker pentests than a frontier model, so take this path only if you know how your chosen model behaves. See [Local and self-hosted models](docs/ai-providers.md#local-and-self-hosted-models).\n\n### Does Shannon actually exploit vulnerabilities, or just scan?\n\nShannon executes real exploits. It reports a finding only when it has produced a working proof-of-concept, and discards hypotheses it cannot prove. It is a pentester, not a passive scanner.\n\n**Built by [Keygraph](https://keygraph.io)**\n\n---\n\n# File: docs/development.md\n\n# Source Build and CLI Commands\n\nThis guide covers the source-build workflow, common CLI commands, repository paths, and output locations. For the fastest first run, use the `npx` workflow in the main README.\n\n## Prerequisites\n\n- Docker\n- Node.js 18+\n- pnpm\n- AI provider credentials\n\n## Clone and Build\n\nUse the source-build workflow if you want to run Shannon from a local clone, modify the open-source CLI, or keep the worker image built locally.\n\n```bash\n# 1. Clone Shannon.\ngit clone https://github.com/KeygraphHQ/shannon.git\ncd shannon\n\n# 2. Configure credentials.\ncp .env.example .env\n\n# 3. Install dependencies and build.\npnpm install\npnpm build\n\n# 4. Run a pentest.\n./shannon start -u https://your-app.com -r /path/to/your-repo\n```\n\nAt minimum, your `.env` file should include one supported AI provider credential, such as:\n\n```bash\nANTHROPIC_API_KEY=your-api-key\n```\n\nEnvironment variables can also be exported directly:\n\n```bash\nexport ANTHROPIC_API_KEY=\"your-api-key\"\n```\n\n## Prepare Your Repository\n\nShannon can scan any repository on your machine. Pass an absolute or relative path with `-r`.\n\n```bash\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo\n./shannon start -u https://example.com -r ./relative/path\n```\n\nThe target repository is mounted read-only inside the worker container.\n\n## Common Commands\n\nMonitor progress:\n\n```bash\nnpx @keygraph/shannon logs [<workspace>]     # defaults to the single running scan, else the most recent\nnpx @keygraph/shannon status [<workspace>]   # same default target; add --json for a machine-readable snapshot\nnpx @keygraph/shannon scans\nnpx @keygraph/shannon version\n```\n\nWith no workspace, `logs` and `status` follow the single running scan; when several are running, name one.\n\nSource-build equivalents:\n\n```bash\n./shannon logs [<workspace>]                 # the combined live log (unchanged default)\n./shannon logs [<workspace>] --agent <name>  # tail one agent's own log\n./shannon logs [<workspace>] --list-agents   # list the agents with their own log\n./shannon status [<workspace>]\n./shannon scans\n./shannon version\n```\n\nEvery scan writes one combined `.shannon/workflow.log` and a per-agent projection of it under\n`.shannon/agents/`: one file per pipeline agent (`recon.log`, `xss-vuln.log`, …) and one per Capella\nstage (`agentic-sast-research.log`, …). Delegated subagents fold into their parent's file, and a\nCapella stage's concurrent sessions share its file with an inline session label. The combined log\nstays canonical; the per-agent files are best-effort projections.\n\nOpen the Temporal Web UI for detailed monitoring:\n\n```bash\nopen http://localhost:8233\n```\n\nStop Shannon:\n\n```bash\nnpx @keygraph/shannon stop [<workspace>] # stop one scan (defaults to the single running scan; confirms first; add --yes/-y to skip)\nnpx @keygraph/shannon stop --all         # stop all scans (Temporal stays up)\nnpx @keygraph/shannon reset              # stop everything and wipe all Temporal data (type 'confirm' to proceed; cannot be skipped)\n```\n\nSource-build equivalents:\n\n```bash\n./shannon stop [<workspace>]             # stop one scan (defaults to the single running scan; confirms first; add --yes/-y to skip)\n./shannon stop --all                     # stop all scans (Temporal stays up)\n./shannon reset                          # stop everything and wipe all Temporal data (type 'confirm' to proceed; cannot be skipped)\n```\n\nUsage examples:\n\n```bash\n# Basic pentest.\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo\n\n# With a configuration file.\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -c /path/to/my-config.yaml\n\n# Custom output directory.\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -o ./my-reports\n\n# Named workspace.\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -w q1-audit\n\n# Stream the log until the scan finishes, then exit on its outcome (useful in CI).\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo --follow\n\n# Validate the configured login only, then stop (no pentest or report).\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -c /path/to/my-config.yaml --validate-auth\n\n# List running and completed scans.\nnpx @keygraph/shannon scans\n```\n\nSource-build examples:\n\n```bash\n./shannon start -u https://example.com -r /path/to/repo\n./shannon start -u https://example.com -r /path/to/repo -c /path/to/my-config.yaml\n./shannon start -u https://example.com -r /path/to/repo -o ./my-reports\n./shannon start -u https://example.com -r /path/to/repo -w q1-audit\n./shannon start -u https://example.com -r /path/to/repo --follow\n./shannon start -u https://example.com -r /path/to/repo -c /path/to/my-config.yaml --validate-auth\n./shannon scans\n\n# Rebuild the worker image.\n./shannon build --no-cache\n```\n\n## Output and Results\n\nResults are saved to the workspaces directory:\n\n- `./workspaces/` in source-build mode\n- `~/.shannon/workspaces/` in `npx` mode\n\nUse `-o <path>` to copy deliverables to a custom output directory after a run completes.\n\nOutput structure — the run directory's top level holds the final report, in PDF and Markdown; everything else is nested under a hidden `.shannon/` directory:\n\n```text\nworkspaces/{hostname}_{sessionId}/\n|-- Security-Assessment-Report.pdf  # the final report (PDF)\n|-- Security-Assessment-Report.md   # the final report (Markdown)\n`-- .shannon/                       # internals\n    |-- deliverables/               # report source, per-phase analysis, queues\n    |-- agents/                     # per-agent log projections, one file per agent/Capella stage\n    |-- prompts/                    # rendered prompts\n    |-- scratchpad/                 # screenshots, scripts\n    |-- session.json                # resume state\n    `-- workflow.log\n```\n\n---\n\n# File: docs/configuration.md\n\n# Configuration\n\nShannon can run without a configuration file, but configuration enables authenticated testing, scope guidance, rules of engagement, and report filtering.\n\n## Credential Precedence\n\nSource-build mode resolves credentials from:\n\n1. Environment variables, such as `export ANTHROPIC_API_KEY=...`\n2. `./.env`\n\n`npx` mode resolves credentials from:\n\n1. Environment variables\n2. `~/.shannon/config.toml`, created by `npx @keygraph/shannon setup`\n\nEnvironment variables always win, so you can override saved config for a single session without editing files.\n\n## Create a Configuration File\n\nCopy and modify the example configuration:\n\n```bash\ncp configs/example-config.yaml ./my-app-config.yaml\n```\n\nRun with:\n\n```bash\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -c ./my-app-config.yaml\n```\n\nSource-build equivalent:\n\n```bash\n./shannon start -u https://example.com -r /path/to/repo -c ./my-app-config.yaml\n```\n\n## Basic Configuration Structure\n\n```yaml\n# Describe your target environment.\ndescription: \"Next.js e-commerce app on PostgreSQL. Local dev environment; .env files contain local-only credentials.\"\n\n# Every scan runs all five vulnerability classes.\n\n# Agentic static analysis. `enabled` is its only setting.\n# agentic_sast:\n#   enabled: \"true\"\n\n# Skip the exploitation phase.\n# exploit: \"false\"\n\n# Free-form rules of engagement.\n# rules_of_engagement: |\n#   - No password brute-force; cap login attempts at 5 per account.\n#   - Throttle to under 5 requests per second per endpoint; back off 60s on any 429.\n#   - Use placeholders like [order_id] in deliverables; no real data values.\n\nauthentication:\n  login_type: form\n  login_url: \"https://your-app.com/login\"\n  credentials:\n    username: \"test@example.com\"\n    password: \"yourpassword\"\n    totp_secret: \"LB2E2RX7XFHSTGCK\"\n\n    # Optional mailbox credentials for magic-link or email-OTP flows.\n    # email_login:\n    #   address: \"inbox@example.com\"\n    #   password: \"mailbox-password\"\n    #   totp_secret: \"JBSWY3DPEHPK3PXP\"\n\n  login_flow:\n    - \"Type $username into the email field\"\n    - \"Type $password into the password field\"\n    - \"Click the 'Sign In' button\"\n\n  success_condition:\n    type: url_contains\n    value: \"/dashboard\"\n\nrules:\n  avoid:\n    - description: \"AI should avoid testing logout functionality\"\n      type: url_path\n      value: \"/logout\"\n\n    # code_path values are repo-relative file paths or globs.\n    # - description: \"Out-of-scope vendored libraries\"\n    #   type: code_path\n    #   value: \"src/vendor/**\"\n\n  focus:\n    - description: \"AI should emphasize testing API endpoints\"\n      type: url_path\n      value: \"/api\"\n\n# Report options applied when assembling the final report.\n# report:\n#   min_severity: low\n#   min_confidence: low\n#   guidance: |\n#     Drop findings about missing security headers and rate-limit gaps.\n#   sarif: \"false\"\n```\n\n## Analysis Scope and Agentic SAST\n\nEvery scan runs all five analysis classes: Injection, Cross-Site Scripting, Authentication, Authorization, and\nServer-Side Request Forgery. The class set is fixed and has no configuration selector.\n\nAgentic static analysis is opt-in:\n\n```yaml\nagentic_sast:\n  enabled: \"true\"\n```\n\n`enabled` is the only setting. Omitting the block, or setting `enabled: \"false\"`, turns agentic static analysis off;\n`\"true\"` turns it on. Either way, all five vulnerability classes still run.\n\nAgentic static analysis reads the repository for vulnerabilities before the pentest and passes what it finds into the\nexploitation phase. It adds model time and cost. If it fails, the pentest continues without its findings and the scan\nfinishes as \"partial\".\n\n## Report Options\n\n| Key | Effect |\n| --- | --- |\n| `min_severity` | Drops findings rated below this severity. Applies in both exploitative and analysis-only runs. |\n| `min_confidence` | Drops findings rated below this confidence. Applies only when `exploit` is `\"false\"`. |\n| `guidance` | Free-text instruction to the report agent, such as which topics to exclude. |\n| `sarif` | SARIF 2.1.0 log alongside the Markdown report. On by default for exploit runs; set `\"false\"` to opt out. Ignored when `exploit` is `\"false\"`. |\n\nEvery finding carries a severity, but it does not mean the same thing in each mode: an exploitative run measures severity from what the exploit demonstrated, while an analysis-only run assesses it from the class of flaw and the impact it would have. An analysis-only finding carries a confidence rating alongside its severity, since nothing was proven. Setting `min_confidence` on an exploitative run is ignored, and Shannon logs a warning naming the threshold to use instead.\n\n### SARIF Output\n\nOn exploit-mode runs Shannon writes `report.sarif` next to `Security-Assessment-Report.pdf` at the workspace root by default, for upload to GitHub code scanning or any other SARIF consumer. No configuration is needed; set `sarif: \"false\"` to opt out.\n\n```yaml\nreport:\n  sarif: \"false\"\n```\n\nEach finding becomes one SARIF result, filed under a rule per vulnerability class (`shannon/injection`, `shannon/xss`, `shannon/auth`, `shannon/authz`, `shannon/ssrf`, and `shannon/miscellaneous` for findings outside those classes) and tagged with its OWASP Top Ten 2025 category. Results are anchored to the code location the analysis phase recorded, falling back to the HTTP entry point when the finding names no file. Severity maps onto SARIF's three levels: `critical` and `high` become `error`, `medium` becomes `warning`, everything else becomes `note`.\n\nIf the SARIF log cannot be written, the JSON and Markdown reports are still produced and the scan finishes as \"partial\".\n\nThe log is written only for exploitative runs. `sarif` is ignored when `exploit` is `\"false\"`.\n\nSupported rule types include `url_path`, `subdomain`, `domain`, `method`, `header`, `parameter`, and `code_path`.\n\n## Writing Login Flow\n\nLog in once in a fresh private browser window. Write the steps in the same order you perform them:\n\n- When typing into a field, reference the field by its exact label or placeholder.\n- When clicking a button, reference the exact button text.\n\nSupported placeholders:\n\n- `$username`\n- `$password`\n- `$totp`\n- `$email_address`\n- `$email_password`\n- `$email_totp`\n\nAt runtime, Shannon replaces these placeholders with the credentials passed in the config.\n\n```yaml\nlogin_flow:\n  - \"Type $username in <exact email field label or placeholder>\"\n  - \"Click <exact button text>\"\n  - \"Type $password in <exact password field label or placeholder>\"\n  - \"Click <exact button text>\"\n  - \"If prompted for 2FA, type $totp in <exact code field label or placeholder>\"\n  - \"Click <exact button text>\"\n```\n\n### Validating Authentication Only\n\nTo confirm your login flow works before committing to a full scan, add `--validate-auth` to `start`:\n\n```bash\nnpx @keygraph/shannon start -u https://your-app.com -r /path/to/repo -c config.yaml --validate-auth\n```\n\nThe run performs preflight and the single real login, then stops. No pentest, reconciliation, or report\nis produced. It requires an `authentication` block in the config.\n\n---\n\n# File: docs/ai-providers.md\n\n# AI Providers\n\nOne model runs the entire scan — pre-recon, recon, vulnerability analysis, exploitation, and reporting. A single setting names both the provider and the model:\n\n```bash\nexport SHANNON_AI_MODEL=<provider>:<model-id>\n```\n\nThe provider half decides where the request goes, which credential is used, and which API dialect is spoken. You never configure those separately.\n\n## Supported providers\n\n| Provider | Value | Credential |\n| --- | --- | --- |\n| Anthropic | `anthropic` | `SHANNON_AI_API_KEY` (or `CLAUDE_CODE_OAUTH_TOKEN`) |\n| OpenAI | `openai` | `SHANNON_AI_API_KEY` |\n| xAI | `xai` | `SHANNON_AI_API_KEY` |\n| AWS Bedrock | `amazon-bedrock` | `AWS_REGION` and `AWS_BEARER_TOKEN_BEDROCK` |\n\n`SHANNON_AI_API_KEY` holds the key for whichever provider `SHANNON_AI_MODEL` names. Bedrock is the exception — it authenticates through its `AWS_` variables only. If `SHANNON_AI_MODEL` is unset, Shannon uses `anthropic:claude-sonnet-4-6`.\n\nAnthropic, OpenAI, and xAI also accept their native variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `XAI_API_KEY`); if one of those is set, it is used instead of `SHANNON_AI_API_KEY`.\n\nShannon forwards only the selected provider's credential into the scan container. Keys for other providers stay on your machine.\n\n### Any other provider\n\nShannon accepts any provider and model present in the Pi harness catalogue. Browse them at [pi.dev/models](https://pi.dev/models).\n\n```bash\nexport SHANNON_AI_API_KEY=your-api-key                     # the provider's key — or the gateway's when a base URL is set\nexport SHANNON_AI_MODEL=openrouter:moonshotai/kimi-k3      # <provider>:<model-id>\nexport SHANNON_AI_BASE_URL=https://llm-gateway.example.com # optional: route through a proxy or LLM gateway\n```\n\nThis path covers providers whose credential is a single API key. Providers that need more than that are not currently supported.\n\nA model the catalogue does not carry is reachable by describing it yourself. See [Custom model configuration](#custom-model-configuration).\n\n`npx @keygraph/shannon setup` exposes this as the **Other provider** option.\n\n> [!IMPORTANT]\n> Models are validated against the harness catalogue, but capability varies. A model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker pentests. Evaluate the model you choose against your own targets before depending on its results.\n\n## Cyber safeguards (do this before your first scan)\n\nAnthropic and OpenAI both apply real-time safeguards to cyber-security workloads. Shannon is exactly such a workload. If a safeguard engages mid-run, the model can refuse, and the scan fails partway through rather than at the start.\n\nReview each vendor's guidance and complete the verification or enrollment they ask of legitimate security testers before running Shannon:\n\n- Anthropic - [Real-time cyber safeguards on Claude Opus and Sonnet](https://support.claude.com/en/articles/14604842-real-time-cyber-safeguards-on-claude-opus-and-sonnet)\n- OpenAI - [Cyber](https://chatgpt.com/cyber)\n\nThis applies to the Anthropic and OpenAI providers, including when either is reached through an LLM gateway. Bedrock serves Claude models and is subject to Anthropic's safeguards as well.\n\nTo confirm your model is ready before committing to a full scan, add `--validate-model` to `start`:\n\n```bash\nnpx @keygraph/shannon start -u https://your-app.com -r /path/to/repo --validate-model\n```\n\nThe run performs the preflight model checks only — credential and registry resolution for any provider, plus a single cyber-access verification against Anthropic and OpenAI that trips the cyber safeguard if your account is not approved — then stops. No pentest or report is produced, and it needs no config. A decline fails the run with the vendor's enrollment link.\n\n## Suggested models\n\nThese are the models `npx @keygraph/shannon setup` offers, best-first. They are suggestions: the wizard also takes a typed model ID, and `SHANNON_AI_MODEL` accepts any model in the provider's catalogue.\n\n| Provider | Suggested model IDs |\n| --- | --- |\n| `anthropic` | `claude-sonnet-5`, `claude-opus-5`, `claude-sonnet-4-6`, `claude-opus-4-8`, `claude-opus-4-7`, `claude-haiku-4-5-20251001` |\n| `openai` | `gpt-6-sol`, `gpt-5.6-sol`, `gpt-5.5`, `gpt-5.4` |\n| `xai` | `grok-4.7` |\n| `amazon-bedrock` | `us.anthropic.claude-sonnet-4-6`, `us.anthropic.claude-opus-4-8`, `us.anthropic.claude-opus-4-7` |\n\nBedrock IDs are region-prefixed and must be enabled in your account, so the ID that works for you may differ from the one listed here.\n\n## Switching provider\n\nThe pattern is learned once: export the provider's key, name the model. Two lines change, nothing else.\n\nAnthropic (default):\n\n```bash\nexport SHANNON_AI_API_KEY=sk-ant-...\nexport SHANNON_AI_MODEL=anthropic:claude-sonnet-4-6\n```\n\nOpenAI:\n\n```bash\nexport SHANNON_AI_API_KEY=sk-...\nexport SHANNON_AI_MODEL=openai:gpt-6-sol\n```\n\nxAI:\n\n```bash\nexport SHANNON_AI_API_KEY=xai-...\nexport SHANNON_AI_MODEL=xai:grok-4.7\n```\n\nSource-build mode reads the same variables from a `.env` file.\n\n## AWS Bedrock\n\nRun `npx @keygraph/shannon setup` and select **AWS Bedrock**, or export directly:\n\n```bash\nexport AWS_REGION=us-east-1\nexport AWS_BEARER_TOKEN_BEDROCK=your-bearer-token\nexport SHANNON_AI_MODEL=amazon-bedrock:us.anthropic.claude-opus-4-8\n```\n\nBedrock uses bearer-token authentication only. IAM access keys, session tokens, assumed roles, and instance profiles are not supported. The model must be enabled in your region.\n\n## Custom base URL\n\n`SHANNON_AI_BASE_URL` routes model traffic through a proxy or LLM gateway instead of the provider's default endpoint — an LLM gateway such as LiteLLM, a regional endpoint, or any other host you choose. It is a plain endpoint override: it changes only *where* requests go. The provider half of `SHANNON_AI_MODEL` still decides which credential is sent and which API dialect is spoken, and that is unchanged by the base URL.\n\nThis works for **any** provider, curated or not, subject to two rules. A provider's dialect is fixed, so the endpoint you point at must speak that provider's dialect:\n\n| Provider prefix | Dialect the endpoint must speak |\n| --- | --- |\n| `anthropic:` | Anthropic Messages |\n| `openai:` | OpenAI Responses |\n\nAnd the model ID must still resolve in the harness catalogue. A base URL changes only the address; it grants no exemption from that check. A gateway serving a model under its own name needs that name described in a [custom model configuration](#custom-model-configuration) file.\n\nAnthropic Messages LLM gateway:\n\n```bash\nexport SHANNON_AI_API_KEY=sk-ant-...\nexport SHANNON_AI_MODEL=anthropic:claude-sonnet-4-6\nexport SHANNON_AI_BASE_URL=https://llm-gateway.example.com\n```\n\nOpenAI Responses LLM gateway:\n\n```bash\nexport SHANNON_AI_API_KEY=sk-...\nexport SHANNON_AI_MODEL=openai:gpt-6-sol\nexport SHANNON_AI_BASE_URL=https://llm-gateway.example.com/v1\n```\n\n`npx @keygraph/shannon setup` configures a base URL two ways: **Custom Base URL** covers the common Anthropic Messages and OpenAI Responses LLM gateways, and **Other provider** takes any provider ID plus an optional base URL of its own.\n\n## Custom model configuration\n\nA custom model configuration is a Pi `models.json` file that describes a model the harness catalogue does not carry: one a router or gateway serves under its own ID, or a local server (see [Local and self-hosted models](#local-and-self-hosted-models)). You pass it with `--models-config`, and Shannon merges its definitions over the catalogue so `SHANNON_AI_MODEL` can then name the model like any other:\n\n```bash\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo --models-config ./models.json\n```\n\n```bash\n./shannon start -u https://example.com -r ./my-repo --models-config ./models.json\n```\n\n[pi.dev/models](https://pi.dev/models) supplies the file contents. Find the model under the provider you want, since the same model has a different ID per provider, then open its page and expand **Show configuration** for a ready-to-paste snippet:\n\n```json\n{\n  \"providers\": {\n    \"openrouter\": {\n      \"apiKey\": \"YOUR_API_KEY\",\n      \"models\": [\n        {\n          \"id\": \"z-ai/glm-5.3\",\n          \"name\": \"Z.ai: GLM 5.3\",\n          \"reasoning\": true,\n          \"input\": [\n            \"text\"\n          ],\n          \"thinkingLevelMap\": {\n            \"off\": null,\n            \"minimal\": null,\n            \"low\": \"low\",\n            \"medium\": null,\n            \"high\": \"high\",\n            \"xhigh\": null,\n            \"max\": \"max\"\n          },\n          \"contextWindow\": 1048576,\n          \"maxTokens\": 943718,\n          \"cost\": {\n            \"input\": 1.4,\n            \"output\": 4.4,\n            \"cacheRead\": 0.26,\n            \"cacheWrite\": 0\n          },\n          \"compat\": {\n            \"supportsDeveloperRole\": false,\n            \"thinkingFormat\": \"openrouter\"\n          }\n        }\n      ],\n      \"api\": \"openai-completions\",\n      \"baseUrl\": \"https://openrouter.ai/api/v1\"\n    }\n  }\n}\n```\n\nThen name the model the usual way:\n\n```bash\nexport SHANNON_AI_API_KEY=your-api-key\nexport SHANNON_AI_MODEL=openrouter:z-ai/glm-5.3\n```\n\nLeave `YOUR_API_KEY` exactly as it is. Shannon sends the credential from your environment, and that takes precedence over anything the file declares, so the file describes the model and never has to hold a secret.\n\nPi's [models documentation](https://pi.dev/docs/latest/models) describes the full format, including provider routing preferences and compatibility flags.\n\n## Local and self-hosted models\n\nOllama, LM Studio, vLLM, and any other OpenAI-compatible server are reached through the same mechanism. Describe the server as a provider in a model config file, then name its model with `SHANNON_AI_MODEL`.\n\n> [!IMPORTANT]\n> Use `host.docker.internal`, not `localhost`. The scan runs inside a container, so `localhost` points at the container itself rather than at your machine.\n\nA `models.json` for Ollama:\n\n```json\n{\n  \"providers\": {\n    \"ollama\": {\n      \"baseUrl\": \"http://host.docker.internal:11434/v1\",\n      \"api\": \"openai-completions\",\n      \"apiKey\": \"ollama\",\n      \"models\": [\n        { \"","isInternal":false,"tokens":11559,"sizeBytes":50026},{"name":"llms.txt","path":"llms.txt","rawUrl":"https://raw.githubusercontent.com/KeygraphHQ/shannon/HEAD/llms.txt","title":"LLM Index & Context Digest","category":"root-instruction","format":"text","content":"# Shannon\n\n> Shannon is an autonomous AI pentesting project by Keygraph. This repository contains Shannon, the AGPL-3.0 open-source white-box pentesting CLI. Keygraph Pro and Keygraph Enterprise are Keygraph's commercial continuous pentesting and AppSec editions.\n\nUse this file as the concise entry point for AI agents and LLMs reading this repository. For a single combined context file, use [llms-full.txt](llms-full.txt).\n\n## Start Here\n\n- [README](README.md): Main project overview, editions, quick start, Shannon capabilities, CI/CD integrations, common questions, safety notes, licensing, and support links.\n\n## Shannon\n\n- [Development](docs/development.md): Source-build workflow, common CLI commands, repository paths, and output locations.\n- [Configuration](docs/configuration.md): Authenticated testing, login flows, rules of engagement (including rate-limit guidance), report filters, and credential precedence.\n- [AI Providers](docs/ai-providers.md): Anthropic, OpenAI, xAI, AWS Bedrock, any other Pi-supported provider, custom LLM gateway setup, custom model configuration for models not yet in the Pi catalogue, and local self-hosted runtimes (Ollama, LM Studio, vLLM).\n- [Platforms and Networking](docs/platforms.md): Windows/WSL2, Linux, macOS, Docker networking, local applications, and custom hostnames.\n- [Workspaces and Resuming](docs/workspaces.md): Workspace storage, naming, resuming interrupted scans, and examples.\n- [Safety and Limitations](docs/safety.md): Authorized-use requirements, non-production guidance, mutative effects, model caveats, scope limits, cost, and performance.\n- [Coverage and Roadmap](docs/coverage-roadmap.md): Current Shannon coverage and roadmap direction.\n\n## Keygraph Pro and Enterprise\n\n- [Pro and Enterprise](docs/keygraph-platform.md): Keygraph's commercial continuous pentesting and AppSec editions, compared edition by edition with Shannon. The Community Program, Pro, and Enterprise all include black-box and white-box pentesting, parsed-code SAST, SCA and secrets scanning, findings management, fix pull requests and retests, Jira sync, and SSO, RBAC, and audit logs. Pro and the Community Program are cloud-hosted; Enterprise is self-hosted or air-gapped. Shannon Lite is now just Shannon. Shannon Pro is now Keygraph Pro.\n\n## External Links\n\n- [Keygraph website](https://keygraph.io): Company and commercial product information.\n- [Keygraph pricing](https://keygraph.io/pricing): Prices and the feature table for Shannon, the Community Program, Pro, and Enterprise.\n- [Keygraph demo](https://cal.com/team/keygraph/keygraph-technical-demo): Technical demo booking.\n- [Community Discord](https://discord.gg/cmctpMBXwE): Community support and discussion.\n","isInternal":false,"tokens":600,"sizeBytes":2715},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/KeygraphHQ/shannon/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# CLAUDE.md\n\nAI-powered penetration testing agent for defensive security analysis. Automates vulnerability assessment by combining reconnaissance tools with AI-powered code analysis.\n\n## Commands\n\n**Prerequisites:** Docker, AI provider credentials (`.env` for local, `npx @keygraph/shannon setup` or env vars for npx)\n\n### Dual CLI\n\nShannon supports two CLI modes, auto-detected based on the current working directory:\n\n| | **npx** (`npx @keygraph/shannon`) | **Local** (`./shannon`) |\n|---|---|---|\n| **Install** | Zero-install via npm | Clone the repo |\n| **Image** | Pulled from Docker Hub (`keygraph/shannon:latest`) | Built locally (`shannon-worker`) |\n| **State** | `~/.shannon/` | Project directory |\n| **Credentials** | `~/.shannon/config.toml` (via `npx @keygraph/shannon setup`) or env vars | `./.env` |\n| **Config** | `~/.shannon/config.toml` (via `npx @keygraph/shannon setup`) | N/A |\n| **Prompts** | Bundled in Docker image | Mounted from `./apps/worker/prompts/` (live-editable) |\n\nMode auto-detection: local mode activates when env var `SHANNON_LOCAL=1` is set by the `./shannon` entry point (`apps/cli/src/mode.ts`). Otherwise npx mode.\n\n### npx Quick Start\n\n```bash\n# Configure credentials (interactive wizard)\nnpx @keygraph/shannon setup\n\n# Or export env vars directly (non-interactive / CI)\nexport ANTHROPIC_API_KEY=your-key\n\n# Run\nnpx @keygraph/shannon start -u <url> -r /path/to/repo\n```\n\n### Local (Development) Quick Start\n\n```bash\n# Setup\necho \"ANTHROPIC_API_KEY=your-key\" > .env\n\n# Build (auto-runs if image missing)\n./shannon build\n\n# Run\n./shannon start -u <url> -r ./my-repo\n./shannon start -u <url> -r ./my-repo -c ./apps/worker/configs/my-config.yaml\n./shannon start -u <url> -r /any/path/to/repo\n```\n\n### Common Commands\n\n```bash\n# Setup (npx mode only — one-time credential configuration)\nnpx @keygraph/shannon setup\n\n# Workspaces & Resume\n./shannon start -u <url> -r ./my-repo -w my-audit    # New named workspace\n./shannon start -u <url> -r ./my-repo -w my-audit    # Resume (same command)\n\n# Monitor\n./shannon scans                       # List running and completed scans, with each report's path\n./shannon logs [<workspace>]          # Show a scan's live log (default: the single running scan, else the most recent)\n./shannon logs [<workspace>] --agent <name>   # Tail one agent's own log (from .shannon/agents/)\n./shannon logs [<workspace>] --list-agents    # List the agents that have their own log\n./shannon status [<workspace>]        # Live phase/agent progress of one scan, read from Temporal (redraws, then exits; same default target)\n# Dashboard: http://localhost:8233\n\n# Stop\n./shannon stop [<workspace>]          # Stop one scan (default: the single running scan; confirms first; --yes/-y to skip)\n./shannon stop --all                  # Stop all running scans (Temporal stays up; confirms first)\n./shannon reset                       # Stop everything and wipe all Temporal data + volumes (type 'confirm' to proceed; cannot be skipped)\n\n# Version\n./shannon version                     # npx: package version; local: git SHA\n\n# Image management\n./shannon build [--no-cache]          # Local mode: build worker image\n\n# Build TypeScript (development)\npnpm run build                       # Build all packages via Turborepo\npnpm run check                       # Type-check all packages\npnpm biome                           # Biome lint + format + import sorting check\npnpm biome:fix                       # Auto-fix lint, format, and import sorting\n```\n\n**Monorepo tooling:** pnpm workspaces, Turborepo for task orchestration, Biome for linting/formatting. TypeScript compiler options shared via `tsconfig.base.json` at the root. All packages extend it, overriding only `rootDir` and `outDir`. Shared devDependencies (`typescript`, `@types/node`, `turbo`, `@biomejs/biome`) are hoisted to the root workspace.\n\n**Options:** `-c <file>` (YAML config), `--models-config <file>` (pi `models.json` defining models pi's catalogue lacks), `-o <path>` (output directory), `-w <name>` (named workspace; auto-resumes if exists), `--validate-auth` (run preflight and auth validation only, then stop; no pentest or report; requires a fresh workspace and an `authentication` block in the config), `--validate-model` (run the preflight model checks only — credential/registry resolution plus the cyber-access verification for cyber-gated providers — then stop; no pentest or report; requires a fresh workspace; needs no config; mutually exclusive with `--validate-auth`), `--pipeline-testing` (minimal prompts, 10s retries), `--keep-container` (preserve worker container after exit for log inspection), `--yes`/`-y` (skip the confirmation prompt on `stop`; required for non-interactive use; `reset` requires a typed `confirm` and cannot be skipped)\n\n## Architecture\n\n### Monorepo Layout\n\n```\napps/cli/        — @keygraph/shannon (published to npm, bundled with tsdown)\napps/worker/     — @shannon/worker (private, Temporal worker + pipeline logic)\n```\n\n### CLI Package (`apps/cli/`)\nPublished as `@keygraph/shannon` on npm. Contains Docker orchestration and a direct `@temporalio/client` integration for read-only status plus bounded workflow lifecycle operations; no worker/pipeline business logic or prompts. Bundled with tsdown for single-file ESM output (deps stay external).\n\n- `apps/cli/src/index.ts` — CLI dispatcher (`setup`, `start`, `stop`, `reset`, `logs`, `status`, `scans`, `build`, `version`)\n- `apps/cli/src/temporal-client.ts` — `@temporalio/client` integration: connects to the frontend on `127.0.0.1:7233` (published by compose), provides `describeScan` (status + `pendingActivities` → running agents), `queryProgress` (live `getProgress` query → `PipelineState`), `getTerminalOutcome` (workflow `result()`), and bounded lifecycle RPCs for `stop`. `stop` requests cancellation first, waits up to 10 seconds, then requests termination only when necessary and verifies closure within a bounded window. No worker of its own; scans are visible within Temporal's retention window, which `ensureInfra` (`apps/cli/src/docker.ts`) converges to `168h` (7 days) on every successful `shannon start`; override with `SHANNON_TEMPORAL_RETENTION` (a positive whole-hour value like `72h`)\n- `apps/cli/src/scan/` — `status` rendering: `pipeline.ts` (static phase/agent plan + `run*Agent` activity-type→agent map + mirrored `PipelineState`/`AgentMetrics` types; keep in sync with the worker), `derive.ts` (pure phase/agent state derivation shared by the tree and `--json`), `render.ts` (one renderer for both the live query state and the terminal result). The tree shows model work only: every row is an agent, an Agentic SAST stage, or a report step that is currently running or failed. Reconciliation is model work owned by a class, so its wall time renders as a trailing `+ duration` on that class's exploitation row (its analysis row when `exploit: false`) rather than as a row of its own; deterministic bookkeeping stages (`report:*` renumber/assemble/finalize/surface) never appear once they complete. `DerivedPhase.children` (renders sub-rows) and `DerivedPhase.meta` (`duration` vs a `k/N done` tally) are independent — Agentic SAST lists stages under a duration, exploitation lists classes under a tally\n- `apps/cli/src/mode.ts` — Auto-detection: local mode if `SHANNON_LOCAL=1` env var is set\n- `apps/cli/src/docker.ts` — Compose lifecycle, image pull/build, and ephemeral `docker run` worker spawning. Each worker carries workspace, task-queue, and preselected workflow-ID labels so stop can correlate the local worker with its Temporal execution before `session.json` exists. Before `docker run`, start fsyncs that exact candidate under the workspace's hidden internals and clears it only when `session.json` registers the same ID; stop reconciles any candidate left by an interrupted launch. Start also checks the image's workflow-ID protocol label and refuses a stale worker that would ignore the preselected ID\n- `apps/cli/src/home.ts` — State directory management (`~/.shannon/` for npx, `./` for local)\n- `apps/cli/src/env.ts` — `.env` loading, TOML fallback (npx only) via `apps/cli/src/config/resolver.ts`, credential validation, provider-scoped env flag building\n- `apps/cli/src/model-spec.ts` — `SHANNON_AI_MODEL` (`<provider>:<model-id>`) parsing; mirrors `apps/worker/src/ai/models.ts`\n- `apps/cli/src/config/resolver.ts` — Cascading config (npx only): env vars → `~/.shannon/config.toml` (parsed with `smol-toml`)\n- `apps/cli/src/config/writer.ts` — TOML serialization and secure file persistence (0o600)\n- `apps/cli/src/commands/setup.ts` — Interactive TUI wizard (`@clack/prompts`) for provider credential setup (npx only)\n- `apps/cli/src/paths.ts` — Repo/config/models-config path resolution (any absolute or relative path). `MODELS_CONFIG_CONTAINER_PATH` is fixed at `/app/models.json` because the worker names it to pi rather than discovering it\n- `apps/cli/src/version.ts` — Version reporting (npx: `package.json` version; local: `git-<sha>`)\n- `apps/cli/src/tty.ts` — Terminal capability detection: `requireInteractive` guard (fails fast off-TTY instead of hanging on a prompt), `supportsColor` color gating (`NO_COLOR`/`FORCE_COLOR`), and `stdoutIsTerminal` for spinner/cursor output\n- `apps/cli/src/commands/` — Command handlers\n- `apps/cli/infra/compose.yml` — Bundled Temporal compose file for npx mode\n- `apps/cli/tsdown.config.ts` — tsdown bundler config\n- `shannon` — Node.js entry point (`#!/usr/bin/env node`) that delegates to `apps/cli/dist/index.mjs`\n\n### Docker Architecture\nInfra (Temporal) runs via `docker-compose.yml`. Workers are ephemeral `docker run --rm` containers, one per scan, each with a unique task queue, preselected workflow ID, matching identity labels, and isolated volume mounts. `shannon stop --all` takes the union of labeled running workers and Temporal-running workflows, so an orphaned workflow is still stopped after its worker has disappeared.\n\n- `docker-compose.yml` — Infra only: `shannon-temporal` (port 7233/8233). Network: `shannon-net`\n- `Dockerfile` — 2-stage build (builder + Chainguard Wolfi runtime). Uses pnpm. Entrypoint: `CMD [\"node\", \"apps/worker/dist/temporal/worker.js\"]`\n- No `docker-compose.docker.yml` — host gateway handled via `--add-host` flag in CLI\n- `/etc/hosts` forwarding — at worker spawn, `forwardEtcHostsFlags` in `apps/cli/src/docker.ts` reads the host's `/etc/hosts` and emits one `--add-host` flag per valid user-added entry. Loopback IPs (`127.x`, `::1`) are rewritten to `host-gateway`; IPv6 addresses are bracketed. Disable per-scan via `SHANNON_FORWARD_HOSTS=false`. Native Windows is refused at startup (`blockNativeWindows` in `apps/cli/src/index.ts`, pointing to the WSL2 guide in `docs/platforms.md`); WSL2 reads its own `/etc/hosts` via the Linux path.\n\n### Worker Package (`apps/worker/`)\n- `apps/worker/src/paths.ts` — Centralized path constants (`PROMPTS_DIR`, `CONFIGS_DIR`, `WORKSPACES_DIR`)\n- `apps/worker/src/session-manager.ts` — Agent definitions (`AGENTS` record). Agent types in `apps/worker/src/types/agents.ts`\n- `apps/worker/src/config-parser.ts` — YAML config parsing with JSON Schema validation\n- `apps/worker/src/ai/pi/pi-executor.ts` — pi harness integration (agent-level retry disabled so Temporal owns restarts; provider-level retry on, see `apps/worker/src/ai/pi/retry-settings.ts`)\n- `apps/worker/src/services/` — Business logic layer (Temporal-agnostic). Activities delegate here. Key: `agent-execution.ts`, `error-handling.ts`, `container.ts`\n- `apps/worker/src/types/` — Consolidated types: `Result<T,E>`, `ErrorCode`, `AgentName`, `ActivityLogger`, etc.\n- `apps/worker/src/utils/` — Shared utilities (file I/O, formatting, concurrency)\n\n### Temporal Orchestration\nDurable workflow orchestration with crash recovery, queryable progress, intelligent retry, and parallel execution (5 concurrent agents in vuln/exploit phases).\n\n- `apps/worker/src/temporal/workflows.ts` — Main workflow (`pentestPipelineWorkflow`)\n- `apps/worker/src/temporal/activities.ts` — Thin wrappers — heartbeat loop, error classification, container lifecycle. Business logic delegated to `apps/worker/src/services/`\n- `apps/worker/src/temporal/activity-logger.ts` — `TemporalActivityLogger` implementation of `ActivityLogger` interface\n- `apps/worker/src/temporal/summary-mapper.ts` — Maps `PipelineSummary` to `WorkflowSummary`\n- `apps/worker/src/temporal/worker.ts` — Combined worker + client entry point (per-invocation task queue, submits workflow, waits for result)\n- `apps/worker/src/temporal/shared.ts` — Types, interfaces, query definitions\n### Five-Phase Pipeline\n\n1. **Pre-Recon** (`pre-recon`) — Source code analysis to build the architectural baseline\n2. **Recon** (`recon`) — Attack surface mapping from initial findings\n3. **Vulnerability Analysis** (5 parallel agents) — injection, xss, auth, authz, ssrf\n4. **Exploitation** (5 parallel agents, conditional) — Exploits confirmed vulnerabilities\n5. **Reporting** (`report`) — Executive-level security report\n\nAround those phases:\n\n- Optional agentic static analysis runs before the pentest when `agentic_sast.enabled` is `\"true\"`, as a child workflow.\n- After each class's analysis, reconciliation groups its findings into exploitation tasks.\n- Findings outside the five classes form an internal `miscellaneous` class with its own exploitation agent (`miscellaneous-exploit`).\n- A scan can finish `completed`, `partial`, `failed`, or `cancelled`; `partial` carries an ordered set of reasons.\n\n### Supporting Systems\n- **Configuration** — YAML configs in `apps/worker/configs/` use the closed JSON Schema in `config-schema.json`. Every fresh scan runs the fixed five analysis classes; there is no public class selector. `agentic_sast.enabled` is the only public agentic-SAST setting. Finding reconciliation runs on every scan and has no public setting of its own. Config also supports authentication (MFA/TOTP), URL/code rule scoping (`rules.avoid`/`rules.focus`), `exploit`, free-form `rules_of_engagement`, and post-hoc `report` options (`min_severity`, `min_confidence`, `guidance`, and exploit-only `sarif` output via `apps/worker/src/services/sarif-renderer.ts`, on by default for exploit runs and opt out with `report.sarif: \"false\"`). `code_path` avoid rules are enforced via the `@gotgenes/pi-permission-system` extension: `apps/worker/src/temporal/activities.ts:syncCodePathDenyRules` writes a global `path` deny config once per workflow (`apps/worker/src/ai/pi/permission-system.ts:syncPermissionSystemConfig`), and the executor loads the extension when that config is present (`apps/worker/src/ai/pi/pi-executor.ts`), so denies fire across every tool and child `task` session. Credential resolution — local mode: env vars → `./.env`; npx mode: env vars → `~/.shannon/config.toml` (via `npx @keygraph/shannon setup`)\n- **Agentic SAST progress** — Capella runs as a child workflow, so its activities are absent from the parent's `pendingActivities` and invisible to the CLI. The child signals each stage boundary up via `capellaStageProgress` (`apps/worker/src/temporal/shared.ts`); the parent's handler validates the payload and writes the child-supplied `startedAt` and `durationMs` directly to `operationalStages['agentic-sast:<stage>']`, so both the live `getProgress` query and the terminal result carry per-stage rows. Signalling is best-effort and every failure is swallowed — a closed or unreachable parent must never fail a SAST run. `CAPELLA_STAGE_LABELS` in `apps/worker/src/ai/sast/types.ts` is the one label table, shared by the scan log and the status tree; `CAPELLA_PROGRESS_STAGES` omits `export`, which runs no model and so never becomes a row. Scans predating the signal keep the aggregate `agentic-sast` span and render as a bare phase line\n- **Prompts** — Per-phase templates in `apps/worker/prompts/` with variable substitution (`{{TARGET_URL}}`, `{{CONFIG_CONTEXT}}`). Shared partials in `apps/worker/prompts/shared/` via `apps/worker/src/services/prompt-manager.ts`, including `_code-path-rules.txt` (focus/avoid `[FILE]`/`[GLOB]` routing) and `_rules-of-engagement.txt` (free-text engagement rules). When `exploit: false`, `apps/worker/src/services/findings-renderer.ts` deterministically converts each `*_exploitation_queue.json` into a `*_findings.md` for report assembly — no LLM in the loop\n- **Agent Harness (pi)** — Uses the **pi harness** (`@earendil-works/pi-coding-agent`, requires Node ≥ 22.19) via `apps/worker/src/ai/pi/pi-executor.ts` (`runPiPrompt` → `createAgentSession`). Retry is split in `apps/worker/src/ai/pi/retry-settings.ts`: pi's agent-level loop is off so Temporal owns agent restarts, while `provider.maxRetries` stays on — pi reads the `provider` block independently of the `enabled` flag — so transport faults are absorbed in-session rather than costing a full agent re-run. `maxRetryDelayMs` is left at pi's 60s default. One model runs every phase, named by `SHANNON_AI_MODEL=<provider>:<model-id>` (default `anthropic:claude-sonnet-4-6`). `apps/worker/src/ai/models.ts` parses the spec — splitting on the **first** colon only, so Bedrock IDs keep theirs — and resolves it through pi's `ModelRuntime`. pi ships the `CredentialStore` interface but no in-memory implementation (its own reads `auth.json` from disk), so `RuntimeCredentialStore` in that file supplies one: credentials arrive as env vars in an ephemeral container and must never touch disk. `createModelRuntime(providerId, apiKey)` builds the runtime with `allowModelNetwork: true`, so `ModelRuntime.create()` refreshes the model catalogue over the network at scan start and a freshly released model resolves without a `--models-config` file. The fetch is bounded (10s) and falls back to the static catalogue on timeout, so an unreachable catalogue endpoint cannot hang the scan. The refresh does not override a `--models-config`: pi reloads and re-applies that file as a config overlay on every refresh (it reloads `this.config` at the top of `refresh()`), so custom definitions still win over the fetched catalogue; the merge semantics below are unchanged, just layered over a fresher base. `resolveModelSelection()` is **async** because `ModelRuntime.create()` is. Any pi-ai provider id is accepted — `parseModelSpec` no longer rejects against a hardcoded list, so pi's registry is the authority (an unknown provider/model surfaces as a clear \"not found in pi registry\" error at preflight, which points to the browsable catalogue at `pi.dev/models` — `PI_CATALOG_URL` in `apps/worker/src/ai/models.ts`, appended to the not-found errors and shown in the setup wizard's \"Other provider\" hint). Four providers are **curated** (`CURATED_PROVIDERS`: `anthropic`, `openai`, `xai`, `amazon-bedrock`) with their own credential variables, config sections, and setup flows; each provider's API key env var is declared once in `PROVIDER_API_KEY_ENV` — Shannon uses each vendor's own variable name (`OPENAI_API_KEY`, `XAI_API_KEY`, …), never an invented one; Bedrock's entry is `AWS_BEARER_TOKEN_BEDROCK`, paired with `AWS_REGION`, which preflight requires separately as provider config rather than a credential. Any other provider uses the **generic** credential path: `SHANNON_AI_API_KEY` (`GENERIC_API_KEY_ENV`) supplies the key for any provider whose credential is a plain API key. Curated providers' own variables take precedence over it, and it also works as a fallback for them — Bedrock is the sole exception (it authenticates through its AWS_ variables, so the generic key never stands in for it). The CLI forwards `SHANNON_AI_API_KEY` in `COMMON_FORWARD_VARS` (it is provider-neutral, binding to whatever `SHANNON_AI_MODEL` names, so the \"only one provider configured\" guard counts only named credentials), and stores it under a generic `[provider]` config.toml section (`provider.api_key`). `npx @keygraph/shannon setup` exposes this as the \"Other provider\" option: free-text provider id + model id + key (a curated provider id is rejected there, since it has its own option). A model pi's catalogue does not carry, such as a self-hosted model, is reachable without an SDK bump: `--models-config <file>` mounts a pi `models.json` read-only at `/app/models.json`. The mount is the entire CLI→worker protocol: nothing is forwarded through the environment, and `modelsConfigPath()` detects the file at that fixed path, exactly as `piAuthPresent()` detects the pi auth mount whose flag is likewise not forwarded (`MODELS_CONFIG_CONTAINER_PATH` in the CLI and `MODELS_CONFIG_PATH` in `apps/worker/src/paths.ts` must stay in sync). `createModelRuntime` always names `modelsPath` explicitly — the mounted path, or **`null` when no config was supplied**, which switches models.json off outright. It is never left to pi's default of `<agent dir>/models.json`, because that dir is shared with the pi auth mount, so a file landing there must not silently contribute model definitions to a scan that did not ask for one. `modelsStorePath` is pinned to the agent dir alongside it, since pi otherwise derives it from `dirname(modelsPath)` and would try to write beside a read-only mount. Custom definitions merge over the built-in catalogue: a matching model id replaces the built-in entry, a new id is added alongside, and `modelOverrides` adjusts a built-in without replacing the provider's list. Omitted fields take pi's defaults (`contextWindow` 128000, `maxTokens` 16384), so a large-context model needs them stated. Credentials are unaffected: `RuntimeCredentialStore` outranks any `apiKey` the file carries, so a snippet pasted from `pi.dev/models` can keep its placeholder key while the real secret stays in the environment — and pi's `!command` config-value form is never reached through `apiKey`. Preflight reports `modelRuntime.getError()` **before** the \"model not found\" check, because a file that fails to parse, fails schema validation, or composes badly otherwise leaves pi with an empty or fallback provider and surfaces as a model-id error that blames `SHANNON_AI_MODEL`. `SHANNON_AI_BASE_URL` overrides the endpoint for any provider (proxies/gateways); the credential and API dialect are unchanged. It is applied after model resolution, so it also wins over a `baseUrl` set in a model config. A base URL only changes the address — `resolveModel` (`apps/worker/src/ai/models.ts`) carries it onto the model descriptor and nothing else, and it grants **no exemption from registry validation**: the model id must resolve for every provider and endpoint alike. A gateway serving a model under its own name is described in a `--models-config` file, which puts a real descriptor in the registry rather than guessing one. pi's builtin `openai` provider serves a single API (Responses) and dispatches on the provider rather than `model.api`, so an `openai:` gateway run always speaks Responses; a Chat-Completions-only gateway must be reached through a completions-native provider such as `openrouter` instead. `buildEnvFlags` forwards only the selected provider's credential into the worker container. The CLI mirrors the parse rule and the provider/credential tables in `apps/cli/src/model-spec.ts` (it cannot import from the worker package); the two must stay in sync. pi ships no JSON-schema output or `Task`/`TodoWrite` built-ins, so structured queues are captured via a `submit_exploitation_queue` custom tool (`apps/worker/src/ai/queue-schemas.ts`), and `task` (child sessions scoped to `read`, `grep`, `find`, `ls`, `write`, and `bash` — no nested `task` or collector tools; `CHILD_TOOLS` in `apps/worker/src/ai/pi/task-tool.ts`) + `todo_write` (`apps/worker/src/ai/pi/session-tools.ts`) are provided as custom tools; the per-phase collectors are pi custom tools (TypeBox `defineTool` in `apps/worker/src/collectors/`). Every agent `createAgentSession` call passes `thinkingLevel: PI_THINKING_LEVEL` (`'high'`, `apps/worker/src/ai/pi/thinking-level.ts`) for deeper analysis than pi's default (`medium`). The preflight credential probe (`apps/worker/src/services/preflight.ts`) passes none and runs at pi's default. The reconciliation-enrichment `completeSimple` call (`apps/worker/src/ai/pi/structured-generation.ts`) passes no `reasoning`, so it runs with thinking off. There is no public thinking setting. Browser automation via `playwright-cli` with session isolation (`-s=<session>`). TOTP generation via `generate-totp` CLI tool. Login flow template at `apps/worker/prompts/shared/login-instructions.txt` supports form, SSO, API, and basic auth. On authenticated whitebox scans, the `validate-authentication` preflight performs the single real login and saves the browser session to `auth-state.json` in the per-session audit directory (path from `authStateFile()` in `apps/worker/src/audit/utils.ts`, derived from `generateAuditPath()`). The validation activity (`apps/worker/src/services/validate-authentication.ts`) removes any stale file from a prior run before the agent runs and verifies the file parses and contains cookies or storage before the preflight is marked complete; `logWorkflowComplete` deletes it when the workflow ends so authenticated cookies don't sit on disk between scans. Agent prompts opt in to session reuse by `@include(shared/_shared-session.txt)` before their `<login_instructions>` block — the partial restores the session and falls through to the full login flow if verification fails. `vuln-auth`/`exploit-auth` omit the include and own their own login\n- **Pi Credential Reuse** — `SHANNON_USE_PI_AUTH=1` opts into reusing the host's Pi login, including an `openai-codex` ChatGPT Plus/Pro subscription (`SHANNON_AI_MODEL=openai-codex:<model-id>`) or an `xai` Grok subscription (`SHANNON_AI_MODEL=xai:<model-id>`); the mechanism is provider-agnostic and works for any Pi login. `apps/cli/src/env.ts` requires `~/.pi/agent/auth.json`; `start.ts` passes its path to `spawnWorker`, which mounts only that file read-write at `/tmp/.pi/agent/auth.json`. The flag itself is not forwarded: the worker detects the file with `piAuthPresent()` and passes its path to `ModelRuntime.create`. CLI and worker API-key presence checks are skipped on this path, but the normal preflight model probe still validates the credential. The image and UID-remapping entrypoint keep `/tmp/.pi/agent` owned by `pentest` so adjacent Pi/Shannon configuration remains writable. Refreshed OAuth state is persisted to the host for subsequent scans.\n- **Audit System** — Crash-safe append-only logging in `workspaces/{hostname}_{sessionId}/`. The run directory's top level holds the human-facing report in both formats (`Security-Assessment-Report.pdf` and `Security-Assessment-Report.md`, `FINAL_REPORT_PDF_FILENAME`/`FINAL_REPORT_MD_FILENAME` in `apps/worker/src/paths.ts`); everything else — deliverables, per-agent logs, prompts, `session.json`, `workflow.log`, and browser artifacts — is nested under a hidden `.shannon/` internals dir (`INTERNAL_DIR`) so a customer sees only the report. Audit path helpers route through `generateInternalPath` (`apps/worker/src/audit/utils.ts`); the CLI nests the overlay backing dirs under the same `.shannon/` (`apps/cli/src/docker.ts`, `start.ts`). `session.json`/`workflow.log` reads use dual-read resolvers (`resolveSessionJsonPath`, `resolveRunFile`) that prefer `.shannon/` and fall back to the legacy run-root layout, so pre-restructure workspaces stay listable (`scans`/`logs`) without migration. A pre-restructure workspace cannot be resumed: `classifyWorkspaceLaunch` (`apps/cli/src/commands/start.ts`) requires `.shannon/launch.json`, and its absence fails the launch as \"created by an earlier version of Shannon\" before anything on disk is touched. There is no in-place migration — the workspace's files and report are left untouched, and the operator starts a new scan under a different `-w` name. The report agent writes structured findings to `report.json`, from which `report-renderer.ts` renders the assembled markdown and `report-json-adapter.ts` produces the Typst-shaped JSON that `pdf-renderer.ts` compiles into `comprehensive_security_assessment_report.pdf` using the bundled `apps/worker/templates/typst/report.typ` template (the `typst` binary is installed in the worker image). `copyReportToRunRoot` (`apps/worker/src/services/reporting.ts`) surfaces both the PDF and the markdown to the run root as `Security-Assessment-Report.pdf` and `Security-Assessment-Report.md`; the deliverables-dir copies remain as the git-checkpointed sources. PDF compilation is best-effort — a failure is logged and the run still completes. WorkflowLogger (`apps/worker/src/audit/workflow-logger.ts`) provides unified human-readable per-workflow logs, backed by LogStream (`apps/worker/src/audit/log-stream.ts`) shared stream primitive. Every combined-log line is also projected into a per-agent file under `.shannon/agents/<slug>.log` (one per pipeline agent, one per Capella stage; subagents fold into the parent's file, and a stage's concurrent sessions share its file with an inline session label). The projection boundary is `apps/worker/src/audit/actor-projection.ts` (`projectActor` maps a `TraceActor` to its combined prefix and owning file slug — slugs come only from closed fields); fan-out is best-effort and never blocks the canonical combined log. A lifecycle owner holds a `LogStream` lease per agent file (the pipeline agent's `logAgent` span, or a Capella stage activity's `try/finally`) so per-line writes ride the reference count; `CapellaStageTrace.drain()` flushes a stage's trace queue before its activity returns. The CLI tails one file with `shannon logs --agent <name>` (`--list-agents` to enumerate); the default `shannon logs` path is unchanged\n- **Deliverables** — Saved to `.shannon/deliverables/` in the target repo via the `save-deliverable` CLI script (`apps/worker/src/scripts/save-deliverable.ts`)\n- **Workspaces & Resume** — Named workspaces via `-w <name>` or auto-named from URL+timestamp. Resume detects completed agents via `session.json`. `loadResumeState()` in `apps/worker/src/temporal/activities.ts` validates deliverable existence, restores git checkpoints, and cleans up incomplete deliverables\n\n## Development Notes\n\n### Adding a New Agent\n1. Define agent in `apps/worker/src/session-manager.ts` (add to `AGENTS` record). `ALL_AGENTS`/`AgentName` types live in `apps/worker/src/types/agents.ts`\n2. Create prompt template in `apps/worker/prompts/` (e.g., `vuln-newtype.txt`)\n3. Two-layer pattern: add a thin activity wrapper in `apps/worker/src/temporal/activities.ts` (heartbeat + error classification). `AgentExecutionService` in `apps/worker/src/services/agent-execution.ts` handles the agent lifecycle automatically via the `AGENTS` registry\n4. Register activity in `apps/worker/src/temporal/workflows.ts` within the appropriate phase\n\n### Modifying Prompts\n- Variable substitution: `{{TARGET_URL}}`, `{{CONFIG_CONTEXT}}`, `{{LOGIN_INSTRUCTIONS}}`\n- Shared partials in `apps/worker/prompts/shared/` included via `apps/worker/src/services/prompt-manager.ts`\n- Test with `--pipeline-testing` for fast iteration\n\n### Key Design Patterns\n- **Configuration-Driven** — YAML configs with JSON Schema validation\n- **Progressive Analysis** — Each phase builds on previous results\n- **Harness-First** — the pi harness (`@earendil-works/pi-coding-agent`) handles autonomous analysis\n- **Modular Error Handling** — `ErrorCode` enum, `Result<T,E>` for explicit error propagation, automatic retry (3 attempts per agent)\n- **Services Boundary** — Activities are thin Temporal wrappers; `apps/worker/src/services/` owns business logic, accepts `ActivityLogger`, returns `Result<T,E>`. No Temporal imports in services\n- **DI Container** — Per-workflow in `apps/worker/src/services/container.ts`. `AuditSession` excluded (parallel safety)\n- **Ephemeral Workers** — Each scan runs in its own `docker run --rm` container with a per-invocation task queue. Temporal routes activities by queue name, so per-scan queues ensure activities never land on a worker with the wrong repo mounted\n\n### Security\nDefensive security tool only. Use only on systems you own or have explicit permission to test.\n\n## Code Style Guidelines\n\n### Formatting\nBiome handles formatting and linting. Run `pnpm biome:fix` to auto-fix. Config in `biome.json`: single quotes, semicolons, trailing commas, 2-space indent, 120 char line width.\n\n### Clarity Over Brevity\n- Optimize for readability, not line count — three clear lines beat one dense expression\n- Use descriptive names that convey intent\n- Prefer explicit logic over clever one-liners\n\n### Structure\n- Keep functions focused on a single responsibility\n- Use early returns and guard clauses instead of deep nesting\n- Never use nested ternary operators — use if/else or switch\n- Extract complex conditions into well-named boolean variables\n\n### TypeScript Conventions\n- Use `function` keyword for top-level functions (not arrow functions)\n- Explicit return type annotations on exported/top-level functions\n- Prefer `readonly` for data that shouldn't be mutated\n- `exactOptionalPropertyTypes` is enabled — use spread for optional props, not direct `undefined` assignment\n\n### Avoid\n- Combining multiple concerns into a single function to \"save lines\"\n- Dense callback chains when sequential logic is clearer\n- Sacrificing readability for DRY — some repetition is fine if clearer\n- Abstractions for one-time operations\n- Backwards-compatibility shims, deprecated wrappers, or re-exports for removed code — delete the old code, don't preserve it\n\n### Comments\nComments must be **timeless** — no references to this conversation, refactoring history, or the AI.\n\n**Patterns used in this codebase:**\n- `/** JSDoc */` — file headers (after license) and exported functions/interfaces\n- `// N. Description` — numbered sequential steps inside function bodies. Use when a\n  function has 3+ distinct phases where at least one isn't immediately obvious from the\n  code. Each step marks the start of a logical phase. Reference: `AgentExecutionService.execute`\n  (steps 1-9) and `injectModelIntoReport` (steps 1-5)\n- `// === Section ===` — high-level dividers between groups of functions in long files,\n  or to label major branching/classification blocks (e.g., `// === SPENDING CAP SAFEGUARD ===`).\n  Not for sequential steps inside function bodies — use numbered steps for that\n- `// NOTE:` / `// WARNING:` / `// IMPORTANT:` — gotchas and constraints\n\n**Never:** obvious comments, conversation references (\"as discussed\"), history (\"moved from X\")\n\n## Key Files\n\n**CLI:** `shannon` (entry point), `apps/cli/src/index.ts` (dispatcher), `apps/cli/src/docker.ts` (orchestration), `apps/cli/src/mode.ts` (auto-detection)\n\n**Entry Points:** `apps/worker/src/temporal/workflows.ts`, `apps/worker/src/temporal/activities.ts`, `apps/worker/src/temporal/worker.ts`\n\n**Core Logic:** `apps/worker/src/session-manager.ts`, `apps/worker/src/ai/pi/pi-executor.ts`, `apps/worker/src/ai/pi/permission-system.ts` (writes `code_path` deny rules to the `@gotgenes/pi-permission-system` global config), `apps/worker/src/config-parser.ts`, `apps/worker/src/services/` (incl. `preflight.ts`, `findings-renderer.ts`, `reporting.ts`), `apps/worker/src/audit/`\n\n**Config:** `docker-compose.yml`, `apps/cli/infra/compose.yml`, `apps/worker/configs/`, `apps/worker/prompts/`, `tsconfig.base.json` (shared compiler options), `turbo.json`, `biome.json`\n\n**CI/CD:** `.github/workflows/release.yml` (Docker Hub push + npm publish + GitHub release, manual dispatch)\n\n## Package Installation\n\nPackage managers are configured with a minimum release age (7 days). Requires pnpm >= 10.16.0. If `pnpm install` fails due to a package being too new, **do not attempt to bypass it** — report the blocked package to the user and stop.\n\n## Troubleshooting\n\n- **\"Repository not found\"** — Pass a path to the target repo (`-r /path/to/repo` or `-r ./my-repo`)\n- **\"Temporal not ready\"** — Wait for health check or `docker compose logs temporal`\n- **Worker not processing** — Check `docker ps --filter \"name=shannon-worker-\"`\n- **Reset state** — `./shannon reset`\n- **Local apps unreachable** — Use `host.docker.internal` instead of `localhost`\n- **Container permissions** — On Linux, may need `sudo` for docker commands\n","isInternal":false,"tokens":8737,"sizeBytes":36429},{"name":"debug.md","path":".claude/commands/debug.md","rawUrl":"https://raw.githubusercontent.com/KeygraphHQ/shannon/HEAD/.claude/commands/debug.md","title":"Command: /debug","category":"command-prompt","format":"markdown","content":"---\ndescription: Systematically debug errors using context analysis and structured recovery\n---\n\nYou are debugging an issue. Follow this structured approach to avoid spinning in circles.\n\n## Step 1: Capture Error Context\n- Read the full error message and stack trace\n- Identify the layer where the error originated:\n  - **CLI/Args** - Input validation, path resolution\n  - **Config Parsing** - YAML parsing, JSON Schema validation (`src/config-parser.ts`)\n  - **Session Management** - Agent definitions (`src/session-manager.ts`), mutex (`src/utils/concurrency.ts`)\n  - **DI Container** - Container initialization/lookup (`src/services/container.ts`)\n  - **Services** - AgentExecutionService, ConfigLoaderService, ExploitationCheckerService, error-handling (`src/services/`)\n  - **Audit System** - Logging, metrics tracking, atomic writes (`src/audit/`)\n  - **Claude SDK** - Agent execution, MCP servers, turn handling (`src/ai/claude-executor.ts`)\n  - **Git Operations** - Checkpoints, rollback, commit (`src/services/git-manager.ts`)\n  - **Validation** - Deliverable checks, queue validation (`src/services/queue-validation.ts`)\n\n## Step 2: Check Relevant Logs\n\n**Session audit logs:**\n```bash\n# Find most recent session\nls -lt workspaces/ | head -5\n\n# Check session metrics and errors\ncat workspaces/<session>/session.json | jq '.errors, .agentMetrics'\n\n# Check agent execution logs\nls -lt workspaces/<session>/agents/\ncat workspaces/<session>/agents/<latest>.log\n```\n\n## Step 3: Trace the Call Path\n\nFor Shannon, trace through these layers:\n\n1. **Worker + Client** → `src/temporal/worker.ts` - Combined worker + workflow submission\n2. **Workflow** → `src/temporal/workflows.ts` - Pipeline orchestration\n3. **Activities** → `src/temporal/activities.ts` - Thin wrappers: heartbeat, error classification\n4. **Container** → `src/services/container.ts` - Per-workflow DI\n5. **Services** → `src/services/agent-execution.ts` - Agent lifecycle\n6. **Config** → `src/config-parser.ts` via `src/services/config-loader.ts`\n7. **Prompts** → `src/services/prompt-manager.ts`\n8. **Audit** → `src/audit/audit-session.ts` - Logging facade, metrics tracking\n9. **Executor** → `src/ai/claude-executor.ts` - SDK calls, MCP setup, retry logic\n10. **Validation** → `src/services/queue-validation.ts` - Deliverable checks\n\n## Step 4: Identify Root Cause\n\n**Common Shannon-specific issues:**\n\n| Symptom | Likely Cause | Fix |\n|---------|--------------|-----|\n| Agent hangs indefinitely | MCP server crashed, Playwright timeout | Check Playwright logs in `/tmp/playwright-*` |\n| \"Validation failed: Missing deliverable\" | Agent didn't create expected file | Check `deliverables/` dir, review prompt |\n| Git checkpoint fails | Uncommitted changes, git lock | Run `git status`, remove `.git/index.lock` |\n| \"Session limit reached\" | Claude API billing limit | Not retryable - check API usage |\n| Parallel agents all fail | Shared resource contention | Check mutex usage, stagger startup timing |\n| Cost/timing not tracked | Metrics not reloaded before update | Add `metricsTracker.reload()` before updates |\n| session.json corrupted | Partial write during crash | Delete and restart, or restore from backup |\n| YAML config rejected | Invalid schema or unsafe content | Run through AJV validator manually |\n| Prompt variable not replaced | Missing `{{VARIABLE}}` in context | Check `src/services/prompt-manager.ts` interpolation |\n| Service returns Err result | Check `ErrorCode` in Result | Trace through `classifyErrorForTemporal()` in `src/services/error-handling.ts` |\n| Container not found | `getOrCreateContainer()` not called | Check activity setup code in `src/temporal/activities.ts` |\n| ActivityLogger undefined | `createActivityLogger()` not called | Must be called at top of each activity function |\n\n**MCP Server Issues:**\n```bash\n# Check if Playwright browsers are installed\nnpx playwright install chromium\n\n# Check MCP server startup (look for connection errors)\ngrep -i \"mcp\\|playwright\" workspaces/<session>/agents/*.log\n```\n\n**Git State Issues:**\n```bash\n# Check for uncommitted changes\ngit status\n\n# Check for git locks\nls -la .git/*.lock\n\n# View recent git operations from Shannon\ngit reflog | head -10\n```\n\n## Step 5: Apply Fix with Retry Limit\n\n- **CRITICAL**: Track consecutive failed attempts\n- After **3 consecutive failures** on the same issue, STOP and:\n  - Summarize what was tried\n  - Explain what's blocking progress\n  - Ask the user for guidance or additional context\n- After a successful fix, reset the failure counter\n\n## Step 6: Validate the Fix\n\n**For code changes:**\n```bash\n# Compile TypeScript\nnpx tsc --noEmit\n\n# Quick validation run\nshannon <URL> <REPO> --pipeline-testing\n```\n\n**For audit/session issues:**\n- Verify `session.json` is valid JSON after fix\n- Check that atomic writes complete without errors\n- Confirm mutex release in `finally` blocks\n\n**For agent issues:**\n- Verify deliverable files are created in correct location\n- Check that validation functions return expected results\n- Confirm retry logic triggers on appropriate errors\n\n## Anti-Patterns to Avoid\n\n- Don't delete `session.json` without checking if session is active\n- Don't modify git state while an agent is running\n- Don't retry billing/quota errors (they're not retryable)\n- Don't ignore PentestError type - it indicates the error category\n- Don't make random changes hoping something works\n- Don't fix symptoms without understanding root cause\n- Don't bypass mutex protection for \"quick fixes\"\n\n## Quick Reference: Error Types\n\n`ErrorCode` enum in `src/types/errors.ts` provides finer-grained classification used by `classifyErrorForTemporal()` in `src/services/error-handling.ts`.\n\n| PentestError Type | Meaning | Retryable? |\n|-------------------|---------|------------|\n| `config` | Configuration file issues | No |\n| `network` | Connection/timeout issues | Yes |\n| `prompt` | Claude SDK/API issues | Sometimes |\n| `filesystem` | File read/write errors | Sometimes |\n| `validation` | Deliverable validation failed | Yes (via retry) |\n| `billing` | API quota/billing limit | No |\n| `unknown` | Unexpected error | Depends |\n\n---\n\nNow analyze the error and begin debugging systematically.\n","frontmatter":{"description":"Systematically debug errors using context analysis and structured recovery"},"isInternal":false,"tokens":1436,"sizeBytes":6203},{"name":"pr.md","path":".claude/commands/pr.md","rawUrl":"https://raw.githubusercontent.com/KeygraphHQ/shannon/HEAD/.claude/commands/pr.md","title":"Command: /pr","category":"command-prompt","format":"markdown","content":"---\ndescription: Create a PR to main branch using conventional commit style for the title\n---\n\nCreate a pull request from the current branch to the `main` branch.\n\n## Arguments\n\nThe user may provide issue numbers that this PR fixes: `$ARGUMENTS`\n\n- If provided (e.g., `123` or `123,456`), use these issue numbers\n- If not provided, check the branch name for issue numbers (e.g., `fix/123-bug` or `issue-456-feature` → extract `123` or `456`)\n- If no issues are found, omit the \"Closes\" section\n\n## Steps\n\nFirst, analyze the current branch to understand what changes have been made:\n1. Run `git log --oneline -10` to see recent commit history and understand commit style\n2. Run `git log main..HEAD --oneline` to see all commits on this branch that will be included in the PR\n3. Run `git diff main...HEAD --stat` to see a summary of file changes\n4. Run `git branch --show-current` to get the branch name for issue detection (if no explicit issues provided)\n\nThen generate a PR title that:\n- Follows conventional commit format (e.g., `fix:`, `feat:`, `chore:`, `refactor:`)\n- Is concise and accurately describes the changes\n- Matches the style of recent commits in the repository\n\nGenerate a PR body with:\n- A `## Summary` section using rich bullets with bold action leads\n- A `Closes #X` line for each issue number (if any were provided or detected from branch name)\n\nEach Summary bullet must follow this format:\n- **Bold action phrase** (imperative verb: \"Add X\", \"Replace Y\", \"Fix Z\") — followed by em dash and a 1-2 sentence conceptual description of what changed and why\n- Keep descriptions conceptual — no inline code references (no backticks for function/file names). The diff shows the code\n- Use 2-5 bullets, scaling with PR size. Group related changes into single bullets rather than listing every file touched\n\nExample:\n```\n## Summary\n\n- **Add preflight validation** — validates repo path, config, and credentials before agent execution. Fails fast with actionable errors\n- **Replace error strings** — pipe-delimited segments rendered as multi-line blocks with phase context, type, message, and remediation hint\n- **Add error classification** — new error codes for repo, auth, and billing failures with proper retry classification\n```\n\nFinally, create the PR using the gh CLI:\n```\ngh pr create --base main --title \"<generated title>\" --body \"$(cat <<'EOF'\n## Summary\n<rich bullets>\n\nCloses #<issue1>\nCloses #<issue2>\nEOF\n)\"\n```\n\nNote: Omit the \"Closes\" lines entirely if no issues are associated with this PR.\n\nIMPORTANT:\n- Do NOT include any Claude Code attribution in the PR\n- Use the conventional commit prefix that best matches the changes (fix, feat, chore, refactor, docs, etc.)\n- The `Closes #X` syntax will automatically close the referenced issues when the PR is merged\n","frontmatter":{"description":"Create a PR to main branch using conventional commit style for the title"},"isInternal":false,"tokens":667,"sizeBytes":2799},{"name":"review.md","path":".claude/commands/review.md","rawUrl":"https://raw.githubusercontent.com/KeygraphHQ/shannon/HEAD/.claude/commands/review.md","title":"Command: /review","category":"command-prompt","format":"markdown","content":"---\ndescription: Review code changes for Shannon-specific patterns, security, and common mistakes\n---\n\nReview the current changes (staged or working directory) with focus on Shannon-specific patterns and common mistakes.\n\n## Step 1: Gather Changes\nRun these commands to understand the scope:\n```bash\ngit diff --stat HEAD\ngit diff HEAD\n```\n\n## Step 2: Check Shannon-Specific Patterns\n\n### Error Handling (CRITICAL)\n- [ ] **All errors use PentestError** - Never use raw `Error`. Use `new PentestError(message, type, retryable, context)`\n- [ ] **Error type is appropriate** - Use correct type: 'config', 'network', 'tool', 'prompt', 'filesystem', 'validation', 'billing', 'unknown'\n- [ ] **Retryable flag matches behavior** - If error will be retried, set `retryable: true`\n- [ ] **Context includes debugging info** - Add relevant paths, tool names, error codes to context object\n- [ ] **Never swallow errors silently** - Always log or propagate errors\n- [ ] **Use ErrorCode enum** - Prefer `ErrorCode.CONFIG_INVALID` over string matching for classification\n- [ ] **Result<T,E> for service returns** - Services return `Result`, not throw\n\n### Audit System & Concurrency (CRITICAL)\n- [ ] **Mutex protection for parallel operations** - Use `sessionMutex.lock()` when updating `session.json` during parallel agent execution\n- [ ] **Reload before modify** - Always call `this.metricsTracker.reload()` before updating metrics in mutex block\n- [ ] **Atomic writes for session.json** - Use `atomicWrite()` for session metadata, never `fs.writeFile()` directly\n- [ ] **Stream drain handling** - Log writes must wait for buffer drain before resolving\n- [ ] **Semaphore release in finally** - Git semaphore must be released in `finally` block\n\n### Claude SDK Integration (CRITICAL)\n- [ ] **MCP server configuration** - Verify Playwright MCP uses `--isolated` and unique `--user-data-dir`\n- [ ] **Prompt variable interpolation** - Check all `{{VARIABLE}}` placeholders are replaced\n- [ ] **Turn counting** - Increment `turnCount` on assistant messages, not tool calls\n- [ ] **Cost tracking** - Extract cost from final `result` message, track even on failure\n- [ ] **API error detection** - Check for \"session limit reached\" (fatal) vs other errors\n\n### Configuration & Validation (CRITICAL)\n- [ ] **FAILSAFE_SCHEMA for YAML** - Never use default schema (prevents code execution)\n- [ ] **Security pattern detection** - Check for path traversal (`../`), HTML injection (`<>`), JavaScript URLs\n- [ ] **Rule conflict detection** - Rules cannot appear in both `avoid` AND `focus`\n- [ ] **Duplicate rule detection** - Same `type:url_path` cannot appear twice\n- [ ] **JSON Schema validation before use** - Config must pass AJV validation\n\n### Services Layer & DI Container (CRITICAL)\n- [ ] **Business logic in services, not activities** — Activities: heartbeat loop, error classification, container calls only. Domain logic → `src/services/`\n- [ ] **Services accept ActivityLogger** — Never import `@temporalio/*` in services. Use `ActivityLogger` interface from `src/types/`\n- [ ] **Result type for fallible operations** — Service methods return `Result<T, PentestError>`, unwrap with `isOk()`/`isErr()`. Activities call `executeOrThrow()` at the boundary\n- [ ] **Container lifecycle** — `getOrCreateContainer()` at activity start, `removeContainer()` only in workflow cleanup\n- [ ] **AuditSession not in container** — Must be passed per-agent call (parallel safety)\n\n### Session & Agent Management (CRITICAL)\n- [ ] **Deliverable dependencies respected** - Exploitation agents only run if vulnerability queue exists AND has items\n- [ ] **Queue validation before exploitation** - Use `safeValidateQueueAndDeliverable()` to check eligibility\n- [ ] **Git checkpoint before agent run** - Create checkpoint for rollback on failure\n- [ ] **Git rollback on retry** - Call `rollbackGitWorkspace()` before each retry attempt\n- [ ] **Agent prerequisites checked** - Verify prerequisite agents completed before running dependent agent\n\n### Parallel Execution\n- [ ] **Promise.allSettled for parallel agents** - Never use `Promise.all` (partial failures should not crash batch)\n- [ ] **Staggered startup** - 2-second delay between parallel agent starts to prevent API throttle\n- [ ] **Individual retry loops** - Each agent retries independently (3 attempts max)\n- [ ] **Results aggregated correctly** - Handle both 'fulfilled' and 'rejected' results from `Promise.allSettled`\n\n## Step 3: TypeScript Safety\n\n### Type Assertions (WARNING)\n- [ ] **No double casting** - Never use `as unknown as SomeType` (bypasses type safety)\n- [ ] **Validate before casting** - JSON parsed data should be validated (JSON Schema) before `as Type`\n- [ ] **Prefer type guards** - Use `instanceof` or property checks instead of assertions where possible\n\n### Null/Undefined Handling\n- [ ] **Explicit null checks** - Use `if (x === null || x === undefined)` not truthy checks for critical paths\n- [ ] **Nullish coalescing** - Use `??` for null/undefined, not `||` which also catches empty string/0\n- [ ] **Optional chaining** - Use `?.` for nested property access on potentially undefined objects\n\n### Imports & Types\n- [ ] **Type imports** - Use `import type { ... }` for type-only imports\n- [ ] **No implicit any** - All function parameters and returns must have explicit types\n- [ ] **Readonly for constants** - Use `Object.freeze()` and `Readonly<>` for immutable data\n\n## Step 4: Security Review\n\n### Defensive Tool Security\n- [ ] **No credentials in logs** - Check that passwords, tokens, TOTP secrets are not logged to audit files\n- [ ] **Config file size limit** - Ensure 1MB max for config files (DoS prevention)\n- [ ] **Safe shell execution** - Command arguments must be escaped/sanitized\n\n### Code Injection Prevention\n- [ ] **YAML safe parsing** - FAILSAFE_SCHEMA only\n- [ ] **No eval/Function** - Never use dynamic code evaluation\n- [ ] **Input validation at boundaries** - URLs, paths validated before use\n\n## Step 5: Common Mistakes to Avoid\n\n### Anti-Patterns Found in Codebase\n- [ ] **Catch + re-throw without context** - Don't just `throw error`, wrap with additional context\n- [ ] **Silent failures in session loading** - Corrupted session files should warn user, not silently reset\n- [ ] **Duplicate retry logic** - Don't implement retry at both caller and callee level\n- [ ] **Hardcoded error message matching** - Prefer error codes over regex on error.message\n- [ ] **Missing timeout on long operations** - Git operations and API calls should have timeouts\n- [ ] **Console.log in services** — Use `ActivityLogger`. Only CLI display code (`client.ts`, `worker.ts`, `output-formatters.ts`) uses console.log\n- [ ] **Temporal imports in services** — Services must stay Temporal-agnostic. If you need Temporal APIs, it belongs in activities\n\n### Code Quality\n- [ ] **No dead code added** - Remove unused imports, functions, variables\n- [ ] **No over-engineering** - Don't add abstractions for single-use operations\n- [ ] **Comments only where needed** - Self-documenting code preferred over excessive comments\n- [ ] **Consistent file naming** - kebab-case for files (e.g., `queue-validation.ts`)\n\n## Step 6: Provide Feedback\n\nFor each issue found:\n1. **Location**: File and line number\n2. **Issue**: What's wrong and why it matters\n3. **Fix**: How to correct it (with code example if helpful)\n4. **Severity**: Critical / Warning / Suggestion\n\n### Severity Definitions\n- **Critical**: Will cause bugs, crashes, data loss, or security issues\n- **Warning**: Code smell, inconsistent pattern, or potential future issue\n- **Suggestion**: Style improvement or minor enhancement\n\nSummarize with:\n- Total issues by severity\n- Overall assessment (Ready to commit / Needs fixes / Needs discussion)\n\n---\n\nNow review the current changes.\n","frontmatter":{"description":"Review code changes for Shannon-specific patterns, security, and common mistakes"},"isInternal":false,"tokens":1820,"sizeBytes":7804}],"systemPromptSnippet":"<yakaai_skills repo=\"KeygraphHQ/shannon\">\n<!-- File: llms-full.txt (Tokens: ~11559 | Category: root-instruction) -->\n# Shannon Full LLM Context\n\n> Combined README and documentation for AI agents and LLMs, generated verbatim from the local files referenced in llms.txt. For the concise index, see [llms.txt](llms.txt).\n\n> Retired names: Shannon Lite is now just Shannon. Shannon Pro is now Keygraph Pro.\n\n---\n\n# File: README.md\n\n> [!NOTE]\n> **[Cyber verification for Anthropic and OpenAI models](https://github.com/KeygraphHQ/shannon/discussions/483):** complete your provider's cyber verification program to prevent model failures and refusals during cyber workloads.\n\n<div align=\"center\">\n\n<picture>\n<source media=\"(prefers-color-scheme: dark)\" srcset=\"./assets/github-banner-dark.png\">\n<source media=\"(prefers-color-scheme: light)\" srcset=\"./assets/github-banner-light.png\">\n<img src=\"./assets/github-banner-light.png\" alt=\"Shannon, AI Pentester for Web Apps and APIs, by Keygraph\" width=\"100%\">\n</picture>\n\n<a href=\"https://trendshift.io/repositories/15604\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/15604\" alt=\"KeygraphHQ%2Fshannon | Trendshift\" style=\"width: 250px; height: 55px;\" width=\"250\" height=\"55\"/></a>\n\n### Shannon is an autonomous, AI pentester for web applications and APIs.\n\nIt analyzes your source code, identifies attack paths, and executes real exploits to prove vulnerabilities before they reach production. **No exploit, no report.**\n\n**This repository is Shannon: the full open-source agent, run locally from your command line.**\n\n<p><strong>Launch Shannon</strong></p>\n\n```bash\nnpx @keygraph/shannon@latest\n```\n\n<sub>The interactive launcher will guide you through setup and your first pentest.</sub>\n\n---\n\n<a href=\"https://discord.gg/9ZqQPuhJB7\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"./assets/discord_button_dark.png\"><source media=\"(prefers-color-scheme: light)\" srcset=\"./assets/discord_button_light.png\"><img src=\"./assets/discord_button_light.png\" height=\"40\" alt=\"Join Discord\"></picture></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"https://keygraph.io/\"><picture><source media=\"(prefers-color-scheme: dark)\" srcset=\"./assets/keygraph_button_dark.png\"><source media=\"(prefers-color-scheme: light)\" srcset=\"./assets/keygraph_button_light.png\"><img src=\"./assets/keygraph_button_light.png\" height=\"40\" alt=\"Visit Keygraph.io\"></picture></a>\n\n---\n\n</div>\n\n> [!TIP]\n> **AI agents and LLMs:** start with [llms.txt](llms.txt) for a concise map of this repository, or use [llms-full.txt](llms-full.txt) for the README and docs combined into one file.\n\n\n\n## Table of Contents\n\n- [Table of Contents](#table-of-contents)\n- [What is Shannon?](#what-is-shannon)\n  - [Why Shannon Exists](#why-shannon-exists)\n  - [Why \"Shannon\"?](#why-shannon)\n  - [Not a replacement for human pentesters](#not-a-replacement-for-human-pentesters)\n- [Shannon in Action](#shannon-in-action)\n- [Quick Start](#quick-start)\n  - [Prerequisites](#prerequisites)\n  - [Run Shannon](#run-shannon)\n- [Key Capabilities](#key-capabilities)\n- [CI/CD Integrations](#cicd-integrations)\n  - [GitHub Actions](#github-actions)\n- [Editions](#editions)\n- [Architecture](#architecture)\n- [Documentation](#documentation)\n- [Safety, Scope, and Limitations](#safety-scope-and-limitations)\n- [License](#license)\n- [Acknowledgements](#acknowledgements)\n- [About Keygraph](#about-keygraph)\n- [Community and Support](#community-and-support)\n- [Common Questions](#common-questions)\n  - [Can I self-host Shannon?](#can-i-self-host-shannon)\n  - [Does Shannon support bring your own key (BYOK)?](#does-shannon-support-bring-your-own-key-byok)\n  - [Does Shannon output SARIF?](#does-shannon-output-sarif)\n  - [Which AI providers does Shannon support?](#which-ai-providers-does-shannon-support)\n  - [Can I run Shannon on a local or self-hosted model?](#can-i-run-shannon-on-a-local-or-self-hosted-model)\n  - [Does Shannon actually exploit vulnerabilities, or just scan?](#does-shannon-actually-exploit-vulnerabilities-or-just-scan)\n\n\n\n## What is Shannon?\n\nShannon is an autonomous AI pentester developed by [Keygraph](https://keygraph.io). It performs security testing of web applications and their underlying APIs by combining source-code analysis with live exploitation.\n\nShannon analyzes your web application's source code to identify potential attack vectors, then uses browser automation and command-line tools to execute real exploits against the running application and its APIs. Only vulnerabilities with a working proof-of-concept are included in the final report.\n\nShannon is the agent. This repository is Shannon, the open-source pentester you run yourself. The same Shannon also powers Keygraph's commercial editions, [Pro and Enterprise](https://keygraph.io/pricing). See [Editions](#editions) for how Shannon compares with Pro, Enterprise, and the Community Program.\n\n<a id=\"why-shannon-exists\"></a>\n<details>\n<summary><strong>Why Shannon Exists</strong></summary>\n\nThanks to tools like Claude Code and Cursor, your team ships code non-stop. But your penetration test? That happens once a year. This creates a massive security gap. For the other 364 days, you could be unknowingly shipping vulnerabilities to production.\n\nShannon closes that gap by providing on-demand, automated penetration testing that can run against every build or release.\n\n</details>\n\n<a id=\"why-shannon\"></a>\n<details>\n<summary><strong>Why \"Shannon\"?</strong></summary>\n\nIt's named after Claude Shannon, the father of information theory. At its core, pentesting is an information problem: every probe reduces uncertainty about a system's state. The best tools maximize the signal gained from every request, turning those bits of knowledge into an exploit path.\n\nAlso, we wanted you to be able to say, \"Hey Claude, run Shannon\" to find all the security flaws in your vibe-coded app.\n\n</details>\n\n<a id=\"not-a-replacement-for-human-pentesters\"></a>\n<details>\n<summary><strong>Not a replacement for human pentesters</strong></summary>\n\nShannon is built to work alongside expert pentesters and red teamers, not replace them. Great pentesters understand the business, chain attacks in ways nobody anticipated, and bring years of judgment that current models can't match.\n\nShannon solves a different problem: there is far more software to test than security teams have time to cover. Critical systems get periodic expert assessments, while the long tail of internal apps, APIs, and fast-moving services rarely gets tested at all.\n\nShannon shifts pentesting left into the software development lifecycle (SDLC). Use it to run exploitation-backed tests against staging environments and releases at the cadence they actually ship, and save expert human time for the risks that need someone who knows the organization.\n\n</details>\n\n## Shannon in Action\n\n![Shannon running an autonomous pentest](assets/Shannon3GIF.gif)\n\nThese reports are from Shannon OSS scans of Photoview 2.4.0, one of the applications in Doyensec's comparison of Aikido and XBOW. We ran Shannon against the same application version and evaluated its results separately. Read the [Doyensec study](https://doyensec.com/resources/ComparingAIApplicationSecurityTestingPlatforms_Doyensec.pdf) and our [Shannon follow-up comparison](docs/shannon-xbow-aikido-benchmark.md) for the methodology, limitations, costs, and results.\n\n\n| Model             | Report                                                                       | SARIF                                                            |\n| ----------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------- |\n| DeepSeek v4 Flash | [View report](benchmark/photoview-deepseek-v4-flash.pdf) | [SARIF](benchmark/photoview-deepseek-v4-flash.sarif) |\n| Grok 4.6          | [View report](benchmark/photoview-grok-4-6.pdf)          | [SARIF](benchmark/photoview-grok-4-6.sarif)          |\n| Claude Opus 5     | [View report](benchmark/photoview-opus-5.pdf)            | [SARIF](benchmark/photoview-opus-5.sarif)            |\n\n## Quick Start\n\n\n\n### Prerequisites\n\n- **Docker**: required for the worker container.\n- **Node.js 18+**: required for the recommended `npx` workflow.\n- **AI provider credentials**: Shannon runs on Anthropic, OpenAI, xAI, AWS Bedrock, and [any other provider](docs/ai-providers.md#any-other-provider) in the harness catalogue — each of which you can point at a proxy or LLM gateway through a [custom base URL](docs/ai-providers.md#custom-base-url), and a model the catalogue does not carry can be described with a [custom model configuration](docs/ai-providers.md#custom-model-configuration). You bring your own key, and Keygraph never proxies your model traffic. Shannon is provider-agnostic. See [AI providers](docs/ai-providers.md#suggested-models) for suggested model IDs.\n- **Cyber safeguards cleared with your provider**: Anthropic and OpenAI apply real-time safeguards to cyber-security workloads, which can interrupt a scan mid-run. Complete their guidance for legitimate security testers before your first run - see [AI providers](docs/ai-providers.md#cyber-safeguards-do-this-before-your-first-scan) and the [cyber verification announcement](https://github.com/KeygraphHQ/shannon/discussions/483).\n\n\n\n### Run Shannon\n\n> [!WARNING]\n> Shannon actively executes exploits. Run it only against applications and environments you own or have explicit written authorization to test. Do not run Shannon against production systems.\n\n```bash\n# Configure credentials with the interactive wizard.\nnpx @keygraph/shannon@latest setup\n\n# Run a pentest against a source-available target.\nnpx @keygraph/shannon@latest start \\\n  -u https://your-app.com \\\n  -r /path/to/your/repo\n```\n\nShannon pulls the worker image from Docker Hub, starts the required local infrastructure, mounts the target repository read-only inside an ephemeral worker container, and writes results to a local workspace.\n\nFor source builds, authenticated scans, provider-specific setup, and platform notes, see [Documentation](#documentation).\n\n> [!TIP]\n> **Prefer to use a subscription instead of API credits?**\n>\n> - **OpenAI Codex:** The latest version of Shannon supports ChatGPT Plus and Pro subscriptions. Follow the [OpenAI Codex subscription setup guide](docs/ai-providers.md#openai-codex-chatgpt-pluspro-subscription) to get started.\n> - **xAI (Grok):** The latest version of Shannon supports xAI subscriptions. Follow the [xAI subscription setup guide](docs/ai-providers.md#xai-grok-subscription) to get started.\n> - **Claude Code:** The latest version of Shannon does not support Claude Code subscriptions. Follow the [Claude Code subscription setup guide](docs/ai-providers.md#claude-code-subscription) to use version `1.9.0`, which is the final release built on the Claude Agent SDK.\n\n\n\n## Key Capabilities\n\n- **No exploit, no report**: Reports only vulnerabilities confirmed with a reproducible proof of concept, reducing speculative scanner noise.\n- **Advanced code analysis**: Maps architecture, trust boundaries, interfaces, data flows, and critical assets before sending credible attack paths to live pentesting agents.\n- **Autonomous execution**: Runs reconnaissance, analysis, exploitation, and reporting from a single command.\n- **Live terminal experience**: Simplifies scan setup and shows agent progress and results without exposing orchestration logs.\n- **Authenticated testing**: Supports credentials, login flows, TOTP, email authentication, focus areas, and rules of engagement through configuration.\n- **OWASP-focused coverage**: Tests for exploitable injection, XSS, SSRF, broken authentication, and broken authorization.\n- **Resumable workspaces**: Resumes interrupted scans without repeating completed work.\n- **Native CI/CD integrations**: Runs through the official GitHub Action or GitLab CI/CD component, preserves artifacts, publishes findings, and gates releases on proven vulnerabilities.\n- **Multi-format reports**: Produces evidence-rich PDF and Markdown reports plus JSON and SARIF 2.1.0. SARIF is enabled by default for exploit-mode scans.\n- **Provider agnostic and BYOK**: Supports Anthropic, OpenAI, xAI, AWS Bedrock, compatible APIs and LLM gateways, and local models served through Ollama, vLLM, or LM Studio.\n- **Private by design**: Runs in your infrastructure, stores results locally, and sends model requests directly to your chosen endpoint. A local endpoint keeps data inside your environment.\n\n\n\n## CI/CD Integrations\n\nShannon can run continuously against deployed staging and development environments through official integrations for [GitHub Actions](https://github.com/KeygraphHQ/shannon-action) and [GitLab CI/CD](https://gitlab.com/KeygraphHQ/shannon-ci).\n\nBoth integrations:\n\n- analyze the checked-out source repository while attacking a running target;\n- preserve PDF, Markdown, and SARIF reports as pipeline artifacts;\n- preserve scan and agent logs for debugging, including incomplete runs;\n- support pull-request, release, and scheduled pentests;\n- distinguish an incomplete assessment from a completed scan with no findings; and\n- optionally fail the pipeline when Shannon exploits a vulnerability at or above a configured severity threshold.\n\nA code-analysis hypothesis does not fail the pipeline. Severity gates count only findings with `status: exploited`.\n\n### GitHub Actions\n\n```yaml\nname: Shannon Pentest\n\non:\n  workflow_dispatch:\n\npermissions:\n  security-events: write\n\njobs:\n  pentest:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Run Shannon\n        uses: KeygraphHQ/shannon-action@v1\n        with:\n          url: https://staging.example.com\n          api-key: ${{ secrets.SHANNON_AI_API_KEY }}\n          fail-on-severity: high\n          upload-sarif: true\n```\n\nThe Action defaults `repo` to the checked-out GitHub workspace. It uploads one artifact containing the security assessment reports and SARIF, plus a separate run artifact containing scan and agent logs. Enabling `upload-sarif` publishes supported findings to GitHub code scanning.\n\nRequirements:\n\n- a private repository;\n- a runner with Docker and Docker Compose v2;\n- access to the running staging or development target; and\n- a model-provider credential stored as a GitHub Actions secret.\n\nSee the [Shannon GitHub Action documentation](https://github.com/KeygraphHQ/shannon-action) and [GitHub Marketplace listing](https://github.com/marketplace/actions/shannon-ai-pentester).\n\n## Editions\n\n| Edition | What it is | Price |\n| --- | --- | --- |\n| **Shannon** (open source) | This repository. A complete autonomous pentester you run yourself, locally or in CI/CD, against an application whose source code you have. Well suited to individual developers and small teams. | Open source under AGPL-3.0. You pay only your own model costs. |\n| **Community Program** | Keygraph Pro at no cost, for U.S.-based 501(c)(3) nonprofits and for seed or pre-Series-A startups with 20 or fewer active developers. | $0 in cloud service fees while you qualify. |\n| **Pro** | Keygraph's cloud-hosted edition, managed by Keygraph, with every module included. | $50 per active developer per month. |\n| **Enterprise** | Everything in Pro, self-hosted in your own environment or fully air-gapped. | Custom. |\n\nSee [keygraph.io/pricing](https://keygraph.io/pricing) for current prices and the full feature table.\n\n**Pro**, **Enterprise**, and the **Community Program** are powered by an enhanced, enterprise-grade version of the Shannon engine, and all three add:\n\n- **Black-box pentesting**: tests the running application from the outside, with no source code needed.\n- **Dependency (SCA) and secret checks**: SCA with reachability, and secrets scanning that includes repository history.\n- **Findings management**: one record per vulnerability per repository across scans and scanners, with status history, owners, SLAs, and dashboards.\n- **Fix pull requests and retests**: reviewable fix pull requests, with each fix verified by re-analysis and exploit replay, without a full rescan.\n- **Jira and access control**: two-way Jira sync, SSO, RBAC, and audit logs.\n\nNo source code? Use the [Blackbox Pentester](https://keygraph.io/agentic-blackbox-pentester).\n\n[Compare Shannon, Pro, and Enterprise in detail →](docs/keygraph-platform.md)\n\n## Architecture\n\nShannon combines multi-stage security code analysis with live reconnaissance and exploitation:\n\n```mermaid\nflowchart TD\n    S[\"Source code\"] --> EXISTING[\"Recon + vulnerability analysis\"]\n    S --> SAST[\"Agentic security code analysis\"]\n\n    EXISTING -- \"Pentest candidates\" --> REC[\"Finding reconciliation<br/>(merge + deduplicate)\"]\n    SAST -- \"SAST candidates\" --> REC\n\n    REC -- \"Reconciled exploitation queue\" --> EXP[\"Exploitation agents\"]\n    APP[\"Running application\"] --> EXP\n\n    EXP -- \"Exploit demonstrated\" --> REPORT[\"Reporting<br/>PDF · Markdown · SARIF\"]\n    EXP -- \"No exploit demonstrated\" --> DROP[\"Discard\"]\n\n    REPORT --> CICD[\"CI/CD gate\"]\n```\n\n\n\nStage by stage:\n\n1. **Recon and vulnerability analysis** explores the running application, ties runtime behavior back to the source, and runs specialized agents across Injection, XSS, SSRF, Authentication, and Authorization.\n2. **Agentic security code analysis** maps the application's architecture, trust boundaries, exposed interfaces, dependencies, data flows, and high-risk assets, then opens targeted investigations against them.\n3. **Finding reconciliation** merges both streams of candidates, deduplicates the overlap, and groups what remains into an exploitation queue.\n4. **Exploitation agents** attempt real proof-of-concept attacks against the running application.\n5. **Validation** throws out every candidate Shannon can't demonstrate.\n6. **Reporting** produces PDF and Markdown reports with the evidence attached, plus structured JSON and SARIF for downstream systems.\n\nOnly live-validated vulnerabilities become Shannon pentest findings or count toward CI/CD severity gates.\n\nEach scan runs in an ephemeral Docker container with an isolated workspace and per-invocation orchestration.\n\n## Documentation\n\nUse these guides for operational detail:\n\n\n| Guide                                                     | Use it for                                                                                                                                                                |\n| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [Source build and CLI commands](docs/development.md)      | Cloning, building, common commands, output paths, and local development.                                                                                                  |\n| [Configuration](docs/configuration.md)                    | Authenticated testing, login flows, rules of engagement, and report filters.                                                                                              |\n| [AI providers](docs/ai-providers.md)                      | Selecting the model, the supported providers (Anthropic, OpenAI, xAI, AWS Bedrock, and any other Pi-supported provider), and custom LLM gateways.                             |\n| [Platforms and networking](docs/platforms.md)             | Windows/WSL2, Linux, macOS, Docker networking, local apps, and custom hostnames.                                                                                          |\n| [Workspaces and resuming](docs/workspaces.md)             | Naming workspaces, resuming interrupted scans, and workspace storage.                                                                                                     |\n| [Safety and limitations](docs/safety.md)                  | Authorized-use requirements, non-production guidance, mutative effects, cost, and model caveats.                                                                          |\n| [Coverage and roadmap](docs/coverage-roadmap.md)          | Current vulnerability coverage and planned work.                                                                                                                          |\n| [Pro and Enterprise](docs/keygraph-platform.md)           | Shannon compared with Pro, Enterprise, and the Community Program: black-box pentesting, SCA and secrets, findings management, fix pull requests, retests, Jira, and deployment. |\n\n\n\n\n## Safety, Scope, and Limitations\n\nShannon is not a passive scanner. Its exploitation agents can create users, submit forms, mutate application state, trigger outbound requests, and otherwise affect the target system. Use sandboxed, staging, or local development environments with disposable data.\n\nYou are responsible for using Shannon legally and ethically. Do not point Shannon at systems, repositories, or applications you do not own or do not have explicit authorization to test.\n\nImportant limitations:\n\n- Shannon is tuned for fast, code-informed pentesting in everyday development and CI/CD. Exhaustive agentic SAST, broader scanner coverage, centralized governance, and full-lifecycle vulnerability management are delivered through Pro, Enterprise, and the Community Program.\n- Findings still require human review. LLM-generated reports can contain weakly supported or incorrect details.\n- Anthropic, OpenAI, xAI, and AWS Bedrock are built-in providers, and any other provider in the harness catalogue works too — each reachable through a custom base URL that points it at a proxy or LLM gateway. Model capability varies, and a model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker results.\n- A full run can take roughly 1 to 1.5 hours and may incur LLM API costs depending on model pricing and application complexity.\n- Do not scan untrusted or adversarial codebases. AI-powered tools that read source code can be exposed to prompt injection.\n\nRead the full [Safety and limitations](docs/safety.md) guide before running Shannon in a new environment.\n\n## License\n\nShannon is licensed under the [GNU Affero General Public License v3.0](LICENSE).\n\nCommercial and enterprise licensing is available for organizations that need different license terms, commercial support, private redistribution, managed-service use, or broader deployment options, including Keygraph Pro and Enterprise.\n\nFor commercial licensing, contact [shannon@keygraph.io](mailto:shannon@keygraph.io).\n\n## Acknowledgements\n\nThanks to [Pi](https://github.com/earendil-works/pi),\n[Playwright CLI](https://github.com/microsoft/playwright-cli),\nand [Mantis](https://github.com/google/mantis).\n\nSee [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md) for licensing and attribution details.\n\n## About Keygraph\n\n**Keygraph** is the company behind Shannon. It also builds **Keygraph Pro** and **Keygraph Enterprise**, the commercial editions that close the full AppSec lifecycle and run an enhanced build of Shannon as their pentesting engine.\n\n## Community and Support\n\n**Community office hours** are available for hands-on help with bugs, deployments, and configuration questions.\n\n- US/EU: Thursday, 10:00 AM PT\n- Asia: Thursday, 2:00 PM IST\n- [Book a slot](https://cal.com/george-flores-keygraph/shannon-community-office-hours)\n\n[Join Discord](https://discord.gg/cmctpMBXwE) to ask questions, share feedback, and connect with other Shannon users.\n\nAt this time, Keygraph is not accepting external code contributions. Issues are welcome for bug reports and feature requests:\n\n- [Report bugs](https://github.com/KeygraphHQ/shannon/issues)\n- [Suggest features](https://github.com/KeygraphHQ/shannon/discussions)\n\nStay connected:\n\n- [Keygraph website](https://keygraph.io)\n- [Twitter/X: @KeygraphHQ](https://twitter.com/KeygraphHQ)\n- [LinkedIn: Keygraph](https://linkedin.com/company/keygraph)\n\n\n\n## Common Questions\n\n\n\n### Can I self-host Shannon?\n\nYes. Shannon runs inside your infrastructure in an ephemeral worker container. It mounts the repository read-only and writes results to a local workspace.\n\nKeygraph never receives your source code and never proxies your model traffic. Your model requests go straight to the provider or endpoint you configure, and they carry source and application context with them. Point Shannon at a locally hosted endpoint and that traffic stays inside your environment too.\n\n### Does Shannon support bring your own key (BYOK)?\n\nYes, always. You provide the LLM credentials Shannon uses to run a pentest, in every deployment, open source and commercial. Keygraph never proxies your model traffic.\n\n### Does Shannon output SARIF?\n\nYes. Shannon emits SARIF 2.1.0, the OASIS standard format for static analysis results, alongside structured JSON. Any SARIF consumer reads it: code scanning services, vulnerability management platforms, security dashboards, and CI/CD pipelines. It is written by default on exploit-mode scans; set `report.sarif` to `\"false\"` in your configuration file to opt out.\n\n### Which AI providers does Shannon support?\n\nAnthropic, OpenAI, xAI, and AWS Bedrock are built in and configured directly by provider ID. Beyond those, Shannon runs on any provider in the Pi harness catalogue, named the same `<provider>:<model-id>` way. Any provider can be pointed at a proxy or LLM gateway through a custom base URL, which overrides only the endpoint and keeps that provider's API dialect. A model the catalogue does not carry, such as one a router or gateway serves under its own ID, or a self-hosted model, is described in a [custom model configuration](docs/ai-providers.md#custom-model-configuration) file and passed with `--models-config`. Shannon uses a single unified model setting throughout a pentest.\n\n### Can I run Shannon on a local or self-hosted model?\n\nShannon works with local models served through Ollama, vLLM, or LM Studio, which expose an OpenAI-compatible endpoint, as well as routers such as OpenRouter and LLM gateways such as LiteLLM. A model the harness catalogue does not carry, which most self-hosted models are, is described in a [custom model configuration](docs/ai-providers.md#custom-model-configuration) file passed with `--models-config`; routers and gateways can also be reached with a custom base URL. Capability varies, and a model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker pentests than a frontier model, so take this path only if you know how your chosen model behaves. See [Local and self-hosted models](docs/ai-providers.md#local-and-self-hosted-models).\n\n### Does Shannon actually exploit vulnerabilities, or just scan?\n\nShannon executes real exploits. It reports a finding only when it has produced a working proof-of-concept, and discards hypotheses it cannot prove. It is a pentester, not a passive scanner.\n\n**Built by [Keygraph](https://keygraph.io)**\n\n---\n\n# File: docs/development.md\n\n# Source Build and CLI Commands\n\nThis guide covers the source-build workflow, common CLI commands, repository paths, and output locations. For the fastest first run, use the `npx` workflow in the main README.\n\n## Prerequisites\n\n- Docker\n- Node.js 18+\n- pnpm\n- AI provider credentials\n\n## Clone and Build\n\nUse the source-build workflow if you want to run Shannon from a local clone, modify the open-source CLI, or keep the worker image built locally.\n\n```bash\n# 1. Clone Shannon.\ngit clone https://github.com/KeygraphHQ/shannon.git\ncd shannon\n\n# 2. Configure credentials.\ncp .env.example .env\n\n# 3. Install dependencies and build.\npnpm install\npnpm build\n\n# 4. Run a pentest.\n./shannon start -u https://your-app.com -r /path/to/your-repo\n```\n\nAt minimum, your `.env` file should include one supported AI provider credential, such as:\n\n```bash\nANTHROPIC_API_KEY=your-api-key\n```\n\nEnvironment variables can also be exported directly:\n\n```bash\nexport ANTHROPIC_API_KEY=\"your-api-key\"\n```\n\n## Prepare Your Repository\n\nShannon can scan any repository on your machine. Pass an absolute or relative path with `-r`.\n\n```bash\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo\n./shannon start -u https://example.com -r ./relative/path\n```\n\nThe target repository is mounted read-only inside the worker container.\n\n## Common Commands\n\nMonitor progress:\n\n```bash\nnpx @keygraph/shannon logs [<workspace>]     # defaults to the single running scan, else the most recent\nnpx @keygraph/shannon status [<workspace>]   # same default target; add --json for a machine-readable snapshot\nnpx @keygraph/shannon scans\nnpx @keygraph/shannon version\n```\n\nWith no workspace, `logs` and `status` follow the single running scan; when several are running, name one.\n\nSource-build equivalents:\n\n```bash\n./shannon logs [<workspace>]                 # the combined live log (unchanged default)\n./shannon logs [<workspace>] --agent <name>  # tail one agent's own log\n./shannon logs [<workspace>] --list-agents   # list the agents with their own log\n./shannon status [<workspace>]\n./shannon scans\n./shannon version\n```\n\nEvery scan writes one combined `.shannon/workflow.log` and a per-agent projection of it under\n`.shannon/agents/`: one file per pipeline agent (`recon.log`, `xss-vuln.log`, …) and one per Capella\nstage (`agentic-sast-research.log`, …). Delegated subagents fold into their parent's file, and a\nCapella stage's concurrent sessions share its file with an inline session label. The combined log\nstays canonical; the per-agent files are best-effort projections.\n\nOpen the Temporal Web UI for detailed monitoring:\n\n```bash\nopen http://localhost:8233\n```\n\nStop Shannon:\n\n```bash\nnpx @keygraph/shannon stop [<workspace>] # stop one scan (defaults to the single running scan; confirms first; add --yes/-y to skip)\nnpx @keygraph/shannon stop --all         # stop all scans (Temporal stays up)\nnpx @keygraph/shannon reset              # stop everything and wipe all Temporal data (type 'confirm' to proceed; cannot be skipped)\n```\n\nSource-build equivalents:\n\n```bash\n./shannon stop [<workspace>]             # stop one scan (defaults to the single running scan; confirms first; add --yes/-y to skip)\n./shannon stop --all                     # stop all scans (Temporal stays up)\n./shannon reset                          # stop everything and wipe all Temporal data (type 'confirm' to proceed; cannot be skipped)\n```\n\nUsage examples:\n\n```bash\n# Basic pentest.\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo\n\n# With a configuration file.\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -c /path/to/my-config.yaml\n\n# Custom output directory.\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -o ./my-reports\n\n# Named workspace.\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -w q1-audit\n\n# Stream the log until the scan finishes, then exit on its outcome (useful in CI).\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo --follow\n\n# Validate the configured login only, then stop (no pentest or report).\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -c /path/to/my-config.yaml --validate-auth\n\n# List running and completed scans.\nnpx @keygraph/shannon scans\n```\n\nSource-build examples:\n\n```bash\n./shannon start -u https://example.com -r /path/to/repo\n./shannon start -u https://example.com -r /path/to/repo -c /path/to/my-config.yaml\n./shannon start -u https://example.com -r /path/to/repo -o ./my-reports\n./shannon start -u https://example.com -r /path/to/repo -w q1-audit\n./shannon start -u https://example.com -r /path/to/repo --follow\n./shannon start -u https://example.com -r /path/to/repo -c /path/to/my-config.yaml --validate-auth\n./shannon scans\n\n# Rebuild the worker image.\n./shannon build --no-cache\n```\n\n## Output and Results\n\nResults are saved to the workspaces directory:\n\n- `./workspaces/` in source-build mode\n- `~/.shannon/workspaces/` in `npx` mode\n\nUse `-o <path>` to copy deliverables to a custom output directory after a run completes.\n\nOutput structure — the run directory's top level holds the final report, in PDF and Markdown; everything else is nested under a hidden `.shannon/` directory:\n\n```text\nworkspaces/{hostname}_{sessionId}/\n|-- Security-Assessment-Report.pdf  # the final report (PDF)\n|-- Security-Assessment-Report.md   # the final report (Markdown)\n`-- .shannon/                       # internals\n    |-- deliverables/               # report source, per-phase analysis, queues\n    |-- agents/                     # per-agent log projections, one file per agent/Capella stage\n    |-- prompts/                    # rendered prompts\n    |-- scratchpad/                 # screenshots, scripts\n    |-- session.json                # resume state\n    `-- workflow.log\n```\n\n---\n\n# File: docs/configuration.md\n\n# Configuration\n\nShannon can run without a configuration file, but configuration enables authenticated testing, scope guidance, rules of engagement, and report filtering.\n\n## Credential Precedence\n\nSource-build mode resolves credentials from:\n\n1. Environment variables, such as `export ANTHROPIC_API_KEY=...`\n2. `./.env`\n\n`npx` mode resolves credentials from:\n\n1. Environment variables\n2. `~/.shannon/config.toml`, created by `npx @keygraph/shannon setup`\n\nEnvironment variables always win, so you can override saved config for a single session without editing files.\n\n## Create a Configuration File\n\nCopy and modify the example configuration:\n\n```bash\ncp configs/example-config.yaml ./my-app-config.yaml\n```\n\nRun with:\n\n```bash\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo -c ./my-app-config.yaml\n```\n\nSource-build equivalent:\n\n```bash\n./shannon start -u https://example.com -r /path/to/repo -c ./my-app-config.yaml\n```\n\n## Basic Configuration Structure\n\n```yaml\n# Describe your target environment.\ndescription: \"Next.js e-commerce app on PostgreSQL. Local dev environment; .env files contain local-only credentials.\"\n\n# Every scan runs all five vulnerability classes.\n\n# Agentic static analysis. `enabled` is its only setting.\n# agentic_sast:\n#   enabled: \"true\"\n\n# Skip the exploitation phase.\n# exploit: \"false\"\n\n# Free-form rules of engagement.\n# rules_of_engagement: |\n#   - No password brute-force; cap login attempts at 5 per account.\n#   - Throttle to under 5 requests per second per endpoint; back off 60s on any 429.\n#   - Use placeholders like [order_id] in deliverables; no real data values.\n\nauthentication:\n  login_type: form\n  login_url: \"https://your-app.com/login\"\n  credentials:\n    username: \"test@example.com\"\n    password: \"yourpassword\"\n    totp_secret: \"LB2E2RX7XFHSTGCK\"\n\n    # Optional mailbox credentials for magic-link or email-OTP flows.\n    # email_login:\n    #   address: \"inbox@example.com\"\n    #   password: \"mailbox-password\"\n    #   totp_secret: \"JBSWY3DPEHPK3PXP\"\n\n  login_flow:\n    - \"Type $username into the email field\"\n    - \"Type $password into the password field\"\n    - \"Click the 'Sign In' button\"\n\n  success_condition:\n    type: url_contains\n    value: \"/dashboard\"\n\nrules:\n  avoid:\n    - description: \"AI should avoid testing logout functionality\"\n      type: url_path\n      value: \"/logout\"\n\n    # code_path values are repo-relative file paths or globs.\n    # - description: \"Out-of-scope vendored libraries\"\n    #   type: code_path\n    #   value: \"src/vendor/**\"\n\n  focus:\n    - description: \"AI should emphasize testing API endpoints\"\n      type: url_path\n      value: \"/api\"\n\n# Report options applied when assembling the final report.\n# report:\n#   min_severity: low\n#   min_confidence: low\n#   guidance: |\n#     Drop findings about missing security headers and rate-limit gaps.\n#   sarif: \"false\"\n```\n\n## Analysis Scope and Agentic SAST\n\nEvery scan runs all five analysis classes: Injection, Cross-Site Scripting, Authentication, Authorization, and\nServer-Side Request Forgery. The class set is fixed and has no configuration selector.\n\nAgentic static analysis is opt-in:\n\n```yaml\nagentic_sast:\n  enabled: \"true\"\n```\n\n`enabled` is the only setting. Omitting the block, or setting `enabled: \"false\"`, turns agentic static analysis off;\n`\"true\"` turns it on. Either way, all five vulnerability classes still run.\n\nAgentic static analysis reads the repository for vulnerabilities before the pentest and passes what it finds into the\nexploitation phase. It adds model time and cost. If it fails, the pentest continues without its findings and the scan\nfinishes as \"partial\".\n\n## Report Options\n\n| Key | Effect |\n| --- | --- |\n| `min_severity` | Drops findings rated below this severity. Applies in both exploitative and analysis-only runs. |\n| `min_confidence` | Drops findings rated below this confidence. Applies only when `exploit` is `\"false\"`. |\n| `guidance` | Free-text instruction to the report agent, such as which topics to exclude. |\n| `sarif` | SARIF 2.1.0 log alongside the Markdown report. On by default for exploit runs; set `\"false\"` to opt out. Ignored when `exploit` is `\"false\"`. |\n\nEvery finding carries a severity, but it does not mean the same thing in each mode: an exploitative run measures severity from what the exploit demonstrated, while an analysis-only run assesses it from the class of flaw and the impact it would have. An analysis-only finding carries a confidence rating alongside its severity, since nothing was proven. Setting `min_confidence` on an exploitative run is ignored, and Shannon logs a warning naming the threshold to use instead.\n\n### SARIF Output\n\nOn exploit-mode runs Shannon writes `report.sarif` next to `Security-Assessment-Report.pdf` at the workspace root by default, for upload to GitHub code scanning or any other SARIF consumer. No configuration is needed; set `sarif: \"false\"` to opt out.\n\n```yaml\nreport:\n  sarif: \"false\"\n```\n\nEach finding becomes one SARIF result, filed under a rule per vulnerability class (`shannon/injection`, `shannon/xss`, `shannon/auth`, `shannon/authz`, `shannon/ssrf`, and `shannon/miscellaneous` for findings outside those classes) and tagged with its OWASP Top Ten 2025 category. Results are anchored to the code location the analysis phase recorded, falling back to the HTTP entry point when the finding names no file. Severity maps onto SARIF's three levels: `critical` and `high` become `error`, `medium` becomes `warning`, everything else becomes `note`.\n\nIf the SARIF log cannot be written, the JSON and Markdown reports are still produced and the scan finishes as \"partial\".\n\nThe log is written only for exploitative runs. `sarif` is ignored when `exploit` is `\"false\"`.\n\nSupported rule types include `url_path`, `subdomain`, `domain`, `method`, `header`, `parameter`, and `code_path`.\n\n## Writing Login Flow\n\nLog in once in a fresh private browser window. Write the steps in the same order you perform them:\n\n- When typing into a field, reference the field by its exact label or placeholder.\n- When clicking a button, reference the exact button text.\n\nSupported placeholders:\n\n- `$username`\n- `$password`\n- `$totp`\n- `$email_address`\n- `$email_password`\n- `$email_totp`\n\nAt runtime, Shannon replaces these placeholders with the credentials passed in the config.\n\n```yaml\nlogin_flow:\n  - \"Type $username in <exact email field label or placeholder>\"\n  - \"Click <exact button text>\"\n  - \"Type $password in <exact password field label or placeholder>\"\n  - \"Click <exact button text>\"\n  - \"If prompted for 2FA, type $totp in <exact code field label or placeholder>\"\n  - \"Click <exact button text>\"\n```\n\n### Validating Authentication Only\n\nTo confirm your login flow works before committing to a full scan, add `--validate-auth` to `start`:\n\n```bash\nnpx @keygraph/shannon start -u https://your-app.com -r /path/to/repo -c config.yaml --validate-auth\n```\n\nThe run performs preflight and the single real login, then stops. No pentest, reconciliation, or report\nis produced. It requires an `authentication` block in the config.\n\n---\n\n# File: docs/ai-providers.md\n\n# AI Providers\n\nOne model runs the entire scan — pre-recon, recon, vulnerability analysis, exploitation, and reporting. A single setting names both the provider and the model:\n\n```bash\nexport SHANNON_AI_MODEL=<provider>:<model-id>\n```\n\nThe provider half decides where the request goes, which credential is used, and which API dialect is spoken. You never configure those separately.\n\n## Supported providers\n\n| Provider | Value | Credential |\n| --- | --- | --- |\n| Anthropic | `anthropic` | `SHANNON_AI_API_KEY` (or `CLAUDE_CODE_OAUTH_TOKEN`) |\n| OpenAI | `openai` | `SHANNON_AI_API_KEY` |\n| xAI | `xai` | `SHANNON_AI_API_KEY` |\n| AWS Bedrock | `amazon-bedrock` | `AWS_REGION` and `AWS_BEARER_TOKEN_BEDROCK` |\n\n`SHANNON_AI_API_KEY` holds the key for whichever provider `SHANNON_AI_MODEL` names. Bedrock is the exception — it authenticates through its `AWS_` variables only. If `SHANNON_AI_MODEL` is unset, Shannon uses `anthropic:claude-sonnet-4-6`.\n\nAnthropic, OpenAI, and xAI also accept their native variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `XAI_API_KEY`); if one of those is set, it is used instead of `SHANNON_AI_API_KEY`.\n\nShannon forwards only the selected provider's credential into the scan container. Keys for other providers stay on your machine.\n\n### Any other provider\n\nShannon accepts any provider and model present in the Pi harness catalogue. Browse them at [pi.dev/models](https://pi.dev/models).\n\n```bash\nexport SHANNON_AI_API_KEY=your-api-key                     # the provider's key — or the gateway's when a base URL is set\nexport SHANNON_AI_MODEL=openrouter:moonshotai/kimi-k3      # <provider>:<model-id>\nexport SHANNON_AI_BASE_URL=https://llm-gateway.example.com # optional: route through a proxy or LLM gateway\n```\n\nThis path covers providers whose credential is a single API key. Providers that need more than that are not currently supported.\n\nA model the catalogue does not carry is reachable by describing it yourself. See [Custom model configuration](#custom-model-configuration).\n\n`npx @keygraph/shannon setup` exposes this as the **Other provider** option.\n\n> [!IMPORTANT]\n> Models are validated against the harness catalogue, but capability varies. A model that does not follow Shannon's instructions or tool-use constraints reliably will produce weaker pentests. Evaluate the model you choose against your own targets before depending on its results.\n\n## Cyber safeguards (do this before your first scan)\n\nAnthropic and OpenAI both apply real-time safeguards to cyber-security workloads. Shannon is exactly such a workload. If a safeguard engages mid-run, the model can refuse, and the scan fails partway through rather than at the start.\n\nReview each vendor's guidance and complete the verification or enrollment they ask of legitimate security testers before running Shannon:\n\n- Anthropic - [Real-time cyber safeguards on Claude Opus and Sonnet](https://support.claude.com/en/articles/14604842-real-time-cyber-safeguards-on-claude-opus-and-sonnet)\n- OpenAI - [Cyber](https://chatgpt.com/cyber)\n\nThis applies to the Anthropic and OpenAI providers, including when either is reached through an LLM gateway. Bedrock serves Claude models and is subject to Anthropic's safeguards as well.\n\nTo confirm your model is ready before committing to a full scan, add `--validate-model` to `start`:\n\n```bash\nnpx @keygraph/shannon start -u https://your-app.com -r /path/to/repo --validate-model\n```\n\nThe run performs the preflight model checks only — credential and registry resolution for any provider, plus a single cyber-access verification against Anthropic and OpenAI that trips the cyber safeguard if your account is not approved — then stops. No pentest or report is produced, and it needs no config. A decline fails the run with the vendor's enrollment link.\n\n## Suggested models\n\nThese are the models `npx @keygraph/shannon setup` offers, best-first. They are suggestions: the wizard also takes a typed model ID, and `SHANNON_AI_MODEL` accepts any model in the provider's catalogue.\n\n| Provider | Suggested model IDs |\n| --- | --- |\n| `anthropic` | `claude-sonnet-5`, `claude-opus-5`, `claude-sonnet-4-6`, `claude-opus-4-8`, `claude-opus-4-7`, `claude-haiku-4-5-20251001` |\n| `openai` | `gpt-6-sol`, `gpt-5.6-sol`, `gpt-5.5`, `gpt-5.4` |\n| `xai` | `grok-4.7` |\n| `amazon-bedrock` | `us.anthropic.claude-sonnet-4-6`, `us.anthropic.claude-opus-4-8`, `us.anthropic.claude-opus-4-7` |\n\nBedrock IDs are region-prefixed and must be enabled in your account, so the ID that works for you may differ from the one listed here.\n\n## Switching provider\n\nThe pattern is learned once: export the provider's key, name the model. Two lines change, nothing else.\n\nAnthropic (default):\n\n```bash\nexport SHANNON_AI_API_KEY=sk-ant-...\nexport SHANNON_AI_MODEL=anthropic:claude-sonnet-4-6\n```\n\nOpenAI:\n\n```bash\nexport SHANNON_AI_API_KEY=sk-...\nexport SHANNON_AI_MODEL=openai:gpt-6-sol\n```\n\nxAI:\n\n```bash\nexport SHANNON_AI_API_KEY=xai-...\nexport SHANNON_AI_MODEL=xai:grok-4.7\n```\n\nSource-build mode reads the same variables from a `.env` file.\n\n## AWS Bedrock\n\nRun `npx @keygraph/shannon setup` and select **AWS Bedrock**, or export directly:\n\n```bash\nexport AWS_REGION=us-east-1\nexport AWS_BEARER_TOKEN_BEDROCK=your-bearer-token\nexport SHANNON_AI_MODEL=amazon-bedrock:us.anthropic.claude-opus-4-8\n```\n\nBedrock uses bearer-token authentication only. IAM access keys, session tokens, assumed roles, and instance profiles are not supported. The model must be enabled in your region.\n\n## Custom base URL\n\n`SHANNON_AI_BASE_URL` routes model traffic through a proxy or LLM gateway instead of the provider's default endpoint — an LLM gateway such as LiteLLM, a regional endpoint, or any other host you choose. It is a plain endpoint override: it changes only *where* requests go. The provider half of `SHANNON_AI_MODEL` still decides which credential is sent and which API dialect is spoken, and that is unchanged by the base URL.\n\nThis works for **any** provider, curated or not, subject to two rules. A provider's dialect is fixed, so the endpoint you point at must speak that provider's dialect:\n\n| Provider prefix | Dialect the endpoint must speak |\n| --- | --- |\n| `anthropic:` | Anthropic Messages |\n| `openai:` | OpenAI Responses |\n\nAnd the model ID must still resolve in the harness catalogue. A base URL changes only the address; it grants no exemption from that check. A gateway serving a model under its own name needs that name described in a [custom model configuration](#custom-model-configuration) file.\n\nAnthropic Messages LLM gateway:\n\n```bash\nexport SHANNON_AI_API_KEY=sk-ant-...\nexport SHANNON_AI_MODEL=anthropic:claude-sonnet-4-6\nexport SHANNON_AI_BASE_URL=https://llm-gateway.example.com\n```\n\nOpenAI Responses LLM gateway:\n\n```bash\nexport SHANNON_AI_API_KEY=sk-...\nexport SHANNON_AI_MODEL=openai:gpt-6-sol\nexport SHANNON_AI_BASE_URL=https://llm-gateway.example.com/v1\n```\n\n`npx @keygraph/shannon setup` configures a base URL two ways: **Custom Base URL** covers the common Anthropic Messages and OpenAI Responses LLM gateways, and **Other provider** takes any provider ID plus an optional base URL of its own.\n\n## Custom model configuration\n\nA custom model configuration is a Pi `models.json` file that describes a model the harness catalogue does not carry: one a router or gateway serves under its own ID, or a local server (see [Local and self-hosted models](#local-and-self-hosted-models)). You pass it with `--models-config`, and Shannon merges its definitions over the catalogue so `SHANNON_AI_MODEL` can then name the model like any other:\n\n```bash\nnpx @keygraph/shannon start -u https://example.com -r /path/to/repo --models-config ./models.json\n```\n\n```bash\n./shannon start -u https://example.com -r ./my-repo --models-config ./models.json\n```\n\n[pi.dev/models](https://pi.dev/models) supplies the file contents. Find the model under the provider you want, since the same model has a different ID per provider, then open its page and expand **Show configuration** for a ready-to-paste snippet:\n\n```json\n{\n  \"providers\": {\n    \"openrouter\": {\n      \"apiKey\": \"YOUR_API_KEY\",\n      \"models\": [\n        {\n          \"id\": \"z-ai/glm-5.3\",\n          \"name\": \"Z.ai: GLM 5.3\",\n          \"reasoning\": true,\n          \"input\": [\n            \"text\"\n          ],\n          \"thinkingLevelMap\": {\n            \"off\": null,\n            \"minimal\": null,\n            \"low\": \"low\",\n            \"medium\": null,\n            \"high\": \"high\",\n            \"xhigh\": null,\n            \"max\": \"max\"\n          },\n          \"contextWindow\": 1048576,\n          \"maxTokens\": 943718,\n          \"cost\": {\n            \"input\": 1.4,\n            \"output\": 4.4,\n            \"cacheRead\": 0.26,\n            \"cacheWrite\": 0\n          },\n          \"compat\": {\n            \"supportsDeveloperRole\": false,\n            \"thinkingFormat\": \"openrouter\"\n          }\n        }\n      ],\n      \"api\": \"openai-completions\",\n      \"baseUrl\": \"https://openrouter.ai/api/v1\"\n    }\n  }\n}\n```\n\nThen name the model the usual way:\n\n```bash\nexport SHANNON_AI_API_KEY=your-api-key\nexport SHANNON_AI_MODEL=openrouter:z-ai/glm-5.3\n```\n\nLeave `YOUR_API_KEY` exactly as it is. Shannon sends the credential from your environment, and that takes precedence over anything the file declares, so the file describes the model and never has to hold a secret.\n\nPi's [models documentation](https://pi.dev/docs/latest/models) describes the full format, including provider routing preferences and compatibility flags.\n\n## Local and self-hosted models\n\nOllama, LM Studio, vLLM, and any other OpenAI-compatible server are reached through the same mechanism. Describe the server as a provider in a model config file, then name its model with `SHANNON_AI_MODEL`.\n\n> [!IMPORTANT]\n> Use `host.docker.internal`, not `localhost`. The scan runs inside a container, so `localhost` points at the container itself rather than at your machine.\n\nA `models.json` for Ollama:\n\n```json\n{\n  \"providers\": {\n    \"ollama\": {\n      \"baseUrl\": \"http://host.docker.internal:11434/v1\",\n      \"api\": \"openai-completions\",\n      \"apiKey\": \"ollama\",\n      \"models\": [\n        { \"\n\n<!-- File: llms.txt (Tokens: ~600 | Category: root-instruction) -->\n# Shannon\n\n> Shannon is an autonomous AI pentesting project by Keygraph. This repository contains Shannon, the AGPL-3.0 open-source white-box pentesting CLI. Keygraph Pro and Keygraph Enterprise are Keygraph's commercial continuous pentesting and AppSec editions.\n\nUse this file as the concise entry point for AI agents and LLMs reading this repository. For a single combined context file, use [llms-full.txt](llms-full.txt).\n\n## Start Here\n\n- [README](README.md): Main project overview, editions, quick start, Shannon capabilities, CI/CD integrations, common questions, safety notes, licensing, and support links.\n\n## Shannon\n\n- [Development](docs/development.md): Source-build workflow, common CLI commands, repository paths, and output locations.\n- [Configuration](docs/configuration.md): Authenticated testing, login flows, rules of engagement (including rate-limit guidance), report filters, and credential precedence.\n- [AI Providers](docs/ai-providers.md): Anthropic, OpenAI, xAI, AWS Bedrock, any other Pi-supported provider, custom LLM gateway setup, custom model configuration for models not yet in the Pi catalogue, and local self-hosted runtimes (Ollama, LM Studio, vLLM).\n- [Platforms and Networking](docs/platforms.md): Windows/WSL2, Linux, macOS, Docker networking, local applications, and custom hostnames.\n- [Workspaces and Resuming](docs/workspaces.md): Workspace storage, naming, resuming interrupted scans, and examples.\n- [Safety and Limitations](docs/safety.md): Authorized-use requirements, non-production guidance, mutative effects, model caveats, scope limits, cost, and performance.\n- [Coverage and Roadmap](docs/coverage-roadmap.md): Current Shannon coverage and roadmap direction.\n\n## Keygraph Pro and Enterprise\n\n- [Pro and Enterprise](docs/keygraph-platform.md): Keygraph's commercial continuous pentesting and AppSec editions, compared edition by edition with Shannon. The Community Program, Pro, and Enterprise all include black-box and white-box pentesting, parsed-code SAST, SCA and secrets scanning, findings management, fix pull requests and retests, Jira sync, and SSO, RBAC, and audit logs. Pro and the Community Program are cloud-hosted; Enterprise is self-hosted or air-gapped. Shannon Lite is now just Shannon. Shannon Pro is now Keygraph Pro.\n\n## External Links\n\n- [Keygraph website](https://keygraph.io): Company and commercial product information.\n- [Keygraph pricing](https://keygraph.io/pricing): Prices and the feature table for Shannon, the Community Program, Pro, and Enterprise.\n- [Keygraph demo](https://cal.com/team/keygraph/keygraph-technical-demo): Technical demo booking.\n- [Community Discord](https://discord.gg/cmctpMBXwE): Community support and discussion.\n\n<!-- File: CLAUDE.md (Tokens: ~8737 | Category: claude-rule) -->\n# CLAUDE.md\n\nAI-powered penetration testing agent for defensive security analysis. Automates vulnerability assessment by combining reconnaissance tools with AI-powered code analysis.\n\n## Commands\n\n**Prerequisites:** Docker, AI provider credentials (`.env` for local, `npx @keygraph/shannon setup` or env vars for npx)\n\n### Dual CLI\n\nShannon supports two CLI modes, auto-detected based on the current working directory:\n\n| | **npx** (`npx @keygraph/shannon`) | **Local** (`./shannon`) |\n|---|---|---|\n| **Install** | Zero-install via npm | Clone the repo |\n| **Image** | Pulled from Docker Hub (`keygraph/shannon:latest`) | Built locally (`shannon-worker`) |\n| **State** | `~/.shannon/` | Project directory |\n| **Credentials** | `~/.shannon/config.toml` (via `npx @keygraph/shannon setup`) or env vars | `./.env` |\n| **Config** | `~/.shannon/config.toml` (via `npx @keygraph/shannon setup`) | N/A |\n| **Prompts** | Bundled in Docker image | Mounted from `./apps/worker/prompts/` (live-editable) |\n\nMode auto-detection: local mode activates when env var `SHANNON_LOCAL=1` is set by the `./shannon` entry point (`apps/cli/src/mode.ts`). Otherwise npx mode.\n\n### npx Quick Start\n\n```bash\n# Configure credentials (interactive wizard)\nnpx @keygraph/shannon setup\n\n# Or export env vars directly (non-interactive / CI)\nexport ANTHROPIC_API_KEY=your-key\n\n# Run\nnpx @keygraph/shannon start -u <url> -r /path/to/repo\n```\n\n### Local (Development) Quick Start\n\n```bash\n# Setup\necho \"ANTHROPIC_API_KEY=your-key\" > .env\n\n# Build (auto-runs if image missing)\n./shannon build\n\n# Run\n./shannon start -u <url> -r ./my-repo\n./shannon start -u <url> -r ./my-repo -c ./apps/worker/configs/my-config.yaml\n./shannon start -u <url> -r /any/path/to/repo\n```\n\n### Common Commands\n\n```bash\n# Setup (npx mode only — one-time credential configuration)\nnpx @keygraph/shannon setup\n\n# Workspaces & Resume\n./shannon start -u <url> -r ./my-repo -w my-audit    # New named workspace\n./shannon start -u <url> -r ./my-repo -w my-audit    # Resume (same command)\n\n# Monitor\n./shannon scans                       # List running and completed scans, with each report's path\n./shannon logs [<workspace>]          # Show a scan's live log (default: the single running scan, else the most recent)\n./shannon logs [<workspace>] --agent <name>   # Tail one agent's own log (from .shannon/agents/)\n./shannon logs [<workspace>] --list-agents    # List the agents that have their own log\n./shannon status [<workspace>]        # Live phase/agent progress of one scan, read from Temporal (redraws, then exits; same default target)\n# Dashboard: http://localhost:8233\n\n# Stop\n./shannon stop [<workspace>]          # Stop one scan (default: the single running scan; confirms first; --yes/-y to skip)\n./shannon stop --all                  # Stop all running scans (Temporal stays up; confirms first)\n./shannon reset                       # Stop everything and wipe all Temporal data + volumes (type 'confirm' to proceed; cannot be skipped)\n\n# Version\n./shannon version                     # npx: package version; local: git SHA\n\n# Image management\n./shannon build [--no-cache]          # Local mode: build worker image\n\n# Build TypeScript (development)\npnpm run build                       # Build all packages via Turborepo\npnpm run check                       # Type-check all packages\npnpm biome                           # Biome lint + format + import sorting check\npnpm biome:fix                       # Auto-fix lint, format, and import sorting\n```\n\n**Monorepo tooling:** pnpm workspaces, Turborepo for task orchestration, Biome for linting/formatting. TypeScript compiler options shared via `tsconfig.base.json` at the root. All packages extend it, overriding only `rootDir` and `outDir`. Shared devDependencies (`typescript`, `@types/node`, `turbo`, `@biomejs/biome`) are hoisted to the root workspace.\n\n**Options:** `-c <file>` (YAML config), `--models-config <file>` (pi `models.json` defining models pi's catalogue lacks), `-o <path>` (output directory), `-w <name>` (named workspace; auto-resumes if exists), `--validate-auth` (run preflight and auth validation only, then stop; no pentest or report; requires a fresh workspace and an `authentication` block in the config), `--validate-model` (run the preflight model checks only — credential/registry resolution plus the cyber-access verification for cyber-gated providers — then stop; no pentest or report; requires a fresh workspace; needs no config; mutually exclusive with `--validate-auth`), `--pipeline-testing` (minimal prompts, 10s retries), `--keep-container` (preserve worker container after exit for log inspection), `--yes`/`-y` (skip the confirmation prompt on `stop`; required for non-interactive use; `reset` requires a typed `confirm` and cannot be skipped)\n\n## Architecture\n\n### Monorepo Layout\n\n```\napps/cli/        — @keygraph/shannon (published to npm, bundled with tsdown)\napps/worker/     — @shannon/worker (private, Temporal worker + pipeline logic)\n```\n\n### CLI Package (`apps/cli/`)\nPublished as `@keygraph/shannon` on npm. Contains Docker orchestration and a direct `@temporalio/client` integration for read-only status plus bounded workflow lifecycle operations; no worker/pipeline business logic or prompts. Bundled with tsdown for single-file ESM output (deps stay external).\n\n- `apps/cli/src/index.ts` — CLI dispatcher (`setup`, `start`, `stop`, `reset`, `logs`, `status`, `scans`, `build`, `version`)\n- `apps/cli/src/temporal-client.ts` — `@temporalio/client` integration: connects to the frontend on `127.0.0.1:7233` (published by compose), provides `describeScan` (status + `pendingActivities` → running agents), `queryProgress` (live `getProgress` query → `PipelineState`), `getTerminalOutcome` (workflow `result()`), and bounded lifecycle RPCs for `stop`. `stop` requests cancellation first, waits up to 10 seconds, then requests termination only when necessary and verifies closure within a bounded window. No worker of its own; scans are visible within Temporal's retention window, which `ensureInfra` (`apps/cli/src/docker.ts`) converges to `168h` (7 days) on every successful `shannon start`; override with `SHANNON_TEMPORAL_RETENTION` (a positive whole-hour value like `72h`)\n- `apps/cli/src/scan/` — `status` rendering: `pipeline.ts` (static phase/agent plan + `run*Agent` activity-type→agent map + mirrored `PipelineState`/`AgentMetrics` types; keep in sync with the worker), `derive.ts` (pure phase/agent state derivation shared by the tree and `--json`), `render.ts` (one renderer for both the live query state and the terminal result). The tree shows model work only: every row is an agent, an Agentic SAST stage, or a report step that is currently running or failed. Reconciliation is model work owned by a class, so its wall time renders as a trailing `+ duration` on that class's exploitation row (its analysis row when `exploit: false`) rather than as a row of its own; deterministic bookkeeping stages (`report:*` renumber/assemble/finalize/surface) never appear once they complete. `DerivedPhase.children` (renders sub-rows) and `DerivedPhase.meta` (`duration` vs a `k/N done` tally) are independent — Agentic SAST lists stages under a duration, exploitation lists classes under a tally\n- `apps/cli/src/mode.ts` — Auto-detection: local mode if `SHANNON_LOCAL=1` env var is set\n- `apps/cli/src/docker.ts` — Compose lifecycle, image pull/build, and ephemeral `docker run` worker spawning. Each worker carries workspace, task-queue, and preselected workflow-ID labels so stop can correlate the local worker with its Temporal execution before `session.json` exists. Before `docker run`, start fsyncs that exact candidate under the workspace's hidden internals and clears it only when `session.json` registers the same ID; stop reconciles any candidate left by an interrupted launch. Start also checks the image's workflow-ID protocol label and refuses a stale worker that would ignore the preselected ID\n- `apps/cli/src/home.ts` — State directory management (`~/.shannon/` for npx, `./` for local)\n- `apps/cli/src/env.ts` — `.env` loading, TOML fallback (npx only) via `apps/cli/src/config/resolver.ts`, credential validation, provider-scoped env flag building\n- `apps/cli/src/model-spec.ts` — `SHANNON_AI_MODEL` (`<provider>:<model-id>`) parsing; mirrors `apps/worker/src/ai/models.ts`\n- `apps/cli/src/config/resolver.ts` — Cascading config (npx only): env vars → `~/.shannon/config.toml` (parsed with `smol-toml`)\n- `apps/cli/src/config/writer.ts` — TOML serialization and secure file persistence (0o600)\n- `apps/cli/src/commands/setup.ts` — Interactive TUI wizard (`@clack/prompts`) for provider credential setup (npx only)\n- `apps/cli/src/paths.ts` — Repo/config/models-config path resolution (any absolute or relative path). `MODELS_CONFIG_CONTAINER_PATH` is fixed at `/app/models.json` because the worker names it to pi rather than discovering it\n- `apps/cli/src/version.ts` — Version reporting (npx: `package.json` version; local: `git-<sha>`)\n- `apps/cli/src/tty.ts` — Terminal capability detection: `requireInteractive` guard (fails fast off-TTY instead of hanging on a prompt), `supportsColor` color gating (`NO_COLOR`/`FORCE_COLOR`), and `stdoutIsTerminal` for spinner/cursor output\n- `apps/cli/src/commands/` — Command handlers\n- `apps/cli/infra/compose.yml` — Bundled Temporal compose file for npx mode\n- `apps/cli/tsdown.config.ts` — tsdown bundler config\n- `shannon` — Node.js entry point (`#!/usr/bin/env node`) that delegates to `apps/cli/dist/index.mjs`\n\n### Docker Architecture\nInfra (Temporal) runs via `docker-compose.yml`. Workers are ephemeral `docker run --rm` containers, one per scan, each with a unique task queue, preselected workflow ID, matching identity labels, and isolated volume mounts. `shannon stop --all` takes the union of labeled running workers and Temporal-running workflows, so an orphaned workflow is still stopped after its worker has disappeared.\n\n- `docker-compose.yml` — Infra only: `shannon-temporal` (port 7233/8233). Network: `shannon-net`\n- `Dockerfile` — 2-stage build (builder + Chainguard Wolfi runtime). Uses pnpm. Entrypoint: `CMD [\"node\", \"apps/worker/dist/temporal/worker.js\"]`\n- No `docker-compose.docker.yml` — host gateway handled via `--add-host` flag in CLI\n- `/etc/hosts` forwarding — at worker spawn, `forwardEtcHostsFlags` in `apps/cli/src/docker.ts` reads the host's `/etc/hosts` and emits one `--add-host` flag per valid user-added entry. Loopback IPs (`127.x`, `::1`) are rewritten to `host-gateway`; IPv6 addresses are bracketed. Disable per-scan via `SHANNON_FORWARD_HOSTS=false`. Native Windows is refused at startup (`blockNativeWindows` in `apps/cli/src/index.ts`, pointing to the WSL2 guide in `docs/platforms.md`); WSL2 reads its own `/etc/hosts` via the Linux path.\n\n### Worker Package (`apps/worker/`)\n- `apps/worker/src/paths.ts` — Centralized path constants (`PROMPTS_DIR`, `CONFIGS_DIR`, `WORKSPACES_DIR`)\n- `apps/worker/src/session-manager.ts` — Agent definitions (`AGENTS` record). Agent types in `apps/worker/src/types/agents.ts`\n- `apps/worker/src/config-parser.ts` — YAML config parsing with JSON Schema validation\n- `apps/worker/src/ai/pi/pi-executor.ts` — pi harness integration (agent-level retry disabled so Temporal owns restarts; provider-level retry on, see `apps/worker/src/ai/pi/retry-settings.ts`)\n- `apps/worker/src/services/` — Business logic layer (Temporal-agnostic). Activities delegate here. Key: `agent-execution.ts`, `error-handling.ts`, `container.ts`\n- `apps/worker/src/types/` — Consolidated types: `Result<T,E>`, `ErrorCode`, `AgentName`, `ActivityLogger`, etc.\n- `apps/worker/src/utils/` — Shared utilities (file I/O, formatting, concurrency)\n\n### Temporal Orchestration\nDurable workflow orchestration with crash recovery, queryable progress, intelligent retry, and parallel execution (5 concurrent agents in vuln/exploit phases).\n\n- `apps/worker/src/temporal/workflows.ts` — Main workflow (`pentestPipelineWorkflow`)\n- `apps/worker/src/temporal/activities.ts` — Thin wrappers — heartbeat loop, error classification, container lifecycle. Business logic delegated to `apps/worker/src/services/`\n- `apps/worker/src/temporal/activity-logger.ts` — `TemporalActivityLogger` implementation of `ActivityLogger` interface\n- `apps/worker/src/temporal/summary-mapper.ts` — Maps `PipelineSummary` to `WorkflowSummary`\n- `apps/worker/src/temporal/worker.ts` — Combined worker + client entry point (per-invocation task queue, submits workflow, waits for result)\n- `apps/worker/src/temporal/shared.ts` — Types, interfaces, query definitions\n### Five-Phase Pipeline\n\n1. **Pre-Recon** (`pre-recon`) — Source code analysis to build the architectural baseline\n2. **Recon** (`recon`) — Attack surface mapping from initial findings\n3. **Vulnerability Analysis** (5 parallel agents) — injection, xss, auth, authz, ssrf\n4. **Exploitation** (5 parallel agents, conditional) — Exploits confirmed vulnerabilities\n5. **Reporting** (`report`) — Executive-level security report\n\nAround those phases:\n\n- Optional agentic static analysis runs before the pentest when `agentic_sast.enabled` is `\"true\"`, as a child workflow.\n- After each class's analysis, reconciliation groups its findings into exploitation tasks.\n- Findings outside the five classes form an internal `miscellaneous` class with its own exploitation agent (`miscellaneous-exploit`).\n- A scan can finish `completed`, `partial`, `failed`, or `cancelled`; `partial` carries an ordered set of reasons.\n\n### Supporting Systems\n- **Configuration** — YAML configs in `apps/worker/configs/` use the closed JSON Schema in `config-schema.json`. Every fresh scan runs the fixed five analysis classes; there is no public class selector. `agentic_sast.enabled` is the only public agentic-SAST setting. Finding reconciliation runs on every scan and has no public setting of its own. Config also supports authentication (MFA/TOTP), URL/code rule scoping (`rules.avoid`/`rules.focus`), `exploit`, free-form `rules_of_engagement`, and post-hoc `report` options (`min_severity`, `min_confidence`, `guidance`, and exploit-only `sarif` output via `apps/worker/src/services/sarif-renderer.ts`, on by default for exploit runs and opt out with `report.sarif: \"false\"`). `code_path` avoid rules are enforced via the `@gotgenes/pi-permission-system` extension: `apps/worker/src/temporal/activities.ts:syncCodePathDenyRules` writes a global `path` deny config once per workflow (`apps/worker/src/ai/pi/permission-system.ts:syncPermissionSystemConfig`), and the executor loads the extension when that config is present (`apps/worker/src/ai/pi/pi-executor.ts`), so denies fire across every tool and child `task` session. Credential resolution — local mode: env vars → `./.env`; npx mode: env vars → `~/.shannon/config.toml` (via `npx @keygraph/shannon setup`)\n- **Agentic SAST progress** — Capella runs as a child workflow, so its activities are absent from the parent's `pendingActivities` and invisible to the CLI. The child signals each stage boundary up via `capellaStageProgress` (`apps/worker/src/temporal/shared.ts`); the parent's handler validates the payload and writes the child-supplied `startedAt` and `durationMs` directly to `operationalStages['agentic-sast:<stage>']`, so both the live `getProgress` query and the terminal result carry per-stage rows. Signalling is best-effort and every failure is swallowed — a closed or unreachable parent must never fail a SAST run. `CAPELLA_STAGE_LABELS` in `apps/worker/src/ai/sast/types.ts` is the one label table, shared by the scan log and the status tree; `CAPELLA_PROGRESS_STAGES` omits `export`, which runs no model and so never becomes a row. Scans predating the signal keep the aggregate `agentic-sast` span and render as a bare phase line\n- **Prompts** — Per-phase templates in `apps/worker/prompts/` with variable substitution (`{{TARGET_URL}}`, `{{CONFIG_CONTEXT}}`). Shared partials in `apps/worker/prompts/shared/` via `apps/worker/src/services/prompt-manager.ts`, including `_code-path-rules.txt` (focus/avoid `[FILE]`/`[GLOB]` routing) and `_rules-of-engagement.txt` (free-text engagement rules). When `exploit: false`, `apps/worker/src/services/findings-renderer.ts` deterministically converts each `*_exploitation_queue.json` into a `*_findings.md` for report assembly — no LLM in the loop\n- **Agent Harness (pi)** — Uses the **pi harness** (`@earendil-works/pi-coding-agent`, requires Node ≥ 22.19) via `apps/worker/src/ai/pi/pi-executor.ts` (`runPiPrompt` → `createAgentSession`). Retry is split in `apps/worker/src/ai/pi/retry-settings.ts`: pi's agent-level loop is off so Temporal owns agent restarts, while `provider.maxRetries` stays on — pi reads the `provider` block independently of the `enabled` flag — so transport faults are absorbed in-session rather than costing a full agent re-run. `maxRetryDelayMs` is left at pi's 60s default. One model runs every phase, named by `SHANNON_AI_MODEL=<provider>:<model-id>` (default `anthropic:claude-sonnet-4-6`). `apps/worker/src/ai/models.ts` parses the spec — splitting on the **first** colon only, so Bedrock IDs keep theirs — and resolves it through pi's `ModelRuntime`. pi ships the `CredentialStore` interface but no in-memory implementation (its own reads `auth.json` from disk), so `RuntimeCredentialStore` in that file supplies one: credentials arrive as env vars in an ephemeral container and must never touch disk. `createModelRuntime(providerId, apiKey)` builds the runtime with `allowModelNetwork: true`, so `ModelRuntime.create()` refreshes the model catalogue over the network at scan start and a freshly released model resolves without a `--models-config` file. The fetch is bounded (10s) and falls back to the static catalogue on timeout, so an unreachable catalogue endpoint cannot hang the scan. The refresh does not override a `--models-config`: pi reloads and re-applies that file as a config overlay on every refresh (it reloads `this.config` at the top of `refresh()`), so custom definitions still win over the fetched catalogue; the merge semantics below are unchanged, just layered over a fresher base. `resolveModelSelection()` is **async** because `ModelRuntime.create()` is. Any pi-ai provider id is accepted — `parseModelSpec` no longer rejects against a hardcoded list, so pi's registry is the authority (an unknown provider/model surfaces as a clear \"not found in pi registry\" error at preflight, which points to the browsable catalogue at `pi.dev/models` — `PI_CATALOG_URL` in `apps/worker/src/ai/models.ts`, appended to the not-found errors and shown in the setup wizard's \"Other provider\" hint). Four providers are **curated** (`CURATED_PROVIDERS`: `anthropic`, `openai`, `xai`, `amazon-bedrock`) with their own credential variables, config sections, and setup flows; each provider's API key env var is declared once in `PROVIDER_API_KEY_ENV` — Shannon uses each vendor's own variable name (`OPENAI_API_KEY`, `XAI_API_KEY`, …), never an invented one; Bedrock's entry is `AWS_BEARER_TOKEN_BEDROCK`, paired with `AWS_REGION`, which preflight requires separately as provider config rather than a credential. Any other provider uses the **generic** credential path: `SHANNON_AI_API_KEY` (`GENERIC_API_KEY_ENV`) supplies the key for any provider whose credential is a plain API key. Curated providers' own variables take precedence over it, and it also works as a fallback for them — Bedrock is the sole exception (it authenticates through its AWS_ variables, so the generic key never stands in for it). The CLI forwards `SHANNON_AI_API_KEY` in `COMMON_FORWARD_VARS` (it is provider-neutral, binding to whatever `SHANNON_AI_MODEL` names, so the \"only one provider configured\" guard counts only named credentials), and stores it under a generic `[provider]` config.toml section (`provider.api_key`). `npx @keygraph/shannon setup` exposes this as the \"Other provider\" option: free-text provider id + model id + key (a curated provider id is rejected there, since it has its own option). A model pi's catalogue does not carry, such as a self-hosted model, is reachable without an SDK bump: `--models-config <file>` mounts a pi `models.json` read-only at `/app/models.json`. The mount is the entire CLI→worker protocol: nothing is forwarded through the environment, and `modelsConfigPath()` detects the file at that fixed path, exactly as `piAuthPresent()` detects the pi auth mount whose flag is likewise not forwarded (`MODELS_CONFIG_CONTAINER_PATH` in the CLI and `MODELS_CONFIG_PATH` in `apps/worker/src/paths.ts` must stay in sync). `createModelRuntime` always names `modelsPath` explicitly — the mounted path, or **`null` when no config was supplied**, which switches models.json off outright. It is never left to pi's default of `<agent dir>/models.json`, because that dir is shared with the pi auth mount, so a file landing there must not silently contribute model definitions to a scan that did not ask for one. `modelsStorePath` is pinned to the agent dir alongside it, since pi otherwise derives it from `dirname(modelsPath)` and would try to write beside a read-only mount. Custom definitions merge over the built-in catalogue: a matching model id replaces the built-in entry, a new id is added alongside, and `modelOverrides` adjusts a built-in without replacing the provider's list. Omitted fields take pi's defaults (`contextWindow` 128000, `maxTokens` 16384), so a large-context model needs them stated. Credentials are unaffected: `RuntimeCredentialStore` outranks any `apiKey` the file carries, so a snippet pasted from `pi.dev/models` can keep its placeholder key while the real secret stays in the environment — and pi's `!command` config-value form is never reached through `apiKey`. Preflight reports `modelRuntime.getError()` **before** the \"model not found\" check, because a file that fails to parse, fails schema validation, or composes badly otherwise leaves pi with an empty or fallback provider and surfaces as a model-id error that blames `SHANNON_AI_MODEL`. `SHANNON_AI_BASE_URL` overrides the endpoint for any provider (proxies/gateways); the credential and API dialect are unchanged. It is applied after model resolution, so it also wins over a `baseUrl` set in a model config. A base URL only changes the address — `resolveModel` (`apps/worker/src/ai/models.ts`) carries it onto the model descriptor and nothing else, and it grants **no exemption from registry validation**: the model id must resolve for every provider and endpoint alike. A gateway serving a model under its own name is described in a `--models-config` file, which puts a real descriptor in the registry rather than guessing one. pi's builtin `openai` provider serves a single API (Responses) and dispatches on the provider rather than `model.api`, so an `openai:` gateway run always speaks Responses; a Chat-Completions-only gateway must be reached through a completions-native provider such as `openrouter` instead. `buildEnvFlags` forwards only the selected provider's credential into the worker container. The CLI mirrors the parse rule and the provider/credential tables in `apps/cli/src/model-spec.ts` (it cannot import from the worker package); the two must stay in sync. pi ships no JSON-schema output or `Task`/`TodoWrite` built-ins, so structured queues are captured via a `submit_exploitation_queue` custom tool (`apps/worker/src/ai/queue-schemas.ts`), and `task` (child sessions scoped to `read`, `grep`, `find`, `ls`, `write`, and `bash` — no nested `task` or collector tools; `CHILD_TOOLS` in `apps/worker/src/ai/pi/task-tool.ts`) + `todo_write` (`apps/worker/src/ai/pi/session-tools.ts`) are provided as custom tools; the per-phase collectors are pi custom tools (TypeBox `defineTool` in `apps/worker/src/collectors/`). Every agent `createAgentSession` call passes `thinkingLevel: PI_THINKING_LEVEL` (`'high'`, `apps/worker/src/ai/pi/thinking-level.ts`) for deeper analysis than pi's default (`medium`). The preflight credential probe (`apps/worker/src/services/preflight.ts`) passes none and runs at pi's default. The reconciliation-enrichment `completeSimple` call (`apps/worker/src/ai/pi/structured-generation.ts`) passes no `reasoning`, so it runs with thinking off. There is no public thinking setting. Browser automation via `playwright-cli` with session isolation (`-s=<session>`). TOTP generation via `generate-totp` CLI tool. Login flow template at `apps/worker/prompts/shared/login-instructions.txt` supports form, SSO, API, and basic auth. On authenticated whitebox scans, the `validate-authentication` preflight performs the single real login and saves the browser session to `auth-state.json` in the per-session audit directory (path from `authStateFile()` in `apps/worker/src/audit/utils.ts`, derived from `generateAuditPath()`). The validation activity (`apps/worker/src/services/validate-authentication.ts`) removes any stale file from a prior run before the agent runs and verifies the file parses and contains cookies or storage before the preflight is marked complete; `logWorkflowComplete` deletes it when the workflow ends so authenticated cookies don't sit on disk between scans. Agent prompts opt in to session reuse by `@include(shared/_shared-session.txt)` before their `<login_instructions>` block — the partial restores the session and falls through to the full login flow if verification fails. `vuln-auth`/`exploit-auth` omit the include and own their own login\n- **Pi Credential Reuse** — `SHANNON_USE_PI_AUTH=1` opts into reusing the host's Pi login, including an `openai-codex` ChatGPT Plus/Pro subscription (`SHANNON_AI_MODEL=openai-codex:<model-id>`) or an `xai` Grok subscription (`SHANNON_AI_MODEL=xai:<model-id>`); the mechanism is provider-agnostic and works for any Pi login. `apps/cli/src/env.ts` requires `~/.pi/agent/auth.json`; `start.ts` passes its path to `spawnWorker`, which mounts only that file read-write at `/tmp/.pi/agent/auth.json`. The flag itself is not forwarded: the worker detects the file with `piAuthPresent()` and passes its path to `ModelRuntime.create`. CLI and worker API-key presence checks are skipped on this path, but the normal preflight model probe still validates the credential. The image and UID-remapping entrypoint keep `/tmp/.pi/agent` owned by `pentest` so adjacent Pi/Shannon configuration remains writable. Refreshed OAuth state is persisted to the host for subsequent scans.\n- **Audit System** — Crash-safe append-only logging in `workspaces/{hostname}_{sessionId}/`. The run directory's top level holds the human-facing report in both formats (`Security-Assessment-Report.pdf` and `Security-Assessment-Report.md`, `FINAL_REPORT_PDF_FILENAME`/`FINAL_REPORT_MD_FILENAME` in `apps/worker/src/paths.ts`); everything else — deliverables, per-agent logs, prompts, `session.json`, `workflow.log`, and browser artifacts — is nested under a hidden `.shannon/` internals dir (`INTERNAL_DIR`) so a customer sees only the report. Audit path helpers route through `generateInternalPath` (`apps/worker/src/audit/utils.ts`); the CLI nests the overlay backing dirs under the same `.shannon/` (`apps/cli/src/docker.ts`, `start.ts`). `session.json`/`workflow.log` reads use dual-read resolvers (`resolveSessionJsonPath`, `resolveRunFile`) that prefer `.shannon/` and fall back to the legacy run-root layout, so pre-restructure workspaces stay listable (`scans`/`logs`) without migration. A pre-restructure workspace cannot be resumed: `classifyWorkspaceLaunch` (`apps/cli/src/commands/start.ts`) requires `.shannon/launch.json`, and its absence fails the launch as \"created by an earlier version of Shannon\" before anything on disk is touched. There is no in-place migration — the workspace's files and report are left untouched, and the operator starts a new scan under a different `-w` name. The report agent writes structured findings to `report.json`, from which `report-renderer.ts` renders the assembled markdown and `report-json-adapter.ts` produces the Typst-shaped JSON that `pdf-renderer.ts` compiles into `comprehensive_security_assessment_report.pdf` using the bundled `apps/worker/templates/typst/report.typ` template (the `typst` binary is installed in the worker image). `copyReportToRunRoot` (`apps/worker/src/services/reporting.ts`) surfaces both the PDF and the markdown to the run root as `Security-Assessment-Report.pdf` and `Security-Assessment-Report.md`; the deliverables-dir copies remain as the git-checkpointed sources. PDF compilation is best-effort — a failure is logged and the run still completes. WorkflowLogger (`apps/worker/src/audit/workflow-logger.ts`) provides unified human-readable per-workflow logs, backed by LogStream (`apps/worker/src/audit/log-stream.ts`) shared stream primitive. Every combined-log line is also projected into a per-agent file under `.shannon/agents/<slug>.log` (one per pipeline agent, one per Capella stage; subagents fold into the parent's file, and a stage's concurrent sessions share its file with an inline session label). The projection boundary is `apps/worker/src/audit/actor-projection.ts` (`projectActor` maps a `TraceActor` to its combined prefix and owning file slug — slugs come only from closed fields); fan-out is best-effort and never blocks the canonical combined log. A lifecycle owner holds a `LogStream` lease per agent file (the pipeline agent's `logAgent` span, or a Capella stage activity's `try/finally`) so per-line writes ride the reference count; `CapellaStageTrace.drain()` flushes a stage's trace queue before its activity returns. The CLI tails one file with `shannon logs --agent <name>` (`--list-agents` to enumerate); the default `shannon logs` path is unchanged\n- **Deliverables** — Saved to `.shannon/deliverables/` in the target repo via the `save-deliverable` CLI script (`apps/worker/src/scripts/save-deliverable.ts`)\n- **Workspaces & Resume** — Named workspaces via `-w <name>` or auto-named from URL+timestamp. Resume detects completed agents via `session.json`. `loadResumeState()` in `apps/worker/src/temporal/activities.ts` validates deliverable existence, restores git checkpoints, and cleans up incomplete deliverables\n\n## Development Notes\n\n### Adding a New Agent\n1. Define agent in `apps/worker/src/session-manager.ts` (add to `AGENTS` record). `ALL_AGENTS`/`AgentName` types live in `apps/worker/src/types/agents.ts`\n2. Create prompt template in `apps/worker/prompts/` (e.g., `vuln-newtype.txt`)\n3. Two-layer pattern: add a thin activity wrapper in `apps/worker/src/temporal/activities.ts` (heartbeat + error classification). `AgentExecutionService` in `apps/worker/src/services/agent-execution.ts` handles the agent lifecycle automatically via the `AGENTS` registry\n4. Register activity in `apps/worker/src/temporal/workflows.ts` within the appropriate phase\n\n### Modifying Prompts\n- Variable substitution: `{{TARGET_URL}}`, `{{CONFIG_CONTEXT}}`, `{{LOGIN_INSTRUCTIONS}}`\n- Shared partials in `apps/worker/prompts/shared/` included via `apps/worker/src/services/prompt-manager.ts`\n- Test with `--pipeline-testing` for fast iteration\n\n### Key Design Patterns\n- **Configuration-Driven** — YAML configs with JSON Schema validation\n- **Progressive Analysis** — Each phase builds on previous results\n- **Harness-First** — the pi harness (`@earendil-works/pi-coding-agent`) handles autonomous analysis\n- **Modular Error Handling** — `ErrorCode` enum, `Result<T,E>` for explicit error propagation, automatic retry (3 attempts per agent)\n- **Services Boundary** — Activities are thin Temporal wrappers; `apps/worker/src/services/` owns business logic, accepts `ActivityLogger`, returns `Result<T,E>`. No Temporal imports in services\n- **DI Container** — Per-workflow in `apps/worker/src/services/container.ts`. `AuditSession` excluded (parallel safety)\n- **Ephemeral Workers** — Each scan runs in its own `docker run --rm` container with a per-invocation task queue. Temporal routes activities by queue name, so per-scan queues ensure activities never land on a worker with the wrong repo mounted\n\n### Security\nDefensive security tool only. Use only on systems you own or have explicit permission to test.\n\n## Code Style Guidelines\n\n### Formatting\nBiome handles formatting and linting. Run `pnpm biome:fix` to auto-fix. Config in `biome.json`: single quotes, semicolons, trailing commas, 2-space indent, 120 char line width.\n\n### Clarity Over Brevity\n- Optimize for readability, not line count — three clear lines beat one dense expression\n- Use descriptive names that convey intent\n- Prefer explicit logic over clever one-liners\n\n### Structure\n- Keep functions focused on a single responsibility\n- Use early returns and guard clauses instead of deep nesting\n- Never use nested ternary operators — use if/else or switch\n- Extract complex conditions into well-named boolean variables\n\n### TypeScript Conventions\n- Use `function` keyword for top-level functions (not arrow functions)\n- Explicit return type annotations on exported/top-level functions\n- Prefer `readonly` for data that shouldn't be mutated\n- `exactOptionalPropertyTypes` is enabled — use spread for optional props, not direct `undefined` assignment\n\n### Avoid\n- Combining multiple concerns into a single function to \"save lines\"\n- Dense callback chains when sequential logic is clearer\n- Sacrificing readability for DRY — some repetition is fine if clearer\n- Abstractions for one-time operations\n- Backwards-compatibility shims, deprecated wrappers, or re-exports for removed code — delete the old code, don't preserve it\n\n### Comments\nComments must be **timeless** — no references to this conversation, refactoring history, or the AI.\n\n**Patterns used in this codebase:**\n- `/** JSDoc */` — file headers (after license) and exported functions/interfaces\n- `// N. Description` — numbered sequential steps inside function bodies. Use when a\n  function has 3+ distinct phases where at least one isn't immediately obvious from the\n  code. Each step marks the start of a logical phase. Reference: `AgentExecutionService.execute`\n  (steps 1-9) and `injectModelIntoReport` (steps 1-5)\n- `// === Section ===` — high-level dividers between groups of functions in long files,\n  or to label major branching/classification blocks (e.g., `// === SPENDING CAP SAFEGUARD ===`).\n  Not for sequential steps inside function bodies — use numbered steps for that\n- `// NOTE:` / `// WARNING:` / `// IMPORTANT:` — gotchas and constraints\n\n**Never:** obvious comments, conversation references (\"as discussed\"), history (\"moved from X\")\n\n## Key Files\n\n**CLI:** `shannon` (entry point), `apps/cli/src/index.ts` (dispatcher), `apps/cli/src/docker.ts` (orchestration), `apps/cli/src/mode.ts` (auto-detection)\n\n**Entry Points:** `apps/worker/src/temporal/workflows.ts`, `apps/worker/src/temporal/activities.ts`, `apps/worker/src/temporal/worker.ts`\n\n**Core Logic:** `apps/worker/src/session-manager.ts`, `apps/worker/src/ai/pi/pi-executor.ts`, `apps/worker/src/ai/pi/permission-system.ts` (writes `code_path` deny rules to the `@gotgenes/pi-permission-system` global config), `apps/worker/src/config-parser.ts`, `apps/worker/src/services/` (incl. `preflight.ts`, `findings-renderer.ts`, `reporting.ts`), `apps/worker/src/audit/`\n\n**Config:** `docker-compose.yml`, `apps/cli/infra/compose.yml`, `apps/worker/configs/`, `apps/worker/prompts/`, `tsconfig.base.json` (shared compiler options), `turbo.json`, `biome.json`\n\n**CI/CD:** `.github/workflows/release.yml` (Docker Hub push + npm publish + GitHub release, manual dispatch)\n\n## Package Installation\n\nPackage managers are configured with a minimum release age (7 days). Requires pnpm >= 10.16.0. If `pnpm install` fails due to a package being too new, **do not attempt to bypass it** — report the blocked package to the user and stop.\n\n## Troubleshooting\n\n- **\"Repository not found\"** — Pass a path to the target repo (`-r /path/to/repo` or `-r ./my-repo`)\n- **\"Temporal not ready\"** — Wait for health check or `docker compose logs temporal`\n- **Worker not processing** — Check `docker ps --filter \"name=shannon-worker-\"`\n- **Reset state** — `./shannon reset`\n- **Local apps unreachable** — Use `host.docker.internal` instead of `localhost`\n- **Container permissions** — On Linux, may need `sudo` for docker commands\n\n<!-- File: .claude/commands/debug.md (Tokens: ~1436 | Category: command-prompt) -->\n---\ndescription: Systematically debug errors using context analysis and structured recovery\n---\n\nYou are debugging an issue. Follow this structured approach to avoid spinning in circles.\n\n## Step 1: Capture Error Context\n- Read the full error message and stack trace\n- Identify the layer where the error originated:\n  - **CLI/Args** - Input validation, path resolution\n  - **Config Parsing** - YAML parsing, JSON Schema validation (`src/config-parser.ts`)\n  - **Session Management** - Agent definitions (`src/session-manager.ts`), mutex (`src/utils/concurrency.ts`)\n  - **DI Container** - Container initialization/lookup (`src/services/container.ts`)\n  - **Services** - AgentExecutionService, ConfigLoaderService, ExploitationCheckerService, error-handling (`src/services/`)\n  - **Audit System** - Logging, metrics tracking, atomic writes (`src/audit/`)\n  - **Claude SDK** - Agent execution, MCP servers, turn handling (`src/ai/claude-executor.ts`)\n  - **Git Operations** - Checkpoints, rollback, commit (`src/services/git-manager.ts`)\n  - **Validation** - Deliverable checks, queue validation (`src/services/queue-validation.ts`)\n\n## Step 2: Check Relevant Logs\n\n**Session audit logs:**\n```bash\n# Find most recent session\nls -lt workspaces/ | head -5\n\n# Check session metrics and errors\ncat workspaces/<session>/session.json | jq '.errors, .agentMetrics'\n\n# Check agent execution logs\nls -lt workspaces/<session>/agents/\ncat workspaces/<session>/agents/<latest>.log\n```\n\n## Step 3: Trace the Call Path\n\nFor Shannon, trace through these layers:\n\n1. **Worker + Client** → `src/temporal/worker.ts` - Combined worker + workflow submission\n2. **Workflow** → `src/temporal/workflows.ts` - Pipeline orchestration\n3. **Activities** → `src/temporal/activities.ts` - Thin wrappers: heartbeat, error classification\n4. **Container** → `src/services/container.ts` - Per-workflow DI\n5. **Services** → `src/services/agent-execution.ts` - Agent lifecycle\n6. **Config** → `src/config-parser.ts` via `src/services/config-loader.ts`\n7. **Prompts** → `src/services/prompt-manager.ts`\n8. **Audit** → `src/audit/audit-session.ts` - Logging facade, metrics tracking\n9. **Executor** → `src/ai/claude-executor.ts` - SDK calls, MCP setup, retry logic\n10. **Validation** → `src/services/queue-validation.ts` - Deliverable checks\n\n## Step 4: Identify Root Cause\n\n**Common Shannon-specific issues:**\n\n| Symptom | Likely Cause | Fix |\n|---------|--------------|-----|\n| Agent hangs indefinitely | MCP server crashed, Playwright timeout | Check Playwright logs in `/tmp/playwright-*` |\n| \"Validation failed: Missing deliverable\" | Agent didn't create expected file | Check `deliverables/` dir, review prompt |\n| Git checkpoint fails | Uncommitted changes, git lock | Run `git status`, remove `.git/index.lock` |\n| \"Session limit reached\" | Claude API billing limit | Not retryable - check API usage |\n| Parallel agents all fail | Shared resource contention | Check mutex usage, stagger startup timing |\n| Cost/timing not tracked | Metrics not reloaded before update | Add `metricsTracker.reload()` before updates |\n| session.json corrupted | Partial write during crash | Delete and restart, or restore from backup |\n| YAML config rejected | Invalid schema or unsafe content | Run through AJV validator manually |\n| Prompt variable not replaced | Missing `{{VARIABLE}}` in context | Check `src/services/prompt-manager.ts` interpolation |\n| Service returns Err result | Check `ErrorCode` in Result | Trace through `classifyErrorForTemporal()` in `src/services/error-handling.ts` |\n| Container not found | `getOrCreateContainer()` not called | Check activity setup code in `src/temporal/activities.ts` |\n| ActivityLogger undefined | `createActivityLogger()` not called | Must be called at top of each activity function |\n\n**MCP Server Issues:**\n```bash\n# Check if Playwright browsers are installed\nnpx playwright install chromium\n\n# Check MCP server startup (look for connection errors)\ngrep -i \"mcp\\|playwright\" workspaces/<session>/agents/*.log\n```\n\n**Git State Issues:**\n```bash\n# Check for uncommitted changes\ngit status\n\n# Check for git locks\nls -la .git/*.lock\n\n# View recent git operations from Shannon\ngit reflog | head -10\n```\n\n## Step 5: Apply Fix with Retry Limit\n\n- **CRITICAL**: Track consecutive failed attempts\n- After **3 consecutive failures** on the same issue, STOP and:\n  - Summarize what was tried\n  - Explain what's blocking progress\n  - Ask the user for guidance or additional context\n- After a successful fix, reset the failure counter\n\n## Step 6: Validate the Fix\n\n**For code changes:**\n```bash\n# Compile TypeScript\nnpx tsc --noEmit\n\n# Quick validation run\nshannon <URL> <REPO> --pipeline-testing\n```\n\n**For audit/session issues:**\n- Verify `session.json` is valid JSON after fix\n- Check that atomic writes complete without errors\n- Confirm mutex release in `finally` blocks\n\n**For agent issues:**\n- Verify deliverable files are created in correct location\n- Check that validation functions return expected results\n- Confirm retry logic triggers on appropriate errors\n\n## Anti-Patterns to Avoid\n\n- Don't delete `session.json` without checking if session is active\n- Don't modify git state while an agent is running\n- Don't retry billing/quota errors (they're not retryable)\n- Don't ignore PentestError type - it indicates the error category\n- Don't make random changes hoping something works\n- Don't fix symptoms without understanding root cause\n- Don't bypass mutex protection for \"quick fixes\"\n\n## Quick Reference: Error Types\n\n`ErrorCode` enum in `src/types/errors.ts` provides finer-grained classification used by `classifyErrorForTemporal()` in `src/services/error-handling.ts`.\n\n| PentestError Type | Meaning | Retryable? |\n|-------------------|---------|------------|\n| `config` | Configuration file issues | No |\n| `network` | Connection/timeout issues | Yes |\n| `prompt` | Claude SDK/API issues | Sometimes |\n| `filesystem` | File read/write errors | Sometimes |\n| `validation` | Deliverable validation failed | Yes (via retry) |\n| `billing` | API quota/billing limit | No |\n| `unknown` | Unexpected error | Depends |\n\n---\n\nNow analyze the error and begin debugging systematically.\n\n<!-- File: .claude/commands/pr.md (Tokens: ~667 | Category: command-prompt) -->\n---\ndescription: Create a PR to main branch using conventional commit style for the title\n---\n\nCreate a pull request from the current branch to the `main` branch.\n\n## Arguments\n\nThe user may provide issue numbers that this PR fixes: `$ARGUMENTS`\n\n- If provided (e.g., `123` or `123,456`), use these issue numbers\n- If not provided, check the branch name for issue numbers (e.g., `fix/123-bug` or `issue-456-feature` → extract `123` or `456`)\n- If no issues are found, omit the \"Closes\" section\n\n## Steps\n\nFirst, analyze the current branch to understand what changes have been made:\n1. Run `git log --oneline -10` to see recent commit history and understand commit style\n2. Run `git log main..HEAD --oneline` to see all commits on this branch that will be included in the PR\n3. Run `git diff main...HEAD --stat` to see a summary of file changes\n4. Run `git branch --show-current` to get the branch name for issue detection (if no explicit issues provided)\n\nThen generate a PR title that:\n- Follows conventional commit format (e.g., `fix:`, `feat:`, `chore:`, `refactor:`)\n- Is concise and accurately describes the changes\n- Matches the style of recent commits in the repository\n\nGenerate a PR body with:\n- A `## Summary` section using rich bullets with bold action leads\n- A `Closes #X` line for each issue number (if any were provided or detected from branch name)\n\nEach Summary bullet must follow this format:\n- **Bold action phrase** (imperative verb: \"Add X\", \"Replace Y\", \"Fix Z\") — followed by em dash and a 1-2 sentence conceptual description of what changed and why\n- Keep descriptions conceptual — no inline code references (no backticks for function/file names). The diff shows the code\n- Use 2-5 bullets, scaling with PR size. Group related changes into single bullets rather than listing every file touched\n\nExample:\n```\n## Summary\n\n- **Add preflight validation** — validates repo path, config, and credentials before agent execution. Fails fast with actionable errors\n- **Replace error strings** — pipe-delimited segments rendered as multi-line blocks with phase context, type, message, and remediation hint\n- **Add error classification** — new error codes for repo, auth, and billing failures with proper retry classification\n```\n\nFinally, create the PR using the gh CLI:\n```\ngh pr create --base main --title \"<generated title>\" --body \"$(cat <<'EOF'\n## Summary\n<rich bullets>\n\nCloses #<issue1>\nCloses #<issue2>\nEOF\n)\"\n```\n\nNote: Omit the \"Closes\" lines entirely if no issues are associated with this PR.\n\nIMPORTANT:\n- Do NOT include any Claude Code attribution in the PR\n- Use the conventional commit prefix that best matches the changes (fix, feat, chore, refactor, docs, etc.)\n- The `Closes #X` syntax will automatically close the referenced issues when the PR is merged\n\n<!-- File: .claude/commands/review.md (Tokens: ~1820 | Category: command-prompt) -->\n---\ndescription: Review code changes for Shannon-specific patterns, security, and common mistakes\n---\n\nReview the current changes (staged or working directory) with focus on Shannon-specific patterns and common mistakes.\n\n## Step 1: Gather Changes\nRun these commands to understand the scope:\n```bash\ngit diff --stat HEAD\ngit diff HEAD\n```\n\n## Step 2: Check Shannon-Specific Patterns\n\n### Error Handling (CRITICAL)\n- [ ] **All errors use PentestError** - Never use raw `Error`. Use `new PentestError(message, type, retryable, context)`\n- [ ] **Error type is appropriate** - Use correct type: 'config', 'network', 'tool', 'prompt', 'filesystem', 'validation', 'billing', 'unknown'\n- [ ] **Retryable flag matches behavior** - If error will be retried, set `retryable: true`\n- [ ] **Context includes debugging info** - Add relevant paths, tool names, error codes to context object\n- [ ] **Never swallow errors silently** - Always log or propagate errors\n- [ ] **Use ErrorCode enum** - Prefer `ErrorCode.CONFIG_INVALID` over string matching for classification\n- [ ] **Result<T,E> for service returns** - Services return `Result`, not throw\n\n### Audit System & Concurrency (CRITICAL)\n- [ ] **Mutex protection for parallel operations** - Use `sessionMutex.lock()` when updating `session.json` during parallel agent execution\n- [ ] **Reload before modify** - Always call `this.metricsTracker.reload()` before updating metrics in mutex block\n- [ ] **Atomic writes for session.json** - Use `atomicWrite()` for session metadata, never `fs.writeFile()` directly\n- [ ] **Stream drain handling** - Log writes must wait for buffer drain before resolving\n- [ ] **Semaphore release in finally** - Git semaphore must be released in `finally` block\n\n### Claude SDK Integration (CRITICAL)\n- [ ] **MCP server configuration** - Verify Playwright MCP uses `--isolated` and unique `--user-data-dir`\n- [ ] **Prompt variable interpolation** - Check all `{{VARIABLE}}` placeholders are replaced\n- [ ] **Turn counting** - Increment `turnCount` on assistant messages, not tool calls\n- [ ] **Cost tracking** - Extract cost from final `result` message, track even on failure\n- [ ] **API error detection** - Check for \"session limit reached\" (fatal) vs other errors\n\n### Configuration & Validation (CRITICAL)\n- [ ] **FAILSAFE_SCHEMA for YAML** - Never use default schema (prevents code execution)\n- [ ] **Security pattern detection** - Check for path traversal (`../`), HTML injection (`<>`), JavaScript URLs\n- [ ] **Rule conflict detection** - Rules cannot appear in both `avoid` AND `focus`\n- [ ] **Duplicate rule detection** - Same `type:url_path` cannot appear twice\n- [ ] **JSON Schema validation before use** - Config must pass AJV validation\n\n### Services Layer & DI Container (CRITICAL)\n- [ ] **Business logic in services, not activities** — Activities: heartbeat loop, error classification, container calls only. Domain logic → `src/services/`\n- [ ] **Services accept ActivityLogger** — Never import `@temporalio/*` in services. Use `ActivityLogger` interface from `src/types/`\n- [ ] **Result type for fallible operations** — Service methods return `Result<T, PentestError>`, unwrap with `isOk()`/`isErr()`. Activities call `executeOrThrow()` at the boundary\n- [ ] **Container lifecycle** — `getOrCreateContainer()` at activity start, `removeContainer()` only in workflow cleanup\n- [ ] **AuditSession not in container** — Must be passed per-agent call (parallel safety)\n\n### Session & Agent Management (CRITICAL)\n- [ ] **Deliverable dependencies respected** - Exploitation agents only run if vulnerability queue exists AND has items\n- [ ] **Queue validation before exploitation** - Use `safeValidateQueueAndDeliverable()` to check eligibility\n- [ ] **Git checkpoint before agent run** - Create checkpoint for rollback on failure\n- [ ] **Git rollback on retry** - Call `rollbackGitWorkspace()` before each retry attempt\n- [ ] **Agent prerequisites checked** - Verify prerequisite agents completed before running dependent agent\n\n### Parallel Execution\n- [ ] **Promise.allSettled for parallel agents** - Never use `Promise.all` (partial failures should not crash batch)\n- [ ] **Staggered startup** - 2-second delay between parallel agent starts to prevent API throttle\n- [ ] **Individual retry loops** - Each agent retries independently (3 attempts max)\n- [ ] **Results aggregated correctly** - Handle both 'fulfilled' and 'rejected' results from `Promise.allSettled`\n\n## Step 3: TypeScript Safety\n\n### Type Assertions (WARNING)\n- [ ] **No double casting** - Never use `as unknown as SomeType` (bypasses type safety)\n- [ ] **Validate before casting** - JSON parsed data should be validated (JSON Schema) before `as Type`\n- [ ] **Prefer type guards** - Use `instanceof` or property checks instead of assertions where possible\n\n### Null/Undefined Handling\n- [ ] **Explicit null checks** - Use `if (x === null || x === undefined)` not truthy checks for critical paths\n- [ ] **Nullish coalescing** - Use `??` for null/undefined, not `||` which also catches empty string/0\n- [ ] **Optional chaining** - Use `?.` for nested property access on potentially undefined objects\n\n### Imports & Types\n- [ ] **Type imports** - Use `import type { ... }` for type-only imports\n- [ ] **No implicit any** - All function parameters and returns must have explicit types\n- [ ] **Readonly for constants** - Use `Object.freeze()` and `Readonly<>` for immutable data\n\n## Step 4: Security Review\n\n### Defensive Tool Security\n- [ ] **No credentials in logs** - Check that passwords, tokens, TOTP secrets are not logged to audit files\n- [ ] **Config file size limit** - Ensure 1MB max for config files (DoS prevention)\n- [ ] **Safe shell execution** - Command arguments must be escaped/sanitized\n\n### Code Injection Prevention\n- [ ] **YAML safe parsing** - FAILSAFE_SCHEMA only\n- [ ] **No eval/Function** - Never use dynamic code evaluation\n- [ ] **Input validation at boundaries** - URLs, paths validated before use\n\n## Step 5: Common Mistakes to Avoid\n\n### Anti-Patterns Found in Codebase\n- [ ] **Catch + re-throw without context** - Don't just `throw error`, wrap with additional context\n- [ ] **Silent failures in session loading** - Corrupted session files should warn user, not silently reset\n- [ ] **Duplicate retry logic** - Don't implement retry at both caller and callee level\n- [ ] **Hardcoded error message matching** - Prefer error codes over regex on error.message\n- [ ] **Missing timeout on long operations** - Git operations and API calls should have timeouts\n- [ ] **Console.log in services** — Use `ActivityLogger`. Only CLI display code (`client.ts`, `worker.ts`, `output-formatters.ts`) uses console.log\n- [ ] **Temporal imports in services** — Services must stay Temporal-agnostic. If you need Temporal APIs, it belongs in activities\n\n### Code Quality\n- [ ] **No dead code added** - Remove unused imports, functions, variables\n- [ ] **No over-engineering** - Don't add abstractions for single-use operations\n- [ ] **Comments only where needed** - Self-documenting code preferred over excessive comments\n- [ ] **Consistent file naming** - kebab-case for files (e.g., `queue-validation.ts`)\n\n## Step 6: Provide Feedback\n\nFor each issue found:\n1. **Location**: File and line number\n2. **Issue**: What's wrong and why it matters\n3. **Fix**: How to correct it (with code example if helpful)\n4. **Severity**: Critical / Warning / Suggestion\n\n### Severity Definitions\n- **Critical**: Will cause bugs, crashes, data loss, or security issues\n- **Warning**: Code smell, inconsistent pattern, or potential future issue\n- **Suggestion**: Style improvement or minor enhancement\n\nSummarize with:\n- Total issues by severity\n- Overall assessment (Ready to commit / Needs fixes / Needs discussion)\n\n---\n\nNow review the current changes.\n</yakaai_skills>"}