@stellar-agent-services/provider-kit 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +506 -2
  3. package/dist/approve/assets/index-BazIJAI2.css +1 -0
  4. package/dist/approve/assets/index-CL28cSJy.js +420 -0
  5. package/dist/approve/favicon.svg +1 -0
  6. package/dist/approve/index.html +18 -0
  7. package/dist/cli/approval-view.d.ts +80 -0
  8. package/dist/cli/approval-view.d.ts.map +1 -0
  9. package/dist/cli/approval-view.js +5 -0
  10. package/dist/cli/approval-view.js.map +1 -0
  11. package/dist/cli/approval.d.ts +132 -0
  12. package/dist/cli/approval.d.ts.map +1 -0
  13. package/dist/cli/approval.js +542 -0
  14. package/dist/cli/approval.js.map +1 -0
  15. package/dist/cli/check.d.ts +3 -0
  16. package/dist/cli/check.d.ts.map +1 -0
  17. package/dist/cli/check.js +137 -0
  18. package/dist/cli/check.js.map +1 -0
  19. package/dist/cli/dev-account.d.ts +28 -0
  20. package/dist/cli/dev-account.d.ts.map +1 -0
  21. package/dist/cli/dev-account.js +62 -0
  22. package/dist/cli/dev-account.js.map +1 -0
  23. package/dist/cli/dev.d.ts +27 -0
  24. package/dist/cli/dev.d.ts.map +1 -0
  25. package/dist/cli/dev.js +226 -0
  26. package/dist/cli/dev.js.map +1 -0
  27. package/dist/cli/discovery.d.ts +20 -0
  28. package/dist/cli/discovery.d.ts.map +1 -0
  29. package/dist/cli/discovery.js +202 -0
  30. package/dist/cli/discovery.js.map +1 -0
  31. package/dist/cli/env-file.d.ts +16 -0
  32. package/dist/cli/env-file.d.ts.map +1 -0
  33. package/dist/cli/env-file.js +77 -0
  34. package/dist/cli/env-file.js.map +1 -0
  35. package/dist/cli/fly.d.ts +28 -0
  36. package/dist/cli/fly.d.ts.map +1 -0
  37. package/dist/cli/fly.js +92 -0
  38. package/dist/cli/fly.js.map +1 -0
  39. package/dist/cli/index.d.ts +3 -0
  40. package/dist/cli/index.d.ts.map +1 -0
  41. package/dist/cli/index.js +11 -0
  42. package/dist/cli/index.js.map +1 -0
  43. package/dist/cli/init.d.ts +3 -0
  44. package/dist/cli/init.d.ts.map +1 -0
  45. package/dist/cli/init.js +170 -0
  46. package/dist/cli/init.js.map +1 -0
  47. package/dist/cli/launch.d.ts +21 -0
  48. package/dist/cli/launch.d.ts.map +1 -0
  49. package/dist/cli/launch.js +627 -0
  50. package/dist/cli/launch.js.map +1 -0
  51. package/dist/cli/main.d.ts +2 -0
  52. package/dist/cli/main.d.ts.map +1 -0
  53. package/dist/cli/main.js +158 -0
  54. package/dist/cli/main.js.map +1 -0
  55. package/dist/cli/owner-chain.d.ts +88 -0
  56. package/dist/cli/owner-chain.d.ts.map +1 -0
  57. package/dist/cli/owner-chain.js +284 -0
  58. package/dist/cli/owner-chain.js.map +1 -0
  59. package/dist/cli/owner-steps.d.ts +33 -0
  60. package/dist/cli/owner-steps.d.ts.map +1 -0
  61. package/dist/cli/owner-steps.js +121 -0
  62. package/dist/cli/owner-steps.js.map +1 -0
  63. package/dist/cli/processes.d.ts +47 -0
  64. package/dist/cli/processes.d.ts.map +1 -0
  65. package/dist/cli/processes.js +129 -0
  66. package/dist/cli/processes.js.map +1 -0
  67. package/dist/cli/reconcile.d.ts +3 -0
  68. package/dist/cli/reconcile.d.ts.map +1 -0
  69. package/dist/cli/reconcile.js +76 -0
  70. package/dist/cli/reconcile.js.map +1 -0
  71. package/dist/cli/register.d.ts +9 -0
  72. package/dist/cli/register.d.ts.map +1 -0
  73. package/dist/cli/register.js +209 -0
  74. package/dist/cli/register.js.map +1 -0
  75. package/dist/cli/request-validation.d.ts +5 -0
  76. package/dist/cli/request-validation.d.ts.map +1 -0
  77. package/dist/cli/request-validation.js +144 -0
  78. package/dist/cli/request-validation.js.map +1 -0
  79. package/dist/cli/serve.d.ts +25 -0
  80. package/dist/cli/serve.d.ts.map +1 -0
  81. package/dist/cli/serve.js +73 -0
  82. package/dist/cli/serve.js.map +1 -0
  83. package/dist/cli/shared.d.ts +142 -0
  84. package/dist/cli/shared.d.ts.map +1 -0
  85. package/dist/cli/shared.js +110 -0
  86. package/dist/cli/shared.js.map +1 -0
  87. package/dist/cli/status.d.ts +32 -0
  88. package/dist/cli/status.d.ts.map +1 -0
  89. package/dist/cli/status.js +248 -0
  90. package/dist/cli/status.js.map +1 -0
  91. package/dist/cli/target.d.ts +14 -0
  92. package/dist/cli/target.d.ts.map +1 -0
  93. package/dist/cli/target.js +43 -0
  94. package/dist/cli/target.js.map +1 -0
  95. package/dist/cli/trustline.d.ts +3 -0
  96. package/dist/cli/trustline.d.ts.map +1 -0
  97. package/dist/cli/trustline.js +114 -0
  98. package/dist/cli/trustline.js.map +1 -0
  99. package/dist/cli/try.d.ts +54 -0
  100. package/dist/cli/try.d.ts.map +1 -0
  101. package/dist/cli/try.js +448 -0
  102. package/dist/cli/try.js.map +1 -0
  103. package/dist/cli/tunnel.d.ts +13 -0
  104. package/dist/cli/tunnel.d.ts.map +1 -0
  105. package/dist/cli/tunnel.js +43 -0
  106. package/dist/cli/tunnel.js.map +1 -0
  107. package/dist/cli/validators.d.ts +6 -0
  108. package/dist/cli/validators.d.ts.map +1 -0
  109. package/dist/cli/validators.js +14 -0
  110. package/dist/cli/validators.js.map +1 -0
  111. package/dist/define-service.d.ts +71 -0
  112. package/dist/define-service.d.ts.map +1 -0
  113. package/dist/define-service.js +86 -0
  114. package/dist/define-service.js.map +1 -0
  115. package/dist/gateway/config.d.ts +88 -0
  116. package/dist/gateway/config.d.ts.map +1 -0
  117. package/dist/gateway/config.js +201 -0
  118. package/dist/gateway/config.js.map +1 -0
  119. package/dist/gateway/openapi.d.ts +69 -0
  120. package/dist/gateway/openapi.d.ts.map +1 -0
  121. package/dist/gateway/openapi.js +677 -0
  122. package/dist/gateway/openapi.js.map +1 -0
  123. package/dist/gateway/upstream.d.ts +15 -0
  124. package/dist/gateway/upstream.d.ts.map +1 -0
  125. package/dist/gateway/upstream.js +95 -0
  126. package/dist/gateway/upstream.js.map +1 -0
  127. package/dist/http/bounded.d.ts +3 -0
  128. package/dist/http/bounded.d.ts.map +1 -0
  129. package/dist/http/bounded.js +23 -0
  130. package/dist/http/bounded.js.map +1 -0
  131. package/dist/http/node-server.d.ts +15 -0
  132. package/dist/http/node-server.d.ts.map +1 -0
  133. package/dist/http/node-server.js +120 -0
  134. package/dist/http/node-server.js.map +1 -0
  135. package/dist/http/rate-limit.d.ts +11 -0
  136. package/dist/http/rate-limit.d.ts.map +1 -0
  137. package/dist/http/rate-limit.js +35 -0
  138. package/dist/http/rate-limit.js.map +1 -0
  139. package/dist/http/router.d.ts +29 -0
  140. package/dist/http/router.d.ts.map +1 -0
  141. package/dist/http/router.js +329 -0
  142. package/dist/http/router.js.map +1 -0
  143. package/dist/index.d.ts +20 -0
  144. package/dist/index.d.ts.map +1 -0
  145. package/dist/index.js +31 -0
  146. package/dist/index.js.map +1 -0
  147. package/dist/jobs/memory-store.d.ts +3 -0
  148. package/dist/jobs/memory-store.d.ts.map +1 -0
  149. package/dist/jobs/memory-store.js +121 -0
  150. package/dist/jobs/memory-store.js.map +1 -0
  151. package/dist/jobs/reconcile.d.ts +22 -0
  152. package/dist/jobs/reconcile.d.ts.map +1 -0
  153. package/dist/jobs/reconcile.js +82 -0
  154. package/dist/jobs/reconcile.js.map +1 -0
  155. package/dist/jobs/recovery.d.ts +19 -0
  156. package/dist/jobs/recovery.d.ts.map +1 -0
  157. package/dist/jobs/recovery.js +65 -0
  158. package/dist/jobs/recovery.js.map +1 -0
  159. package/dist/jobs/runner.d.ts +38 -0
  160. package/dist/jobs/runner.d.ts.map +1 -0
  161. package/dist/jobs/runner.js +194 -0
  162. package/dist/jobs/runner.js.map +1 -0
  163. package/dist/jobs/scheduler.d.ts +66 -0
  164. package/dist/jobs/scheduler.d.ts.map +1 -0
  165. package/dist/jobs/scheduler.js +197 -0
  166. package/dist/jobs/scheduler.js.map +1 -0
  167. package/dist/jobs/sqlite-store.d.ts +8 -0
  168. package/dist/jobs/sqlite-store.d.ts.map +1 -0
  169. package/dist/jobs/sqlite-store.js +231 -0
  170. package/dist/jobs/sqlite-store.js.map +1 -0
  171. package/dist/jobs/store.d.ts +118 -0
  172. package/dist/jobs/store.d.ts.map +1 -0
  173. package/dist/jobs/store.js +36 -0
  174. package/dist/jobs/store.js.map +1 -0
  175. package/dist/manifest.d.ts +11 -0
  176. package/dist/manifest.d.ts.map +1 -0
  177. package/dist/manifest.js +99 -0
  178. package/dist/manifest.js.map +1 -0
  179. package/dist/payments/chain.d.ts +23 -0
  180. package/dist/payments/chain.d.ts.map +1 -0
  181. package/dist/payments/chain.js +108 -0
  182. package/dist/payments/chain.js.map +1 -0
  183. package/dist/payments/facilitator.d.ts +44 -0
  184. package/dist/payments/facilitator.d.ts.map +1 -0
  185. package/dist/payments/facilitator.js +125 -0
  186. package/dist/payments/facilitator.js.map +1 -0
  187. package/dist/payments/onchain.d.ts +36 -0
  188. package/dist/payments/onchain.d.ts.map +1 -0
  189. package/dist/payments/onchain.js +191 -0
  190. package/dist/payments/onchain.js.map +1 -0
  191. package/dist/payments/payload-checks.d.ts +22 -0
  192. package/dist/payments/payload-checks.d.ts.map +1 -0
  193. package/dist/payments/payload-checks.js +113 -0
  194. package/dist/payments/payload-checks.js.map +1 -0
  195. package/dist/payments/settlement.d.ts +22 -0
  196. package/dist/payments/settlement.d.ts.map +1 -0
  197. package/dist/payments/settlement.js +65 -0
  198. package/dist/payments/settlement.js.map +1 -0
  199. package/dist/payments/x402-server.d.ts +8 -0
  200. package/dist/payments/x402-server.d.ts.map +1 -0
  201. package/dist/payments/x402-server.js +74 -0
  202. package/dist/payments/x402-server.js.map +1 -0
  203. package/dist/provider.d.ts +63 -0
  204. package/dist/provider.d.ts.map +1 -0
  205. package/dist/provider.js +177 -0
  206. package/dist/provider.js.map +1 -0
  207. package/dist/registration.d.ts +86 -0
  208. package/dist/registration.d.ts.map +1 -0
  209. package/dist/registration.js +162 -0
  210. package/dist/registration.js.map +1 -0
  211. package/dist/testing.d.ts +13 -0
  212. package/dist/testing.d.ts.map +1 -0
  213. package/dist/testing.js +48 -0
  214. package/dist/testing.js.map +1 -0
  215. package/dist/validation-request.d.ts +36 -0
  216. package/dist/validation-request.d.ts.map +1 -0
  217. package/dist/validation-request.js +101 -0
  218. package/dist/validation-request.js.map +1 -0
  219. package/package.json +55 -4
  220. package/schemas/gateway-config.schema.json +140 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stellar Agent Services contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,507 @@
