Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
a79130b
docs(specs): Spec 101 US3 tasks — refresh lifecycle, fetch adapter, r…
Dumbris Oct 21, 2026
1bc7de3
feat(security): Spec 101 US3 wave 0 — refresh contract stubs and spec…
Dumbris Oct 21, 2026
5a69986
test(security): bundle refresh route and freshness fields on the REST…
Dumbris Oct 21, 2026
45aa60a
test(security): TPA bundle re-protect marker round trip, corrupt valu…
Dumbris Oct 21, 2026
d751aa3
chore(security): declare telemetry v16 refresh counter fields (unwired)
Dumbris Oct 21, 2026
fbce0aa
test(security): telemetry v16 refresh counters, schema pins and fetch…
Dumbris Oct 21, 2026
1847f92
feat(security): persist the TPA bundle re-protect completion marker
Dumbris Oct 21, 2026
67b9e69
test(security): TPA refresh config keys, env override, validation and…
Dumbris Oct 21, 2026
ebd9529
test(security): bundle refresh command, freshness lines and string ru…
Dumbris Oct 21, 2026
9469c85
test(security): activation self-check unit, parity and pipeline tests…
Dumbris Oct 21, 2026
f58cc51
feat(security): telemetry schema 16 TPA bundle refresh counters
Dumbris Oct 21, 2026
af394b9
test(security): B16 doctor warning truth table for the signature bund…
Dumbris Oct 21, 2026
6f2c5b7
docs(security): telemetry schema 15 bundle keys and schema 16 refresh…
Dumbris Oct 21, 2026
0e9d57a
feat(security): TPA refresh config validation, fetch env override and…
Dumbris Oct 21, 2026
28ab8e0
docs(security): document TPA refresh and fetch config keys (Spec 101 …
Dumbris Oct 21, 2026
6b093fb
test(security): signature bundle panel freshness rows and refresh pro…
Dumbris Oct 21, 2026
0d42283
feat(security): POST /security/bundle/refresh route (Spec 101 US3 T371)
Dumbris Oct 21, 2026
01c2ad3
feat(security): security bundle refresh command and freshness lines (…
Dumbris Oct 21, 2026
eb8d801
test(security): runtime owns the TPA bundle refresher lifecycle
Dumbris Oct 21, 2026
816d0ae
fix(cli): doctor prints plain-string runtime warnings (Spec 101 US3 T…
Dumbris Oct 21, 2026
d598f9b
feat(security): signature bundle doctor warnings (Spec 101 US3 T375)
Dumbris Oct 21, 2026
c26e182
feat(security): signature bundle panel freshness rows and refresh pro…
Dumbris Oct 21, 2026
06d7e32
test(security): count refresher goroutines by stack, drop the racy Cl…
Dumbris Oct 21, 2026
ddff20d
feat(security): runtime starts and joins the TPA bundle refresher
Dumbris Oct 21, 2026
0f08a28
test(security): held-scan evidence refresh after a bundle activates
Dumbris Oct 21, 2026
cd8656f
feat(security): refresh held-scan evidence after a TPA bundle activates
Dumbris Oct 21, 2026
7e7a705
test(security): cached-contract re-protection scan builds an informat…
Dumbris Oct 21, 2026
171bf8d
test(security): self-check recall leg on a synthetic corpus, vacuity …
Dumbris Oct 21, 2026
66c8617
feat(security): real activation self-check with embedded corpus (Spec…
Dumbris Oct 21, 2026
75e98b1
feat(security): scan a server's approved cached contracts after a bun…
Dumbris Oct 21, 2026
b1b3863
test(security): post-activation re-protection sweep contract
Dumbris Oct 21, 2026
dfbe475
feat(security): re-protect approved tools after a TPA bundle activates
Dumbris Oct 21, 2026
26bfb2f
test(security): server construction wires the TPA bundle refresh life…
Dumbris Oct 21, 2026
dc4dab6
feat(security): wire the TPA bundle refresh lifecycle into the server
Dumbris Oct 21, 2026
71adc12
test(security): RT-02 holds for the real re-protection pipeline
Dumbris Oct 21, 2026
0dd8a18
test(security): US3 G2 runtime core, fetch adapter and scheduler test…
Dumbris Oct 21, 2026
69da1d0
feat(security): list approved tool contracts for the cached re-protec…
Dumbris Oct 21, 2026
99e602c
feat(security): US3 G2 runtime core, fetch adapter and refresh schedu…
Dumbris Oct 21, 2026
4fb0ec9
test(security): strengthen G2 bite coverage
Dumbris Oct 21, 2026
4354b86
Merge US3 g1-config
Dumbris Oct 21, 2026
b99e65e
Merge US3 g2-runtime-fetch
Dumbris Oct 21, 2026
94ee8ac
Merge US3 g3-selfcheck
Dumbris Oct 21, 2026
782b19a
Merge US3 g4-telemetry
Dumbris Oct 21, 2026
e47f6e8
Merge US3 g5-wiring-reprotect
Dumbris Oct 21, 2026
0b7348c
Merge US3 g6-surfaces
Dumbris Oct 21, 2026
a07efd8
chore(oas): regenerate OpenAPI for the TPA bundle refresh surfaces (T…
Dumbris Oct 21, 2026
59be3f7
test(security): end-to-end TPA bundle fetch lifecycle with production…
Dumbris Oct 21, 2026
e544a58
docs(security): document the TPA bundle refresh, optional fetch and r…
Dumbris Oct 21, 2026
1015c2b
fix(security): address US3 judge findings
Dumbris Oct 21, 2026
354811a
fix(security): address US3 review findings
Dumbris Oct 21, 2026
f291c19
fix(security): address Sol round-1 findings (fetch)
Dumbris Oct 21, 2026
ecd30cc
fix(security): address Sol round-1 findings (misc)
Dumbris Oct 21, 2026
9f0ed94
test(security): wait for the swap instead of a fixed sleep in the ref…
Dumbris Oct 21, 2026
2126a53
fix(security): address Sol round-1 findings (reprotect)
Dumbris Oct 21, 2026
ccf5f18
fix(security): address Sol round-1 findings (refresh)
Dumbris Oct 22, 2026
619e020
Merge Sol round-1 fixes (fetch)
Dumbris Oct 22, 2026
ccbd189
Merge Sol round-1 fixes (refresh)
Dumbris Oct 22, 2026
725edde
Merge Sol round-1 fixes (reprotect)
Dumbris Oct 22, 2026
1af5a37
Merge Sol round-1 fixes (misc)
Dumbris Oct 22, 2026
7af13bf
fix(security): address Sol round-2 findings (loader)
Dumbris Oct 22, 2026
278a06e
fix(security): address Sol round-2 findings (refresh)
Dumbris Oct 22, 2026
7363427
fix(security): address Sol round-2 findings (reprotect)
Dumbris Oct 22, 2026
2aa76aa
Merge Sol round-2 fixes (refresh)
Dumbris Oct 22, 2026
e02f136
Merge Sol round-2 fixes (reprotect)
Dumbris Oct 22, 2026
b44bf3a
Merge Sol round-2 fixes (loader)
Dumbris Oct 22, 2026
ddc698f
test(security): make self-check tests independent of the corpus size
Dumbris Oct 22, 2026
35c07be
fix(security): address Sol round-3 findings (config)
Dumbris Oct 22, 2026
df810ed
fix(security): address Sol round-3 findings (refresh)
Dumbris Oct 22, 2026
b8ea662
fix(security): address Sol round-3 findings (reprotect)
Dumbris Oct 22, 2026
a3ee9ad
Merge Sol round-3 fixes (refresh)
Dumbris Oct 22, 2026
fb0481b
Merge Sol round-3 fixes (reprotect)
Dumbris Oct 22, 2026
f4b476b
Merge Sol round-3 fixes (config)
Dumbris Oct 22, 2026
7bc6942
Merge US3 self-check corpus-robustness tests
Dumbris Oct 22, 2026
97a8d27
test(security): wait for the nudge re-arm before ticking in two refre…
Dumbris Oct 22, 2026
2e3f20d
test(security): restore the review-test tick; that reload does not re…
Dumbris Oct 22, 2026
4586118
fix(security): address Sol round-4 findings (config)
Dumbris Oct 22, 2026
a51188a
fix(security): address Sol round-4 findings (reprotect)
Dumbris Oct 22, 2026
2afe4ac
fix(security): address Sol round-4 findings (refresh)
Dumbris Oct 22, 2026
d7b8a9e
Merge Sol round-4 fixes (refresh)
Dumbris Oct 22, 2026
7ee7338
Merge Sol round-4 fixes (reprotect)
Dumbris Oct 22, 2026
771c026
Merge Sol round-4 fixes (config)
Dumbris Oct 22, 2026
f1f2e6f
fix(security): address Sol round-5 findings (reprotect)
Dumbris Oct 22, 2026
562da2d
fix(security): address Sol round-5 findings (config)
Dumbris Oct 22, 2026
b573af0
fix(security): address Sol round-5 findings (refresh)
Dumbris Oct 22, 2026
0deeb99
Merge Sol round-5 fixes (refresh)
Dumbris Oct 22, 2026
855c0e4
Merge Sol round-5 fixes (reprotect)
Dumbris Oct 22, 2026
aba3811
Merge Sol round-5 fixes (config)
Dumbris Oct 22, 2026
46f672b
Merge remote-tracking branch 'origin/main' into claude/brave-torvalds…
Dumbris Oct 22, 2026
717cbe6
docs(specs): tick Spec 101 US3 tasks and mark tpa-db-refresh done
Dumbris Oct 22, 2026
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
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ tail -f ~/Library/Logs/mcpproxy/main.log # main log (macOS; Linux: ~/.mcpproxy/
- **Windows installer**: [docs/github-actions-windows-wix-research.md](docs/github-actions-windows-wix-research.md). **Prerelease** (`next` branch + `v*-rc.*` tags, opt-in, off stable channels): [docs/prerelease-builds.md](docs/prerelease-builds.md).

## Recent Changes
- 101-tpa-db US3 + 087-tpa-daily-refresh (tpa-bundle-refresh): Go 1.26, Vue 3 Web UI — a refresh cycle (`internal/security/scanner/tpa_refresh.go`: file leg then optional fetch leg, jittered `tpa_refresh_interval` default 24h, single-flight joins, `Nudge` on `config.reloaded`, joined in `Runtime.Close`) feeds the same source-neutral `evaluateCandidate` pipeline; the opt-in fetch (`tpa_fetch_enabled` default off, `tpa_fetch_url` no compiled-in default, env `MCPPROXY_TPA_FETCH_ENABLED`; Releases-API `latest` or direct `<url>` + `<url>.sig`, signed-only via `evalEnv.MustSign`, https/loopback-only transport with dial-time egress checks, host-only logs, outcomes kept apart from `last_rejection`) works today only with operator-signed bundles (publisher key set still empty); the activation self-check (`tpa_selfcheck.go`, parity-tested port of the `cmd/scan-eval` gate, embedded `bundled/selfcheck-corpus.json`) refuses a candidate as `gate_failure`; fetched bundles survive reload/restart (`externalConfigured`) and disabling fetch keeps them serving; activation observers drive the cached-only re-protection sweep (`internal/server/scan_bundle_reprotect.go`, held-evidence leg + `Service.StartScanFromCachedContracts` gated by `auto_baseline_scan`, marker `storage.BundleReprotectState`, RT-02: never approves, unquarantines or revokes); `BundleInfo` gains `last_checked_at`, `last_activated_at`, `stale`, `refresh`, `reprotect`; surfaces `POST /api/v1/security/bundle/refresh`, `mcpproxy security bundle refresh`, `BundleDoctorWarnings`, telemetry schema 16 refresh counters. Docs: `docs/features/tpa-bundle-signing.md` "Daily refresh and optional fetch", `docs/features/security-quarantine.md` "Re-protection after a bundle activates". **No new dependencies.**
- 101-tpa-db US1 (tpa-db-format): Go 1.26, Vue 3 Web UI — external TPA bundles are signed artifacts: `<bundle>.sig` four-key Ed25519 sidecar (`internal/security/bundlesig`, pure, contract `specs/101-tpa-db/contracts/sidecar-format.md`), verify-before-parse source-neutral `Candidate` pipeline (`scanner/tpa_candidate.go`, FR-008 order, ceilings 8 MiB / 2000 rules / 4 KiB / 5 s via `candidate_open_{unix,windows}.go` no-follow descriptor opens), per-authority (publisher/operator) epoch-scoped watermark + ratchet + pin/deny-list in BBolt `tpa_bundle_state` (one tx with the history row) and content-addressed LKG files `<datadir>/tpa-lkg/`, full-digest rollback, `coverageOK` + async scan-mode admission degrade on the four FR-007/009a/009b conditions. Config: `require_signed_bundle` (default off), `tpa_active_root`, `tpa_operator_signers`, `tpa_operator_baseline`, `tpa_operator_epoch`. Surfaces: `GET/POST /api/v1/security/bundle{,/history,/rollback,/unpin,/reload}`, `mcpproxy security bundle …`, `cmd/tpa-db keygen|sign|verify`, telemetry schema 15 bundle keys. Publisher key set ships EMPTY. Docs: `docs/features/tpa-bundle-signing.md`. **No new dependencies.**
- 118-auto-rescan-on-change: Go 1.26, small Web UI change — after a tool definition changes (or a tool is added after the baseline) in `manual`/`scan` trust mode the in-process TPA scan of its current definition is recorded as hold evidence (`held_reason: awaiting_review` + `held_verdict: clean` for a clean verdict; findings/coverage as before) and a debounced informational server rescan (`scan_informational.go`, fed by `Runtime.AddToolDefinitionsChangedObserver`) brings the review scan line back to "current". Switch: `security.auto_rescan_on_change` (default on, env `MCPPROXY_AUTO_RESCAN_ON_CHANGE`) AND `auto_baseline_scan`. RT-02 untouched: a clean verdict never approves a change. Docs: `docs/features/security-quarantine.md` "Automatic informational baseline scan". **No new dependencies.**
- quarantine-approval-pilot: Go 1.26, tests + CI only — independent policy model `internal/qapilot` (no production imports), deterministic scenario inventory S01–S26 + 896-cell dispatch table + REST authz table + counting-upstream dispatch check, check ledger (`QAPILOT_REPORT_DIR`) gated by `go run ./cmd/qapilot report`, versioned mutation set `tests/qapilot/mutations.json` run by `qapilot mutate` in disposable worktrees; workflow `.github/workflows/quarantine-pilot.yml` (3 PR checks + scheduled rapid exploration). Docs: `docs/development/quarantine-approval-pilot.md`. **No new dependencies.**
Expand Down
12 changes: 6 additions & 6 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ graph LR
<details>
<summary>🔵 tpa-db: versioned TPA signature database for the offline scanner — In progress · P1</summary>

> Vision pillar 'feel protected': the deterministic detect engine (Spec 076/077) ships with built-in checks but no updatable knowledge of in-the-wild Tool Poisoning Attacks. Build a versioned, offline-first signature/pattern database (known TPA campaigns, malicious phrase corpora, IoC hashes) that the engine consumes — bundled with the binary, refreshable out-of-band, community-contributable, and guarded by the existing scan-eval recall/FP CI gate. SPEC STAGE: specs/101-tpa-db MERGED as PR #1028 on 2026-08-27 (kept in this note, not in pr:, because pr: is implementation evidence and a docs(specs) merge is not that); 2026-10-20: US1 (tpa-db-format: signed four-key Ed25519 sidecar, verify-before-parse loader, per-authority anti-downgrade state, full-digest rollback, coverage degrade) shipped as #1701; US2 corpus in a sibling session; US3 refresh waits on Spec 087.
> Vision pillar 'feel protected': the deterministic detect engine (Spec 076/077) ships with built-in checks but no updatable knowledge of in-the-wild Tool Poisoning Attacks. Build a versioned, offline-first signature/pattern database (known TPA campaigns, malicious phrase corpora, IoC hashes) that the engine consumes — bundled with the binary, refreshable out-of-band, community-contributable, and guarded by the existing scan-eval recall/FP CI gate. SPEC STAGE: specs/101-tpa-db MERGED as PR #1028 on 2026-08-27 (kept in this note, not in pr:, because pr: is implementation evidence and a docs(specs) merge is not that); 2026-10-20: US1 (tpa-db-format: signed four-key Ed25519 sidecar, verify-before-parse loader, per-authority anti-downgrade state, full-digest rollback, coverage degrade) shipped as #1701; US2 corpus in a sibling session (#1702, open); 2026-10-22: US3 (tpa-db-refresh, implements Spec 087 US1+US2: daily file+optional signed fetch refresh, activation self-check, freshness surfaces, cached re-protection sweep) shipped as #1720. Epic stays in_progress until the corpus (US2) lands.

Spec: [101-tpa-db](./specs/101-tpa-db/)

Expand All @@ -238,15 +238,15 @@ graph LR

classDef done fill:#1f7a1f,stroke:#0d3d0d,color:#ffffff;
classDef todo fill:#6e7781,stroke:#3d4248,color:#ffffff;
class tpa_db_format done;
class tpa_db_corpus,tpa_db_refresh todo;
class tpa_db_format,tpa_db_refresh done;
class tpa_db_corpus todo;
```

| Task | Status | Refs |
| --- | --- | --- |
| Signature DB format + loader (versioned, signed, bundled default) | 🟢 Done | #1701 |
| Seed corpus: catalog known public TPA campaigns/patterns into the DB | ⚪ Todo | — |
| Out-of-band refresh (offline-friendly: manual file drop + optional fetch), eval-gated | ⚪ Todo | — |
| Out-of-band refresh (offline-friendly: manual file drop + optional fetch), eval-gated | 🟢 Done | #1720 |

</details>

Expand Down Expand Up @@ -1008,7 +1008,7 @@ graph LR
| Web UI + macOS app UX audit | In progress | P0 | — | | |
| Release qualification gate (auto-QA matrix blocks the tag) | In progress | P0 | 4/4 (100%) | [081-release-qa-gate](./specs/081-release-qa-gate/) | |
| Action log / transparency — info at a glance | In progress | P1 | — | | |
| tpa-db: versioned TPA signature database for the offline scanner | In progress | P1 | 30/30 (100%) | [101-tpa-db](./specs/101-tpa-db/) | |
| tpa-db: versioned TPA signature database for the offline scanner | In progress | P1 | 81/81 (100%) | [101-tpa-db](./specs/101-tpa-db/) | |
| Token-efficiency benchmark: measured savings, published results | In progress | P1 | 62/64 (97%) | [103-token-bench](./specs/103-token-bench/) | |
| Telemetry identity & data quality (machine_id + CI-filter hardening) | In progress | P1 | — | | |
| Telemetry v7: honest funnel + churn instrumentation | In progress | P1 | — | [080-telemetry-v7-churn](./specs/080-telemetry-v7-churn/) | |
Expand Down Expand Up @@ -1158,7 +1158,7 @@ Legend: `shipped` ≥95% checked · `in-flight` 1–94% · `drafted` 0% · `—`
| [098-tools-preflight](./specs/098-tools-preflight/) | `in-flight` | 28/33 (85%) |
| [099-describe-check-mode](./specs/099-describe-check-mode/) | `in-flight` | 9/10 (90%) |
| [100-prompt-rugpull-baseline](./specs/100-prompt-rugpull-baseline/) | — | — |
| [101-tpa-db](./specs/101-tpa-db/) | `shipped` | 30/30 (100%) |
| [101-tpa-db](./specs/101-tpa-db/) | `shipped` | 81/81 (100%) |
| [102-schema-deferred](./specs/102-schema-deferred/) | `shipped` | 89/89 (100%) |
| [103-token-bench](./specs/103-token-bench/) | `shipped` | 62/64 (97%) |
| [104-auto-routing-mode](./specs/104-auto-routing-mode/) | — | — |
Expand Down
10 changes: 10 additions & 0 deletions cmd/mcpproxy/doctor_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,16 @@ func outputDiagnosticsWithProfileChecks(diag map[string]interface{}, info map[st
fmt.Println("⚠️ Runtime Warnings")
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
for _, warningItem := range runtimeWarnings {
// The daemon's management Doctor emits free-text warnings as
// plain strings (config findings, audit counters, the
// signature-bundle freshness warnings); the legacy path emits
// structured objects. Both render here.
if warningText, ok := warningItem.(string); ok {
if warningText != "" {
fmt.Printf(" • %s\n", warningText)
}
continue
}
if warningMap, ok := warningItem.(map[string]interface{}); ok {
message := getStringField(warningMap, "message")
severity := getStringField(warningMap, "severity")
Expand Down
40 changes: 40 additions & 0 deletions cmd/mcpproxy/doctor_cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,46 @@ func TestOutputDiagnostics_WarningWithoutTitle(t *testing.T) {
}
}

// The daemon's management Doctor emits runtime_warnings as plain strings
// (appData denial, config findings, audit counters and, from Spec 101 US3, the
// signature-bundle freshness warnings registered via AddRuntimeWarningSource).
// A string warning must render under "Runtime Warnings" and must suppress the
// all-clear verdict; before this it was counted in total_issues but printed
// nowhere.
func TestOutputDiagnostics_StringRuntimeWarningRendersAndSuppressesAllClear(t *testing.T) {
const warning = "TPA signature bundle is 81 days old (stale after 30): run `mcpproxy security bundle refresh`"
diag := map[string]interface{}{
"total_issues": 1,
"runtime_warnings": []interface{}{warning},
}

oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
defer func() { os.Stdout = oldStdout }()

doctorOutput = "pretty"
err := outputDiagnostics(diag, nil, nil, "", nil)

w.Close()
var buf bytes.Buffer
buf.ReadFrom(r)
output := buf.String()

if err != nil {
t.Errorf("outputDiagnostics() returned error: %v", err)
}
if !strings.Contains(output, "Runtime Warnings") {
t.Error("Missing runtime warnings section header for a string warning")
}
if !strings.Contains(output, warning) {
t.Errorf("A plain-string runtime warning must be printed, got:\n%s", output)
}
if strings.Contains(output, "All systems operational") {
t.Error("A runtime warning must suppress the all-clear verdict")
}
}

func TestOutputDiagnostics_HighSeverityWarning(t *testing.T) {
diag := map[string]interface{}{
"total_issues": 1,
Expand Down
Loading
Loading