sincor-x402 — Verified x402 + MCP wrapper
"Pay-and-pray" is the gap. Coinbase's x402 lets an agent pay USDC to an
endpoint up front — with no escrow, no committed spec, and no guarantee the
seller won't return garbage or time out after taking the funds. sincor-x402
wraps any x402 endpoint or MCP tool in the missing piece:
request → HTTP 402 (escrow terms + committed spec hash)
→ payment verified → funds held in escrow
→ work executes (timeout-bounded)
→ result verified against the committed spec (SIGIL-style commitment)
→ SEAL release on pass · refund on fail or timeout
Honesty first — what is real and what is not
- Live on getsincor.com today: CNP task bidding/auctions, pool settlement, and KYA, plus the platform's own x402 seller/buyer code this package's wire shapes are modelled on.
- Integration-pilot framing: SEAL (EIP-712 settlement) and SIGIL
(verification) are the platform concepts this wrapper's interfaces are
shaped around. This package implements them as pluggable interfaces
with labelled local reference implementations (
LocalReferencePlatform,LocalFacilitator,ReferenceVerifier). They do NOT talk to a live SIGIL service or a production escrow endpoint — none is wired yet. - No SINCOR escrow contract is deployed on Base mainnet. Nothing in this package moves real funds. Do not represent it otherwise.
Networks & constants
- Base mainnet: chain ID 8453
- Base Sepolia: chain ID 84532 — testnet rehearsal only
- USDC on Base:
0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 - Live agent card: https://getsincor.com/.well-known/agent-card.json
Nothing in this package is deployed on either network — these are the chain IDs and contract constants the wire shapes and the Hub engine are written against, not live deployments.
Install
pip install sincor-x402 # core: zero dependencies, stdlib only
pip install "sincor-x402[fastapi]" # + the FastAPI adapter
Server: wrap an endpoint (2 lines)
from sincor_x402 import VerifiedEndpoint, PaymentRequirement
endpoint = VerifiedEndpoint(summarize, requirement=REQ, spec={"required_keys": ["summary"]})
REQ is your price/terms (PaymentRequirement(scheme="exact", network="base", max_amount_required="10000", asset=<USDC on Base>, pay_to=<your address>, max_timeout_seconds=60)). Feed requests to endpoint.handle(headers=..., kwargs=...):
no payment header → a 402 challenge carrying your accepts[] + escrow terms;
a paid request runs the full escrow → execute → verify → release/refund loop.
With FastAPI (sincor-x402[fastapi]):
from sincor_x402.fastapi_adapter import sincor_verified
@app.post("/summarize")
@sincor_verified(requirement=REQ, spec={"required_keys": ["summary"]})
def summarize(payload: dict): ...
MCP: wrap a tool (2 lines)
from sincor_x402 import verified_tool
@verified_tool(requirement=REQ, spec={"required_keys": ["answer"]})
def lookup(symbol: str): ...
No MCP SDK is imported or required: the wrapped tool exposes
tool_definition() (an MCP tools/list-shaped descriptor) and
call_paid(payment_header=..., **kwargs) for your host's tools/call
handler, plus a best-effort register(server) for FastMCP-style hosts.
Buyer: pay a wrapped endpoint — exactly once per instruction
from sincor_x402 import VerifiedBuyer
buyer = VerifiedBuyer(wallet_address, signer_fn) # signer is injected; keys never enter this package
out = buyer.fetch(url, instruction_id="job-123", body={"q": "..."})
- Instruction-scoped idempotency:
instruction_idis your unit of work. Re-callingfetchwith the same id and the same request returns the recorded outcome and never signs a second payment — a historical x402 retry bug produced 40 payments for one request; the journal + same-payload resend design here makes that impossible (covered by a 40× retry-storm test). Reusing an id with a different request raisesIdempotencyConflict. - Per-payee velocity cap: distinct instructions to one
payToare capped by count and total amount per sliding window (VelocityCap), checked before signing, so a capped attempt creates no payment. - No blind retries: an ambiguous paid response raises
AmbiguousPaymentand locks the instruction rather than risking a double-pay. - Per-call cap: challenges above
max_payment_usdare refused unsigned.
Server safety: the accepts[] hedge
A client echoes the terms it "accepted" inside its payment payload — and a
buggy or malicious client can rewrite them (lower price, swapped payTo).
This server never trusts the client's copy: it matches the claim against
its own retained accepts[] entry on scheme, network, amount, asset,
payTo, and timeout, rejects mismatches before the facilitator is touched,
and passes its own PaymentRequirement object to the facilitator.
Price-rewrite and payTo-swap attacks have dedicated negative tests.
Hub settlement engine (sincor_x402.hub_engine)
A client for the SincorSettlementHub contract interface from the
2026-11-11 spec drop — the on-chain counterpart to the wrapper above.
Requires the hub extra: pip install "sincor-x402[hub]".
- Buyer side:
create_buyer_seal_header(...)signs an EIP-712 SEAL envelope (SealAuthorizationunder domainSINCOR_SEAL/"1", nonce read fresh from the Hub'snonces(buyer)) and returns it as a base64 X-PAYMENT header, schemesincor-seal-v1. The buyer's key stays with the injected signer callable; the package never holds keys. - Settler side:
verify_and_settle(...)runs the package'sVerifierseam against the committed spec first, then submitsverifyAndSettleSealthrough an injected relayer signer and parses theSealSettledevent (gross / net payout / protocol fee). - SIGIL canon on this path:
keccak256(taskId ‖ canonical-JSON output)— the EVM-native hash, matching the Hub's own typehashes.
Status: the Hub is NOT deployed
The Hub contract exists today only as an unverified spec-drop draft;
deploying it is a founder gate and nothing here deploys anything. The
engine is proven end-to-end against a local test chain
(tests/test_hub_engine.py): a vendored test copy of the Hub + mock USDC
on eth-tester, Python-signed SEALs settling through the real Solidity
ecrecover path.
Setup step buyers must not skip: the USDC allowance
The SEAL signature alone moves nothing — the Hub pulls funds with
ERC-20 transferFrom at settlement time, and nothing is escrowed at
authorization time. Before a buyer's first settlement, the buyer must
approve the Hub on the USDC contract for at least the seal amount. The
engine preflights this and raises AllowanceInsufficient naming the exact
approval step instead of letting the settlement revert on-chain.
Trust model — stated plainly
The Hub does not verify work output: it records whatever
sigilCommitment the submitter hands it, and never checks the delivered
output against the seal's expectedSchemaHash on-chain. On-chain
isTaskVerified therefore proves only that a settlement was recorded
with that commitment. The enforcement layer is this engine, off-chain:
it recomputes the SIGIL commitment from the delivered result, requires
the seal's expectedSchemaHash to equal the committed spec hash, runs
the Verifier, and refuses to submit when verification fails — the Hub
is never called. Anyone submitting directly to the Hub bypasses that
enforcement; the Hub alone is not a verification system.
Fee canon
The locked SINCOR platform fee is 5% (500 bps). The spec-drop draft
hardcodes 100 bps — a known defect. The vendored test copy under
tests/fixtures/ sets protocolFeeBps = 500 (change recorded in its
header), and the chain tests assert the canon split on-chain: provider
95% / treasury 5%.
Lower effective rates exist only as negotiated enterprise/volume arrangements applied at the platform layer — never as a different code or docs default. The default in this package's code, tests, and docs is 500 bps.
Plugging in real SIGIL / SEAL / facilitator services
Three protocols, three seams — implement one method each and pass the
object in (verifier=, platform=, facilitator=):
Verifier.verify(*, spec, spec_hash, result)— judge a delivered result against the spec committed before payment; fail closed ifsha256(spec) != spec_hash. A SIGIL service client drops in here.PlatformClient.open_escrow / release / refund— the SEAL settlement surface.LocalReferencePlatformis an in-memory ledger with settle-exactly-once semantics; a production client implements the same protocol against the real platform when its endpoint ships.FacilitatorClient.verify_and_hold(*, payment_payload, requirement)— x402 facilitator verification (e.g. Coinbase CDP).LocalFacilitatorchecks structure only; it does not verify EIP-712 signatures or touch a chain, and is labelled accordingly.
Tests
pip install pytest && python -m pytest # 46 tests, no network
Full loop with mocked platform + mocked facilitator: good result →
release; garbage → refund; timeout → refund; exception → refund; 40× retry
storms → exactly one payment; accepts[] rewrite attacks → rejected. Plus
the Hub engine's cross-implementation suite (needs the dev extra:
web3 + eth-tester + py-solc-x): Python-signed SEALs settling through the
compiled Solidity Hub on a local chain — canon 95/5 fee split,
isTaskVerified, and the Hub's own reverts (wrong signer →
InvalidSealSignature, expired → SealExpired, replay → InvalidNonce),
with the engine-refusal and allowance-preflight paths proven to never
reach the chain.
PyPI publish checklist (founder gate — do NOT publish without sign-off)
- Founder approves publishing under the
sincor-x402name - Confirm PyPI name availability / claim the name (and
sincor-mcpif used) - Bump
versioninpyproject.toml; tag the release commit -
python -m build→ inspect sdist + wheel contents (no stray files) - Upload to TestPyPI first; install from TestPyPI in a clean venv; run tests
- Verify README renders on the PyPI project page (honesty section intact)
- Publish to PyPI via a project-scoped API token stored in the vault — never a password, never a token in the repo
- Post-publish:
pip install sincor-x402in a clean venv and smoke-test the 2-line server + MCP examples from this README
License
Apache-2.0 (declared in pyproject.toml).
Metadata
Release files for sincor-x402 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| sincor_x402-0.1.0.tar.gz | 39.9 kB | With provenance | Details for sincor_x402-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| sincor_x402-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for sincor_x402-0.1.0-py3-none-any.whl |
Total release size: 72.2 kB
Release files / sincor_x402-0.1.0.tar.gz
| Download URL | sincor_x402-0.1.0.tar.gz |
|---|---|
| Size | 39.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a6a5c15782d047da5b225a5b883bd88a5f7377400422a28a0dd5eae425b852c1
|
|
BLAKE2b-256 checksum How to use checksums |
3afafa434c306ddbd52c3356b423029bf661dc19afe6f67dd1d12d2c31f99df7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.
Transparency logRelease files / sincor_x402-0.1.0-py3-none-any.whl
| Download URL | sincor_x402-0.1.0-py3-none-any.whl |
|---|---|
| Size | 32.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ebc5c528321ef7e826d73ccc7350a622d7761223ec2c2a50f4cd8f8446764b96
|
|
BLAKE2b-256 checksum How to use checksums |
4519a94ff029b8ad6ce429bd8fa5ad438a1e143756ab953fe95db9453d206582
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.
Transparency logSecurity for sincor-x402 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for sincor_x402-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.