@ixo/decision-oracle 0.2.31 → 0.2.33
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.
- package/README.md +5 -4
- package/docs/guide.md +6 -2
- package/package.json +1 -1
- package/skills/README.md +5 -3
- package/skills/create-collection/SKILL.md +35 -11
- package/skills/create-collection/reference/collection.md +1 -1
- package/skills/create-oracle/SKILL.md +73 -25
- package/skills/decision-api-client/SKILL.md +19 -25
- package/skills/decision-api-oracle/SKILL.md +150 -145
- package/skills/operate-oracle/SKILL.md +234 -0
- package/skills/publish-protocol/SKILL.md +3 -2
- package/skills/reference/engine-contract.md +11 -8
- package/skills/update-pricing/SKILL.md +132 -0
- package/skills/write-facts/SKILL.md +23 -11
- package/skills/write-facts/reference/facts-api.md +2 -2
- package/templates/project/CLAUDE.md +4 -3
package/README.md
CHANGED
|
@@ -43,9 +43,10 @@ publishing a protocol and its rubric, and for anyone who needs a claim judged th
|
|
|
43
43
|
- **Be an OpenHands checker with one flag.** `init --openhands` wires the add-on: the OpenHands
|
|
44
44
|
app's hire webhook makes your oracle authorize itself on each ask that picked it, register it and
|
|
45
45
|
start judging, with a backstop that keeps every hired ask topped up.
|
|
46
|
-
- **Drive all of it with the shipped Claude skills.** `init` copies
|
|
46
|
+
- **Drive all of it with the shipped Claude skills.** `init` copies ten skills into
|
|
47
47
|
`.claude/skills/`: `create-oracle`, `write-facts`, `create-rubric`, `publish-protocol`,
|
|
48
|
-
`create-collection`, `decision-api-oracle`, `decision-api-client`, `add-openhands-support
|
|
48
|
+
`create-collection`, `decision-api-oracle`, `decision-api-client`, `add-openhands-support`,
|
|
49
|
+
`update-pricing`, `operate-oracle`.
|
|
49
50
|
|
|
50
51
|
## How it works
|
|
51
52
|
|
|
@@ -738,7 +739,7 @@ packages/eval-oracle/ THE package — @ixo/decision-oracle
|
|
|
738
739
|
src/ the Worker: routes, workflows, the facts lane, the CLI
|
|
739
740
|
src/facts/ src/testing/ the two node-safe public subpaths
|
|
740
741
|
migrations/ the D1 migrations — shipped, never hand-written by a consumer
|
|
741
|
-
skills/ the
|
|
742
|
+
skills/ the ten Claude skills `init` copies into a new project
|
|
742
743
|
templates/project/ exactly what `decision-oracle init` generates
|
|
743
744
|
dev/ engine-conformance harnesses (ours, not published)
|
|
744
745
|
docs/ api.md (the API reference) + guide.md (the walkthrough)
|
|
@@ -786,6 +787,6 @@ symlinks are not packed). CI diffs them — change one, copy it to the other.
|
|
|
786
787
|
|
|
787
788
|
## Status
|
|
788
789
|
|
|
789
|
-
Published on npm: `@ixo/decision-oracle` 0.2.
|
|
790
|
+
Published on npm: `@ixo/decision-oracle` 0.2.33 and `@ixo/decision-oracle-client` 0.1.3.
|
|
790
791
|
|
|
791
792
|
Apache License 2.0: `LICENSE` at the repo root and in each package.
|
package/docs/guide.md
CHANGED
|
@@ -71,7 +71,7 @@ bun run cf-typegen
|
|
|
71
71
|
| `wrangler.jsonc` | **yours to fill** | Bindings, vars, the cron, the custom domains. Binding and var **names are the package's contract** — never rename one |
|
|
72
72
|
| `.dev.vars` | **yours, never committed** | Local secrets. `.dev.vars.example` lists every one with what it does |
|
|
73
73
|
| `tsconfig.json`, `biome.json`, `vitest.config.ts`, `package.json` | generated | Scripts: `dev`, `deploy`, `deploy:devnet`, `deploy:testnet`, `deploy:production`, `build`, `cf-typegen`, `db:migrate`, `db:migrate:remote`, `test`, `typecheck`, `lint`, `check` |
|
|
74
|
-
| `.claude/skills/` | shipped | The
|
|
74
|
+
| `.claude/skills/` | shipped | The ten skills in §14. Refresh after an upgrade: `bunx decision-oracle skills sync` |
|
|
75
75
|
| `CLAUDE.md` | shipped | The two rules, and where the package docs live |
|
|
76
76
|
|
|
77
77
|
Everything else — pulling claims, the engine handshake, the D1 ledgers, review folds, the chain
|
|
@@ -1652,7 +1652,7 @@ own retries, and a terminal failure clears it.
|
|
|
1652
1652
|
|
|
1653
1653
|
## 14. For AI agents working in a generated project
|
|
1654
1654
|
|
|
1655
|
-
|
|
1655
|
+
Ten skills ship with the package and are copied into `.claude/skills/` by `init` (refresh with
|
|
1656
1656
|
`bunx decision-oracle skills sync`). Invoke the one that matches the job rather than improvising:
|
|
1657
1657
|
|
|
1658
1658
|
| Job | Skill |
|
|
@@ -1663,6 +1663,10 @@ Six skills ship with the package and are copied into `.claude/skills/` by `init`
|
|
|
1663
1663
|
| Anchor a claim form and rubric as an on-chain protocol entity | `publish-protocol` |
|
|
1664
1664
|
| Create a deed and a claim collection, fund it, grant the roles | `create-collection` |
|
|
1665
1665
|
| Turn on, deploy and verify the Decision API lane (`API_LANE`) — engine registration (payable at once on the Standard plan), operator sources and keys, verifying it | `decision-api-oracle` |
|
|
1666
|
+
| Call a Decision API oracle as a customer — test credentials, the SDK and CLI, MCP, paying | `decision-api-client` |
|
|
1667
|
+
| Make the oracle hireable from the Openhands app (`init --openhands`) | `add-openhands-support` |
|
|
1668
|
+
| Change what the oracle charges, and refresh the card apps show | `update-pricing` |
|
|
1669
|
+
| Run a deployed oracle — upgrades, pauses, renewals, reviews, alerts, admin top-ups | `operate-oracle` |
|
|
1666
1670
|
|
|
1667
1671
|
And the three rules that matter more than any of them:
|
|
1668
1672
|
|
package/package.json
CHANGED
package/skills/README.md
CHANGED
|
@@ -11,11 +11,13 @@ installed package (leaving any skill of your own in that directory alone).
|
|
|
11
11
|
| `create-rubric` | authoring a rubric for a claim form and validating it against the engine |
|
|
12
12
|
| `publish-protocol` | anchoring a claim form and rubric on chain as a protocol entity |
|
|
13
13
|
| `create-collection` | creating a claim collection an oracle can be hired against |
|
|
14
|
-
| `decision-api-client` |
|
|
15
|
-
| `decision-api-oracle` | turning on, deploying and verifying the Decision API lane
|
|
14
|
+
| `decision-api-client` | calling a Decision API oracle from the customer side: test credentials (source, keys, payment delegation), the SDK and CLI, MCP from Claude Code, paying by card |
|
|
15
|
+
| `decision-api-oracle` | turning on, deploying and verifying the Decision API lane for your own oracle: protocol, identity, Cloudflare, deploy, engine registration (payable at once), operator, verify, emails, payments |
|
|
16
16
|
| `add-openhands-support` | making an oracle hireable from the Openhands app (asks, the Yoma youth world): the OpenHands add-on (`init --openhands`), who pays for decisions, the account to register with, the logo and the picker's card |
|
|
17
|
+
| `update-pricing` | changing what a running oracle charges: the check fee, per-protocol fees, the Decision API price, and refreshing the Agent Card apps show |
|
|
18
|
+
| `operate-oracle` | looking after a deployed oracle: upgrades and migrations, pausing and resuming collections, renewals and key rotation, reviews and recovery, logs, status and alerts, topping up an admin account |
|
|
17
19
|
|
|
18
20
|
`reference/engine-contract.md` (here, beside the skills) is the decision engine's contract —
|
|
19
|
-
background for all
|
|
21
|
+
background for all ten, not an interface anyone codes against.
|
|
20
22
|
`reference/linked-docs.md` is the linked-document rules: where an entity's documents are uploaded,
|
|
21
23
|
each proof's format, and how to read one.
|
|
@@ -67,8 +67,9 @@ protocol.
|
|
|
67
67
|
- the oracle address to grant Evaluate; the agent address(es) to grant Submit (comma-separated)
|
|
68
68
|
— `--submit addr1,addr2` is also how to restrict who may claim, e.g. a payouts collection only
|
|
69
69
|
two people may submit to
|
|
70
|
-
- quota and window in days if the defaults (100, 90) aren't right —
|
|
71
|
-
than silently applying them
|
|
70
|
+
- the collection's claim quota and window in days if the defaults (100, 90) aren't right —
|
|
71
|
+
state them explicitly rather than silently applying them. `--quota` is the COLLECTION's quota
|
|
72
|
+
only; the grants' own quota is fixed (step 5)
|
|
72
73
|
|
|
73
74
|
2. **Log in once** if there is no session for this network yet:
|
|
74
75
|
`bunx decision-oracle login --network devnet`.
|
|
@@ -96,9 +97,14 @@ protocol.
|
|
|
96
97
|
[--max-custom-amount 0uixo] [--phase collection|fund|grants|all] [--yes]
|
|
97
98
|
```
|
|
98
99
|
|
|
99
|
-
`--eval-denom` must equal the oracle's `EVALUATION_DENOM`
|
|
100
|
-
`
|
|
101
|
-
|
|
100
|
+
`--eval-denom` must equal the oracle's `EVALUATION_DENOM` (read `GET <oracle>/v1/info`
|
|
101
|
+
`pricing`). A third-party oracle charges IXO (`uixo`, any network) or, on mainnet, USDC.inj
|
|
102
|
+
(`ibc/77C12480BD8C29BF0ACEC2143736C15BAF8CD232CA27BAE09336677B828955E6`). PAY (`upay`) is only
|
|
103
|
+
for oracles IXO runs: register refuses it on any other (`pay_requires_ixo_managed`).
|
|
104
|
+
`--eval-amount 0` writes an EMPTY evaluation leg (the chain refuses a zero coin): that is what a
|
|
105
|
+
collection for a per-review-billed or 0-minimum oracle registers with. `--fund` always sends
|
|
106
|
+
`uixo`: an admin that pays evaluations in USDC is topped up with
|
|
107
|
+
`bunx decision-oracle org fund --entity <deed did> --amount <whole USDC> --coin usdc --network mainnet`.
|
|
102
108
|
|
|
103
109
|
`--max-custom-amount` is the evaluate grant's `MaxCustomAmount`, written explicitly (`0<eval
|
|
104
110
|
denom>` by default). The chain holds EVERY non-empty evaluation amount to it (its denom must be
|
|
@@ -138,6 +144,20 @@ protocol.
|
|
|
138
144
|
it is a bearer credential — and it expires on its own schedule (30 days by default), independent
|
|
139
145
|
of the grants' 365-day expiry.
|
|
140
146
|
|
|
147
|
+
**The grants' limits.** Each grant expires after 365 days, and the evaluate grant's
|
|
148
|
+
`agentQuota` is hard-coded to 100 — `--quota` does not change it. Every `MsgEvaluateClaim` the
|
|
149
|
+
oracle signs uses one slot, and a claim that goes to a person uses TWO: the oracle signs it
|
|
150
|
+
FLAGGED while the person decides (it pays nothing), then signs the final verdict. So 100 slots
|
|
151
|
+
cover 100 decided claims, or 50 reviewed ones. When the grant runs out or expires, the engine
|
|
152
|
+
answers `NO_EVALUATE_GRANT` and the collection pauses `engine-no-grant`. Re-grant with the grants
|
|
153
|
+
phase alone (a new grant replaces the old one), then re-register to resume:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
bunx decision-oracle create-collection --entity <deed did> --protocol <protocol did> \
|
|
157
|
+
--phase grants --evaluate <oracle address> [--max-custom-amount <amount><denom>] [--network <n>] [--yes]
|
|
158
|
+
bunx decision-oracle register --collection <id>
|
|
159
|
+
```
|
|
160
|
+
|
|
141
161
|
The bot returns the claim's JSON re-ordered, so its bytes can't be CID-checked: a claim the oracle
|
|
142
162
|
read only from the Matrix claim bot goes to the engine as `claimContent` with no `document`, and
|
|
143
163
|
is decided like any other. For a payout protocol the oracle also looks past the bot to the IPFS
|
|
@@ -162,16 +182,18 @@ protocol.
|
|
|
162
182
|
[--file <fileQuestion>=<path> …] [--yes]
|
|
163
183
|
```
|
|
164
184
|
|
|
165
|
-
(claim-bot media upload → save into the deed's room → `MsgExec(MsgSubmitClaim)`.
|
|
166
|
-
|
|
185
|
+
(claim-bot media upload → save into the deed's room → `MsgExec(MsgSubmitClaim)`.)
|
|
186
|
+
`answers.json` is a flat object of the claim form's answers keyed by question name, e.g.
|
|
187
|
+
`{ "orderId": "ORD-1", "deliveredAt": "2026-11-01T10:00:00Z" }`; each `--file` answers one file
|
|
188
|
+
question with a local file.
|
|
167
189
|
|
|
168
190
|
7. **Report the run** — the output contract below.
|
|
169
191
|
|
|
170
192
|
Payout ASKS (a deed or request with its organisation links and `#payoutTerms`) are posted only
|
|
171
|
-
through OpenHands, not with these commands. A company
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
193
|
+
through OpenHands, not with these commands. A company that pays people from its OWN collection
|
|
194
|
+
names the amount field and currency per protocol in the oracle's `payouts` config; an approved
|
|
195
|
+
claim's receipt then carries the payment to make. Optional — see
|
|
196
|
+
`node_modules/@ixo/decision-oracle/docs/api.md` (`payouts`).
|
|
175
197
|
|
|
176
198
|
## Output contract
|
|
177
199
|
|
|
@@ -199,6 +221,8 @@ account, a phase that was dry-run but never confirmed, or a delegation nobody ha
|
|
|
199
221
|
| Creating the collection before the deed links the protocol | run `create-deed --phase claims`; the picker reads `linkedClaim` → claims-config |
|
|
200
222
|
| Granting Evaluate and expecting the oracle to pull claims | it also needs the claim-bot delegation (step 5) |
|
|
201
223
|
| Leaving the admin account unfunded | every evaluation debits it, rejects included — pass `--fund` |
|
|
224
|
+
| Expecting `--quota` to size the evaluate grant | the grant's `agentQuota` is always 100 (a reviewed claim uses 2); re-run `--phase grants` when it runs out |
|
|
225
|
+
| `--eval-denom upay` for a third-party oracle | register refuses `pay_requires_ixo_managed` — use `uixo`, or USDC.inj on mainnet |
|
|
202
226
|
| Guessing the evaluation amount | it must meet the oracle's `EVALUATION_MIN_AMOUNT` in its `EVALUATION_DENOM`, or register 402s |
|
|
203
227
|
| `--eval-amount 0` expecting a zero coin | it writes an empty evaluation leg — right only for a per-review-billed or 0-minimum oracle |
|
|
204
228
|
| Running with `--yes` first | dry-run, show the plan, then confirm |
|
|
@@ -58,7 +58,7 @@ explicitly — `0<--eval-denom>` unless the owner passes `--max-custom-amount <a
|
|
|
58
58
|
chain checks EVERY non-empty evaluation amount against it (x/claims `IsCoinsInMaxConstraints`; the
|
|
59
59
|
denom must be listed), and the act lane sends an ordinary claim's own coins by default, so claims
|
|
60
60
|
that carry coins need a cap ≥ the largest claim amount in each denom — at `0` their approvals fail
|
|
61
|
-
at MsgExec. It is also the owner's cap on any amount an evaluator sets
|
|
61
|
+
at MsgExec. It is also the owner's cap on any amount an evaluator sets; the
|
|
62
62
|
oracle side's own switch is the owner's `operatorAmounts` (`PATCH /v1/collections/:id/settings`),
|
|
63
63
|
honoured only while the owner who set it still owns the collection. All grant messages for one run
|
|
64
64
|
broadcast as ONE tx. No addresses given ⇒
|
|
@@ -24,7 +24,8 @@ Write the rules claims are judged by → `create-rubric`. Put a claim form and i
|
|
|
24
24
|
`publish-protocol`. Open a place for claims to be submitted and hire an oracle on it →
|
|
25
25
|
`create-collection`. Let apps and agents ask the oracle for decisions over HTTP →
|
|
26
26
|
`decision-api-oracle`; test that from the customer's side → `decision-api-client`. Be hireable from
|
|
27
|
-
the Openhands app → `add-openhands-support`.
|
|
27
|
+
the Openhands app → `add-openhands-support`. Change what the oracle charges → `update-pricing`. Keep
|
|
28
|
+
a deployed oracle running (upgrades, pauses, renewals, alerts) → `operate-oracle`.
|
|
28
29
|
|
|
29
30
|
**Words used throughout:**
|
|
30
31
|
|
|
@@ -42,7 +43,7 @@ the Openhands app → `add-openhands-support`.
|
|
|
42
43
|
- **did:web** — this oracle's web identity, a name tied to its domain; other services check its
|
|
43
44
|
signatures by fetching `https://<host>/.well-known/did.json`.
|
|
44
45
|
- **Delegation** — a signed permission one party gives another, such as the owner letting the engine
|
|
45
|
-
bill their
|
|
46
|
+
bill their engine credits, or letting the oracle read their claims.
|
|
46
47
|
|
|
47
48
|
## When to use
|
|
48
49
|
|
|
@@ -61,12 +62,23 @@ handlers themselves (`write-facts`, handed off to at step 3).
|
|
|
61
62
|
bunx @ixo/decision-oracle init <dir> [--name <project-name>] [--openhands]
|
|
62
63
|
cd <dir> && bun install
|
|
63
64
|
bun run cf-typegen # after ANY wrangler.jsonc binding/var change, too
|
|
64
|
-
wrangler d1 create <project-name> # paste the id into wrangler.jsonc
|
|
65
|
-
wrangler kv namespace create <project>-cache # paste the id into wrangler.jsonc (KV names are per account)
|
|
66
65
|
bun run db:migrate # the package's own migrations, applied to the local D1
|
|
67
66
|
cp .dev.vars.example .dev.vars # git-ignored; secrets only
|
|
68
67
|
```
|
|
69
68
|
|
|
69
|
+
Local dev needs no Cloudflare resources: the placeholder ids at the top of `wrangler.jsonc` are fine
|
|
70
|
+
for `bun run dev` and the tests. Each env you deploy (`devnet`, `testnet`, `production` in the
|
|
71
|
+
template) needs its own D1 and KV, named exactly as `init` printed them, because that env's block
|
|
72
|
+
already names the database `<project-name>-<env>`:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
bunx wrangler d1 create <project-name>-<env> # → env.<env> d1_databases[0].database_id
|
|
76
|
+
bunx wrangler kv namespace create <project-name>-cache-<env> # → env.<env> kv_namespaces[0].id
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Paste each printed id over its placeholder in that env's block only (KV names are per account, so
|
|
80
|
+
two oracles in one account need two project names).
|
|
81
|
+
|
|
70
82
|
AI checks run on the Cloudflare AI Gateway by default. `AI_GATEWAY_ID` (in `wrangler.jsonc`, every
|
|
71
83
|
env) is the name of a gateway in the provider's own Cloudflare account: dashboard → AI → AI Gateway
|
|
72
84
|
→ create one and use its name. Third-party models through it need AI Gateway credits on that
|
|
@@ -154,6 +166,11 @@ signed-in AI app's own auto-approve limits (the hosted MCP's "Don't ask again")
|
|
|
154
166
|
without a per-claim approval; `false` means every such submit waits for the user's approval of that
|
|
155
167
|
claim. IXO's own oracles turn it on; anyone else decides it explicitly — ask.
|
|
156
168
|
|
|
169
|
+
Signing uses the collection's evaluate grant. Each `MsgEvaluateClaim` the oracle signs costs gas
|
|
170
|
+
(paid by `ORACLE_ADDRESS`, which `oracle init` keeps topped up) and one slot of the grant's quota. A
|
|
171
|
+
claim that goes to a person costs two: it is first signed FLAGGED (status 5, pays nothing) while the
|
|
172
|
+
person decides, then signed again with the final verdict after the review.
|
|
173
|
+
|
|
157
174
|
### 3. The handlers → the `write-facts` skill
|
|
158
175
|
|
|
159
176
|
```bash
|
|
@@ -187,13 +204,14 @@ table produced plus any validation issue (exit 1 if there are issues); `--previe
|
|
|
187
204
|
engine's preview lane and prints the decision they would produce — nothing persisted, nothing billed,
|
|
188
205
|
no grant needed. This is the first end-to-end proof.
|
|
189
206
|
|
|
190
|
-
A flat answers file has no attachments, so any fact that reads one fails closed:
|
|
191
|
-
|
|
192
|
-
|
|
207
|
+
A flat answers file has no attachments, so any fact that reads one fails closed: an AI check over a
|
|
208
|
+
photo freezes as an `error` (its guard's path) rather than reaching the stock judge. Exercising
|
|
209
|
+
that lane needs the envelope, with each file as `{ question, name, mediaType, path }` (`path`
|
|
210
|
+
relative to the claim file) — the `write-facts` skill shows one.
|
|
193
211
|
|
|
194
212
|
### 6. The owner's delegation (engine billing)
|
|
195
213
|
|
|
196
|
-
The engine bills the oracle OWNER's credits, so the owner —
|
|
214
|
+
The engine bills the oracle OWNER's credits, so the owner — the account that buys those credits —
|
|
197
215
|
mints a delegation to the engine before register will work. **Do this step, don't just quote it.**
|
|
198
216
|
|
|
199
217
|
```bash
|
|
@@ -214,13 +232,14 @@ printf 'OWNER_DELEGATION_CAR="%s"\n' "$(cat owner-delegation.car)" >> .dev.vars
|
|
|
214
232
|
before it lapses.
|
|
215
233
|
- Only the human can do the login. Run it in the background so the loopback callback lands, and tell
|
|
216
234
|
them to finish in the browser.
|
|
217
|
-
- **The owner
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
- **A 402 at register is the owner's credits, not a bug**: `NO_SUBSCRIPTION` (
|
|
222
|
-
`INSUFFICIENT_CREDITS` (below one decision's price
|
|
223
|
-
|
|
235
|
+
- **The owner needs IXO decision-engine credits before `engine register`.** The engine bills
|
|
236
|
+
every decision to them. The owner buys credits in the Decision Console (devnet:
|
|
237
|
+
`https://decision-console.devnet.ixo.earth`) › Billing › Credits, signed in as the same account
|
|
238
|
+
that minted the delegation; the first purchase opens their credits account.
|
|
239
|
+
- **A 402 at register is the owner's credits, not a bug**: `NO_SUBSCRIPTION` (no credits account
|
|
240
|
+
yet) or `INSUFFICIENT_CREDITS` (below one decision's price; the engine adds a
|
|
241
|
+
`manageSubscriptionUrl`). `engine register` prints where to buy them (`<CONSOLE_URL>/billing/credits`
|
|
242
|
+
when `CONSOLE_URL` is set). Send the owner there, then register again — nothing here can fake it.
|
|
224
243
|
|
|
225
244
|
### 7. Run it locally — and leave it running
|
|
226
245
|
|
|
@@ -321,16 +340,45 @@ where a live token would have skipped (it mints a new Matrix device — not a ca
|
|
|
321
340
|
|
|
322
341
|
### 9. Deploy it
|
|
323
342
|
|
|
343
|
+
First write that env's secrets file, `.dev.vars.<env>` (git-ignored like `.dev.vars`). It is what
|
|
344
|
+
`--secrets-file` uploads and what `engine register --env-file` reads below. Never echo a value:
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
{
|
|
348
|
+
grep -E '^(SERVICE_ED25519_MNEMONIC|ORACLE_SECP_MNEMONIC|ORACLE_MATRIX_ACCESS_TOKEN)=' .dev.vars
|
|
349
|
+
printf 'OWNER_DELEGATION_CAR="%s"\n' "$(cat owner-delegation.car)"
|
|
350
|
+
printf 'UNIQUE_HMAC_KEY="%s"\n' "$(openssl rand -base64 32)"
|
|
351
|
+
} > .dev.vars.<env> && chmod 600 .dev.vars.<env>
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
What a deployed env needs:
|
|
355
|
+
|
|
356
|
+
| Secret | Needed |
|
|
357
|
+
| --- | --- |
|
|
358
|
+
| `SERVICE_ED25519_MNEMONIC` | always — the did:web key |
|
|
359
|
+
| `ORACLE_SECP_MNEMONIC` | always — signs `MsgEvaluateClaim` and the engine registration |
|
|
360
|
+
| `OWNER_DELEGATION_CAR` | always — register refuses without it. Mint it (step 6) with `--oracle did:web:<this env's host>`: a CAR minted for another host is refused `DELEGATION_NOT_FOR_ORACLE` |
|
|
361
|
+
| `UNIQUE_HMAC_KEY` | when a rubric declares `unique`, and always with `API_LANE` on. One per deployment, for life |
|
|
362
|
+
| `ORACLE_MATRIX_ACCESS_TOKEN` | for review cards. It belongs to one network's homeserver: copy it only into an env on the network step 8 ran on |
|
|
363
|
+
| `OPENROUTER_API_KEY` | only with `AI_PROVIDER=openrouter` (the AI Gateway default needs no secret) |
|
|
364
|
+
| `WEBHOOK_ENCRYPTION_KEY` | only with `API_LANE` on (`openssl rand -base64 32`, for life) |
|
|
365
|
+
| `SENTRY_DSN`, `OPS_ALERT_WEBHOOK_URL` | optional (`operate-oracle`) |
|
|
366
|
+
|
|
367
|
+
The env file must NOT carry a localhost `ORACLE_HOSTNAME` (that env's host comes from its
|
|
368
|
+
`wrangler.jsonc` vars). **Keep a private backup of every secret outside the repo** (a password
|
|
369
|
+
manager, or a 0600 file only the owner can read): Cloudflare never shows a secret again, and a lost
|
|
370
|
+
mnemonic or `UNIQUE_HMAC_KEY` cannot be recovered — a new one is a new identity or a blind
|
|
371
|
+
duplicate registry.
|
|
372
|
+
|
|
324
373
|
```bash
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
wrangler secret put ORACLE_MATRIX_ACCESS_TOKEN # the value step 8 wrote into .dev.vars
|
|
374
|
+
bunx wrangler d1 migrations apply DB --remote --env <env> # BEFORE the first deploy
|
|
375
|
+
bunx wrangler deploy --env <env> --secrets-file .dev.vars.<env> # code + secrets in one version
|
|
376
|
+
bunx wrangler secret put ORACLE_MATRIX_ACCESS_TOKEN --env <env> # later, one secret at a time (e.g. after --relogin)
|
|
329
377
|
```
|
|
330
378
|
|
|
331
|
-
Before deploying: the D1/KV ids for that env are real, that env's `ORACLE_HOSTNAME` is the host
|
|
379
|
+
Before deploying: the D1/KV ids for that env are real, and that env's `ORACLE_HOSTNAME` is the host
|
|
332
380
|
it will be served from (its Custom Domain in `routes`, or, with no `routes`, its `workers.dev`
|
|
333
|
-
host)
|
|
381
|
+
host). Later deploys are `bun run deploy:<env>` (secrets persist). **Then run step 8 again with
|
|
334
382
|
`--kit-url https://<the deployed host>` and `--yes`**: the `service` step re-points `#eval` at the
|
|
335
383
|
real host and `profile` anchors the `#pro` it had to skip. `fund` may act again too — it is a floor
|
|
336
384
|
check, so a wallet the first run's own `iid`/`key` transactions left below 1 IXO is topped back
|
|
@@ -352,7 +400,7 @@ failed check — `identity` means `ORACLE_ENTITY_DID` is missing from that env's
|
|
|
352
400
|
bunx decision-oracle engine register --env <env> --env-file .dev.vars.<env>
|
|
353
401
|
```
|
|
354
402
|
|
|
355
|
-
The chain lane proves its address lazily, on its first `decide`; until then the engine — and the
|
|
403
|
+
The `.dev.vars.<env>` file from the deploy step above supplies the keys. The chain lane proves its address lazily, on its first `decide`; until then the engine — and the
|
|
356
404
|
Decision Console, which lists the engine's `GET /v1/oracles` — does not know this oracle exists.
|
|
357
405
|
`billing: ok` is done; a 402 is the owner's credits (step 6).
|
|
358
406
|
|
|
@@ -394,7 +442,7 @@ the required fee (`pricing.byProtocol` for that collection's protocol, else the
|
|
|
394
442
|
| Standing approvals | may a signed-in AI app's own auto-approve limits pay without a per-claim approval (hosted MCP) — IXO's own oracles say yes | `policies.allowStandingApprovals` (required) |
|
|
395
443
|
| Custom lanes | per judgment-call name: do you control the endpoint / is a stock AI check the right judge | the `src/facts/` table (`write-facts`) |
|
|
396
444
|
| Identity | a fresh wallet, or an existing mnemonic to derive from | `derive`, `.dev.vars` + `wrangler.jsonc` vars |
|
|
397
|
-
| Owner | who owns this oracle — the AuthHub login whose
|
|
445
|
+
| Owner | who owns this oracle — the AuthHub login whose engine credits pay for decisions | `login`, `delegation --for engine` |
|
|
398
446
|
| Funding | nothing to ask — `oracle init` keeps the wallet topped up to 1 IXO for gas | `oracle init` (the `fund` step) |
|
|
399
447
|
| Hosts | the did:web host per env (it IS the identity): a custom domain, or the free `workers.dev` host | `ORACLE_HOSTNAME`, `routes` (none for `workers.dev`) |
|
|
400
448
|
| Contact | the address that gets pause / review notices | `register --contact-email` |
|
|
@@ -418,7 +466,7 @@ the required fee (`pricing.byProtocol` for that collection's protocol, else the
|
|
|
418
466
|
| --- | --- |
|
|
419
467
|
| Reading `rubric.json` to work out what to produce | read the manifest — `protocol review` / `manifest <did>` |
|
|
420
468
|
| A stub that returns a fake `ok` so a run looks green | `fact.error("not implemented: <name>")` |
|
|
421
|
-
| Register answers 402 and the agent retries | it is the owner's credits — send them to the printed
|
|
469
|
+
| Register answers 402 and the agent retries | it is the owner's credits — send them to the printed credits page, then re-run |
|
|
422
470
|
| `.dev.vars` edited but the old value still in play | a running `wrangler dev` reloads source, not secrets — restart it |
|
|
423
471
|
| `ORACLE_ADDRESS` typed by hand | must match the mnemonic exactly — asserted at first use |
|
|
424
472
|
| Guessing `EVALUATION_MIN_AMOUNT` | ask what the collection's on-chain evaluation payment is (or will be) — set too high and register 402s |
|
|
@@ -467,6 +515,6 @@ restart, re-register. Restart both dev servers after any `.dev.vars` edit.
|
|
|
467
515
|
The review output from step 2, the stock-vs-custom verdict and the handlers written (or stubbed,
|
|
468
516
|
named), the `src/index.ts` config block, the `.dev.vars` / `wrangler.jsonc` lines to set (never
|
|
469
517
|
committed, never printed), the `facts run --preview` decision, and — plainly — what the provider
|
|
470
|
-
still owes:
|
|
518
|
+
still owes: the owner's engine credits, the on-chain Evaluate grant (or the step-10
|
|
471
519
|
self-hire), the deployment that replaces `localhost`, the `oracle init` re-run that re-points
|
|
472
520
|
`#eval` at it, and any name still on a stub.
|
|
@@ -19,10 +19,11 @@ A source in billing mode `x402` answers a submit with `402`. The payer pays it w
|
|
|
19
19
|
customer's **payment delegation**, a UCAN to the facilitator carrying a `billing/pay` cap. The
|
|
20
20
|
price is held when the claim is accepted and paid only on the engine's receipt.
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
Point it at your own oracle, or at IXO's public demo: SwiftDrop, at
|
|
23
|
+
`https://shipment-delivery-oracle.devnet.ixo.earth`, protocol
|
|
24
|
+
`did:ixo:entity:c2f5a188d28f3d4bf6351873d3d268ea`, price 60¢. Its `/demo/orders?route=<id>` issues
|
|
25
|
+
a fresh courier route per id, with real order ids and delivery codes. Use a new route (or, on your
|
|
26
|
+
own oracle, new answers) per test claim, so the duplicate lane never sees a repeat.
|
|
26
27
|
|
|
27
28
|
**Core principle: run it, don't recite it.** Every step below is an action you perform. A test
|
|
28
29
|
claim spends real devnet PAY from the session's deed, so say how many claims you will send before
|
|
@@ -30,8 +31,9 @@ sending them.
|
|
|
30
31
|
|
|
31
32
|
## When to use
|
|
32
33
|
|
|
33
|
-
- "
|
|
34
|
-
|
|
34
|
+
- "Call this oracle from my app", "test the SDK / CLI / MCP against devnet", "does the paid flow
|
|
35
|
+
work", "mint a test key".
|
|
36
|
+
- Before you ship an integration, or after the oracle changes its price, protocols or plan.
|
|
35
37
|
- Any symptom in the table at the end.
|
|
36
38
|
|
|
37
39
|
Not for: standing up or deploying the oracle (`decision-api-oracle`), or the chain lane
|
|
@@ -143,25 +145,17 @@ the console) must make the next claim ask again. "Don't ask again" works only on
|
|
|
143
145
|
`policies.allowStandingApprovals` is `true`; otherwise setting it is refused (`403`, reason
|
|
144
146
|
`standing_approvals_off`) and every claim asks.
|
|
145
147
|
|
|
146
|
-
### 4.
|
|
147
|
-
|
|
148
|
-
Card
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
| --- | --- | --- |
|
|
158
|
-
| `pm_card_authenticationRequired` | `prefer: "card"` | `CARD_AUTHENTICATION_REQUIRED` + `confirmUrl`; once confirmed, the resume → paid with the SAME PaymentIntent. Left unconfirmed, the facilitator cancels it ~17 min later, never charged |
|
|
159
|
-
| `pm_card_chargeCustomerFail` | `prefer: "card"` | declined at authorize → paid with PAY for the same 402 |
|
|
160
|
-
| `pm_card_createDispute` | `prefer: "card"`, then again once the dispute is open | paid by card; then `card_blocked` → PAY (no PaymentIntent created); after the dispute is won, card again |
|
|
161
|
-
| `pm_card_visa`, preference `card` | no `prefer` | the SDK reads `/v1/payer/preferences` with the delegation → card |
|
|
162
|
-
|
|
163
|
-
Every claim's record names the rail: `stripe-manual-capture` / `usd` / cents, or `ixo-pay` / `upay`.
|
|
164
|
-
A run that answers `SPEND_CAP` has used up the delegation's `maxAmount`: mint a new one.
|
|
148
|
+
### 4. Paying by card
|
|
149
|
+
|
|
150
|
+
Card is offered only when the protocol costs **≥ 50¢ per claim** (Stripe's minimum capture) and the
|
|
151
|
+
oracle's plan accepts `card`. The payer saves a card once in the Decision Console. An SDK submit
|
|
152
|
+
takes `prefer: "card"` (`"upay"` = PAY first; unset = the payer's console default). When the bank
|
|
153
|
+
asks for 3-D Secure the submit throws `CARD_AUTHENTICATION_REQUIRED` with a `confirmUrl`: confirm
|
|
154
|
+
there, then resume with the SAME offer and key
|
|
155
|
+
(`{ idempotencyKey: err.idempotencyKey, resumePayment: err.paymentRequired }`). Left unconfirmed,
|
|
156
|
+
the hold is cancelled and nothing is charged. Every claim's record names the rail:
|
|
157
|
+
`stripe-manual-capture` / `usd` / cents, or `ixo-pay` / `upay`. A run that answers `SPEND_CAP` has
|
|
158
|
+
used up the delegation's `maxAmount`: mint a new one.
|
|
165
159
|
|
|
166
160
|
## When something fails
|
|
167
161
|
|