{"name":"RAP Conformance Vectors","version":"1.0","lang":"en","purpose":"Turns RAP from a document you read into a set of assertions you can run. Take the vectors, point them at your own base URL, and run them against your own implementation.","how_to_run":{"get_it":"curl -sO https://raw.githubusercontent.com/OceanAlt/mpp-compliance-adapter/main/rap-conformance.mjs","any_implementation":"node rap-conformance.mjs https://your-gateway.example","json_output":"node rap-conformance.mjs https://your-gateway.example --json","requirements":"Node 18+, no dependencies.","source":"https://github.com/OceanAlt/mpp-compliance-adapter/blob/main/rap-conformance.mjs","exit_code":"Non-zero when any critical vector fails — usable as a CI gate."},"conformance_rule":"“Conformant with RAP v1.0” has exactly one definition: every critical vector passes. Failing important vectors must be disclosed but do not negate conformance.","counts":{"total":15,"critical":5,"important":10},"coverage_by_pillar":[{"pillar":"kya","name":"Attribution","vectors":1},{"pillar":"mandate","name":"Mandate & limits","vectors":1},{"pillar":"controls","name":"Firewall","vectors":1},{"pillar":"aml","name":"AML screening","vectors":3},{"pillar":"audit","name":"Auditability","vectors":2},{"pillar":"privacy","name":"Privacy","vectors":3},{"pillar":"interop","name":"Interoperability","vectors":4}],"coverage_note":"Coverage is uneven, and that is reported rather than designed: AML and auditability are easy to test, while much of privacy and interoperability cannot be established from a single HTTP request. A pillar with few vectors is one where this suite is still weak.","fixtures":{"mixer":"0x8589427373D6D84E98730D7795D8f6f8731FDA16","clean":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045","malformed":"0xnot-an-address"},"fixtures_note":"All fixture addresses are real and publicly checkable. A fake address in a vector would require the implementation under test to pretend along with it.","vectors":[{"id":"RAP-AML-01","pillar":"aml","severity":"critical","title":"A known mixer address must produce a negative decision","proves":"Screening actually runs, rather than waving everything through. It is the easiest claim in RAP to fake and the easiest to check.","request":{"method":"GET","path":"/api/decide","query":{"to":"0x8589427373D6D84E98730D7795D8f6f8731FDA16"}},"assertions":[{"path":"decision","op":"oneOf","value":["decline","review"],"why":"Allowing a known mixer means screening is not doing anything."},{"path":"allow","op":"equals","value":false,"why":"The boolean must agree with decision, or an agent branching on either one can be wrong."}]},{"id":"RAP-AML-02","pillar":"aml","severity":"critical","title":"When screening is unavailable, the result must not be reported as clear","proves":"This is the most dangerous failure mode in compliance software: the data source is down and the answer comes back as “no risk found”. A silent PASS is far worse than a loud error.","request":{"method":"GET","path":"/api/decide","query":{"to":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"}},"assertions":[{"path":"verdict","op":"oneOf","value":["clear","caution","risky","uncertain"],"why":"The verdict must come from a defined set, and uncertain must be expressible."},{"path":"retry","op":"exists","why":"When there is no conclusion, the agent needs to know whether to back off and retry."}]},{"id":"RAP-AML-03","pillar":"aml","severity":"important","title":"Malformed input must not be treated as a pass","proves":"Treating an unparseable address as “no risk signal” is the same silent-PASS problem wearing a different face.","request":{"method":"GET","path":"/api/decide","query":{"to":"0xnot-an-address"}},"expect_status":[400,422],"assertions":[{"path":"error","op":"exists","why":"It must fail explicitly rather than return something that looks like a normal allow."}]},{"id":"RAP-AUDIT-01","pillar":"audit","severity":"critical","title":"A verdict must carry verifiable evidence, not just a score","proves":"A score cannot be argued with. Evidence can be opened, re-checked and overturned by a third party — which is what auditability means.","request":{"method":"GET","path":"/api/decide","query":{"to":"0x8589427373D6D84E98730D7795D8f6f8731FDA16"}},"assertions":[{"path":"evidence","op":"nonEmptyArray","why":"A conclusion without grounds cannot be checked or challenged by the reader."},{"path":"evidence.0.url","op":"exists","why":"Evidence must link to the primary source, not merely paraphrase it."}]},{"id":"RAP-AUDIT-02","pillar":"audit","severity":"important","title":"Machine-readable reason codes, not free text","proves":"Free text forces an agent to string-match its way to understanding a refusal — one rewording and it all breaks.","request":{"method":"GET","path":"/api/decide","query":{"to":"0x8589427373D6D84E98730D7795D8f6f8731FDA16"}},"assertions":[{"path":"reason_code","op":"typeOf","value":"string","why":"The reason code must be stable and machine-readable."},{"path":"signal_class","op":"oneOf","value":["ok","payment","regulatory","authorization","uncertain","client_error"],"why":"The class decides whether an agent should retry; it cannot be improvised."}]},{"id":"RAP-MANDATE-01","pillar":"mandate","severity":"critical","title":"A payment above the mandate ceiling must actually be blocked","proves":"This is the line between RAP and “write the model a careful prompt”. Note it must set a ceiling first: without one the gate merely skips, and a gate that always skips lets any implementation “pass”.","setup":[{"method":"POST","path":"/api/pay","body":{"action":"register","agentId":"rapconf-{{nonce}}","entity":"RAP conformance vector (automated)","mandate":{"maxUsdc":10,"dailyUsdc":20,"purpose":"conformance"}},"capture":{"agentId":"agentId","secret":"agentSecret"}}],"request":{"method":"POST","path":"/api/pay","headers":{"x-agent-secret":"{{secret}}"},"body":{"action":"preview","agentId":"{{agentId}}","amountUsdc":999999,"to":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045","purpose":"conformance"}},"assertions":[{"path":"decision","op":"equals","value":"block","why":"Letting 999999 through against a 10 USDC ceiling means the limit exists only in the documentation."},{"path":"blocked","op":"equals","value":true,"why":"There must be an unambiguous boolean at the top level; with only a decision string an agent tends to branch on ok and proceed."},{"path":"checks","op":"nonEmptyArray","why":"It must report per-gate results, not one blanket pass/fail."}],"teardown":[{"method":"POST","path":"/api/pay","headers":{"x-agent-secret":"{{secret}}"},"body":{"action":"revoke","agentId":"{{agentId}}"}}]},{"id":"RAP-KYA-01","pillar":"kya","severity":"important","title":"The service can describe which controls it runs","proves":"A compliance service that will not say which checks it runs cannot be audited, nor assessed by a counterparty.","request":{"method":"GET","path":"/api/pay"},"assertions":[{"path":"gates","op":"nonEmptyArray","why":"The self-description is the entry point to auditability."}]},{"id":"RAP-INTEROP-01","pillar":"interop","severity":"important","title":"Output language is negotiable, and language-independent keys are provided","proves":"In cross-border agent payments the human sentence is for humans; the agent should branch on keys. Prose in a single language ties decision logic to that language.","request":{"method":"GET","path":"/api/risk","query":{"addr":"0x8589427373D6D84E98730D7795D8f6f8731FDA16"}},"assertions":[{"path":"signal_keys","op":"exists","why":"Without language-independent keys an agent has to parse prose."}]},{"id":"RAP-INTEROP-02","pillar":"interop","severity":"important","title":"A machine-discoverable interface description is published","proves":"If “callable by agents” exists only in human documentation, the agent first has to learn to read human documentation.","request":{"method":"GET","path":"/openapi.json"},"assertions":[{"path":"openapi","op":"typeOf","value":"string","why":"OpenAPI is the common way for agents to discover an interface."},{"path":"paths","op":"exists","why":"A version with no paths is nothing."}]},{"id":"RAP-CONTROLS-01","pillar":"controls","severity":"critical","title":"A refusal must come with an actionable next step","proves":"A refusal that only says “no” leaves an autonomous agent either retrying blindly or stuck. Neither is what we want.","request":{"method":"GET","path":"/api/decide","query":{"to":"0x8589427373D6D84E98730D7795D8f6f8731FDA16"}},"assertions":[{"path":"retry","op":"exists","why":"The agent needs to know whether the refusal can be resolved by changing something."},{"path":"advice","op":"typeOf","value":"string","why":"Give the planner one readable next step."}]},{"id":"RAP-PRIVACY-01","pillar":"privacy","severity":"important","title":"Basic screening must not require signup or identity documents","proves":"Compliance should not be premised on “hand yourself over first”. Checking a public address's risk does not require knowing who is asking.","request":{"method":"GET","path":"/api/risk","query":{"addr":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"}},"expect_status":[200],"assertions":[{"path":"verdict","op":"exists","why":"A result should come back with no credential presented at all."}]},{"id":"RAP-PRIVACY-02","pillar":"privacy","severity":"important","title":"Anonymous screening must not set a persistent identifier on the caller","proves":"An endpoint whose only job is to answer whether an address is risky has no reason to remember who asked. Setting a cookie means collecting something the compliance task does not need.","request":{"method":"GET","path":"/api/risk","query":{"addr":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"}},"assertions":[]},{"id":"RAP-PRIVACY-03","pillar":"privacy","severity":"important","title":"The service must publicly state how long it retains data","proves":"“We avoid over-collecting” is unverifiable without a retention period. Anyone deciding whether to send you their queries needs to know how long those queries live, and needs to read that period straight from the machine-readable self-description rather than inferring it from a legal page.","request":{"method":"GET","path":"/api/pay"},"assertions":[{"path":"data_retention","op":"exists","why":"The retention period must be readable from the self-description, not guessed at from a legal page."}]},{"id":"RAP-INTEROP-03","pillar":"interop","severity":"important","title":"Callable cross-origin","proves":"Many agents run in a browser, an extension or a sandbox. Refusing cross-origin calls excludes that entire class — which is precisely the class that most needs to check before paying.","request":{"method":"GET","path":"/api/risk","query":{"addr":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"}},"assertions":[]},{"id":"RAP-INTEROP-04","pillar":"interop","severity":"important","title":"Self-described controls must carry stable machine keys, not only display names","proves":"Display names get translated and reworded. Logic branching on a display name breaks the next time the copy changes — the same principle as signal_keys, one layer over.","request":{"method":"GET","path":"/api/pay"},"assertions":[{"path":"gates","op":"nonEmptyArray","why":"There has to be a self-described list first."},{"path":"gates.0.key","op":"typeOf","value":"string","why":"Each entry needs a language-independent key."}]}],"human_readable":"https://oceanalt.com/en/rap/conformance","standard":"https://oceanalt.com/en/rap"}