This specification is the product of a public design conversation (2026-08-08 → 2026-08-12) across LinkedIn and Dev.to. Every primitive below was shaped by named community contributions; each section cites its origin. The protocol's governing principle, stated by the community and adopted here:
Verification must itself be tested. A check that never fails has never been checked.
1. Purpose
OpenWorkProof is a protocol for verifying AI agent work: producing signed, auditable evidence that a piece of agent execution happened as claimed, and that the verifier producing that evidence is capable of detecting failure.
Two distinct claims are always separated in this protocol:
| Claim | Mechanism | Meaning |
|---|---|---|
| Authenticity | Signature over a receipt | This work was executed and attested as stated |
| Verifier capability | Negative control arm in the receipt | The verifier would have caught a lie |
Signatures alone prove the first. They say nothing about the second — the ln.strip() lesson (Section 4.1).
2. Scope & Non-Goals
In scope:
- Receipt formats for verified executions (positive arm + negative arm).
- Guard definitions and guard inventory semantics.
- Negative control contracts (provocation contract, digest pinning).
- Population manifests (sampling honesty).
- Temporal validity: retraction, policy state, scope change.
- Trust model and bootstrap.
Non-goals (for now):
- Model evaluation or benchmarking (evals measure models; we measure executions).
- Tracing / observability (logs record what happened; receipts let third parties verify it).
- Attestation of environment (TEE / enclave proofs prove where something ran, not that it was correct).
- Policy judgment semantics (that is the JPS layer; OWP carries authorization bindings).
3. Terminology
| Term | Definition |
|---|---|
| Guard | A verification check: command + assertion + expected failure behavior |
| Guard inventory | The set of guards an operator runs, classified proven / unproven / broken |
| Negative control | A deliberately broken input, run on every CI pass, asserting the guard goes red |
| Provocation contract | The formal spec of what a negative control provokes (exit code, stderr pattern, schema scope) |
| Receipt | Signed evidence of one verification event |
| Positive arm | The "did it pass" side of a receipt: test suite, result, population |
| Negative arm | The "would it catch a lie" side: control fixture, control result, control target |
| Population manifest | The honest enumeration of what a check was supposed to examine |
| Eligible seen | Pre-selection count: what reached the gate |
| Selection loss | The auditable gap between eligible_seen and population_size
|
| Rot | Silent decay of a check's capability while output stays green (three kinds: guard, control, population) |
4. Failure Model
The protocol exists because verification can be green and structurally meaningless. The failure model is explicit.
4.1 Structural death (ln.strip())
A production gateway ran a signed, audited check that reported verified: true on every run for months. The bug: a stray newline pushed an assert below a return, so the assertion never executed. Exit code 0. Verdict verified.
Measured impact (fintech engineer's post, 2026-08-09):
| Status | Count |
|---|---|
| Guards total | 40 |
| Proven (can detect failure) | 7 |
| Broken | 0 |
| Unproven | 33 |
Five of eight caller-test shapes produced false passes. The agent had "passed 2,283 tests" and failed in production.
Lesson: the verifier is part of the system being verified. It must be tested with inputs designed to make it fail.
4.2 The three decay modes (rot)
Even with negative controls in place, a check can go silently dead three ways (community taxonomy, 2026-08-12):
| Rot | Failure | Countermeasure | Origin |
|---|---|---|---|
| Guard rot | Guard stops catching real failures | Continuous negative control on every CI pass | Max Quimby |
| Control rot | Control stops testing the right failure (recall/precision of the test itself) | Digest pinning + control_schema_version
|
Skillselion |
| Population rot | Guard examines the wrong population, or none at all |
eligible_seen in the population manifest |
Tom Jones |
All three can produce a green checkmark while being structurally meaningless. All three need different countermeasures.
4.3 Catch-rate measurability (23 of 41)
Production data (Ethan Walker, 2026-08-12): a gate caught 23 of 41 known degradations — a 56% catch rate — across eleven green weeks in which nobody asked what fraction it catches.
Lesson: every guard must be measurable against known-bad inputs. The negative control is the cheap, proactive version of the expensive forensic replay Ethan had to do retroactively.
5. Core Primitives
5.1 Guard & Guard Inventory
A guard is a check plus a control:
guard:
id: gw_check_response_shape
description: "Every gateway response matches the documented schema"
command: "check_response.sh"
assertion: "schema_validate $INPUT"
status: unproven # proven | unproven | broken — set by control runs
controls: [gw_control_null_handling]
The guard inventory is a published, versioned list — not a private detail. It is the unit of honesty:
guard_inventory:
schema_version: "1.0"
generated_at: 2026-08-12T00:00:00Z
totals:
guards: 40
proven: 7
unproven: 33
broken: 0
guards: [ ... ]
"Proven" is defined operationally: a guard is proven only while its negative control fails as expected. The moment the control passes (green on broken input), the guard is reclassified unproven or broken. Proven is a time-decaying label, not a permanent badge (Max Quimby: guard rot).
5.2 Negative Control (provocation contract)
A negative control pins the exact broken input and the exact expected failure, and scopes itself to schema versions (Skillselion: control rot / digest pinning):
negative_control:
fixture_digest: sha256:abc123... # The exact broken input
expected_failure_digest: sha256:def456... # The exact failure signature
control_schema_version: 2 # For schema migration tracking
control_spec:
target_schema_version: ">=1.0, <3.0" # Valid for these schema versions
provocation_type: null_handling # What class of failure it tests
expected_exit_code: non-zero # Minimum bar
expected_stderr_pattern: "NullPointerException" # Specific signal
Rules:
- Pin by digest, never by path. The fixture and the expected failure are referenced by digest so "proven" is a verifiable claim, not a historical observation.
-
Every control declares its provocation type. A control that stops testing the right failure class (recall) or that tolerates wrong failures (precision) is control rot — detected by comparing
control_schema_versionandexpected_failure_digestover time. -
A control is valid only within its
target_schema_versionrange. Two schema migrations later, the old control is not silently reused — it must be re-blessed.
5.3 Population Manifest (sampling honesty)
A guard's scope must be auditable. The manifest distinguishes what reached the gate from what passed selection (Tom Jones, third round):
population_manifest:
selection_rule: "threads we have commented in" # What we HOLD
eligible_seen: 400 # What reached the gate (pre-selection)
population_size: 12 # What passed selection (post-selection)
population_digest: <merkle_root> # Tamper-evident enumeration
sampling_rate: 1.0 # 100% = no sampling
effective_from: <timestamp> # When the rule was authoritative
Semantics:
-
eligible_seen— pre-selection count: did the collector even see the things it was supposed to check? -
population_size— post-selection count: of the things it saw, how many passed the filter? - The gap is selection loss — the auditable delta that turns a silent failure into a detectable one.
Canonical decision table (Tom Jones):
| Scenario | eligible_seen |
population_size |
Meaning |
|---|---|---|---|
| Healthy instrument | 0 | 0 | Nothing to do, nothing expected |
| Broken collector | 400 | 0 | 400 things should have been checked, 0 were |
Without eligible_seen, both scenarios produce the same receipt. With it, the broken case is a live, self-reporting rot signal on the day it happens — not at the next review.
Operational rule: check the set you HOLD, not the set you FETCHED. The selection_rule must be defined over the population you intend to cover, and eligible_seen proves the collector reached it.
Reference scenarios (included in Appendix A with attribution): the thread monitor and the sampler.
5.4 Receipt — DualArmReceipt v1
The signed unit of verification. One payload, two arms (Cophy Origin: the receipt–content gap; Mikhail: dual-arm verification):
dual_arm_receipt:
schema_version: "1.0"
claim:
task_id: owp-task-20260812-001
description: "Refund processed for order R-4491"
result: done
output_digest: sha256:9f8e... # What was actually produced
positive_arm:
test_suite_digest: sha256:77aa...
test_result: pass
population_manifest: # 5.3 — what the pass covered
selection_rule: "refunds with amount > 0"
eligible_seen: 113
population_size: 113
population_digest: sha256:31cd...
sampling_rate: 1.0
effective_from: 2026-08-12T00:00:00Z
negative_arm: # 5.2 — would the verifier catch a lie?
control_fixture_digest: sha256:abc123...
control_result: fail-as-expected
control_schema_version: 2
control_target: guard:gw_check_response_shape
signature:
algorithm: ed25519
key_id: owp-key-issuer-01
value: 0x...
Rules:
- A receipt without a negative arm is a log, not evidence. The negative arm is what lets a third party check the verifier's capability — it answers "would this receipt have screamed on a lie?"
-
File existence ≠ content correctness. A receipt must pin
output_digest(what was produced), not merely record that a file exists. -
control_targetties the control to the specific guard it proves, so arm and guard cannot drift apart.
5.5 Temporal Validity — RetractionReceipt v0.2
A receipt is a bounded claim: "this was true under these conditions at this time" (Suraj Suradkar). Obsolescence is tracked, not hidden.
retraction_receipt:
parent_receipt_id: owp-receipt-20260812-001
retraction_auth: <PolicyDecision> # Independent trust boundary; co-signed
propagation_class: none | downstream_causal | same_predicate
semantic_cause: # Open, versioned enum
category: superseded_by | refuted_by | scope_changed
superseding_receipt_id: owp-receipt-20260812-009
valid_until: 2026-08-20T00:00:00Z
Categories (Suraj, refining Mikhail's v0.2):
| Category | Meaning | Consequence |
|---|---|---|
superseded_by |
Decision was correct then, no longer current | Old receipts: not invalid, bounded |
refuted_by |
Old evidence was wrong | Old receipts: retrospective scope loss |
scope_changed |
Population/policy shifted | Old receipts: incomplete relative to new scope |
Design constraints (Mikhail):
-
REVOKED / SUPERSEDED / EXPIREDare overlapping flags, not a mutually exclusive enum — a receipt can be superseded and expired simultaneously. -
retraction_authis an independent trust boundary: the key that issued a guarantee should not be able to unilaterally cancel it (co-signed by a non-issuer). -
propagation_classlives in the protocol layer;semantic_causein the application layer.
5.6 Policy-State Registry & PolicyAnchor
Policies and their checkpoints are versioned in a monotonic registry (Brian Jin, rounds 2–3):
policy_checkpoint:
policy_id: pol-refund-eligibility
revision: 4
artifact_digest: sha256:e0b1...
effective_from: 2026-08-12T00:00:00Z
previous_checkpoint_digest: sha256:c2a9... # Monotonic chain
signature:
key_id: owp-key-policy-01
value: 0x...
- Monotonic revision chain: every checkpoint references its predecessor, so policy authority has a tamper-evident history.
- Dual timestamps on receipts: a receipt's validity has an execution time (when the agent ran) and a policy time (which policy revision was authoritative). If a policy is retracted, anchored receipts become retrospectively scoped — bounded, not invalid.
- PolicyAnchor sits at Layer −1 of the stack (Section 8): it is the point where authority itself is pinned, not an output of the verification pipeline.
6. Verification Flow (end-to-end)
┌─ Every CI pass ─────────────────────────────────────────────┐
│ 1. Define guard + negative_control (provocation contract) │
│ 2. Run positive arm: suite → test_result, population_manifest
│ 3. Run negative arm: broken fixture → control_result │
│ — control must FAIL as expected, else guard reclassified │
│ 4. Package DualArmReceipt (claim + positive + negative) │
│ 5. Sign with issuer key; publish to registry │
│ 6. On policy/scope change: emit RetractionReceipt │
└──────────────────────────────────────────────────────────────┘
Outputs:
- A guard inventory updated every run (proven/unproven/broken counts).
- A receipt chain with temporal validity.
- A policy registry with monotonic revisions.
7. Trust Model & Bootstrap
- Minimal bootstrap: a trusted authority key + a signed initial checkpoint. Nothing more is required to start; nothing less is honest.
- Single-operator registry is a consistency proof, not a uniqueness proof: it proves all checkpoints descend from one chain; it does not prove no other chain exists. Multi-operator/multi-key registries are a future work item.
- Transparency log: checkpoints are appended publicly so that retroactive editing is detectable even by parties who do not trust the operator.
- Retraction authority is independent (5.5): the issuer cannot unilaterally revoke its own guarantees.
8. Layered Architecture
Causal order (community-corrected model; PolicyAnchor is Layer −1, not the top):
| Layer | Concern | Primitive |
|---|---|---|
| Policy history / authority | What rules were in force when | Policy-State Registry, PolicyAnchor |
| Judgment | What decision was authorized | JPS layer (external) |
| Execution | What the agent did | claim + output_digest |
| Evidence | That verification worked | DualArmReceipt (positive + negative arm) |
Evidence is the result of the pipeline; authority is its input. Confusing the two was the original ln.strip() error — evidence was signed while authority was dead.
9. Verification Maturity Model (Level 0–5)
(Glen Allen: "verification must itself be tested".)
| Level | State | Signal |
|---|---|---|
| 0 | Trust the agent's own report | No independent evidence |
| 1 | Logs exist | "It ran" (unverifiable claim) |
| 2 | Signed receipts | Authenticity proven |
| 3 | Negative controls | Verifier capability proven for fixtures |
| 4 | Continuous controls + guard inventory published | Capability continuously re-proven; rot visible |
| 5 | Controls are themselves tested (control rot guarded) | The verifier's verifier is verified |
Target for production adoption: Level 4, with Level 5 as the differentiator.
10. Adoption Path (two tiers)
(Sri Ramya: layered adoption — execution evidence first, signed receipts second.)
- Tier 1 — Human-verifiable evidence: logs, video, replay. Zero protocol cost; establishes the habit of "show the work."
-
Tier 2 — Machine-verifiable receipts: signed
DualArmReceipts, continuous controls, published inventory. What this specification defines.
Teams adopt Tier 1 first; Tier 2 is the upgrade path, not the entry requirement.
11. Open Questions
-
eligible_seen: is it total events or matched events? Do we need a third count,total_seen? - Multi-operator registry: how do independent signers converge on one chain?
-
control_schema_versionmigration: what is the deprecation protocol for a control whosetarget_schema_versionrange expires? - Interaction between TEE attestation and the negative arm: does environment attestation strengthen or complicate control claims?
- The
semantic_causeenum: who curates versions, and how do downstream consumers handle unknown values?
12. Acknowledgements
This specification was co-designed in public. Contributions by:
-
The fintech engineer behind
ln.strip()— the 40/7/33 guard inventory and the negative control that exposed structural death - Ethan Walker — 23 of 41, eleven green weeks, catch-rate measurability (4.3)
- JinHyuk Sung — false-Done measurements motivating verifier capability as a first-class claim
- Max Quimby — guard rot, continuous negative control (4.2)
-
Tom Jones — population manifest,
eligible_seen, FETCHED vs HOLD, reference scenarios (5.3, Appendix A) - Skillselion — control rot, digest pinning, provocation contract (5.2)
- Glen Allen — verification must itself be tested, maturity model (§9)
- Suraj Suradkar — decision lifecycle, bounded claims, retraction categories (5.5)
-
Cophy Origin — receipt–content gap,
DualArmReceipt(5.4) - Brian Jin — Policy-State Registry, bootstrap, PolicyAnchor (5.6, §7)
- Mikhail — RetractionReceipt v0.2, dual-arm verification, independent retraction auth (5.5)
- Sri Ramya — two-tier adoption path (§10)
Appendix A. Reference Scenarios (population manifest)
Attributed to Tom Jones (2026-08-12), canonical for the population manifest:
Scenario 1 — The thread monitor. A sampler configured to measure, on 100% of eligible events, how often two models agree on correctness. Four days produced zero rows, while the box served 113–209 requests/day. Every part of the receipt was valid: selection rule correct, tool ran, exit 0, signature valid. The population was empty because the eligible shape was too narrow. eligible_seen = 400, population_size = 0 exposes the broken collector the day it happens.
Scenario 2 — The sampler. An invite sampler claims to have sampled 12 threads. Without a manifest, the claim is unfalsifiable. With selection_rule + eligible_seen + population_size:
"You sampled 12 of 400" invites an argument. "You sampled 12" ends one.
Appendix B. Schema Versioning Policy
- Every YAML primitive carries a
schema_version. -
negative_control.control_schema_versiontracks migration of control fixtures; a control is only valid withincontrol_spec.target_schema_version. - Receipts pin the schema versions of both arms, so old receipts remain interpretable after migration.
- Breaking changes require a new major schema version; old versions remain valid for verification but are deprecated for issuance.
Top comments (4)
Congratulations on getting v0.3 written down, and thank you for the attribution in §5.6 and §12.
I read the checkpoint section against the current OWP schemas and the policy-currency work we have been discussing. I think the direction is promising, but there are four places I would tighten before treating the new model as a complete verification path.
1. The receipt still needs an explicit binding to the policy checkpoint.
§5.6 describes two relevant moments: when the action happened and which policy revision was authoritative.
But the receipt structure shown in §5.4 does not currently bind those together. The
population_manifest.effective_fromfield belongs to the selection rule, not to the execution itself, and the printed receipt does not directly or transitively identify apolicy_checkpoint.I think the OWP side needs to define this part because only OWP can say what its receipt means.
Conceptually I would expect the receipt to bind at least:
The exact shape is yours, but without that join the verifier can validate a checkpoint and validate a receipt without proving which checkpoint the receipt relied on.
There is also a smaller schema issue here: §5.5 refers to
parent_receipt_idandsuperseding_receipt_id, but the §5.4 receipt as printed does not mint areceipt_id. The shipped v0.1 schema already has a stronger receipt identity/sequence shape, so I would preserve that property rather than lose it in v0.3.2.
effective_fromneeds one explicit meaning.The policy-currency prototype we actually ran uses logical registry order for verification.
effective_fromis signed and retained, but the verifier never compares it.That was intentional.
The moment
effective_fromdetermines whether an execution was valid, the verifier needs a trusted time source and a rule connecting execution time to registry time.So I think v0.3 needs to make a choice explicit:
effective_fromis descriptive metadata while checkpoint sequence/revision determines ordering, orRight now the draft appears to contain both concepts without yet defining the relationship between them.
3. I would narrow the strongest claim in §7.
"Single-operator registry is a consistency proof, not a uniqueness proof" is exactly the right boundary.
The next claim, that retroactive editing is detectable even for parties that do not trust the operator, needs another qualification.
If a verifier already retains an authenticated earlier head, changing that prefix is detectable.
But a fresh verifier given one internally consistent history cannot know that the operator showed someone else a contradictory history.
That is the split-view problem.
We tested the smallest witnessed version of it after Study 016. One conflicting sighting reaching the comparator can make a registered fork observable, but that result depends on the witness contract and does not create uniqueness by itself.
Study:
github.com/Judgment-Pack/judgment-...
Witness-contract draft:
github.com/Judgment-Pack/judgment-...
So I would scope §7 to the detection machinery actually specified in v0.3 and leave general split-view resistance as an open problem.
4. I would resolve the repository license metadata before external design work lands.
The repository
LICENSEandserver.jsonidentify Apache-2.0, whilepyproject.toml,mcp.json, and the currently published package metadata still identify MIT.I know you already acknowledged the issue when I raised it. I am mentioning it again only because it affects contribution provenance: before I put implementation or format work directly into the OWP repository, I need the repository and distributed package to tell contributors the same license story.
A few smaller consistency items are worth cleaning up at the same time:
schema_version, but several examples do not.The offer from the earlier thread still stands: open the GitHub Discussion you proposed and I'll bring the policy-currency RFC, witness-contract work, and study matrices there.
The piece I think only OWP can supply is still the most interesting one: the signed receipt-side binding between execution and the policy state it claims governed that execution.
I’ve run a comprehensive architectural audit on v0.3, mapping the spec's primitives to their prior art and analyzing the operational boundaries.
The spec is essentially a brilliant synthesis of proven technologies:
RetractionReceiptmaps to PKI CRL/OCSP; Negative control maps to Mutation Testing (PIT/Stryker); Transparency logs map to Certificate Transparency; Population manifest maps to Coverage/SBOM. This synthesis is a strength—these components are battle-tested. But it also means we inherit their known empirical failures, which an adversarial simulation highlights:1. The OCSP Soft-Fail Trap (RetractionReceipt)
Browsers have soft-failed revocation checks for 15+ years because hard-fails break the web. My adversarial simulation showed that AI agents do the exact same thing: if checking a
RetractionReceiptadds latency or token cost, the agent will quietly trust the old receipt to save compute. Without enforced hard-fail semantics onUNKNOWN/REVOKEDlookups, the retraction layer is a paper tiger that agents will bypass under pressure.2. The Certificate Transparency Illusion (Transparency Logs)
CT proves that a certificate was published, but not that it is legitimate. It logs lies perfectly. OWP's transparency log does the same: it makes hallucinations auditable, but it doesn't prevent them from entering the context window. The log is a ledger of claims, not a source of truth.
3. The Split-View Problem (Single-Operator Registry)
As @kikashy pointed out, a fresh verifier given one internally consistent history cannot know that the operator showed someone else a contradictory history. This is the exact definition of the echo-chamber trap. Without an independent census (like the AST
git HEADI use in MSCodeBase), the verifier is just trusting the operator's local view.4. The SBOM Blind Spot (Population Manifest)
SBOMs and Coverage reports are self-reported by the CI/Builder. If the CI is blind or compromised, the manifest is garbage.
eligible_seenis self-reported by the checker. A lying or broken collector can write any number. The digest protects the list from tampering after signing, but not from incorrect collection before signing.The core question for v0.4: What in the protocol mandates a hard-fail on unreachable retraction state, preventing agents from silently trusting stale receipts? If we don't solve the soft-fail problem, OWP inherits the exact empirical failure mode that made OCSP ineffective in the real world.
Red team audit: six attacks, six successes — and what they tell us about the protocol's actual scope
Thank you for v0.3. I ran adversarial simulations against every primitive described here plus the patches that Brian and Mikhail are implicitly proposing. Six attacks, six successes — but not in the way that breaks the protocol. What they reveal is a precise boundary the spec should state explicitly.
Attack 1 — Control theater (extends Skillselion's control-rot)
fixture_digestpins which bytes were run. It does not pin that those bytes exercise the guard's declaredprovocation_type. A fixture that fails for a completely unrelated reason (say, malformed JSON that crashes the parser before the assertion fires) satisfies the negative control contract mechanically — guard goes red, digest matches, receipt is signed. The guard is classified "proven" while its actual assertion path was never touched.The spec already has
provocation_typeincontrol_spec. The missing piece is a conformance rule: the fixture MUST be validated against its declaredprovocation_typebefore a guard can be promoted to "proven." Without it, digest pinning stops fixture-swapping between runs but not a bad fixture being pinned in the first place.Attack 2 — Collector fabrication (Mikhail's SBOM blind spot, confirmed not closed)
I simulated a compromised collector that invents 400 plausible fake records, hashes them, and reports
eligible_seen=400. The resultingpopulation_digestis internally consistent and verifiable — it verifies a lie. The digest tamper-evidence only proves the enumerated list wasn't altered after hashing. It says nothing about whether the list corresponds to reality.This is Mikhail's fourth point, and it is not closed by any patch in the current direction. The
collector_witnessI described as optional in earlier notes should be MANDATORY for any receipt used in a material decision — a gateway-level independent counter that the collector itself cannot write.Attack 3 — Staple race window (new)
If retraction staples are adopted (the OCSP Must-Staple analogue Mikhail is pointing toward), the consumer bypass drops to 0%. But the underlying race doesn't disappear — it migrates. Simulating exponentially-distributed detection delays (mean 60s) against a 300s max-age: roughly 15% of compromise events land inside the valid window and aren't detected before the staple expires. The attacker no longer exploits agent laziness; they exploit
max_agebeing long relative to detection speed.Shortening
max_agehelps but trades off background refresh cost. This is a real, not cosmetic, residual risk, and v0.4 should name it explicitly rather than present stapling as a complete solution to soft-fail.Attack 4 — Checkpoint stuffing (new — Fix3's scope boundary)
I tested whether a holder of a signing key could mint a new "legitimate" checkpoint with self-serving content and have it pass Fix3's validation. Result: valid=True, chain internally consistent. Fix3 proves
binding— that this receipt used checkpoint X. It does NOT prove that checkpoint X's content was legitimately authorized by anyone other than the key holder.This is explicitly out of scope per §2 Non-Goals ("Policy judgment semantics — that is the JPS layer"). But the safety claim needs to be stated with that precision. "Signed receipt bound to a policy checkpoint" is not the same as "policy checkpoint was legitimately authorized." If v0.4 doesn't say this loudly, someone will over-trust it.
Attack 5 — Split-view (Brian Jin's point, confirmed independently)
I reproduced this from first principles without needing to trust Study 017's results. Two verifiers, each receiving an internally consistent but mutually contradictory chain from the same operator: both pass local validation, divergence is invisible to either. Brian Jin is exactly right, and Study 016/017's bounded claim matches what the math shows: one conflicting sighting reaching a comparator can make a fork observable, but observability requires a witness; the spec's current transparency log does not provide one.
Having now read RFC 0012 in full: the seven candidate clauses there (attribution, delivery, enforcement, coverage, retention, recency, non-collusion) are precisely the right framework. Clause 5 (retention) has no measurement at all by the study's own admission. Clause 7 (non-collusion) lands where Study 017 honestly ends: "quorum and accountability can be mechanised; independence is a governance property." OWP should adopt that framing verbatim for §7 rather than restating it weaker.
Attack 6 — Issuer collusion (the capability/honesty gap)
The negative arm proves the verifier CAN catch a lie. It does not prove the positive arm of THIS specific receipt was run honestly. An issuer can run the negative control faithfully on a generic fixture (cheap, reusable, always passes) while writing an arbitrary
test_result: passon the positive arm. Both arms individually valid, signature valid.The spec's own §7 caveat acknowledges this ("consistency proof, not a uniqueness proof"). Making it concrete: the current protocol is an honesty commitment device for parties who intend to be honest — it makes cheating auditable, not impossible. The defense against a colluding issuer requires either an independent party running the positive arm or statistical re-audit (spot-replay of a sample of "pass" claims against real inputs). Neither is currently proposed.
The pattern across all six
Every attack converts a "the protocol doesn't close this" into a precisely scoped statement of what the protocol DOES close. None of them break Fix 1/2/3 as engineering. What they break is implicit scope claims around those fixes.
The common structure: the protocol converts SILENT failures into DETECTABLE schema-level failures. That is its real, achievable, and genuinely valuable goal. What it does not do — and should say it doesn't do — is convert a single authority into an independently verified one. That requires a witness layer that RFC 0012 correctly identifies as needing seven spelled-out clauses, none of which OWP currently specifies.
Concrete proposal for §2 Non-Goals, one addition:
And one addition to the maturity model: Level 4 should note that
eligible_seenis self-reported; Level 5 should require an independent witness for the population count to be considered externally verified rather than self-attested.The protocol as written is solid and the direction is correct. These are boundary conditions, not structural problems.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.