1
- # Temporary Holding Version
1
+ # @stellar-agent-services/provider-kit
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Turn a function, or an existing HTTP API in any language, into a paid Stellar service that follows
4
+ [Provider Standard v1](../../docs/provider-standard.md). The kit covers:
5
+
6
+ - the service description (manifest), checked against the standard before the server starts
7
+ - pricing and the x402 USDC payment, with the payment checked locally before the facilitator sees it
8
+ - job recovery: a client that lost its response collects the result without paying again
9
+ - capacity: a queue per service, and `429`/`503` before payment when it is full
10
+ - durable jobs: paid jobs survive restarts, and workers can run in separate processes
11
+ - the CLI from nothing to live: run it with no wallet, pay it once, then register, verify and deploy it in one go
12
+
13
+ Two ways to use it:
14
+
15
+ ```text
16
+ library: defineService(...) -> createProvider(...) -> { fetch(request) } -> Hono / Node / Workers / Deno / Bun
17
+ gateway: provider-kit serve --config <file> -> forwards paid jobs to your existing API (also as a Docker image)
18
+ ```
19
+
20
+ - The provider runtime holds **no Stellar secret keys**; it only knows its `payTo` public address.
21
+ - Owner operations (the USDC trustline, 8004 registration, validation requests) run from the CLI and are
22
+ approved in your own wallet, on a page the CLI opens on your computer. See [Approving in your wallet](#approving-in-your-wallet).
23
+
24
+ The step-by-step guide is [docs/provider-setup-guide.md](../../docs/provider-setup-guide.md).
25
+
26
+ Installed from npm into a project of your own, add the override this repository has, which `create-stellar-service`
27
+ also writes, to `package.json`: `"overrides": { "@trionlabs/stellar8004": { "@stellar/stellar-sdk": "16.4.1" } }`.
28
+ The Stellar 8004 SDK the kit uses for registration asks for Stellar SDK 15. Without the override, npm installs a second
29
+ SDK for it: everything still works, but the copy adds 18 MB and an axios with known vulnerabilities.
30
+
31
+ ## From nothing to live
32
+
33
+ ```bash
34
+ provider-kit dev --config provider.config.json # runs it here, paid to a throwaway testnet account: no wallet yet
35
+ provider-kit try # pays for one call with test money (starts the provider if needed)
36
+ provider-kit launch --config provider.config.json # one page in your wallet: trustline, registration, verification
37
+ provider-kit status # where it stands, and the one command to run next
38
+ ```
39
+
40
+ - **`dev`** reads `.env`. With `PAY_TO` empty, it makes a [throwaway payout account](#the-throwaway-payout-account),
41
+ so nothing needs a wallet until the service goes live. `--with '<command>'` runs the API a gateway calls next to
42
+ it, and `-- <command>` runs any other provider, such as `provider-kit dev -- node dist/server.js`. `--tunnel` also
43
+ serves it at a temporary public HTTPS address (cloudflared).
44
+ - **`launch`** takes the public address from `--url`, `fly.toml` or an `https://` `PUBLIC_ORIGIN`, builds the
45
+ manifest that address will serve, and opens one approval page for every owner step. It saves `PAY_TO` and
46
+ `AGENT_ID`, deploys with `flyctl` when there is a `fly.toml` and you agree, and waits for the public manifest. See
47
+ [Launching](#launching).
48
+ - **`status`** reads this computer, the chain and the public address: running here, paid once, registered, live,
49
+ verified. `--json` prints it for scripts and coding agents.
50
+
51
+ `npm create stellar-service` makes a project with these as npm scripts ([create-stellar-service](../create-stellar-service)).
52
+
53
+ ## Library in short
54
+
55
+ ```ts
56
+ import { createProvider, defineService, sqliteStore } from "@stellar-agent-services/provider-kit";
57
+
58
+ const textStats = defineService({
59
+ name: "text-stats",
60
+ description: "Counts the words in a text.",
61
+ price: "0.01", // USDC
62
+ charge: "on-success", // charged only if the handler succeeds
63
+ expectedDurationSeconds: 1,
64
+ inputSchema: { type: "object", required: ["text"], properties: { text: { type: "string" } } },
65
+ outputSchema: { type: "object", required: ["words"], properties: { words: { type: "integer" } } },
66
+ handler: ({ text }: { text: string }) => ({ words: text.split(/\s+/).filter(Boolean).length }),
67
+ });
68
+
69
+ const provider = createProvider({
70
+ baseUrl: "https://api.example.com",
71
+ network: "stellar:testnet",
72
+ payTo: "G...", // public address only
73
+ facilitator: "https://x402.org/facilitator",
74
+ provider: { name: "Example provider" },
75
+ services: [textStats],
76
+ store: sqliteStore("./jobs.sqlite"),
77
+ });
78
+
79
+ await provider.ready(); // checks the facilitator, resumes jobs a restart left behind
80
+ // Mount provider.fetch on any Fetch-API server, or use the kit's own: await listen(provider, { port: 8787 })
81
+ ```
82
+
83
+ `createProvider()` throws if the manifest it builds would fail the standard's checks (the JSON Schema and
84
+ rules M1–P11, examples against their schemas), so a broken provider never takes a payment. The manifest is
85
+ served at `/.well-known/stellar-agent-services.json`. Create-job, check-status and get-result are under
86
+ `baseUrl`, with CORS open for browser clients.
87
+
88
+ ### When the user is charged
89
+
90
+ | `charge` | Order | If the work fails |
91
+ | --- | --- | --- |
92
+ | `on-success` | verify payment → work → settle | Not charged. The work must finish before the signed payment expires, so keep it under half of `maxTimeoutSeconds` (default 60). |
93
+ | `upfront` (default) | verify → settle → queue → work | Charged. Use it for long or expensive work. |
94
+
95
+ A handler gets `{ signal, jobId }`. `signal` aborts when the job runs out of time (`timeoutSeconds`, default
96
+ twice `expectedDurationSeconds`). An `upfront` job whose worker died is run again, so a handler with side
97
+ effects can use `jobId` to do them once.
98
+
99
+ ## Gateway mode
100
+
101
+ For an API that already exists, in any language, write a config file instead of code:
102
+
103
+ ```json
104
+ {
105
+ "$schema": "./node_modules/@stellar-agent-services/provider-kit/schemas/gateway-config.schema.json",
106
+ "baseUrl": "https://api.example.com",
107
+ "network": "stellar:testnet",
108
+ "payTo": "env:PAY_TO",
109
+ "facilitator": "https://x402.org/facilitator",
110
+ "provider": { "name": "Example provider" },
111
+ "store": "./jobs.sqlite",
112
+ "services": [
113
+ {
114
+ "name": "summarize",
115
+ "description": "Summarizes English text up to 20,000 characters.",
116
+ "price": "0.05",
117
+ "charge": "upfront",
118
+ "expectedDurationSeconds": 20,
119
+ "inputSchema": "./schemas/summarize.input.json",
120
+ "outputSchema": "./schemas/summarize.output.json",
121
+ "capacity": { "concurrency": 2, "maxQueueWaitSeconds": 120 },
122
+ "upstream": { "url": "http://127.0.0.1:5000/summarize", "headers": { "Authorization": "env:UPSTREAM_TOKEN" } }
123
+ }
124
+ ]
125
+ }
126
+ ```
127
+
128
+ ```bash
129
+ PAY_TO=G... provider-kit serve --config provider.config.json
130
+ ```
131
+
132
+ - The gateway POSTs the validated input to `upstream.url` as JSON, with `Idempotency-Key: <job id>`. A 2xx
133
+ JSON object is the output, checked against `outputSchema`. The upstream never sees payment data or job
134
+ tokens.
135
+ - Busy upstreams (`429`, `502`–`504`, connection errors) are retried within the job's time, honoring
136
+ `Retry-After`. Set `capacity.concurrency` to what the upstream takes.
137
+ - Values written `"env:NAME"` come from the environment: the upstream's API key, `payTo`, `baseUrl`.
138
+ - [examples/python-upstream](../../examples/python-upstream) is a Python API behind the gateway.
139
+
140
+ **Docker:** each release publishes the gateway as `ghcr.io/algorialabs/provider-kit:<version>`, such as `0.1.0`
141
+ (`docker build -f packages/provider-kit/Dockerfile .` from a clone builds the same image). Mount the config at
142
+ `/config/provider.config.json` and a volume at `/data` (set `"store": "/data/jobs.sqlite"`). `GET /healthz` answers
143
+ for health checks.
144
+
145
+ ### From an OpenAPI document
146
+
147
+ An API with an OpenAPI 3.0, 3.1 or 3.2 document doesn't need its config written by hand:
148
+
149
+ ```bash
150
+ provider-kit init --openapi http://127.0.0.1:8000/openapi.json # FastAPI's; or a .json or .yaml file
151
+ ```
152
+
153
+ ```text
154
+ Wrote provider.config.json with 4 services:
155
+ summarize POST http://127.0.0.1:8000/summarize
156
+ translate POST http://127.0.0.1:8000/v1/translate with X-API-Key
157
+ …
158
+ Skipped 7 operations:
159
+ GET /health the gateway calls the API with POST
160
+ POST /documents/{document_id}/summarize it has a path parameter ({document_id}); the gateway calls one fixed URL
161
+ POST /upload it takes multipart/form-data, not JSON
162
+ …
163
+ ✓ The config passes Provider Standard v1, checked as provider-kit serve checks it.
164
+ ```
165
+
166
+ - **Which operations:** each `POST` that takes a JSON object (or no body) and answers `2xx` with a JSON object, and
167
+ has no path parameters and no required query, header or cookie parameters: what the gateway can call. Every other
168
+ operation is listed with the reason. A provider has at most 25 services.
169
+ - **Schemas** become JSON Schema 2020-12 [S4]–[S6]: the body's own schema at the root, the components it uses under
170
+ `$defs`, recursion included. OpenAPI 3.0's `nullable`, `example` and boolean `exclusiveMinimum`/`exclusiveMaximum`
171
+ are rewritten, `readOnly` properties leave the input and `writeOnly` ones the output.
172
+ - **From the document:** names from paths (`/v1/text/summarize` is `text-summarize`), titles from summaries,
173
+ descriptions from descriptions (`init` lists the services that have none), tags, and an example when the document
174
+ has a matching input and output.
175
+ - **The API's address:** `--upstream`, else the document's server, else where the document was read from.
176
+ - **The API's own credentials** (an API key header, bearer or basic) become an `upstream.headers` entry read from
177
+ the environment, such as `"X-API-Key": "env:UPSTREAM_X_API_KEY"`. Credentials that can't be a fixed header
178
+ (OAuth2, cookies) get a warning to set by hand.
179
+ - **Checked as `serve` checks it:** a service the kit would refuse, such as a pattern JavaScript can't compile or a
180
+ schema over 32 KiB [S6], is skipped with the problem, so the config always loads.
181
+
182
+ Price (`0.01`), charge (`upfront`), duration and capacity are first guesses: set them before you serve.
183
+
184
+ ## CLI
185
+
186
+ | Command | What it does |
187
+ | --- | --- |
188
+ | `provider-kit dev [--config <file>] [--with '<command>'] [--tunnel] [-- <command> ...]` | Runs a provider on this computer, on testnet: a gateway config (default `./provider.config.json`) in dev mode, or your provider's own command after `--`. Reads `.env`. With `PAY_TO` empty, makes a [throwaway payout account](#the-throwaway-payout-account). `--with` starts the API the gateway calls and waits for it to take connections; `--tunnel` adds a temporary public HTTPS address. |
189
+ | `provider-kit launch [--config <file> \| --start '<command>' \| --manifest <url>] [--url <https origin>]` | Takes it live: one approval page for the trustline, the 8004 registration (or its update) and a validation request, then saves `PAY_TO` and `AGENT_ID`, deploys with `flyctl` (`--deploy`, `--no-deploy`) and waits for the public manifest (`--no-wait` skips it). `--validator <G…>` names the verifier, `--no-validation` asks none. See [Launching](#launching). |
190
+ | `provider-kit status [--config <file>] [--url <https origin>] [--json]` | Where the provider stands: running here, payTo, paid once, registered [I1]–[I3], live, verified. Names the one command to run next. Reads only. |
191
+ | `provider-kit serve --config <file>` | Gateway mode. Graceful shutdown on SIGTERM. `--no-workers` for HTTP-only processes. |
192
+ | `provider-kit init [--upstream <url>] [--openapi <file \| url>]` | Writes a starter gateway config. With `--openapi`, writes one from an OpenAPI 3 document: see [From an OpenAPI document](#from-an-openapi-document). `--json` prints what it wrote and skipped as JSON. |
193
+ | `provider-kit check (--config <file> \| --manifest <url>)` | Self-check without paying: manifest, facilitator, `payTo` trustline [P2], 8004 registration [I1]–[I3]. A provider on this computer that isn't registered yet passes with a note. Exits 1 on any failure. `--manifest` works for any provider, library or native. Then shows [how agents see you](#how-agents-see-you). `--fix` writes the words search would miss into the config's `provider.description`; `--json` prints the result as JSON. |
194
+ | `provider-kit try [<service>] ['<input JSON>' \| @<file>]` | One paid call on testnet, the way any client makes it. Starts the provider for the call when nothing answers. See [Trying a paid call](#trying-a-paid-call). |
195
+ | `provider-kit trustline [--config \| --manifest \| --network <testnet\|mainnet>]` | Adds a USDC trustline to `payTo` [P2], approved in its wallet. A new testnet account gets test XLM from Friendbot first. Without `--config` or `--manifest`, it sets up the account the wallet connects and prints it as `PAY_TO`. Signs nothing when the trustline exists. |
196
+ | `provider-kit register (--config \| --manifest)` | Registers in the Stellar 8004 identity registry from the `payTo` account, or updates the registration from the agent's owner, approved in the wallet. Adds the USDC trustline first when it is missing, and saves the new `agentId` to `--config` [I2]. |
197
+ | `provider-kit request-validation (--config \| --manifest) --validator <G…>` | Asks a verifier for validation in the Stellar 8004 Validation Registry. Checks first that a verifier can validate the provider ([I1]–[I3]), that the connected account may ask for the agent, and that the validator is not the provider itself. Puts the request document on chain as a `data:` URI, has the owner's wallet sign it and prints the request hash for the verifier. `--out` writes the request document. |
198
+ | `provider-kit reconcile (--config <file> \| --store <file> --network <net>)` | Decides `payment_uncertain` jobs from on-chain data [J13]. `--dry-run` only reports; `--rpc <url>` picks the Soroban RPC. It reads the chain, writes only the job store and signs nothing, so it is safe next to a running provider. Exits 1 while a job is undecided. |
199
+
200
+ `launch` runs what `trustline`, `register` and `request-validation` run one at a time. Those three take
201
+ `--source <key>` to sign with the [Stellar CLI](https://developers.stellar.org/docs/tools/cli) keychain instead of a
202
+ wallet, for scripts and CI. With `--source`, `--dry-run` prints the Stellar CLI command. `--rpc <url>` picks the
203
+ Soroban RPC.
204
+
205
+ ### The throwaway payout account
206
+
207
+ A provider has to be paid to an account that holds a USDC trustline [P2]. During development that needn't be yours:
208
+ with `PAY_TO` empty, `provider-kit dev` (and `try`, when it starts a gateway) makes a new testnet account, gets it
209
+ test XLM from Friendbot and signs its trustline with a key that lives in memory only. Then the key is dropped. It is
210
+ never written to a file or the environment, and the provider never sees it: like any provider, it knows only the
211
+ public address.
212
+
213
+ The address goes in `.env` as `PAY_TO`, and as `DEV_PAY_TO`, so `launch` can tell it from an account of yours and
214
+ replace it. What it receives is test money nobody can spend. It is never made on Mainnet.
215
+
216
+ ### Launching
217
+
218
+ ```bash
219
+ provider-kit launch --config provider.config.json # a gateway, its address from fly.toml
220
+ provider-kit launch --start 'node dist/server.js' --url https://api.example.com
221
+ ```
222
+
223
+ ```text
224
+ Launching Text Tools on Stellar Testnet
225
+ manifest https://text-tools.fly.dev/.well-known/stellar-agent-services.json
226
+ services summarize, translate
227
+
228
+ Approve in your wallet
229
+ http://localhost:8790/#…
230
+ ✓ Connected GBQX…7KQM (Freighter)
231
+ ✓ Get test XLM: funded by Friendbot
232
+ ✓ Add USDC: USDC trustline added · https://stellar.expert/explorer/testnet/tx/…
233
+ ✓ Register: agent 42 · https://stellar.expert/explorer/testnet/tx/…
234
+ ✓ Request validation: request 3f0a9c41d2e7… · https://stellar.expert/explorer/testnet/tx/…
235
+ ✓ Saved PAY_TO=GBQX…, AGENT_ID=42 in .env
236
+ ✓ Saved PUBLIC_ORIGIN, PAY_TO, AGENT_ID in fly.toml's [env], for every deploy
237
+
238
+ ✓ Text Tools is agent 42 on Stellar Testnet, paid to GBQX…7KQM
239
+ ✓ Validation requested from GBRP…OX2H (request 3f0a9c41d2e7…): its answer lands in the Validation Registry
240
+
241
+ Deploy to Fly.io now (fly deploy, app text-tools)? [Y/n]
242
+ ```
243
+
244
+ - **The address before the deploy.** `--url`, else `fly.toml`'s app (`https://<app>.fly.dev`), else an `https://`
245
+ `PUBLIC_ORIGIN`. A quick tunnel's address is refused: it changes every run.
246
+ - **The manifest that address will serve**, checked against the standard: from the gateway config, from your
247
+ provider's own command (`--start`, run on a free port with `PUBLIC_ORIGIN`, `PAY_TO` and a throwaway job database),
248
+ or from a deployed provider (`--manifest`). Until a wallet connects, a stand-in `payTo` builds it: the registration
249
+ doesn't name `payTo`, and the registry makes the registering account the agent's wallet [I3].
250
+ - **One page.** The steps the chain still needs, in order: test XLM, the trustline, `register_with_uri` (or
251
+ `set_agent_uri` when the manifest moved), and `validation_request`, built once the agent id is known. The verifier
252
+ is `--validator`, `STELLAR_VALIDATOR`, or the default in `src/cli/validators.ts`; a provider's own account is never
253
+ asked.
254
+ - **Saved.** `PAY_TO` and `AGENT_ID` in `.env` (`DEV_PAY_TO` goes), the agent id where the gateway config reads it,
255
+ and the public settings in `fly.toml`. For a provider started by its own command, that includes `STELLAR_NETWORK`
256
+ (and `FACILITATOR_URL` when set), so it takes payments where it registered. A new agent's id is saved even if a later
257
+ step stops, so running `launch` again never registers a second agent; it signs nothing when everything is in place.
258
+ - **Deployed and checked.** `flyctl` creates the app the first time, and what may hold a key is staged as Fly secrets
259
+ from `.env`: every variable a gateway config reads, such as the API's key (one that isn't set is named, since the
260
+ gateway can't start without it), or a command's `FACILITATOR_AUTH` and `SOROBAN_RPC_URL`. `fly deploy` runs with one
261
+ machine. Then `launch` waits until the public manifest serves the agent id, on the network it registered on, and
262
+ checks [I1]–[I3] from outside. A gateway whose `upstream.url` is on 127.0.0.1 isn't deployed: in a container
263
+ that is the container itself, unless the API runs inside it too, as the Python kind's does.
264
+ - **Mainnet.** The same steps, on `stellar:pubnet`: the wallet's account needs a few XLM, as no Friendbot funds it.
265
+ Mainnet's facilitator, OpenZeppelin's (`https://channels.openzeppelin.com/x402`), wants an API key: a gateway config
266
+ sends it with `"facilitatorHeaders": { "Authorization": "env:FACILITATOR_AUTH" }`. The provider checks it at start,
267
+ and a 401 says so. `npm create stellar-service -- --network mainnet` sets all of this up
268
+ ([Mainnet](../create-stellar-service/README.md#mainnet)).
269
+
270
+ ### Trying a paid call
271
+
272
+ `provider-kit try` pays for one job on testnet, as a client would, so you see the whole flow work before anyone
273
+ else pays:
274
+
275
+ ```bash
276
+ provider-kit try text-stats # the service's example input
277
+ provider-kit try text-stats '{"text":"Hello"}' # or your own, or @input.json
278
+ ```
279
+
280
+ ```text
281
+ ✓ Test XLM from Friendbot
282
+ ✓ Test USDC from the testnet DEX: 0.01 USDC
283
+ ✓ text-stats succeeded: 10.2 s from payment to result
284
+ ✓ Paid 0.01 USDC, confirmed on chain: https://stellar.expert/explorer/testnet/tx/0051061a…cda9
285
+
286
+ {
287
+ "words": 2,
288
+ "sentences": 1,
289
+ "characters": 12
290
+ }
291
+
292
+ GDJB…XA3O (payTo) received 0.01 USDC.
293
+ ```
294
+
295
+ - **The provider:** `--manifest <url>` for any provider, or `--config` for a gateway, read for its `baseUrl` only.
296
+ Without either, `./provider.config.json` when it exists, else `http://localhost:8787`. With several services and
297
+ none named, it lists them.
298
+ - **Not running?** When nothing answers, `try` starts the provider for the call and stops it after: the gateway
299
+ config in its own process (with a throwaway `payTo` when `PAY_TO` is empty), or `--start '<command>'`, such as
300
+ `--start 'npm run dev'`.
301
+ - **The payer:** a throwaway test account, made for this call. It gets test XLM from Friendbot and exactly the price
302
+ in test USDC from the testnet DEX. Its key stays in memory and is never written anywhere. You need no payer wallet.
303
+ - **The payment:** the payment client's own flow, with every check. The exact price, payee and network are shown and
304
+ approved first (`--yes` skips the question, for scripts), and its spending policy allows that one payment only.
305
+ The transfer is confirmed on chain [J16].
306
+ - **Testnet only.** A provider on Mainnet is refused before anything is signed.
307
+
308
+ `--json` prints the outcome as JSON on standard output (the job, its status, the payment and the output) and the
309
+ steps on standard error. It exits 0 only when the job succeeded and its payment is confirmed. If the answer to the paid request is lost, it
310
+ says the payment may have gone through and never calls it unpaid: your provider decides it from the chain
311
+ [J13]. If the testnet DEX has no USDC for sale, nothing is paid: try again later, or pay with `stellar-pay` from a
312
+ test wallet of your own, funded at [faucet.circle.com](https://faucet.circle.com) (setup guide §6).
313
+
314
+ ### How agents see you
315
+
316
+ `provider-kit check` ends with the provider as agents meet it, and hints that never fail the check:
317
+
318
+ ```text
319
+ How agents see you
320
+ Search matches this name and description, which provider-kit launch publishes:
321
+ Text Tools API
322
+ Summaries, translations and labels for English text.
323
+ · On testnet it also matches your services' names, titles, descriptions and tags, once the manifest is live
324
+ and names the agent [I1]–[I3].
325
+ ! translate: a 7-word description, no example
326
+ ! classify-tree: a 3-word description, no example
327
+ An agent picks a service by its description: what it does, when to use it, its limits, with no price or
328
+ URL [§3.4]. An example shows it what to send, and provider-kit try pays with it.
329
+ · On mainnet, search reads no service names, titles or tags: agents searching there for "summarize",
330
+ "translate", "classify", "tree", "random" or "fact" wouldn't find you. provider-kit check --fix puts those
331
+ words in provider.description.
332
+ · Ranking counts reviews from distinct paying clients only. Until the first, search lists you as new and
333
+ unrated.
334
+ · Not registered yet: provider-kit launch publishes this name and description.
335
+ ```
336
+
337
+ - **Search** (stellar-agent-search's `find_agent`, and 8004 explorers) matches a query against the registration: the
338
+ name and description `launch` publishes from `provider.name` and `provider.description`, and the registration's
339
+ services. On testnet, stellar-agent-search 0.2.0 reads agents straight from the registry and also matches a bound
340
+ manifest's services: their names, titles, descriptions and tags. On mainnet, its explorer lists no services, so
341
+ only the name and description count: there, a word from a service's name, title or tags that the name and
342
+ description miss is a warning, and `check --fix` writes the missing words into a gateway config's description.
343
+ Words are matched the way stellar-agent-search 0.2.0 matches them: folded, its stopwords dropped, a crude stem,
344
+ then a substring test.
345
+ - **Once registered,** it shows the name and description the registry has, says when they are older than the
346
+ manifest (`launch` updates them), and prints the stellar-agent-search command that shows the live profile.
347
+ - **Each service:** a description too short to say what it does, when to use it and its limits, a price or URL
348
+ in it [§3.4], or no example.
349
+
350
+ ### Approving in your wallet
351
+
352
+ Without `--source`, an owner command (`launch`, `trustline`, `register`, `request-validation`) opens a page on this
353
+ computer, `http://localhost:8790` (or any free port), where you connect the wallet you already use and approve each
354
+ transaction:
355
+
356
+ 1. **Connect.** Freighter, xBull, Albedo, Rabet, Hana, MetaMask, Ledger and the other wallets
357
+ [Stellar Wallets Kit](https://stellarwalletskit.dev) supports without extra setup, except LOBSTR: its connector
358
+ is GPL-3.0, and the page ships in this MIT package. The page names the account the command needs, such as
359
+ `payTo`, and refuses another one before anything is built.
360
+ 2. **Review.** Each transaction in plain words: what it does, every argument, the network, the most it can cost in
361
+ fees and how long it stays valid. The raw hash and XDR are there to compare with what the wallet shows.
362
+ 3. **Approve** in the wallet. The CLI checks that what came back is that very transaction, signed by the connected
363
+ account for this network, then sends it and waits for the ledger. Your terminal shows each step as it lands.
364
+
365
+ How it stays safe:
366
+
367
+ - **No key in the kit.** The wallet signs. The CLI builds, checks and sends, and never sees a key.
368
+ - **The page answers only you.** It listens on 127.0.0.1 only. Its API needs a one-time token that reaches the page
369
+ in the link's fragment, and refuses other `Host` and `Origin` headers, so no website and no DNS rebinding can drive
370
+ it. The page runs only its own bundled scripts under a strict Content Security Policy.
371
+ - **Nothing is sent twice.** Each transaction is built when it is due. A refused one can be prepared again. One whose
372
+ fate is unknown stops the session with its hash, to look up before trying again.
373
+ - **Ctrl+C cancels**, after the step under way finishes. Over SSH, `--no-open` prints the link without opening a
374
+ browser: forward its port. `--port <n>` picks the port.
375
+
376
+ The page's source is in `approve/` (Svelte), built into `dist/approve` by `npm run build`. `npm run dev:approve`
377
+ serves it alone for design work.
378
+
379
+ ## Payment safety
380
+
381
+ - **Local payment checks.** Before the facilitator sees a payment, the kit decodes it: exactly one USDC
382
+ `transfer(payer, payTo, price)`, authorized by the payer's own signed auth entry and nothing else. A payment
383
+ to another address, amount or asset is refused with a `402`. The signature itself is the facilitator's
384
+ check, since only a simulation can verify it for every kind of account.
385
+ - **One payment, one job.** The fingerprint is the payer's auth-entry nonce, which Soroban accepts once, so a
386
+ signed payment can't buy two jobs even re-wrapped in another transaction [P9].
387
+ - **Uncertain is never failed.** A settlement counts as failed only when the facilitator says it never
388
+ submitted the transaction. Timeouts, errors and unknown answers make the job `payment_uncertain`, never
389
+ charged again [J13]. A settlement past its deadline (`settleTimeoutSeconds`, default `maxTimeoutSeconds` + 30)
390
+ is uncertain too, and its answer is still recorded if it comes later.
391
+ - **Reconciled from the chain.** With a Soroban RPC (`rpc`, the public one by default on testnet; set your own
392
+ on mainnet), the provider decides `payment_uncertain` jobs after every sweep. It looks for the transaction
393
+ that carries the payer's signed auth entry with the job's nonce, so another job's equal transfer is never
394
+ mistaken for it. A paid job then runs or shows its output; one whose payment provably never landed before
395
+ it expired fails uncharged; anything less waits. See [docs/job-recovery.md](../../docs/job-recovery.md#reconciliation).
396
+ - **Timed by ledger.** A signed payment expires at a ledger, so `on-success` jobs are timed by their payment's
397
+ expiration ledger, read from the same RPC. Without it they fall back to the claim time plus
398
+ `maxTimeoutSeconds`.
399
+ - **Crash-safe.** Every state change is stored before the next step. A settlement interrupted by a crash
400
+ becomes `payment_uncertain`; an `on-success` job whose payment expired fails uncharged.
401
+ - **Hardened facilitator client.** HTTPS only, no redirects, deadlines on every call, bounded answers, and a
402
+ startup check that the facilitator settles this network with sponsored fees.
403
+
404
+ ## Capacity and load
405
+
406
+ When ten agents call your service at the same time, nothing fails. Each service runs `concurrency` jobs at
407
+ once and queues the rest. When the queue is full, the kit answers `503` with `Retry-After` **before the `402`**,
408
+ so nobody pays for a job you can't run (Provider Standard §5.8).
409
+
410
+ ```ts
411
+ const imageGen = defineService({
412
+ name: "image-gen",
413
+ // ...
414
+ expectedDurationSeconds: 20,
415
+ capacity: {
416
+ concurrency: 2, // jobs at once: what one GPU or one upstream quota allows (default 4)
417
+ maxQueued: 50, // jobs waiting for a slot (default 100)
418
+ maxQueueWaitSeconds: 80, // longest expected wait before answering 503 instead (default 60; 0 = no queue)
419
+ },
420
+ });
421
+
422
+ const provider = createProvider({
423
+ // ...
424
+ services: [imageGen],
425
+ // Optional: 429 for a caller sending too many create-job requests. You decide who a caller is.
426
+ rateLimit: { requests: 30, windowSeconds: 60, key: (request) => request.headers.get("cf-connecting-ip") ?? undefined },
427
+ });
428
+ ```
429
+
430
+ With 10 requests at once, 2 jobs run and 8 wait. Each waiting job gets `202` with
431
+ `"queue": { "position", "estimatedStartAt" }` and a matching `Retry-After`. The 11th request would wait about
432
+ 100 s, over the 80 s limit, so it gets `503` with `Retry-After` and is never asked to pay. The client sends the
433
+ same request again later.
434
+
435
+ - The manifest publishes `capacity: { concurrency, maxQueueWaitSeconds }` so discovery and clients can see it.
436
+ - `upfront` jobs settle first and then wait for a slot. `on-success` jobs wait only as long as their payment
437
+ lasts: until its expiration ledger, less the work and 10 s to settle. One that can't start in time fails
438
+ with `timeout` and is not charged.
439
+ - Wait estimates start from `expectedDurationSeconds` and then follow the measured durations.
440
+ - check-status and get-result are never limited, so paid jobs can always be collected.
441
+
442
+ ### Restarts, several processes, separate workers
443
+
444
+ The queue lives in the job store. With `sqliteStore(path)`:
445
+
446
+ - a paid `upfront` job keeps its place across restarts and runs when the provider is back [Q4];
447
+ - several processes on one machine can share the file: `concurrency` then counts jobs across all of them;
448
+ - workers lease `upfront` jobs, and a job whose worker died is taken over when the lease (30 s) runs out,
449
+ up to 3 starts;
450
+ - HTTP-only processes (`runQueuedJobs: false`, or `serve --no-workers`) can take jobs that separate worker
451
+ processes, with the same services and store, run. `on-success` jobs always run in the process that
452
+ verified their payment, because that process settles it.
453
+
454
+ `memoryStore()`, the default, is for tests and local development only: it loses jobs on restart. A store for
455
+ another database implements the `JobStore` contract in `src/jobs/store.ts`; `test/job-store.test.ts` is the
456
+ contract test it must pass.
457
+
458
+ ## Testing your provider
459
+
460
+ `@stellar-agent-services/provider-kit/testing` signs real x402 payments offline, so your tests run the kit's
461
+ payment checks without a network:
462
+
463
+ ```ts
464
+ import { signTestPayment } from "@stellar-agent-services/provider-kit/testing";
465
+ const payment = await signTestPayment(requirements, Keypair.random()); // pair it with a fake facilitator
466
+ ```
467
+
468
+ ## Files
469
+
470
+ | File | Contents |
471
+ | --- | --- |
472
+ | `src/define-service.ts` | `defineService()`: price, charge policy, schemas, capacity; checks at definition time |
473
+ | `src/provider.ts` | `createProvider()`: wires everything into one `fetch` handler; startup checks |
474
+ | `src/manifest.ts` | Builds the manifest from the same config as the 402 challenge, and checks it |
475
+ | `src/http/router.ts` | Manifest, create-job, check-status and get-result routes, in §5.4 order |
476
+ | `src/http/node-server.ts` | `listen()`: serves a provider on Node's HTTP server, with `/healthz` and graceful close. A request body is read only as far as the router reads it, so an early answer (`413`) leaves the connection usable. |
477
+ | `src/payments/payload-checks.ts` | Local payment checks and the payment fingerprint |
478
+ | `src/payments/facilitator.ts` | The facilitator client and the startup check |
479
+ | `src/payments/settlement.ts` | Settlement as `settled`, `failed` or `uncertain` (never charge twice) |
480
+ | `src/payments/chain.ts` | Soroban RPC reads and the ledger clock |
481
+ | `src/payments/onchain.ts` | Finds a job's payment on chain, by the payer's signed auth entry and nonce |
482
+ | `src/payments/x402-server.ts` | The 402 challenge, with the Bazaar discovery extension |
483
+ | `src/jobs/runner.ts` | The `on-success` and `upfront` charge policies; leased workers |
484
+ | `src/jobs/scheduler.ts` | Capacity: admission before the 402, turns, workers, queue positions |
485
+ | `src/jobs/recovery.ts` | Sweeps for what a crash left behind, and retention |
486
+ | `src/jobs/reconcile.ts` | Decides `payment_uncertain` jobs from on-chain data |
487
+ | `src/jobs/memory-store.ts`, `src/jobs/sqlite-store.ts` | Job stores |
488
+ | `src/gateway/config.ts`, `src/gateway/upstream.ts` | Gateway mode |
489
+ | `src/gateway/openapi.ts` | Gateway services from an OpenAPI document, for `provider-kit init --openapi` |
490
+ | `src/registration.ts` | 8004 registration file, the I1–I3 check (with the file it read), and who may act for an agent |
491
+ | `src/cli/` | The `provider-kit` command |
492
+ | `src/cli/dev.ts`, `src/cli/dev-account.ts` | `provider-kit dev`, and the throwaway payout account it makes when `PAY_TO` is empty |
493
+ | `src/cli/launch.ts` | `provider-kit launch`: the public address, the manifest it will serve, one approval page, the settings saved, the deploy and the live check |
494
+ | `src/cli/status.ts` | `provider-kit status`: each stage, and the command to run next |
495
+ | `src/cli/serve.ts` | `provider-kit serve`, and `serveGateway()` that `dev` and `try` start a gateway with |
496
+ | `src/cli/env-file.ts`, `src/cli/fly.ts` | Reading and writing `.env` and `fly.toml`'s `[env]`; deploying with `flyctl` |
497
+ | `src/cli/processes.ts`, `src/cli/tunnel.ts` | Starting and stopping a provider's command, waiting for its manifest; the cloudflared quick tunnel |
498
+ | `src/cli/validators.ts` | The verifier `launch` asks by default, per network |
499
+ | `src/cli/approval.ts` | Approving in a wallet: the session (steps, checks, sending) and the local page's server and API |
500
+ | `src/cli/approval-view.ts` | What the page shows: the session as JSON, shared with the page |
501
+ | `src/cli/owner-chain.ts` | The owner's transactions: built, checked once signed, sent and followed into a ledger |
502
+ | `src/cli/owner-steps.ts` | Test XLM, the USDC trustline, registration and the validation request, as approval steps |
503
+ | `src/cli/try.ts` | `provider-kit try`: one paid testnet call through the payment client, from a throwaway payer |
504
+ | `src/cli/discovery.ts` | How agents see you, for `provider-kit check`: search text matched as stellar-agent-search matches it, and hints |
505
+ | `approve/` | The approval page (Svelte, Stellar Wallets Kit), built into `dist/approve` |
506
+ | `schemas/gateway-config.schema.json` | The gateway config file's JSON Schema |
507
+ | `Dockerfile` | The gateway image |
@@ -0,0 +1 @@
1
+ svg.svelte-16un51p{flex:none;display:block}.body.svelte-1bpvs6g{min-width:0}.message.svelte-1bpvs6g{overflow-wrap:anywhere;margin-top:2px}.panel.svelte-1mh64aq{flex-direction:column;flex:1;gap:20px;animation:.18s ease-out rise;display:flex}.account.svelte-1mh64aq{border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--surface-sunken);padding:14px 16px 16px}.account-label.svelte-1mh64aq{color:var(--text-tertiary);letter-spacing:.06em;text-transform:uppercase;font-size:11.5px;font-weight:650}.address-line.svelte-1mh64aq{align-items:center;gap:6px;margin-top:6px;display:flex}.address.svelte-1mh64aq{overflow-wrap:anywhere;flex:1;min-width:0;font-size:13.5px;font-weight:550;line-height:1.45}.reason.svelte-1mh64aq{color:var(--text-secondary);margin-top:8px;font-size:13px}.actions.svelte-1mh64aq{justify-items:center;gap:12px;margin-top:auto;padding-top:4px;display:grid}.wallets.svelte-1mh64aq{color:var(--text-tertiary);text-align:center;font-size:12.5px}.cancel.svelte-1mh64aq{color:var(--text-secondary);font-size:13px}.panel.svelte-lag6xc{text-align:center;justify-items:center;gap:8px;animation:.18s ease-out rise;display:grid}.badge.svelte-lag6xc{border:1px solid var(--border);background:var(--surface-sunken);width:52px;height:52px;color:var(--text-secondary);border-radius:14px;place-items:center;margin-bottom:8px;display:grid}.panel-lead.svelte-lag6xc{max-width:400px}.header.svelte-75mo91{z-index:10;height:var(--header-height);border-bottom:1px solid var(--border);background:color-mix(in srgb, var(--surface) 86%, transparent);-webkit-backdrop-filter:saturate(1.4)blur(10px);backdrop-filter:saturate(1.4)blur(10px);position:sticky;top:0}.inner.svelte-75mo91{justify-content:space-between;align-items:center;gap:16px;max-width:1040px;height:100%;margin:0 auto;padding:0 24px;display:flex}.brand.svelte-75mo91,.status.svelte-75mo91{align-items:center;gap:10px;min-width:0;display:flex}.logo.svelte-75mo91{color:#fff;background:linear-gradient(135deg,#6366f1,#4338ca);border-radius:9px;flex:none;place-items:center;width:30px;height:30px;display:grid;box-shadow:0 1px 2px #4338ca66}.name.svelte-75mo91{letter-spacing:-.01em;white-space:nowrap;font-size:15px;font-weight:650}.divider.svelte-75mo91{background:var(--border-strong);width:1px;height:20px}.command.svelte-75mo91{color:var(--text-secondary);text-overflow:ellipsis;white-space:nowrap;font-size:12.5px;overflow:hidden}.account.svelte-75mo91{border:1px solid var(--border);background:var(--surface);height:32px;color:var(--text-secondary);border-radius:999px;align-items:center;gap:8px;padding:0 12px;font-size:12.5px;display:inline-flex}.account.svelte-75mo91 .mono:where(.svelte-75mo91){color:var(--text);font-weight:550}.wallet.svelte-75mo91{border-left:1px solid var(--border);padding-left:8px}@media (max-width:640px){.divider.svelte-75mo91,.command.svelte-75mo91,.wallet.svelte-75mo91{display:none}.inner.svelte-75mo91{padding:0 16px}}.details.svelte-1iabob4{border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--surface);margin:0}.row.svelte-1iabob4{border-bottom:1px solid var(--border);grid-template-columns:132px minmax(0,1fr);gap:16px;padding:10px 14px;display:grid}.row.svelte-1iabob4:last-child{border-bottom:0}dt.svelte-1iabob4{color:var(--text-tertiary);padding-top:1px;font-size:13px}dd.svelte-1iabob4{align-items:flex-start;gap:4px;min-width:0;margin:0;font-size:13.5px;font-weight:500;display:flex}.mono-row.svelte-1iabob4{align-items:center;margin:-6px 0}.value.svelte-1iabob4{overflow-wrap:anywhere;flex:1;min-width:0}.value.mono.svelte-1iabob4{font-size:12.5px;line-height:1.45}a.value.svelte-1iabob4{align-items:baseline;gap:4px;display:inline-flex}a.value.svelte-1iabob4 svg{opacity:.7;align-self:center}@media (max-width:560px){.row.svelte-1iabob4{grid-template-columns:minmax(0,1fr);gap:2px}.mono-row.svelte-1iabob4{margin:0 0 -6px}}.panel.svelte-o1kohj{flex-direction:column;flex:1;gap:22px;animation:.18s ease-out rise;display:flex}.hero.svelte-o1kohj{text-align:center;justify-items:center;gap:8px;padding:12px 0 4px;display:grid}.badge.svelte-o1kohj{background:var(--surface-sunken);width:56px;height:56px;color:var(--text-secondary);border-radius:999px;place-items:center;margin-bottom:6px;display:grid}.badge.success.svelte-o1kohj{background:var(--success-soft);box-shadow:0 0 0 8px color-mix(in srgb, var(--success-soft) 55%, transparent);color:var(--success)}.badge.danger.svelte-o1kohj{background:var(--danger-soft);color:var(--danger)}.title.svelte-o1kohj{letter-spacing:-.015em;font-size:21px;font-weight:650}.message.svelte-o1kohj{max-width:440px}.closing.svelte-o1kohj{border:1px dashed var(--border-strong);border-radius:var(--radius);color:var(--text-secondary);align-items:center;gap:10px;margin-top:auto;padding:12px 14px;font-size:13px;display:flex}.panel.svelte-kh3r8c{flex-direction:column;flex:1;gap:18px;animation:.18s ease-out rise;display:flex}.head.svelte-kh3r8c{align-items:flex-start;gap:14px;display:flex}.head-icon.svelte-kh3r8c{background:var(--accent-soft);width:40px;height:40px;color:var(--accent);border-radius:12px;flex:none;place-items:center;display:grid}.head-icon.blocked.svelte-kh3r8c{background:var(--danger-soft);color:var(--danger)}.raw.svelte-kh3r8c{border:1px solid var(--border);border-radius:var(--radius);background:var(--surface-sunken)}.raw.svelte-kh3r8c summary:where(.svelte-kh3r8c){color:var(--text-secondary);cursor:pointer;border-radius:var(--radius);align-items:center;gap:6px;padding:10px 12px;font-size:13px;font-weight:550;list-style:none;display:flex}.raw.svelte-kh3r8c summary:where(.svelte-kh3r8c)::-webkit-details-marker{display:none}.raw.svelte-kh3r8c summary:where(.svelte-kh3r8c) svg{transition:transform .14s}.raw[open].svelte-kh3r8c summary:where(.svelte-kh3r8c) svg{transform:rotate(90deg)}.raw-body.svelte-kh3r8c{gap:12px;padding:2px 12px 12px;display:grid}.raw-label.svelte-kh3r8c{color:var(--text-tertiary);font-size:12px;font-weight:600}.raw-value.svelte-kh3r8c{align-items:flex-start;gap:4px;margin-top:4px;display:flex}code.svelte-kh3r8c{border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--surface);min-width:0;color:var(--text-secondary);overflow-wrap:anywhere;flex:1;padding:8px 10px;font-size:11.5px;line-height:1.5}.xdr.svelte-kh3r8c{max-height:112px;display:block;overflow-y:auto}.foot.svelte-kh3r8c{gap:12px;margin-top:auto;padding-top:4px;display:grid}.actions.svelte-kh3r8c{gap:10px;display:flex}.grow.svelte-kh3r8c{flex:1}.hint.svelte-kh3r8c{color:var(--text-tertiary);text-align:center;justify-content:center;align-items:center;gap:6px;font-size:12.5px;display:flex}.hint.expired.svelte-kh3r8c{color:var(--warning)}@media (max-width:520px){.actions.svelte-kh3r8c{flex-direction:column-reverse}.actions.svelte-kh3r8c .btn:where(.svelte-kh3r8c){flex:none;width:100%}.hint.svelte-kh3r8c{text-align:left;align-items:flex-start}}.steps.svelte-1wezgpx{margin:0;padding:0;list-style:none;display:grid}.step.svelte-1wezgpx{grid-template-columns:28px minmax(0,1fr);gap:12px;padding-bottom:22px;display:grid;position:relative}.step.svelte-1wezgpx:last-child{padding-bottom:0}.step.svelte-1wezgpx:not(:last-child):before{content:"";background:var(--border-strong);width:1px;position:absolute;top:32px;bottom:4px;left:13.5px}.step.done.svelte-1wezgpx:not(:last-child):before{background:var(--success-border)}.marker.svelte-1wezgpx{border:1px solid var(--border-strong);background:var(--surface);width:28px;height:28px;color:var(--text-tertiary);font-variant-numeric:tabular-nums;border-radius:999px;place-items:center;font-size:12.5px;font-weight:600;transition:background-color .16s,border-color .16s,color .16s;display:grid}.active.svelte-1wezgpx .marker:where(.svelte-1wezgpx){border-color:var(--accent);background:var(--accent-soft);color:var(--accent);box-shadow:0 0 0 4px var(--accent-soft)}.done.svelte-1wezgpx .marker:where(.svelte-1wezgpx){border-color:var(--success);background:var(--success);color:var(--surface)}.halted.svelte-1wezgpx .marker:where(.svelte-1wezgpx){border-color:var(--border-strong);background:var(--surface);color:var(--text-tertiary);box-shadow:none}.text.svelte-1wezgpx{gap:1px;min-width:0;padding-top:4px;display:grid}.title.svelte-1wezgpx{color:var(--text-tertiary);font-weight:550}.active.svelte-1wezgpx .title:where(.svelte-1wezgpx),.done.svelte-1wezgpx .title:where(.svelte-1wezgpx){color:var(--text)}.note.svelte-1wezgpx{color:var(--text-tertiary);overflow-wrap:anywhere;font-size:12.5px}.note.current.svelte-1wezgpx{color:var(--accent);font-weight:500}.panel.svelte-audtxj{text-align:center;flex:1;place-content:center;justify-items:center;gap:8px;min-height:280px;animation:.18s ease-out rise;display:grid}.ring.svelte-audtxj{border:3px solid var(--accent-soft);border-top-color:var(--accent);border-radius:999px;width:44px;height:44px;margin-bottom:12px;animation:.9s linear infinite spin}.panel-lead.svelte-audtxj{max-width:360px}.page.svelte-1mjb4es{max-width:1040px;margin:0 auto;padding:40px 24px 48px}.frame.svelte-1mjb4es{border:1px solid var(--border);border-radius:var(--radius-xl);background:var(--surface);box-shadow:var(--shadow-md);grid-template-columns:340px minmax(0,1fr);display:grid;overflow:hidden}.frame.single.svelte-1mjb4es{grid-template-columns:minmax(0,1fr);max-width:560px;margin:48px auto 0;padding:44px 32px}.side.svelte-1mjb4es{border-right:1px solid var(--border);background:var(--surface-sunken);flex-direction:column;padding:32px 28px 28px;display:flex}.eyebrow.svelte-1mjb4es{color:var(--accent);letter-spacing:.06em;text-transform:uppercase;font-size:12px;font-weight:650}h1.svelte-1mjb4es{letter-spacing:-.02em;margin-top:8px;font-size:22px;font-weight:680;line-height:1.25}.intro.svelte-1mjb4es{color:var(--text-secondary);margin-top:8px}.steps-wrap.svelte-1mjb4es{margin-top:28px}.assurance.svelte-1mjb4es{color:var(--text-tertiary);align-items:flex-start;gap:8px;margin-top:auto;padding-top:32px;font-size:12.5px;display:flex}.assurance.svelte-1mjb4es svg{margin-top:2px}.main.svelte-1mjb4es{flex-direction:column;min-height:500px;padding:32px 36px;display:flex}.footer.svelte-1mjb4es{color:var(--text-tertiary);text-align:center;margin-top:16px;font-size:12px}@media (max-width:860px){.page.svelte-1mjb4es{padding:20px 16px 32px}.frame.svelte-1mjb4es{grid-template-columns:minmax(0,1fr)}.side.svelte-1mjb4es{border-right:0;border-bottom:1px solid var(--border);padding:24px 20px}.assurance.svelte-1mjb4es{padding-top:24px}.main.svelte-1mjb4es{min-height:0;padding:24px 20px}}:root{--font-sans:ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;--font-mono:ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;--bg:#f4f5f8;--surface:#fff;--surface-sunken:#f7f8fa;--border:#e4e7ec;--border-strong:#d0d5dd;--text:#0f172a;--text-secondary:#475467;--text-tertiary:#667085;--accent:#4f46e5;--accent-solid:#4f46e5;--accent-solid-hover:#4338ca;--accent-soft:#eef2ff;--accent-border:#c7d2fe;--on-accent:#fff;--success:#067647;--success-soft:#ecfdf3;--success-border:#abefc6;--warning:#b54708;--warning-soft:#fffaeb;--warning-border:#fedf89;--danger:#b42318;--danger-soft:#fef3f2;--danger-border:#fecdca;--ring:0 0 0 4px #4f46e52e;--shadow-sm:0 1px 2px #1018280d;--shadow-md:0 1px 3px #10182814, 0 1px 2px #1018280a;--shadow-lg:0 24px 48px -12px #10182838;--radius-sm:8px;--radius:10px;--radius-lg:16px;--radius-xl:20px;--header-height:64px;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}@media (prefers-color-scheme:dark){:root{--bg:#0b0e14;--surface:#12161f;--surface-sunken:#0e121a;--border:#232a36;--border-strong:#333c4b;--text:#f1f5f9;--text-secondary:#c1c9d6;--text-tertiary:#8d97a8;--accent:#a5b4fc;--accent-solid:#6366f1;--accent-solid-hover:#7c7ff5;--accent-soft:#6366f124;--accent-border:#818cf866;--success:#6ce9a6;--success-soft:#12b76a1f;--success-border:#32d58352;--warning:#fec84b;--warning-soft:#f790091f;--warning-border:#fdb02252;--danger:#fda29b;--danger-soft:#f044381f;--danger-border:#f9706652;--ring:0 0 0 4px #818cf847;--shadow-sm:0 1px 2px #0006;--shadow-md:0 1px 3px #00000073;--shadow-lg:0 24px 48px -12px #0009;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}}*,:before,:after{box-sizing:border-box}html{-webkit-text-size-adjust:100%}body{background:var(--bg);min-height:100vh;color:var(--text);font-family:var(--font-sans);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;margin:0;font-size:14px;line-height:1.5}h1,h2,h3,p{margin:0}a{color:var(--accent);text-decoration:none}a:hover{text-decoration:underline}:focus-visible{box-shadow:var(--ring);outline:none}.mono{font-family:var(--font-mono);font-size:.92em}.muted{color:var(--text-tertiary)}.visually-hidden{clip:rect(0 0 0 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.panel-title{letter-spacing:-.01em;font-size:18px;font-weight:650}.panel-lead{color:var(--text-secondary);margin-top:4px}.btn{border-radius:var(--radius);height:40px;font:inherit;white-space:nowrap;cursor:pointer;border:1px solid #0000;justify-content:center;align-items:center;gap:8px;padding:0 16px;font-weight:550;transition:background-color .12s,border-color .12s,color .12s,box-shadow .12s;display:inline-flex}.btn:disabled{cursor:not-allowed;opacity:.55}.btn-primary{background:var(--accent-solid);color:var(--on-accent);box-shadow:var(--shadow-sm)}.btn-primary:hover:not(:disabled){background:var(--accent-solid-hover)}.btn-secondary{background:var(--surface);border-color:var(--border-strong);color:var(--text);box-shadow:var(--shadow-sm)}.btn-secondary:hover:not(:disabled){background:var(--surface-sunken)}.btn-ghost{color:var(--text-secondary);background:0 0}.btn-ghost:hover:not(:disabled){background:var(--surface-sunken);color:var(--text)}.btn-lg{height:48px;padding:0 20px;font-size:15px}.btn-block{width:100%}.btn-icon{width:32px;height:32px;padding:0}.link-button{color:var(--accent);font:inherit;cursor:pointer;background:0 0;border:0;padding:0;font-weight:500}.link-button:hover:not(:disabled){text-decoration:underline}.link-button:disabled{cursor:not-allowed;opacity:.55}.pill{border:1px solid var(--border);background:var(--surface-sunken);height:24px;color:var(--text-secondary);white-space:nowrap;border-radius:999px;align-items:center;gap:6px;padding:0 10px;font-size:12px;font-weight:550;display:inline-flex}.pill-warning{border-color:var(--warning-border);background:var(--warning-soft);color:var(--warning)}.pill-success{border-color:var(--success-border);background:var(--success-soft);color:var(--success)}.dot{background:currentColor;border-radius:999px;width:6px;height:6px}.alert{border:1px solid var(--border);border-radius:var(--radius);background:var(--surface-sunken);color:var(--text-secondary);gap:10px;padding:12px 14px;font-size:13px;display:flex}.alert>svg{flex:none;margin-top:2px}.alert-title{color:var(--text);font-weight:600}.alert-warning{border-color:var(--warning-border);background:var(--warning-soft)}.alert-warning>:first-child{color:var(--warning)}.alert-danger{border-color:var(--danger-border);background:var(--danger-soft)}.alert-danger>:first-child{color:var(--danger)}.spinner{border:2px solid;border-right-color:#0000;border-radius:999px;flex:none;width:16px;height:16px;animation:.75s linear infinite spin;display:inline-block}@keyframes spin{to{transform:rotate(360deg)}}@keyframes rise{0%{opacity:0;transform:translateY(6px)}}@media (prefers-reduced-motion:reduce){*,:before,:after{transition-duration:1ms!important;animation-duration:1ms!important;animation-iteration-count:1!important}}