Repository navigation
Conversation
Change Control Evidence CheckAuthorization — ❌ FailEvidence:
Testing — ✅ PassEvidence:
Approval — ✅ PassEvidence:
Segregation of Duties — ✅ PassValidated:
Last checked: 2026-11-11 02:03 UTC ↩ Re-run Clarissa if tickets, CI, or approvals have changed. |
| data=b"", | ||
| ) | ||
| try: | ||
| with urllib.request.urlopen(request, timeout=60) as response: |
Single-file end-to-end Python example for MPC custody withdrawals: POST /v1/custody/challenge -> independent local challenge verification (rebuilds the canonical signing context and re-derives the SHA-256 challenge; never trusts a server-supplied digest) -> ES256 (COSE P-256) assertions from approved signers -> POST /v2/withdraw. The verification engine is embedded in the same file (stdlib-only section). README documents the support-enrollment prerequisite: custody API enablement, account security policy with an API signer threshold, and signer keypair registration must all be arranged with Gemini support before the API is usable. Validated end-to-end against production (XRP, 3-of-3 threshold).
enforser
force-pushed
the
custody-api-signing-example
branch
from
November 10, 2026 23:37
2020765 to
1d0ad7c
Compare
enforser
marked this pull request as ready for review
November 10, 2026 23:38
switchboard-pr-review-assigner
Bot
requested review from
deeputom
and removed request for
mars-steven
November 10, 2026 23:40
Nostradamus Risk Rating — Medium
|
vjames19
approved these changes
Nov 11, 2026
This branch has not been deployed
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
A single-file Python example (
samples/python/custody_withdrawal.py) for the MPC custody API signing challenge and authorized withdrawals.Why the verification step matters
The hard part of integrating this API is client-side: a verifier must reconstruct the canonical authorization context byte-for-byte and re-derive the challenge. Any re-serialization (different key order, whitespace, re-encoding of the embedded signing strings) produces a different digest. The example therefore embeds the verification engine in the same file and runs it on every flow:
GEMINI-API-SIGNATURE-V1:<challenge>VerificationErrors for every mismatchThe verification section is deliberately stdlib-only and keyless, so integrators can audit it or lift it into their own language.
Files
samples/python/custody_withdrawal.pyPOST /v1/custody/challenge→ local challenge verification → ES256 (COSE P-256) assertion per approved signer → signedPOST /v2/withdraw/:network/:tickersamples/README.mdsamples/python/requirements.txtcryptography(ES256 signing in the example only; the verification section stays stdlib-only)Contract
Implements the
/v1/custody/challengewire contract, version"1"(message prefixGEMINI-API-SIGNATURE-V1:). The protocol constants are carried as code so drift against a future contract version fails loudly.Validation
HTTP 200)memo(rather than sendingnull) when a transfer has no memo; the verifier treats absent and null identically