Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Docs
- Formal ADR catalog under `docs/adr/`: Accepted retrospective records
0002–0015 for shipped topology, scanning, security, CLI, quality, and
Horizon A scope. Number 0001 reserved (draft under review, not published).
Indexes wired from Architecture, STATUS, README, QUICKSTART, CONTRIBUTING,
and plan README.

## [0.3.0] - 2026-08-05

### Added
Expand Down
3 changes: 2 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ Then use this guide for development, quality checks, and sharing changes back. S
3. `tripwire setup` (needs `SUPABASE_DB_URL`) and `./scripts/setup-modal.sh`
4. Run checks below.

Architecture: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md). Scanner output notes:
Architecture: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md). Decisions:
[docs/adr/README.md](docs/adr/README.md). Scanner output notes:
[docs/research/adapters/scanner-output-adapters.md](docs/research/adapters/scanner-output-adapters.md).

## Dev hygiene
Expand Down
1 change: 1 addition & 0 deletions QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,6 @@ setup, re-run, and maintenance command catalog.

- Docs map: [docs/README.md](docs/README.md)
- Architecture: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)
- Decisions: [docs/adr/README.md](docs/adr/README.md)
- Capability status: [docs/STATUS.md](docs/STATUS.md)
- Contributing: [CONTRIBUTING.md](CONTRIBUTING.md)
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ flowchart LR
|---|---|
| Run your first Live scan | [Follow the Quickstart](QUICKSTART.md#first-live-scan) |
| Preview the dashboard or validate locally | [Optional local validation](QUICKSTART.md#validate-locally-optional) |
| Understand results and system shape | [Capability status](docs/STATUS.md) · [Architecture](docs/ARCHITECTURE.md) |
| Understand results and system shape | [Capability status](docs/STATUS.md) · [Architecture](docs/ARCHITECTURE.md) · [Decisions](docs/adr/README.md) |
| Contribute or maintain the project | [Contributing](CONTRIBUTING.md) · [command catalog](docs/user-guide/setup-commands.md) |

For the full documentation map, see [docs/README.md](docs/README.md). The
Expand Down
5 changes: 2 additions & 3 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ from the code.

## 5. Decisions

- Planning decisions: [plan/DECISIONS.md](./plan/DECISIONS.md)
- Formal ADRs: [adr/README.md](./adr/README.md)
- Planning decisions (slice waivers, priority): [plan/DECISIONS.md](./plan/DECISIONS.md)
- Slice progress: [plan/PROGRESS.md](./plan/PROGRESS.md)
- Formal ADRs (`docs/adr/`) — none yet; add when a major technology or boundary
choice needs a durable record
4 changes: 3 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Use this map to move from a safe first look to the level of setup or project det
|---|---|---|
| Run a first Live scan | [Quickstart: ordered Live setup](../QUICKSTART.md#first-live-scan) | [Supabase setup](./user-guide/supabase-setup.md) → [Modal setup](./user-guide/modal-setup.md) → [environment keys](./user-guide/env-vars.md) → [scan and dashboard](../QUICKSTART.md#live-capabilities) |
| Preview or validate locally | [Optional local validation](../QUICKSTART.md#validate-locally-optional) | [README: dashboard preview](../README.md#preview-the-dashboard-optional) |
| Understand results and system shape | [Capability status](./STATUS.md) | [Architecture](./ARCHITECTURE.md) |
| Understand results and system shape | [Capability status](./STATUS.md) | [Architecture](./ARCHITECTURE.md) · [ADRs](./adr/README.md) |
| Contribute or maintain | [Contributing](../CONTRIBUTING.md) | [Setup and maintenance commands](./user-guide/setup-commands.md) |

## Setup and operation
Expand All @@ -27,6 +27,7 @@ Use this map to move from a safe first look to the level of setup or project det
| [user-guide/onboarding-cheatsheet.md](./user-guide/onboarding-cheatsheet.md) | Compact shared onboarding reference |
| [STATUS.md](./STATUS.md) | Evidence-labelled capability claims |
| [ARCHITECTURE.md](./ARCHITECTURE.md) | System diagrams, key flows, and repository layout |
| [adr/README.md](./adr/README.md) | Formal architecture decision records |
| [SECURITY.md](../SECURITY.md) | Vulnerability reporting |

## Project records
Expand All @@ -37,6 +38,7 @@ Use this map to move from a safe first look to the level of setup or project det
| Run the documentation smoke path | [SMOKE_TESTS.md](./plan/SMOKE_TESTS.md) |
| Review CI and Nightly workflows | [CI workflows](#ci-workflows) |
| Inspect project planning | [plan README](./plan/README.md) · [progress](./plan/PROGRESS.md) |
| Read architecture decisions | [ADR index](./adr/README.md) |
| Check scanner adapter research | [scanner output adapters](./research/adapters/scanner-output-adapters.md) |

## CI workflows
Expand Down
17 changes: 16 additions & 1 deletion docs/STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,20 @@ prose slice 16) is **Won't (A)** — reinstate only if a new demo need arises.

---

## DECIDED

Architecture boundaries and quality/security trade-offs are recorded as formal
ADRs ([adr/README.md](./adr/README.md)): runtimes (0002), Modal (0003),
Supabase (0004), scanner adapters (0005), Live/Mock ACL (0006), ship UI (0007),
anon-read / service-role-write (0008), fail-closed evidence (0009), content-hash
idempotency (0010), schema bootstrap (0011), target acquisition (0012),
ship-path quality gates (0013), curated discovery (0014), and Horizon A
excluding Guard/Drift (0015). Slice waivers stay in
[plan/DECISIONS.md](./plan/DECISIONS.md). ADR number 0001 is reserved and not
published while that draft remains under review.

---

## RESEARCH (not VERIFIED)

Exact JSON field names in `sandbox/scanners.py` — cross-check against the pinned
Expand All @@ -103,7 +117,8 @@ Adapter fixture tests (slices 8–9) are planned to tighten this.

Known fixture gaps (not urgent) are listed under
[fixtures/README.md](../fixtures/README.md) (“Not yet built”). Do not treat those
as shipped capabilities.
as shipped capabilities. Guard PreToolUse and Drift/trend remain Future /
Won't (A) — see [ADR-0015](./adr/0015-horizon-a-excludes-guard-and-drift.md).

Coverage audit matrix: [plan/coverage-audit.md](./plan/coverage-audit.md)
(slice 7 ✅). Slice stubs: [plan/README.md](./plan/README.md) (`01-A-…` …
Expand Down
55 changes: 55 additions & 0 deletions docs/adr/0002-node-cli-python-sandbox-split.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# ADR-0002: Node CLI and Python sandbox as separate runtimes

- **Status:** Accepted
- **Date:** 2026-08-01
- **Deciders:** Tripwire maintainers
- **Tags:** runtime, cli, sandbox, languages

## Context

Tripwire must run on an operator workstation (discover targets, bootstrap schema,
spawn jobs) and inside an isolated scan environment (install scanner CLIs, parse
JSON, write findings). Those jobs have different language ecosystems: Node is
natural for a `npm`-distributed CLI and the HTML dashboard; Cisco / Snyk / Tessl
scanners are Python packages consumed via `pip` / `uvx`.

A single-language stack would force either wrapping Python scanners from Node
on the operator machine, or shipping a Python CLI that still has to talk to a
browser dashboard.

## Decision

Keep two production runtimes:

- **CLI** (`cli/`): Node **22** (`.nvmrc`). Discovery, content hashing,
idempotency, schema bootstrap, Modal spawn.
- **Sandbox** (`sandbox/`): Python. Modal image is Debian slim **3.11**; local
contributor pin is **3.12** (`.python-version`). Scanner adapters live here.

The CLI never runs scanner binaries. The sandbox never owns operator UX.

## Consequences

- Contributors need both Node 22 and Python 3.12; CI must gate both stacks.
- Modal image independently pins Node **20.18.1** (Tessl) and Python 3.11 —
local pins and the scan image can drift and must be documented.
- Contract between sides is explicit: CLI passes `target`, `item-type`,
`item-id`, `scan-run-id`; sandbox writes to Supabase.

## Alternatives considered

### A. Python-only (CLI + sandbox)

Rejected: dashboard and `npm link` CLI already existed; Node is the operator
entry the walking skeleton shipped.

### B. Node-only, shell out to scanners from the laptop

Rejected: scanner installs and untrusted target execution belong in isolation
(see [ADR-0003](./0003-modal-isolated-scanner-execution.md)).

## References

- [docs/ARCHITECTURE.md](../ARCHITECTURE.md) §2
- [docs/user-guide/prerequisites.md](../user-guide/prerequisites.md)
- Walking skeleton: `feat: land walking skeleton` (2026-08-01)
62 changes: 62 additions & 0 deletions docs/adr/0003-modal-isolated-scanner-execution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# ADR-0003: Modal for isolated scanner execution

- **Status:** Accepted
- **Date:** 2026-08-01
- **Deciders:** Tripwire maintainers
- **Tags:** compute, isolation, modal, sandbox

## Context

Scanner CLIs execute untrusted skill/MCP trees, pull optional vendor tools, and
need secrets (Supabase service role, Snyk/Tessl/Cisco keys). Running that on the
operator laptop mixes credentials with target code and makes results
non-reproducible across machines.

Live scans also need an ephemeral filesystem and a hard timeout so a hung
scanner cannot block the heatmap forever.

## Decision

Use **Modal** as the Live compute plane.

- App `tripwire-scan` (`sandbox/scan_app.py`): one ephemeral sandbox per
`scan_run`, 300s hard timeout.
- Image bakes scanner packages (`cisco-ai-skill-scanner`, `cisco-ai-mcp-scanner`,
`snyk-agent-scan`) plus Node 20 for Tessl; `scanners` is copied into the image
(`add_local_python_source(..., copy=True)`).
- Secrets sync via `./scripts/setup-modal.sh` (`tripwire-supabase`,
`tripwire-scan-secrets`).
- Findings and console output are written **directly to Supabase** from the
sandbox. They are not relayed through the CLI.
- CLI spawns via `modal run sandbox/scan_app.py` (local entrypoint) so host
directories can be packed; it must not call `scan_item` directly.

`--dry-discover` stays local and never spawns Modal.

## Consequences

- Live path requires a Modal account and CLI auth; Mock / dry-discover do not.
- Cold image builds and `uvx` installs can leave Tessl/Snyk unreachable;
that is reported, not hidden ([ADR-0009](./0009-fail-closed-incomplete-evidence.md)).
- Host paths are invisible on Modal’s remote FS — local targets must be tarred
([ADR-0012](./0012-sandbox-target-acquisition.md)).
- Future packaging/deploy stories may still treat Modal as external SaaS, not
a cluster workload (draft ADR 0001 is under review and not in this catalog).

## Alternatives considered

### A. Local Docker / venv scanners

Rejected for Live: weaker isolation, operator-machine drift, secrets on the
laptop next to target trees.

### B. GitHub Actions as the scan runner

Rejected: scan latency and auth model are operator-driven, not PR-driven.
CI remains for Tripwire’s own quality gates.

## References

- [docs/user-guide/modal-setup.md](../user-guide/modal-setup.md)
- [docs/STATUS.md](../STATUS.md) IMPLEMENTED
- `cli/src/modalClient.js`, `sandbox/scan_app.py`
60 changes: 60 additions & 0 deletions docs/adr/0004-supabase-system-of-record.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# ADR-0004: Supabase/Postgres as system of record

- **Status:** Accepted
- **Date:** 2026-08-01
- **Deciders:** Tripwire maintainers
- **Tags:** storage, supabase, postgres, realtime

## Context

Scan runs, per-scanner status, and findings must be shared between the CLI
(writer), the Modal sandbox (writer), and a browser dashboard (reader) with
near-live updates. A local SQLite file cannot serve the dashboard or the
sandbox. A custom REST API would duplicate what PostgREST already provides.

The product heatmap needs a server-side rollup so the UI does not recompute
risk from raw findings on every poll.

## Decision

Use **hosted Supabase (Postgres + PostgREST + Realtime)** as the Live store.

- Canonical DDL: `db/schema.sql` (items, scan_batches, scan_runs,
scan_run_scanners, findings, coverage, config).
- Heatmap: `tripwire_rollup_item` in Postgres. `partial-failed` still scores
completed engines; `failed` / `running` / empty partial paint `error`.
- Realtime publication on `scan_runs`, `scan_run_scanners`, `findings` so the
dashboard can update in ~1s, with an 8s poll fallback.
- HTTP clients use `SUPABASE_URL` + keys; DDL uses `SUPABASE_DB_URL`
(prefer Session pooler if Direct `db.*` does not resolve).

Raw scanner blobs in Supabase Storage were specified as a dual-write path in
adapter research; they are **not** implemented. Normalized `findings` rows are
the shipped contract.

## Consequences

- Live requires a Supabase project; schema and RLS must be applied together
([ADR-0008](./0008-anon-read-service-role-write.md),
[ADR-0011](./0011-idempotent-sql-schema-bootstrap.md)).
- Application code speaks PostgREST column names; missing columns are handled
with PGRST204-safe fallbacks in the sandbox.
- Other databases are future package flavors only after the app boundary
allows it (packaging ADR draft reserved as 0001; not published yet).

## Alternatives considered

### A. Self-hosted Postgres + custom API

Rejected for Horizon A: extra ops surface; PostgREST + Realtime already match
the dashboard’s read model.

### B. SQLite / JSON files on the operator machine

Rejected: sandbox and browser cannot share that store.

## References

- [docs/ARCHITECTURE.md](../ARCHITECTURE.md) §2–3
- [docs/user-guide/supabase-setup.md](../user-guide/supabase-setup.md)
- [docs/research/adapters/scanner-output-adapters.md](../research/adapters/scanner-output-adapters.md) §0
64 changes: 64 additions & 0 deletions docs/adr/0005-upstream-scanner-cli-adapters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# ADR-0005: Wrap upstream scanner CLIs; normalize findings

- **Status:** Accepted
- **Date:** 2026-08-01
- **Deciders:** Tripwire maintainers
- **Tags:** scanners, adapters, cisco, snyk, tessl

## Context

Tripwire is an assessment orchestrator, not a replacement for Cisco Skill/MCP
Scanner, Snyk Agent Scan, or Tessl. Reimplementing those engines would fork
their detection quality and lag their CLI flags. The dashboard, however, needs
one severity model (`red` / `amber` / `green`) and one `findings` table.

Upstream JSON shapes differ and are marked experimental in places (Snyk).
Adapters must tolerate prefixed stdout, missing binaries, and missing vendor
keys without crashing the sandbox.

## Decision

Treat each engine as a **subprocess adapter** in `sandbox/scanners.py`.

- Shell out with real flags (`skill-scanner`, `mcp-scanner`, `snyk-agent-scan`,
`tessl`); parse documented JSON; map into `findings` + `scan_run_scanners`.
- Collapse upstream severities into Tripwire `red` / `amber` / `green`.
- Missing vendor credentials → `skipped_missing_credential`, not a silent
“configured and clean” result.
- Nonzero exit, timeout, missing binary, or empty/malformed JSON →
`unreachable` (never a crash). Cisco findings from engines that did complete
are still persisted.
- Incremental `on_scanner_done` so the dashboard can show progress
scanner-by-scanner.
- Keep [scanner-output-adapters.md](../research/adapters/scanner-output-adapters.md)
in sync with `scanners.py`. Exact JSON field names remain RESEARCH until
fixture-round-tripped against the pinned CLI.

Skill scans run Cisco + Tessl + Snyk. MCP scans run Cisco MCP + Snyk.

## Consequences

- Image and secrets must include each vendor the operator wants; absent keys
skip that engine rather than blocking the whole Live path.
- Adapter tests lock parse fixtures; they do not replace pinning CLI versions
in the Modal image when upstream output is experimental.
- Dual-write of raw blobs to Storage is specified, not shipped
([ADR-0004](./0004-supabase-system-of-record.md)).

## Alternatives considered

### A. Reimplement detection in-process

Rejected: quality and maintenance cost; product value is orchestration +
normalized review.

### B. Call vendor SaaS HTTP APIs only

Rejected: the shipped engines are CLIs that need the target tree (or MCP
endpoint) inside the sandbox.

## References

- `sandbox/scanners.py`
- [docs/STATUS.md](../STATUS.md) IMPLEMENTED / RESEARCH
- [docs/user-guide/prerequisites.md](../user-guide/prerequisites.md) five-vendor map
Loading
Loading