docs(specs): 101-tpa-db — versioned signed offline TPA signature database - #1028
Open
Dumbris wants to merge 14 commits into
Open
docs(specs): 101-tpa-db — versioned signed offline TPA signature database#1028Dumbris wants to merge 14 commits into
Dumbris wants to merge 14 commits into
Conversation
…database Spec-only (speckit stage 1) for the roadmap P1 tpa-db epic: signed, sequence-versioned bundle format with anti-downgrade + rollback; loader verify-before-parse extending the 086 fail-closed pipeline; seed corpus strategy (>=25 signatures, provenance/license, eval sample pairs); the publication channel riding the spec-087 refresh lifecycle; post-activation informational re-scan; freshness telemetry. Eval-gated on the existing scan-eval recall/FP CI bar.
Deploying mcpproxy-docs with
|
| Latest commit: |
afff3cb
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://23aefa1b.mcpproxy-docs.pages.dev |
| Branch Preview URL: | https://101-tpa-db.mcpproxy-docs.pages.dev |
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
📦 Build ArtifactsWorkflow Run: View Run Available Artifacts
How to DownloadOption 1: GitHub Web UI (easiest)
Option 2: GitHub CLI gh run download 32717860187 --repo smart-mcp-proxy/mcpproxy-go
|
Cross-model review (opencode/gpt-5.6-sol) plus local verification against the shipped loader and eval harness found ten genuine gaps in the stage-1 spec. All were confirmed against the code before being addressed. ## Changes Trust model / anti-downgrade - FR-009: the sequence floor is now a property of SIGNED artifacts only. `sequence` lives in the manifest, so an unsigned drop's sequence is attacker-chosen; an unsigned candidate could previously claim a huge sequence and permanently lock out every legitimate signed release. Unsigned candidates no longer advance the floor, and a signature ratchet stops signing from being stripped off an install that already ran signed. - FR-010: rollback no longer lowers the floor. Resetting the floor to the rolled-back sequence meant the next refresh could immediately re-activate the exact release the operator just rejected. Rollback is now a pinned exception over a monotonic high-watermark, with the rejected (sequence, fingerprint) deny-listed. - FR-009a: the embedded-default availability fallback is stated as the single explicit exemption to SC-002 (it previously contradicted it outright) and must serve as DEGRADED coverage, so deleting the external bundle cannot be used to obtain scan-mode auto-approvals under a weaker corpus. - FR-007: an active unsigned/unverified bundle likewise marks coverage degraded. `inprocess.go` derives `coverageOK` from bundle *presence*, so without this an attacker-supplied unsigned corpus keeps auto-approving. - FR-003: key ids must be canonical public-key fingerprints, duplicates rejected, and manifest id / sidecar id / verifying-key id must all match. - FR-006/FR-008: corrected an impossible pipeline order — the spec required checking the manifest-resident `sequence` before parsing the manifest. Verification is what precedes parsing; it runs over the raw bytes that are then parsed from memory, closing a verify/parse swap race. - FR-011a: activation must be crash-consistent. Spec 087 FR-010's atomicity is in-memory only; a crash between activation and floor persistence reopens the replay window FR-009 exists to close. Eval gate (the gate was measuring nothing) - FR-016a/SC-005/US2: `cmd/scan-eval/gate.go`'s `gateChecks()` registers only the four built-in detect checks — `scanner.BundleCheck` is absent and scan-eval never loads a bundle, so FR-016's "merge-blocking on corpus change" and SC-005 would both have passed vacuously. The harness must load a candidate through the production loader, register the bundle check, and map each new campaign category (`gatedCategory()` enforces a category only when its mapped check id is registered). SC-005 gains a negative control. Telemetry - FR-021: the schema-v8 anonymity backstop hard-whitelists `tpa_scanner` to four integer counters, so string/bool bundle fields would be rejected before transmit. Requires typed whitelist entries, an enum-bounded `source`, and publisher-issued `bundle_version` values only (a self-built corpus version string would itself be a fleet identifier). Consistency - FR-007: states explicitly that this spec supersedes Spec 087 FR-014's precondition, since embedding publisher keys means a fetch is no longer refused merely because the operator configured no key of their own. Related #1028
…01-tpa-db Round-2 cross-model review (opencode/gpt-5.6-sol) on the round-1 revisions. Six findings, all verified genuine; two of them were regressions the round-1 fixes introduced. ## Changes Watermark could be weaponized into a permanent update lockout - FR-005a (new): the trust set carries a monotonic trust EPOCH that only a binary release can advance, and anti-downgrade is scoped (authority, epoch, sequence). Without it, key-compromise recovery was impossible rather than merely painful: an attacker holding a compromised publisher key signs at the maximum representable sequence, and since round-1 made the watermark monotonic, no surviving key could ever outbid it — a one-shot permanent denial of updates. FR-005 and the key-rotation edge case now route compromise recovery through the epoch bump. - FR-004a (new): anti-downgrade state is tracked per SIGNING AUTHORITY, not in one global namespace. An operator with a self-built signed corpus (an explicitly supported persona) would otherwise raise the single global watermark past the publisher's release line and permanently block official updates. All publisher keys group into one authority so per-authority watermarks stay compatible with key rotation. - SC-002a (new): both lockout scenarios must be proven by fixture. Regressions introduced by the round-1 fixes - FR-007/FR-009a: round-1 made "any unsigned active bundle" degrade coverage, but the embedded default has no sidecar — so every fresh install would have shipped with scan-mode auto-approval silently disabled. Degradation now applies to unsigned EXTERNAL candidates only; the embedded default is binary-trusted and degrades solely when serving BELOW a retained signed watermark. - FR-008: round-1 put the rule-count ceiling before signature verification and parsing, which is impossible — rule count is not knowable from raw bytes. The two ceilings are now split: byte size before verification, rule count after parse. Consistency - FR-011a: the crash-consistent transaction now explicitly includes the watermark fingerprint and the FR-010 deny-list. Omitting them let a crash preserve a rollback while losing the state that rejects the rolled-away release, silently undoing the rollback on the next refresh. - Key Entities: "Sequence Floor" still said the floor is "lowered only by explicit rollback", directly contradicting revised FR-010. Renamed to Sequence Watermark, defined as monotonic, with rollback as a pinned exception. Terminology normalized from floor to watermark throughout so the two do not read as separate mechanisms. - Added a threat-model boundary note: an attacker with config write is out of scope (they can already disable quarantine), which is why the ratchet keys on install state and require_signed_bundle is config-resident. Related #1028
Round-3 cross-model review (opencode/gpt-5.6-sol) found one CRITICAL defect, verified genuine: the trust epoch introduced in round 2 was itself a replay window. ## The defect FR-005a made the epoch a property of the binary's trust set only; FR-002's manifest carried sequence and key id but no epoch. After a compromise recovery bumped the epoch and re-baselined the watermark, an OLD, weaker bundle legitimately signed by a still-trusted surviving key would be reinterpreted as belonging to the NEW epoch, clear the fresh (near-zero) watermark, and activate with signature_verified=true and full coverage — reinstating precisely the blind spots the recovery release was published to close. SC-002a tested recovery but not this replay. ## Changes - FR-002: the trust epoch is now carried inside the SIGNED manifest, so it is cryptographically bound to the bundle and cannot be reinterpreted. - FR-005a: epochs are per-authority, and a candidate is accepted only when its manifest epoch EQUALS the current binary-trusted epoch of the verifying authority — never merely less-than-or-equal. A below-epoch bundle is refused as stale-epoch replay even when its signature verifies against a still trusted key. Documents the accepted consequence: a binary on the old epoch stops receiving database updates until updated, which is correct because it still trusts the compromised key, and must surface as a stale-epoch degraded state rather than failing silently. - FR-008: epoch match added to the pipeline, before the anti-downgrade stage. - SC-002a: new fixture (a2) — a pre-bump bundle signed by the surviving key is refused after the bump rather than clearing the re-baselined watermark. - FR-020: stale-epoch replay added to the surfaced rejection reasons; bundle status also reports signing authority and trust epoch. Also, from a self-audit of the round-2 per-authority model: - FR-009a: the embedded default degrades when below a watermark retained by ANY authority (previously ambiguous which authority was meant). - FR-004a: choosing an operator-signed corpus over a higher-sequence publisher one is a deliberate operator decision, not a downgrade and not degraded coverage — but the active authority must be visible in bundle status. Related #1028
…e (101-tpa-db) Round-4 cross-model review (opencode/gpt-5.6-sol) found one HIGH defect, verified genuine: the anti-downgrade guarantee did not exist at first use. ## The defect Every anti-downgrade mechanism in the spec binds only once persisted state exists. On a fresh install — or after any data-directory reset — there is no watermark, so an attacker who can write the bundle path drops an OLD but genuinely publisher-signed, current-epoch bundle (sequence 5 while the shipping release is at 100). Signature verifies, epoch matches, nothing to compare the sequence against: it activates as a fully signature-verified bundle at FULL coverage, silently reinstating every blind spot fixed between 5 and 100. The FR-009 ratchet and the FR-010 pin/deny-list do not help, since both also require pre-existing state. Confirmed no new contradiction was introduced by the round-3 epoch fix. ## Changes - FR-009b (new): watermarks are SEEDED from a trusted baseline rather than starting empty. The publisher authority seeds from the sequence of the bundle embedded in the running binary — a candidate older than what the binary already ships is strictly worse than the fallback and is refused — and the baseline is re-applied on every startup, so wiping the data directory cannot lower it below the binary's own corpus. The operator authority, which has no binary-trusted baseline, gets a minimum sequence configurable alongside the operator key. This also makes the FR-005a epoch re-baseline safe: a bumped epoch starts at the embedded sequence, not zero. - SC-002 / US1 Independent Test: fresh-install and post-wipe downgrade are now explicitly in scope, with fixture (h) covering the old-but-validly-signed bundle on an install with no persisted state. - Edge case "Anti-downgrade state lost": corrected — a wipe resets the watermark to the seeded baseline, not to zero. What a wipe genuinely loses is the ratchet state and any pin/deny-list. - Key Entities: Sequence Watermark is documented as never empty. Related #1028
…e (101-tpa-db) Round-5 cross-model review (opencode/gpt-5.6-sol) found one HIGH defect, verified genuine. It confirmed FR-009b closes the publisher-side replay without contradicting FR-005a, FR-009, FR-009a, or FR-010, but caught that the operator authority was left with the same hole. ## The defect FR-009b gave the operator authority a minimum sequence "defaulting to zero, with the residual exposure being the already-out-of-scope config-write attacker". That parenthetical was wrong. With a zero default, no config write is needed: after a data-directory wipe (or on a fresh install), a bundle-path attacker — squarely inside the threat model — can replay any older genuinely operator-signed bundle at full coverage. The operator authority therefore had no post-wipe anti-downgrade at all by default, while the spec read as though it did. ## Changes - FR-009b: the operator baseline must be declared explicitly in mcp_config.json alongside the operator key, not defaulted to zero and not stored in the data directory — a baseline held in resettable state gives no protection exactly when it is needed. If an operator key is configured without a baseline, operator-authority anti-downgrade is treated as unsupported and coverage is marked degraded while an operator-signed bundle is active, instead of presenting it as fully trusted. The data-directory watermark may only raise the effective minimum above the configured baseline, never lower it. Epoch re-baseline now starts at the authority's baseline rather than zero. - Edge case "Anti-downgrade state lost": states plainly that the operator-side guarantee survives a wipe only with a config-resident baseline. - SC-002a: fixture (c) covers the operator authority after a wipe, both with and without a declared baseline. Note: per the repo's per-PR cross-model review cap, this is the fifth round. This fix has NOT itself been re-reviewed. Related #1028
…ntrol (101-tpa-db)
Cross-model review (opencode / gpt-5.6-sol) round 1 on specs/101-tpa-db/spec.md.
All five findings verified against the current code before changing anything.
## Changes
- FR-004b (new): the active signing authority is a config-resident declaration,
default publisher; a candidate signed by a non-active authority is refused
("authority not active"). Per-authority watermarks alone let a bundle-path
attacker — in the threat model — swap a publisher bundle at sequence 100 for
any operator-signed artifact clearing the operator baseline, silently
narrowing coverage while still reporting signature_verified=true at full
coverage, because the comparison never crosses namespaces.
- FR-009a: the embedded default's sequence is compared ONLY against the
publisher watermark (same namespace); the "any authority" rule made an
operator corpus at sequence 900 permanently degrade a current publisher
fallback at 100. The intent behind it is restated directly as condition (b):
a previously-active external bundle of the active authority having gone
missing.
- FR-016a: corrected the claim about unmapped eval categories — they are
excluded from OverallRecall so their MISSES cannot fail the gate, but their
false positives still count, since FPRate is computed over the whole
hard-negative set regardless of category mapping (gate.go:193-197, 287-289).
- FR-016b (new) + SC-005 + US2 independent test: the anti-vacuity negative
control is made deterministic. Aggregate recall over 25+ signatures absorbs a
single removal within the 0.10 threshold headroom, and built-in checks
(phrase.injection especially) often flag the dropped signature's sample
anyway, so "remove a gating signature and the gate must go red" could pass
vacuously. A designated control signature with no built-in or sibling
overlap, an asserted gate-breach exit code, and a CI assertion of the
no-overlap property replace it.
- SC-001: tampering is defined over the fields the detached signature actually
covers (bundle bytes, signature value, sidecar identity fields, mismatched
pair) instead of "any byte of ... sidecar", which no detached scheme can
guarantee against whitespace/key-order edits.
- SC-002a gains fixture (b2) and US1's independent test gains case (i) for the
authority-not-active refusal; FR-020, the threat-model edge case, Key
Entities, and the Constitution Check note the new config-resident field.
## Testing
- Docs-only change; no Go sources touched.
- Every codebase claim re-verified before editing: cmd/scan-eval/gate.go
(gateChecks/categoryCheck/OverallRecall/FPRate, no --bundle flag today),
internal/security/scanner/inprocess.go (coverageOK), and
internal/telemetry/anonymity.go (schema-v8 tpa_scanner whitelist).
…allback (101-tpa-db) Cross-model review (opencode / gpt-5.6-sol) round 2 on specs/101-tpa-db/spec.md. All three findings verified against the document and gate.go before changing anything. ## Changes - FR-010: the rollback target must be identified by content fingerprint, not by pathname alone. Rollback is the one path that deliberately bypasses the watermark, so naming its target by an attacker-writable path lets a bundle-path attacker choose what the bypass lands on — substituting a genuinely publisher-signed, current-epoch bundle at sequence 5 for the sequence-90 artifact the operator intended, which then gets PINNED. The operator now supplies the expected fingerprint, the path is only a hint, a mismatch is refused, and a rollback target must sit at or above the FR-009b seeded baseline (the embedded default excepted, since it is that baseline). - FR-009a condition (b): narrowed to a missing OPERATOR-authority bundle. As written it degraded coverage whenever any previously-activated external bundle went missing, including the ordinary case of a publisher external file removed after a binary upgrade shipped a strictly newer embedded corpus — where condition (a) already answers the question on the same axis and says full coverage. - US2 independent test: the negative control now runs over the FR-016b control scope instead of re-running the full-corpus command. One miss out of a 25-signature corpus leaves aggregate recall near 0.96, above the 0.90 threshold, so the full-corpus form contradicted FR-016b and would have proven nothing. - US1 independent test, SC-002, FR-020: assertions and rejection reason for the rollback-target mismatch. ## Testing - Docs-only change; no Go sources touched. - Recall/FP semantics re-checked in cmd/scan-eval/gate.go (OverallRecall is gatedDetected/gatedMalicious aggregated across gated categories).
…control (101-tpa-db) Cross-model review (opencode / gpt-5.6-sol) round 3 on specs/101-tpa-db/spec.md. All three findings verified against the document and gate.go before changing anything. ## Changes - FR-011 + FR-008 + edge cases: the sidecar gets its own small byte cap and a bounded read, applied before it is parsed, on both the file-drop and fetch paths. The sidecar is the one artifact that must be read before any signature is verified — its algorithm id, key id, and signature value are verification's own inputs — so FR-006's verify-before-parse protection does not cover it, and a bundle-path attacker could drop a multi-gigabyte sidecar next to an otherwise valid bundle and stall or exhaust the refresh path. The pipeline order and the "two ceilings" note are updated accordingly. - FR-010: the rollback floor is now its OWN authority's FR-009b baseline, not the publisher one in both cases. As written it compared an operator-signed rollback target at sequence 9 against the publisher embedded corpus at 100 and would have refused every legitimate operator rollback, contradicting the per-authority isolation of FR-004a. - FR-016b: "restrict the control to its category" is not by itself provably failing — decide() breaches only on OverallRecall < minRecall, so a control category with ten gated malicious samples lands on exactly 0.90 after one miss and stays green on the boundary. The canonical construction is now a control corpus whose gated malicious set is the control sample alone; any other scope must compute and assert the recall drop rather than assume it. ## Testing - Docs-only change; no Go sources touched. - Boundary semantics re-checked in cmd/scan-eval/gate.go decide() (strict `<` against min-recall) and the per-authority baseline rules in FR-009b.
…tric (101-tpa-db) Cross-model review (opencode / gpt-5.6-sol) round 4 on specs/101-tpa-db/spec.md. Findings verified against the document before changing anything; the telemetry finding was accepted in substance but not in its proposed remedy (see below). ## Changes - FR-011 + Edge Cases: a byte cap bounds bytes, not time. Local candidate artifacts (bundle and sidecar) must be REGULAR files — a FIFO, device, socket, or symlink resolving to one is refused outright rather than read — and every candidate read, local or fetched, runs under an enforceable I/O deadline whose expiry releases Spec 087's single-flight slot. Otherwise the bundle-path attacker leaves a FIFO with no writer and pins the refresh path indefinitely without ever approaching the cap. - FR-010: an authority with no declared baseline — possible only for the operator authority, which FR-009b explicitly permits — has no rollback floor to enforce, and none is invented. The rollback proceeds and the install is marked degraded while that bundle is active, which is already FR-009b's treatment. Refusing the rollback would break FR-010 for a configuration FR-009b allows; assuming a zero floor would claim a guarantee that does not exist. - SC-006: the freshness metric is scoped to publisher-issued corpora and its derivation is stated — age comes from a server-side join of the reported version/sequence to the publisher's own release dates. Installs on an operator-built corpus (the `other` bucket) are not ageable and form their own bucket rather than being folded into the ratio. ## Notes on the rejected remedy The reviewer's suggested fix for SC-006 was to report `generated_at`/age in the heartbeat. That is exactly the near-unique value FR-021's schema-v8 backstop exists to keep out of the payload, and it would buy nothing the version/sequence join does not already give, so the derivation was specified instead. ## Testing - Docs-only change; no Go sources touched.
…uences (101-tpa-db) Cross-model review (opencode / gpt-5.6-sol) round 5 on specs/101-tpa-db/spec.md. Round 5 reported no remaining adversary-reachable security hole; both findings were internal contradictions, verified against the document before editing. ## Changes - FR-009b, the Sequence Watermark entity, and SC-002: the baseline-less operator authority is now named as an explicit exemption everywhere the document made a universal claim. FR-009b permits an operator authority with no declared baseline (marking the install degraded), while the same requirement opened with "every watermark MUST be SEEDED", the entity said "Never empty", and SC-002 called FR-009a the "sole exemption" — an implementation could not satisfy all four at once. There are exactly two exemptions and both are degraded-coverage states. - FR-021: `sequence` is reported only when `bundle_version` resolves to a publisher-issued value; in the `other` bucket it is omitted. Bucketing the version string while transmitting a raw operator-chosen sequence defeats the purpose of the bucket, since an operator-built corpus can number itself with a unix timestamp or private build number and be just as fleet-identifying. SC-006 already scopes freshness to publisher corpora, so no analysis is lost. ## Testing - Docs-only change; no Go sources touched. - Round 5 is the per-PR cross-model review cap (CLAUDE.md escalation trigger 4); these two fixes are not themselves re-reviewed.
…exemption count (101-tpa-db) Cross-model review (opencode / gpt-5.6-sol) round 6 on specs/101-tpa-db/spec.md. All five findings verified against the document and the cited code before editing. - Edge Cases (threat model): the boundary is now explicit about the data directory — the bundle-path attacker may also be able to delete/reset the persisted security state (equivalent to the wipe), against which only the binary/config-anchored guarantees survive (seeded publisher baseline, config-resident operator baseline, require_signed_bundle, active-authority declaration); ratchet and pin/deny-list are best-effort. Chosen-value corruption of the state store is out of scope (that attacker is already past every mcpproxy guarantee). - Edge Cases + FR-009a: two stale 'single exemption to SC-002' statements now name both exemptions, matching SC-002's 'exactly two'. - US1 acceptance 3 + SC-002: 'lower sequence' is qualified everywhere as a same-authority, same-epoch comparison; a deliberate FR-004b authority switch is by definition not a downgrade and needs no exemption. - FR-010 + FR-005a: pin/deny-list entries are keyed (authority, epoch, sequence, fingerprint); an authority switch suspends (not deletes) the non-active authority's entries; an epoch bump retires prior-epoch entries so stale rollback state cannot block the key-compromise recovery bundle. - FR-021: 'resolves to a publisher-issued value' is a provenance test (embedded default or publisher-authority-verified), never a claimed-version-string match — an operator corpus can copy a publisher version string verbatim; the backstop also rejects sequence-present-with-version-other cross-field. - Docs-only change; no Go sources touched. - Round 6 of the raised (10-round) cross-model review cap.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Speckit stage-1 spec for the roadmap P1
tpa-dbepic: Ed25519-signed, sequence-versioned bundle format with anti-downgrade + explicit rollback, verify-before-parse loader extending the 086 fail-closed pipeline, seed corpus strategy (TPA-2026-0001 class and siblings, provenance/license + eval sample pairs), publication channel riding the spec-087 refresh lifecycle, and a post-activation informational re-scan.Everything is gated on the existing scan-eval recall/FP CI bar (recall >= 0.90, hard-negative FP <= 0.05) — blocked at merge AND at activation.
Spec-only PR: no plan, no tasks, no code.
Cross-model review
Five rounds with
opencode(gpt-5.6-sol), each finding verified against the actual code before being addressed. 17 findings across the five rounds (8/6/1/1/1), all verified genuine — none rejected as false positives. Two further gaps were found locally rather than by the reviewer: the telemetry anonymity-whitelist collision and the Spec 087 FR-014 supersession. The trust model changed substantially as a result — the four that mattered most:The eval gate was measuring nothing.
cmd/scan-eval/gate.go'sgateChecks()registers only the four built-indetect.Checks;scanner.BundleCheck— which production does append ininternal/security/scanner/inprocess.go— is absent, and scan-eval never loads a bundle at all. So FR-016's "merge-blocking on corpus change" and SC-005 would both have passed vacuously, with the database entirely unscored. New FR-016a requires the harness to load a candidate through the production loader and map each new campaign category (gatedCategory()only enforces a category whose check id is registered); SC-005 gains a negative control.The anti-downgrade watermark was bypassable, then weaponizable, then absent at first use.
sequencelives in the manifest, so with unsigned drops accepted by default it was attacker-chosen — one unsigned drop claiming a huge sequence could permanently lock out every legitimate signed release. Fixing that by making the watermark monotonic then made key-compromise recovery impossible: a compromised key signs at the maximum sequence and no surviving key can ever outbid it. That needed a binary-release-only trust epoch — which was itself a replay window until the epoch was bound inside the signed manifest with exact-equality matching. And all of it still did nothing on a fresh install, where an absent watermark accepts any old validly-signed bundle, so watermarks are now seeded from the embedded bundle's own sequence.Rollback silently undid itself. Resetting the watermark to the rolled-back sequence meant the next refresh cycle re-activated the exact release the operator had just rejected. Rollback is now a pinned exception over a monotonic watermark, with the rejected artifact deny-listed.
Telemetry as specified could not ship.
internal/telemetry/anonymity.gohard-whitelists thetpa_scannersub-object to four non-negative integer counters, so the proposed string/bool bundle fields would have been rejected pre-transmit. FR-021 now requires typed whitelist entries, an enum-boundedsource, and publisher-issued version values only — a self-built corpus version string would itself be a fleet identifier.Two rounds also caught regressions introduced by the previous round's fixes (an over-broad degradation rule that would have shipped every fresh install with
scan-mode auto-approval silently disabled, and a pipeline ordering that required checking a manifest field before parsing the manifest).The 5th-round fix — making the operator-authority baseline explicit and config-resident — has not itself been re-reviewed, per the repo's per-PR review-round cap.
Two
[NEEDS CLARIFICATION]markers remain by design: artifact hosting location, and whetherrequire_signed_bundleever flips default-on.Follow-up, deliberately not done here to keep this spec-only:
roadmap.yaml'stpa-dbepic has nospec:field pointing atspecs/101-tpa-db(editing it would require regeneratingROADMAP.mdfor the roadmap-up-to-date gate).