Repository navigation
feat(examples): OPA policy engine behind the pre-sign hook (#142) - #242
Open
fernando-mendoza wants to merge 1 commit into
Open
fernando-mendoza wants to merge 1 commit into
fernando-mendoza wants to merge 1 commit into
Conversation
…otocol#142) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
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.
What
Adds
examples/x402-policy-opa/: a pre-sign policy hook whoseevaluate()sends the decoded payment (amount,destination,asset,network,agent) to an Open Policy Agent server and maps its answer toALLOW/DENY. Same layout asx402-policy-daily-budget(tsx + node:test, same tsconfig). No changes to the hook or the SDK.How to run
Tests
test/opa-policy.test.ts(14): unit tests ofOpaPolicyagainst a mocked fetch: allow, deny, OPA down, HTTP 500, 8 malformed shapes, stale revision, aborted request.test/hook-integration.test.ts(11):OpaPolicy.hook()wired into the SDK's real gate,createPolicyGatedSigner(the oneinitX402({ policy })installs), with a mocked OPA fetch. Each test builds a real Sorobantransferauth entry and signs it through stellar-sdk'sauthorizeEntry, and asserts whether the base signer was reached.initX402itself needs a real 402 server and RPC, so the gate is imported from the built SDK'sdist/x402-policy.js; this adds@stellar/stellar-sdk@^16.3.0as a devDependency (same range as the SDK).npm test→# tests 25 # pass 25 # fail 0(Node 22.23, afterrm -rf node_modules && npm ci).Done items
policy/x402.rego: per-payment cap + destination allowlist.opa test policy -v→PASS: 7/7;opa fmt --diff policyandopa check --strict policyare clean (OPA 1.22.0). Docker isn't available on my box, so I ran the same server with theopabinary; the docker command above uses the same arguments.evaluate()throws on fetch failure, non-200, non-JSON, undefined rule, a result that isn't{allow: boolean, reason?: string}, or a missing bundle revision. Nothing catches these. Through the real gate: down/500/malformed →ENGINE_ERROR, slow →TIMEOUT(even when the fetch ignores the abort signal), base signer never called.ctx.contextHash,expiresAt = now + ttlMs(2 s default),requireExpiry: true.policyVersionandcurrentVersion()come from OPA's?provenancefieldprovenance.bundles.<name>.revision(the bundle.manifest). Live end to end, real OPA (--watch --bundle policy) + the real gate + a real auth entry, bumping.manifestfrom r1 to r2 between decision and signing:Also checked:
.github/scripts/typecheck-example.shagainst a packed SDK tarball (nirium 0.16.1) passes for this example and for every other example with atsconfig.json.Open questions for review
bundledefaults topolicy(OPA names a--bundleafter its path). Alternative: read the sole bundle OPA reports in provenance and fail if there are several. I chose explicit config.currentVersion()re-queries the decision path with an empty input to read the revision (harmless; the rule is just undefined for that call). A different endpoint (e.g./v1/statusor a dedicated rule) would work too if you prefer.to_numberafter a digits-only regex. Fine for a reference cap, but very large amounts lose precision as floats; a production policy should compare exact integers.Closes #142