The facilitator-side Bazaar discovery layer for x402 — the piece that does not exist in public code today — and the whole payment loop around it, running end to end on Stellar testnet.
You do not have to take any claim in this README on trust. Every one of them is checkable:
| Claim | How to check it |
|---|---|
| Payments really settle on Stellar | Open c1acc578… → successful: true |
| The buyer needs zero XLM — fees are sponsored | On that transaction, fee_account is the facilitator's FEEPAYER, not the payer. Concretely: on stellar.expert the transaction's source / fee account equals GC4E5Q6WQATXWA5FCL5OV7C7HVQUZOAGXVKNHNCIL2EDY3YNAGRLKADT, while the payer appears only inside the Soroban authorization entry / transfer event. All four account keys: docs/TESTNET-TXS.md |
| Catalog integrity is real, not decorative | npm test → 241 tests, 0 failing (70 of them adversarial) |
| Search quality is a number, not a plan | npm run eval:search → nDCG@10 0.864, Recall@20 0.905, MRR@10 0.920 over a 50-query graded set, with a regression gate in CI |
| We publish our own worst number | docs/LOAD-BASELINE.md — the same payments succeed 4/4 serially and 1/10 concurrently on today's single fee-payer. That gap is what Tranche 1 buys |
| It stays that way | CI runs the suite, the 49 conformance checks and the site build on Node 22 and 24, with a real Redis so nothing skips for want of one |
| Attacks are refused and named | Open the playground and press Try to break it: a replayed authorization, a corrupted signature and an inflated echoed price, each run against a freshly signed entry so the outcome is attributable. Two are refused; the third settles at the original price, because the echoed price is decoration |
| We caught the drift in ourselves | Our own seller advertised x402Version: 2 and answered 402 in the v1 wire format — a stock client broke against it while ours, which had a lenient fallback, did not. Found by pointing an unmodified @x402/fetch client at it, fixed, and now asserted every night. The whole story |
| You can actually run it | npm install && npm run setup — no captcha, no faucet, no API key |
| A developer can ship on it | docs/QUICKSTART-SELLER.md — clone → paid, discoverable endpoint. Every command timed with /usr/bin/time |
That last row is the one worth pausing on. Almost every x402-on-Stellar project requires a Circle faucet captcha and an OpenZeppelin Channels API key before it will start. This one requires neither, by design — see Two blockers removed.
Most x402 builds are variations on one idea: an agent paying for an API, a metered service, a channel-mode feed, a middleware kit. They are good examples. They are also built on ground that is already solved, and the SCF #45 RFP says so in plain language:
"settlement on Stellar is largely solved; the novel work is discovery, the agent facing interface, the upto scheme upstream, and conformance that holds as the spec moves."
So we did not build a payment demo. We built the part that is missing, then built the payment demo around it so you can watch the missing part work.
The bazaar extension spec defines /discovery/resources and /discovery/search |
✅ exists |
@x402/extensions/bazaar implements them |
❌ its own README states it ships only client and server helpers, and no facilitator-side catalog implementation |
| Stellar has a Bazaar | ❌ stellar/x402-stellar#50 — "Explore Bazaar support for Stellar" — open and unassigned since April 2026. The SDF repo's Dockerfile still reads bazaar not used |
An agent that can pay but cannot discover is an agent with a wallet and no map. STELLARSIGHT is the map.
Live at stellarsight.xyz. The same catalog that packages/index
serves on :4022 also deploys as Vercel functions, so the Bazaar is a public, hosted
endpoint any agent can call — which is what the RFP asks for, and what does not exist for
Stellar anywhere else. The facilitator answers on the same origin (/supported, /verify,
/settle) and so does a real paid API under /v1/*, which announces itself into the catalog:
discovery, payment and settlement are one deployment. Run the commands below and they answer.
| Method | Endpoint | What it does |
|---|---|---|
GET |
/discovery/resources |
Paginated catalog, with the spec's type, payTo, scheme, network, extensions, limit, offset filters |
GET |
/discovery/search |
Natural-language search. Results arrive under resources, with partialResults, pagination { limit, cursor }, and _explain per result |
GET |
/discovery/health |
Catalog mode, record counts, durable-store transport, and the commit being served |
GET |
/discovery/integrity |
The catalog-integrity ledger: a fixed hostile corpus replayed through the shipped validator. Labeled source: "replay" — these are verdicts, not observed traffic |
POST |
/discovery/resources |
Auto-cataloging. Requires Authorization: Bearer <STELLARSIGHT_WRITE_TOKEN> |
POST |
/playground/fund |
Grants the demo asset to a testnet account so a browser can pay. Per-account, per-IP and global daily caps; testnet is hardcoded |
GET |
/explorer/feed |
This deployment's settled payments, read from Horizon, each labeled with why it exists |
| any | /discovery/<anything else> |
404 JSON naming the endpoints that do exist — never HTML, never a silent 200 |
?seeded=false is the additive filter that answers "what here can I actually pay for?".
The catalog ships a 27-record demo corpus on .example hosts so the ranker has a realistic
spread to rank — completeness and freshness vary on purpose, which is what makes _explain
legible instead of constant. Every one of those records is flagged seeded: true and
pinned to settlements: 0, the flag survives the wire projection, and one query separates
them from the real ones. Hiding the corpus would hide the ranker; labelling it costs
nothing and is checkable.
CORS is *, because the point is for other people's agents to call it. Every rejection —
404 on an unknown path, 503 when writes are not enabled (no durable store, or a store
with no STELLARSIGHT_WRITE_TOKEN set), 401 when writes are enabled and the caller did
not present that token — carries a non-null, human-readable reason that names what to do
about it.
# Natural-language search over the catalog, ranked
curl -s 'https://stellarsight.xyz/discovery/search?query=invoice%20ocr&limit=3' | jq \
'.resources[] | {resource, score: ._score, name: .serviceName}'
# The full score breakdown on the top hit — BM25 / completeness / settlements / recency
curl -s 'https://stellarsight.xyz/discovery/search?query=convert%20dollars%20to%20reais&limit=1' \
| jq '.resources[0]._explain'
# List, with the spec filters. Note the envelopes differ deliberately: the list
# endpoint returns `items` with offset pagination, search returns `resources`
# with a cursor — that asymmetry is the spec's, not ours.
curl -s 'https://stellarsight.xyz/discovery/resources?type=mcp&limit=5' \
| jq '.total, .items[].resource'
# Which mode the catalog is in, how many records, which commit is serving them
curl -s https://stellarsight.xyz/discovery/health | jqReal output, at the time of writing:
$ curl -s 'https://stellarsight.xyz/discovery/search?query=invoice%20ocr&limit=3' …
0.7736 Invoice OCR
$ curl -s https://stellarsight.xyz/discovery/health …
mode=kv transport=redis records=30 writable=true commit=608080b
$ curl -s -o /dev/null -w '%{http_code} %{content_type}' https://stellarsight.xyz/discovery/nope
404 application/json; charset=utf-8
/discovery/health reports the commit it is serving, so a claim in this README can always
be checked against the code that is actually deployed.
Read that output as a snapshot, not a standing claim — it is the answer at the commit named
in it, and commit is there so you can tell whether what you are reading still describes
what is deployed. Run the command yourself for the current one.
mode: kv means a durable Redis store is attached, so auto-catalogued resources survive
cold starts. With no store configured the same code runs read-only from the seeded catalog
and says so on /discovery/health rather than failing. writable: false alongside kv is
the deliberate third state: the store makes writes possible, STELLARSIGHT_WRITE_TOKEN
makes them permitted, and an unauthenticated write endpoint on a public discovery index is
a spam magnet. See docs/DEPLOY.md for the reasoning.
Both endpoints are validated against the shipped @x402/extensions types by
npm run verify:api, which drives the real withBazaar() client at them and re-checks every
accepts entry with @x402/core's own PaymentRequirementsSchema. The two envelopes differ
deliberately, and that asymmetry is the spec's rather than ours: the list endpoint returns
items with offset pagination, search returns resources with a cursor.
That claim used to read "spec-exact — the same field names", asserted by reading the field
names this repo emits. It was false: search returned items, no item carried accepts, and
withBazaar(client).search() handed a stock consumer undefined. See
Where we had drifted. The assertion now observes what the client
returns instead of restating what the server believes.
CORS is * because the point is for other people's agents to call it.
The endpoints import packages/index directly; the ranking and the catalog-integrity
validation are the same code the local facilitator runs, not a reimplementation. Out of
the box the deployment serves a read-only catalog seeded at cold start. Attach a
Redis/KV store and a write token and the auto-cataloging write path turns on;
/discovery/health reports which of the two is active.
The Sight Board. Every result is a sight — the observation a navigator takes to fix
position. Numbered, ranked, with a bearing readout, and a _EXPLAIN disclosure that breaks
the score into BM25 / metadata completeness / settlements / recency, each with its numeric
contribution and the matched terms with their tf, idf and field weight. Searching
re-orders the board with a FLIP animation.
The Catalog Integrity ledger, on the right, is a replay, and the panel says so on its
own first line. A fixed hostile corpus is pushed through the real validator by
apps/web/scripts/gen-integrity.mjs at build time, and
every rule, verdict and reason it renders is a string that
createCatalog().upsert() actually returned — stamped with
the commit that produced it. It is evidence the validator works. It is not a claim that
anyone attacked the catalog today, and when the index does report live verdicts the panel
switches its label to say that instead.
REJECTED resource.url javascript:alert(1)
resource.url is missing or invalid
SOFT-DROP routeTemplate /v1/%252e%252e/thing
routeTemplate contains path traversal ".." after decoding
SOFT-DROP resource.iconUrl http://169.254.169.254/latest/meta-data/
iconUrl host rejected: IP literal host (decimal/octal/hex)
SOFT-DROP resource.tags[5]:over-limit ["invoice","inv","invoices", … 96 more]
99 tags submitted, catalog keeps 5 — overflow dropped to contain index pollution
That third and fourth line matter: the record survives. Soft drop means a hostile field is discarded and the legitimate metadata around it is kept — which is exactly what the spec requires and exactly the invariant that is easy to get wrong.
An earlier version of this section said the ledger was live and not a mockup. It was neither:
the rows were hand-written, seven of their eight rule names existed nowhere in
packages/index/src/integrity.mjs, and the caps they quoted (16 tags, 2,000 characters)
contradicted the ones the code enforces (5 and 512). The generator exists so that cannot
recur — the numbers above are now read back off the validator's own output rather than
restated, so a drift between doc and code shows up as a failing build artifact instead of a
sentence nobody rechecked. npm test runs the 70 adversarial cases the corpus is drawn from.
STELLARSIGHT is built against the RFP "X402 Facilitator with Bazaar (discovery) support", which names the Bazaar discovery layer as the highest-value part of the scope and says it should carry the largest share of the budget. Every component maps to a numbered requirement:
The full map is docs/RFP-ALIGNMENT.md — every mapped
requirement with its status and the artifact behind it, including the six §3.6 acceptance
criteria and an explicit note about what is not mapped. The rows below are the headline.
| RFP req. | In this repo | Status |
|---|---|---|
| 3.2 Bazaar discovery layer — "the core new capability", "the hardest part of the scope" | packages/index — /discovery/resources + /discovery/search readable by the stock @x402/extensions client, BM25 hybrid ranking with a published formula and per-result _explain, auto-cataloging from the discovery extension, soft-drop validation, EXTENSION-RESPONSES reporting |
Working |
| 3.2 catalog integrity — "the facilitator is a trust boundary" | 70 adversarial tests: routeTemplate traversal under single / double / triple percent-encoding, iconUrl SSRF evasion, tag flooding, external $ref |
70/70 passing |
| 3.1 Facilitator — verify / settle / supported, fee sponsorship, self-facilitation | apps/facilitator — self-hosted on Apache-2.0 @x402/stellar, extra.areFeesSponsored, non-null reason on every rejection |
Working, testnet |
| 3.3 Agent-facing MCP interface | apps/agent — 4 MCP tools with input and output schemas, 17-code error enum |
Settled payments via MCP |
| 3.6 Conformance — "drift, not inability, is the failure mode being screened for" | npm run verify:conformance — an unmodified @x402/fetch client driven through a real 402 → sign → settle → 200. It caught v1 drift in our own seller |
Documented below |
| 3.2 seller helpers — per-parameter descriptions that make an endpoint legible to an agent | apps/seller, declared via declareDiscoveryExtension |
Working |
| UX — "docs to a paid, discoverable endpoint appearing in the Bazaar in well under an hour" | docs/QUICKSTART-SELLER.md — four steps, each ending in a curl check. A resource is listed on seller boot and re-cataloged on settle, so it is discoverable before its first payment |
59s of commands, measured |
| 5.8 Two end-to-end example integrations | Both exist and run in CI: apps/seller is a paid API with three real routes that announces itself into the catalog, and apps/agent is an MCP agent that discovers and pays for a resource with no prior integration — the two examples the RFP names |
Working, testnet |
What we deliberately did not build, and why: no on-chain registry (the RFP itself calls
it an optional stretch and explains the rent/TTL cost and the doubled settlement cost); no
mainnet; no audit; no upto implementation — that scheme has an active design
discussion opened on 3 August 2026 that
deserves a considered answer rather than a rushed one.
The point is to leave behind a piece of public infrastructure the Stellar ecosystem is currently missing, permissively licensed, that anyone can fork and run.
seller ──declares metadata──► STELLARSIGHT INDEX ◄──natural-language search── agent
│ ▲ │
│ │ auto-cataloged on settle (bazaar ext) │
└──────────► SELF-HOSTED FACILITATOR ◄────── 402 → sign → settle ────────────┘
│
stellar:testnet
| Component | What it is |
|---|---|
packages/index |
Catalog + BM25 hybrid search with explainable ranking, catalog-integrity validation |
packages/express |
Drop-in x402 paywall middleware for Express: price a route, take payment in a Stellar token, and get listed in the bazaar before the first payment. 45 of the 241 tests are its. On npm: npm i @stellarsight/express |
api/discovery |
Vercel functions serving that same catalog as a public hosted API — no logic of their own |
apps/facilitator |
Self-hosted x402 facilitator on @x402/stellar, sponsoring network fees |
apps/seller |
Paid API declaring discovery metadata with per-parameter descriptions |
apps/agent |
MCP server + payment client + narrated CLI |
apps/web |
Landing page and live console |
The two things that normally stall an x402 setup on Stellar were eliminated — not by shortcut, but by decisions that are also architecturally better.
1. No faucet, no captcha. Rather than depending on Circle's web faucet for testnet USDC,
STELLARSIGHT issues its own SEP-41 asset (SXT) and wraps it in a SAC. The Stellar exact
scheme accepts any SEP-41 token — USDC is only the default. npm run setup therefore runs
start to finish with no web forms and no API keys.
2. No third-party facilitator. The facilitator is self-hosted on the Apache-2.0
package. That removes any dependency on the OpenZeppelin Relayer / OZ Channels — which is
AGPL-3.0-or-later, and therefore unusable by any project needing a permissive license —
while demonstrating the self-facilitation path the RFP asks for in 3.1. npm run audit:licenses proves the absence rather than asserting it: 191 production packages, all
permissive, zero unknown, and CI fails the build if that ever stops being true.
The FEEPAYER account sponsors network fees, so the paying agent needs zero XLM.
npm install
npm run setup # generates accounts, issues the SXT asset, adds trustlines — all testnet
npm run dev:all # facilitator :4021 · index :4022 · seller :4023
npm run dev:web # console + landing on :5173
npm run demo # full loop: discover → 402 → sign → settle → 200
npm test # 241 tests
npm run verify:api # 49 checks, incl. the stock withBazaar() client against the handlers
npm run verify:conformance # stock @x402/fetch client pays the seller, end to end
npm run eval:search # 50 graded queries -> nDCG@10 / Recall@20 / MRR, with a CI gate
npm run load:baseline # serial vs concurrent settlement, the single-fee-payer "before"No API keys. No captcha. No mainnet. No real money.
Listing your own paid endpoint — the seller side, timed step by step from a clean clone —
is docs/QUICKSTART-SELLER.md.
Deployment — including the routing trap where a SPA catch-all silently swallows
/discovery/* — is documented in docs/DEPLOY.md.
The RFP states that search quality is the hardest part of the scope and the part existing
catalogs most often leave unimplemented. So the ranking here is not a .includes() filter:
- BM25,
k1 = 1.2,b = 0.75, over a field-weighted document —serviceName×3,description×2,tags×2, parameter names and their per-parameter descriptions ×2,output.format×1, URL path segments ×1. - Blend:
1.00·bm25 + 0.12·completeness + 0.08·popularity + 0.05·recency. The quality prior caps at 0.25 against relevance's 1.00 — quality breaks ties, it never overrides relevance. A test asserts that a 900k-settlement record loses to a zero-settlement, 200-day-stale record when the query matches the latter. _explainper result, with the four parts asserted by test to sum exactly to_score.
The evaluation is no longer a plan. npm run eval:search runs a 50-query human-graded
golden set (eval/golden.jsonl) through the real ranker — the same
catalog.search that /discovery/search serves — and publishes:
| Metric | Value |
|---|---|
| nDCG@10 | 0.864 |
| Recall@20 | 0.905 |
| MRR@10 | 0.920 |
| Precision@1 | 0.896 |
| No-match silence | 0.500 |
Full method and the weakest queries: docs/SEARCH-EVAL.md.
CI fails the build if any of these falls more than 0.02 below
eval/baseline.json, so a ranking change that quietly costs
relevance cannot merge unnoticed — it either holds the numbers or updates the baseline
on purpose, in the diff.
Read them as what they are: a known-item measurement over the 27-record demo corpus, graded by the same person who wrote the ranker. Both facts inflate confidence and both are stated in the report rather than buried. The Tranche 1 deliverable replaces this with 150–200 queries plus a rolling sample drawn from the live catalog, which nobody here authored.
Two of the fifty queries have no right answer on purpose (quantum teleportation as a service). Half of them still return something — BM25 will match a stray token — and that
number is published as no-match silence 0.5 instead of quietly excluded. The two worst
real queries are will it rain tomorrow and logistics cost estimation, which return
nothing at all: pure paraphrases with zero lexical overlap, i.e. exactly the failure a
semantic layer fixes. That is the evidence behind the Tranche 2 deliverable, not a hunch.
docs/SEARCH-QUALITY.md documents the retrieval rationale and an
explicit cold-start section stating plainly that popularity is worthless at launch and
gameable forever — with four unimplemented mitigations ranked.
We built against the shipped dist rather than against examples, because on this spec the
two disagree. Below: two places where the surrounding material lags the spec, and one place
where we were the party that had drifted.
Neither of these is a defect in x402. Both match
specs/transports-v2/http.md
exactly — it is the third-party material around the spec that still shows v1 shapes. They
are listed because reading dist instead of trusting an example is what kept us on the
right side of them, not because anything upstream needs fixing.
- v2
PaymentRequirementsusesamount, notmaxAmountRequired, and resource metadata moved toPaymentRequired.resourceas aResourceInfo. The v1 layout is still what most examples show. Our facilitator and index read both shapes. - v2 signs into the
PAYMENT-SIGNATURErequest header, notX-PAYMENT.X-PAYMENTis the v1 spelling and is still what much of the surrounding documentation instructs. Our client sends both; our seller accepts both.
Our own reference seller advertised x402Version: 2 and then answered 402 in the v1 wire
format: the entire PaymentRequired object in the JSON body, no PAYMENT-REQUIRED header,
and a paywall that read only x-payment.
The spec is unambiguous — "The PAYMENT-REQUIRED header is the canonical HTTP transport
location for the PaymentRequired object", with response bodies called a server
implementation concern and the spec's own 402 example shipping {}. @x402/core implements
exactly that: getPaymentRequiredResponse falls back to the body only when
body.x402Version === 1. Ours said 2. So an unmodified @x402/fetch client did this:
THREW: Failed to parse payment requirements: Invalid payment required response
We had not noticed, because our own client carried a fallback that accepted any body with an
accepts array. Our agent could pay our seller. Nobody else's could.
We found it by pointing a stock client at ourselves — the acceptance test the RFP
specifies, and the only test that could have caught it. The fix: emit PAYMENT-REQUIRED and
PAYMENT-RESPONSE using @x402/core's own codecs, accept PAYMENT-SIGNATURE, and delete
the fallback in our client, so the bug cannot return quietly. The v1 spellings and the JSON
body are still emitted for backward compatibility; nothing depends on them.
npm run verify:conformance is that test, kept. It drives an unmodified @x402/fetch client
— wrapFetchWithPayment, no STELLARSIGHT code anywhere on the path — through a real
402 → sign → settle → 200 against a running seller, and prints the settled hash:
1. Unpaid probe — the 402 must carry a PAYMENT-REQUIRED header
PASS HTTP 402 Payment Required
PASS PAYMENT-REQUIRED decoded — x402Version 2, 1 requirement(s)
2. Stock client — wrapFetchWithPayment drives 402 -> sign -> settle -> 200
PASS HTTP 200 in 8903ms
3. Settlement receipt — PAYMENT-RESPONSE header
PASS PAYMENT-RESPONSE decoded, success=true
CONFORMANCE CHECK PASSED
tx 15c4fa24785ac42b1287d9336ad219552b07d7ff81cdf86c18edbc5c250e9726
An earlier version of this README framed the PAYMENT-REQUIRED requirement as an x402 defect
worth filing upstream. It was not — the SDK was right and we were wrong. Retracting that here
is cheaper than being corrected by a reviewer, and a conformance bug we found in ourselves,
with a stock-client test now standing over it, is the stronger story regardless.
The facilitator is a trust boundary. Clients echo the resource block back inside the
payment payload, so every discovery field is attacker-controlled.
routeTemplate— the normative regex^/[a-zA-Z0-9_/:.\-~%]+$permits%, so the..check must run after percent-decoding, and must survive double and triple encoding (%252e%252e). Malformed%fails closed.iconUrl— SSRF evasions:127.0.0.1, decimal2130706433,0x7f.1,0177.0.0.1,[::1],0.0.0.0,169.254.169.254, percent-encoded hosts, userinfo tricks, and thedata:/file:/javascript:schemes.serviceName/tags— control characters, RTL override, length caps, dedupe before cap, and the survival invariant: an invalid field is dropped, the surrounding metadata is kept.
Each test cites the spec rule it enforces.
Real hashes produced by this code, with explorer links:
docs/TESTNET-TXS.md.
108 in total, and the split matters: 100 are x402 payments and 8 are setup and cleanup (5 setup, 3 cleanup) — trustlines, the SAC deploy, minting the test asset, and returning a legacy balance. Only the payment rows are evidence that the payment path works.
The 100 also split by what produced them, because a settlement count without that is not
evidence of anything: 55 from npm run evidence:batch (a serial script paying our own
seller, prefixed load:), 23 from the demo loop, 22 from stock-client conformance runs.
Those counts are rewritten from the table itself by npm run evidence:build, which the
nightly runs and commits — the previous version of this paragraph claimed the same thing
while the script only ever touched docs/, so the README drifted three payments behind
within two days of saying it could not. None of it is third-party demand, and nothing here
is presented as such — the label map is
docs/status/provenance.json and the aggregate is
docs/EVIDENCE.md.
The full engineering document is docs/ARCHITECTURE.md: the Soroban
authorization-entry flow, SEP-41/SAC settlement, the fee-bump ceiling and how it was
derived, the catalog's trust boundary, the planned upto contract, the security model, the
monitoring plan, the deployment topology, and how each of those maps to a funded tranche.
Start there if you are evaluating this rather than running it.
Three things a reviewer will look for, answered here rather than in a support thread.
A settlement through the hosted facilitator writes the post-validation record to the
same durable store api/discovery/* reads. Before, the settle path only ever touched the
instance's own heap: a third-party seller could settle, watch EXTENSION-RESPONSES say
success, and never appear in the public catalog, because the instance that catalogued
them was gone. Auto-cataloging that does not outlive the process is a demo, not a Bazaar.
Durability is reported, never assumed — /health on the facilitator carries
durableCataloging: { enabled, transport }, and a store that rejects a write says so in
the settle response instead of silently claiming success. With no store configured the
behaviour is unchanged and the reason is stated.
docs/LOAD-BASELINE.md, produced by npm run load:baseline:
| Attempted | Succeeded | |
|---|---|---|
| Serial (one at a time) | 4 | 4 — 100% |
| Concurrent | 10 | 1 — 10% |
Same payment, same stack, same signer, same network. Only the timing changed. That is the
controlled experiment, and it is the only honest way to attribute the failures: upstream
@x402/stellar collapses a rejected submission into
settle_exact_stellar_transaction_submission_failed without surfacing the underlying
tx_bad_seq, so the message alone proves nothing. A channel-account pool is the first
funded deliverable, and this file is the "before" it has to beat.
docs/THREAT-MODEL.md— assets, trust boundaries, and thirteen threats each mapped to the control that answers it and the test that proves it. The gaps are marked as gaps: no per-seller identity yet, no alerting on fee-payer drain yet, sequence contention undefended.docs/MONITORING.md— the signal, threshold and response for each of those surfaces, with what exists today marked ✅ and what is funded work marked ⬜.docs/upto-position.md— the position onupto, written against stellar/x402-stellar#72. The design answer up front: ouruptodesign ships a dedicated Soroban settlement contract (settle_uptoviarequire_auth_for_args) — no admin, no persistent storage, never holds a balance. SEP-41 allowances alone cannot enforce recipient binding or single settlement, which is why a contract is required. Implementation follows whatever x402-foundation/x402#3134 converges on rather than a private variant of it.
Apache-2.0, public from the first commit.
github.com/pedro-pelicioni/stellarsight
Built in São Paulo, Brazil.