@ixo/decision-oracle 0.2.30 → 0.2.32
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/dist/contracts/api.d.ts +6 -0
- package/dist/contracts/api.d.ts.map +1 -1
- package/dist/contracts/api.js +1 -0
- package/dist/contracts/api.js.map +1 -1
- package/dist/contracts/webhooks.d.ts +7 -0
- package/dist/contracts/webhooks.d.ts.map +1 -1
- package/dist/db/schema.d.ts +2 -2
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +3 -2
- package/dist/db/schema.js.map +1 -1
- package/dist/lib/ixo/chain-write.d.ts +5 -3
- package/dist/lib/ixo/chain-write.d.ts.map +1 -1
- package/dist/lib/ixo/chain-write.js +68 -2
- package/dist/lib/ixo/chain-write.js.map +1 -1
- package/dist/routes/v1/api/openapi.gen.d.ts.map +1 -1
- package/dist/routes/v1/api/openapi.gen.js +1 -1
- package/dist/routes/v1/api/openapi.gen.js.map +1 -1
- package/dist/routes/v1/shared.d.ts +1 -1
- package/dist/workflows/claim-ingest.d.ts.map +1 -1
- package/dist/workflows/claim-ingest.js +4 -3
- package/dist/workflows/claim-ingest.js.map +1 -1
- package/docs/api.md +10 -2
- package/docs/guide.md +7 -3
- package/openapi/decision-api.yaml +7 -1
- package/package.json +2 -2
- 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 +129 -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
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: decision-api-oracle
|
|
3
|
-
description: Use when setting up, deploying or testing a Decision API oracle (API_LANE on) —
|
|
3
|
+
description: Use when setting up, deploying or testing a Decision API oracle (API_LANE on) — a provider turning the lane on for their own oracle, from protocol to a verified paid claim — or when debugging one - a 401 on the console, PROTOCOL_NOT_ALLOWED, 503 payments-disabled, plan_inactive, an oracle the Decision Console does not list, member or review emails that never arrive.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# A Decision API oracle, from nothing to a
|
|
6
|
+
# A Decision API oracle, from nothing to a verified claim
|
|
7
7
|
|
|
8
8
|
## Overview
|
|
9
9
|
|
|
@@ -14,73 +14,77 @@ plan from the moment it registers, unless IXO sets another);
|
|
|
14
14
|
the payments facilitator settles each claim the ways that plan allows — by card and/or USDC on Base
|
|
15
15
|
for a third-party oracle, PAY only for IXO-run ones — into the oracle's own earnings addresses
|
|
16
16
|
(`EARNINGS_ADDRESS` / `EARNINGS_BASE_WALLET`); the Decision Console
|
|
17
|
-
(`decision-console.devnet.ixo.earth`) is the UI over all of it.
|
|
17
|
+
(`decision-console.devnet.ixo.earth` on devnet) is the UI over all of it.
|
|
18
18
|
|
|
19
|
-
The
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
and the courier demo page (`/courier`). A provider's own project (from
|
|
23
|
-
`decision-oracle init`) follows the same steps with their names. Where the example has a script the
|
|
24
|
-
template does not (`deploy:devnet`, an env-scoped `db:migrate:remote`, `bun run smoke`), the step
|
|
25
|
-
says **example** and gives the template's equivalent — use that in a provider's project.
|
|
19
|
+
The run below is a provider's own project (from `decision-oracle init`), deployed to one wrangler
|
|
20
|
+
env (`devnet`, `testnet` or `production` in the template). `<project-name>` is the name `init` was
|
|
21
|
+
given, `<env>` the wrangler env, `<host>` that env's `ORACLE_HOSTNAME`.
|
|
26
22
|
|
|
27
23
|
**Core principle: every step is an action you perform, in this order.** Run the commands; don't
|
|
28
24
|
recite them. Steps only a human can do are marked **HUMAN** — say exactly what to do and wait.
|
|
29
25
|
**Anything that signs on chain or changes an identity (publish, `oracle init`, a key the oracle
|
|
30
26
|
already uses) is dry-run first, shown, and run with `--yes` only on the owner's explicit OK.**
|
|
31
27
|
|
|
28
|
+
**IXO's public demo.** SwiftDrop Express, a fictional courier with cash on delivery, runs this lane
|
|
29
|
+
on devnet at `https://shipment-delivery-oracle.devnet.ixo.earth` (its courier page is `/courier`).
|
|
30
|
+
Use it to see the lane working from the customer's side (`decision-api-client`); build your own
|
|
31
|
+
from the steps below.
|
|
32
|
+
|
|
32
33
|
## When to use
|
|
33
34
|
|
|
34
|
-
- "
|
|
35
|
-
- A provider
|
|
35
|
+
- "Turn on / deploy / test the Decision API for my oracle"
|
|
36
|
+
- A provider adding `API_LANE` to an oracle that already serves the chain lane
|
|
36
37
|
- Any symptom in the troubleshooting table below
|
|
37
38
|
|
|
38
39
|
Not for: the chain lane's collections (`create-collection`), authoring a rubric (`create-rubric`),
|
|
39
|
-
writing handlers (`write-facts`)
|
|
40
|
+
writing handlers (`write-facts`), changing the price after launch (`update-pricing`), day-2
|
|
41
|
+
operations (`operate-oracle`).
|
|
40
42
|
|
|
41
43
|
## The run — in order
|
|
42
44
|
|
|
43
|
-
Work from the project directory. `bunx decision-oracle`
|
|
44
|
-
is the package CLI.
|
|
45
|
+
Work from the project directory. `bunx decision-oracle` is the package CLI.
|
|
45
46
|
|
|
46
47
|
### 0. Prerequisites
|
|
47
48
|
|
|
48
49
|
```bash
|
|
49
|
-
bunx wrangler whoami
|
|
50
|
-
bunx decision-oracle login --network devnet # the OWNER's AuthHub session — once
|
|
50
|
+
bunx wrangler whoami # the Cloudflare account the oracle deploys to
|
|
51
|
+
bunx decision-oracle login --network devnet # the OWNER's AuthHub session — once per network
|
|
51
52
|
```
|
|
52
53
|
|
|
53
54
|
- `login` opens a browser: run it in the background and tell the human to finish there (**HUMAN**).
|
|
54
55
|
The session is the owner: it publishes the protocol, owns the oracle entity, and is the operator.
|
|
55
56
|
Put the session's OWN DID (`did:ixo:<address>` — `login` prints the address) in
|
|
56
|
-
`ORACLE_ADMIN_DIDS` in every `vars` block of `wrangler.jsonc`.
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
`ORACLE_ADMIN_DIDS` in every `vars` block of `wrangler.jsonc`.
|
|
58
|
+
- A custom domain is optional. Without one, delete each env's `routes` entry and set that env's
|
|
59
|
+
`ORACLE_HOSTNAME` to the Worker's free `<worker name>.<account subdomain>.workers.dev` host (the
|
|
60
|
+
first deploy prints it). With one, the account must hold the zone of the host, or the Custom
|
|
61
|
+
Domain cannot attach — if `whoami` shows another account, stop and say so (**HUMAN**). Either way
|
|
62
|
+
the host IS the did:web: pick it before `oracle init`.
|
|
63
|
+
|
|
64
|
+
### 1. The protocol
|
|
59
65
|
|
|
60
|
-
|
|
66
|
+
Use an existing protocol's DID, or publish your own form and rubric (the `create-rubric` and
|
|
67
|
+
`publish-protocol` skills):
|
|
61
68
|
|
|
62
69
|
```bash
|
|
63
70
|
bunx decision-oracle rubric validate --rubric protocol/rubric.json --form protocol/form.json
|
|
64
71
|
bunx decision-oracle publish-protocol --rubric protocol/rubric.json --form protocol/form.json --network devnet
|
|
65
72
|
```
|
|
66
73
|
|
|
67
|
-
The second line is a dry run: show the plan, get the owner's OK, then re-run it with `--yes`.
|
|
68
|
-
`publish-protocol`
|
|
69
|
-
`src/
|
|
70
|
-
`publish-protocol` left in `protocol/` — those are the anchored bytes. The rubric's four `ext`
|
|
71
|
-
sources share the endpoint `https://shipment-delivery-oracle.devnet.ixo.earth/demo/orders`: it
|
|
72
|
-
answers once step 5 deploys. Each is called with a UCAN from this oracle's did:web to the
|
|
73
|
-
endpoint's own did:web (`did:web:shipment-delivery-oracle.devnet.ixo.earth`), capability
|
|
74
|
-
`ixo:external-source` — the oracle never signs a capability the rubric names, and never an IXO
|
|
75
|
-
service's (`api.md`, "How an external source is called"). Check the work order:
|
|
74
|
+
The second line is a dry run: show the plan, get the owner's OK, then re-run it with `--yes`. Keep
|
|
75
|
+
whatever `publish-protocol` left in `protocol/` — those are the anchored bytes. Put the protocol DID
|
|
76
|
+
in `supportedProtocols` in `src/index.ts`. Then read the work order:
|
|
76
77
|
|
|
77
78
|
```bash
|
|
78
79
|
bunx decision-oracle protocol review <protocol did> --network devnet
|
|
79
80
|
```
|
|
80
81
|
|
|
81
|
-
It
|
|
82
|
-
|
|
83
|
-
`
|
|
82
|
+
It lists every `ext` source and `ai` check the oracle must answer, plus any `unique` / `frequency`
|
|
83
|
+
rule. Write a handler for each name that needs one (`write-facts`) and get `bun run check` green.
|
|
84
|
+
An `ext` source is called with a UCAN from this oracle's did:web to the endpoint's own did:web,
|
|
85
|
+
capability `ixo:external-source` — the oracle never signs a capability the rubric names, and never
|
|
86
|
+
an IXO service's (`api.md`, "How an external source is called"). An endpoint you host yourself must
|
|
87
|
+
be live before claims arrive.
|
|
84
88
|
|
|
85
89
|
### 2. Identity
|
|
86
90
|
|
|
@@ -88,17 +92,20 @@ Do `create-oracle` step 1 (two `derive --new --write` runs, the first `--as SERV
|
|
|
88
92
|
the second is `ORACLE_SECP_MNEMONIC`) and step 6 (the owner's engine delegation), with:
|
|
89
93
|
|
|
90
94
|
- the three public lines (`ORACLE_ADDRESS`, `ORACLE_DID`, `ORACLE_MATRIX_USER_ID`) written into BOTH
|
|
91
|
-
the top-level and `env
|
|
95
|
+
the top-level and `env.<env>` `vars` of `wrangler.jsonc`;
|
|
92
96
|
- the delegation minted for THIS host:
|
|
93
|
-
`bunx decision-oracle delegation --for engine --audience did:web
|
|
97
|
+
`bunx decision-oracle delegation --for engine --audience did:web:<engine host> --oracle did:web:<host> --out owner-delegation.car --yes`
|
|
98
|
+
(the engine host is `decision-engine.devnet.ixo.earth`, `decision-engine.testnet.ixo.earth` or
|
|
99
|
+
`decision-engine.ixo.earth`).
|
|
94
100
|
|
|
95
101
|
An oracle that already has keys keeps them — replacing a live oracle's key is an identity change:
|
|
96
102
|
ask first. Never print a mnemonic or the CAR.
|
|
97
103
|
|
|
104
|
+
Turn the lane on in that env's `vars`: `"API_LANE": "true"`.
|
|
105
|
+
|
|
98
106
|
### 3. Secrets
|
|
99
107
|
|
|
100
|
-
Write `.dev.vars.<env>` (git-ignored, like `.dev.vars
|
|
101
|
-
never echo a value to the terminal:
|
|
108
|
+
Write `.dev.vars.<env>` (git-ignored, like `.dev.vars`) — never echo a value to the terminal:
|
|
102
109
|
|
|
103
110
|
```bash
|
|
104
111
|
{
|
|
@@ -106,31 +113,41 @@ never echo a value to the terminal:
|
|
|
106
113
|
printf 'OWNER_DELEGATION_CAR="%s"\n' "$(cat owner-delegation.car)"
|
|
107
114
|
printf 'WEBHOOK_ENCRYPTION_KEY="%s"\n' "$(openssl rand -base64 32)"
|
|
108
115
|
printf 'UNIQUE_HMAC_KEY="%s"\n' "$(openssl rand -base64 32)"
|
|
109
|
-
} > .dev.vars.<env>
|
|
116
|
+
} > .dev.vars.<env> && chmod 600 .dev.vars.<env>
|
|
110
117
|
```
|
|
111
118
|
|
|
112
|
-
|
|
113
|
-
`
|
|
114
|
-
`
|
|
115
|
-
|
|
116
|
-
|
|
119
|
+
AI checks run on the Cloudflare AI Gateway by default (`AI_GATEWAY_ID` in `wrangler.jsonc` + the
|
|
120
|
+
`ai` binding — no secret; the account needs AI Gateway credits). Only with `AI_PROVIDER=openrouter`:
|
|
121
|
+
`OPENROUTER_API_KEY` — ask the owner for the key, **HUMAN**, and append it the same way. Optional:
|
|
122
|
+
`SENTRY_DSN`. `ORACLE_MATRIX_ACCESS_TOKEN` is required but comes later — `oracle init` writes it
|
|
123
|
+
(step 7).
|
|
117
124
|
`WEBHOOK_ENCRYPTION_KEY` and `UNIQUE_HMAC_KEY` are for life — generate them ONCE per deployment;
|
|
118
125
|
on an existing deployment, never regenerate (rotating strands every webhook secret / blinds the
|
|
119
|
-
duplicate registry).
|
|
126
|
+
duplicate registry). **Keep a private backup of this file outside the repo** (a password manager,
|
|
127
|
+
or a 0600 file only the owner can read): Cloudflare never shows a secret again. `.dev.vars` needs
|
|
128
|
+
its own `WEBHOOK_ENCRYPTION_KEY` for `bun run dev`.
|
|
120
129
|
|
|
121
130
|
### 4. Cloudflare resources
|
|
122
131
|
|
|
123
|
-
|
|
124
|
-
|
|
132
|
+
One set per env, named as `init` printed them (the env's block already names the database and the
|
|
133
|
+
bucket this way):
|
|
125
134
|
|
|
126
135
|
```bash
|
|
127
|
-
bunx wrangler d1 create
|
|
128
|
-
bunx wrangler kv namespace create <project>-cache
|
|
129
|
-
bunx wrangler r2 bucket create
|
|
136
|
+
bunx wrangler d1 create <project-name>-<env> # → env.<env> d1 database_id
|
|
137
|
+
bunx wrangler kv namespace create <project-name>-cache-<env> # → env.<env> kv id
|
|
138
|
+
bunx wrangler r2 bucket create <project-name>-uploads-<env> # optional: file uploads
|
|
130
139
|
```
|
|
131
140
|
|
|
132
141
|
Paste each printed id over its placeholder in `env.<env>` (only there — the top level is local
|
|
133
|
-
dev).
|
|
142
|
+
dev). For uploads, uncomment that env's `API_UPLOADS` line in `wrangler.jsonc`:
|
|
143
|
+
|
|
144
|
+
```jsonc
|
|
145
|
+
"r2_buckets": [{ "binding": "API_UPLOADS", "bucket_name": "<project-name>-uploads-<env>" }],
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Without it `PUT /v1/api/uploads` answers 501 and claims can still attach https URLs. If you also
|
|
149
|
+
bind `ANSWERS_ARCHIVE`, both go in the ONE `r2_buckets` array. Then `bun run cf-typegen` and
|
|
150
|
+
`bun run check`.
|
|
134
151
|
|
|
135
152
|
### 5. Migrate and deploy
|
|
136
153
|
|
|
@@ -140,11 +157,6 @@ bunx wrangler deploy --env <env> --secrets-file .dev.vars.<env> # code + secre
|
|
|
140
157
|
curl -s https://<host>/.well-known/did.json
|
|
141
158
|
```
|
|
142
159
|
|
|
143
|
-
**Example:** `bun run db:migrate:remote` and `bun run deploy:devnet -- --secrets-file
|
|
144
|
-
.dev.vars.devnet` are those two lines for `devnet`; also check the demo orders API and the
|
|
145
|
-
courier page answer (`curl -s https://shipment-delivery-oracle.devnet.ixo.earth/demo/orders/ORD-1-C`,
|
|
146
|
-
`/courier`). The template has no env-scoped migrate script — use the `wrangler` line.
|
|
147
|
-
|
|
148
160
|
Later deploys: `bunx wrangler deploy --env <env>` (secrets persist); one secret: `bunx wrangler
|
|
149
161
|
secret put <NAME> --env <env>`. The did.json `id` must be `did:web:<host>`.
|
|
150
162
|
|
|
@@ -163,29 +175,26 @@ bunx decision-oracle engine register --env <env> --env-file .dev.vars.<env>
|
|
|
163
175
|
```
|
|
164
176
|
|
|
165
177
|
It proves the oracle's address to the engine (ADR-36) with the owner's delegation and prints the
|
|
166
|
-
billing status. A 402 is the owner's credits:
|
|
178
|
+
billing status. A 402 is the owner's engine credits: the owner buys them in the Decision Console
|
|
179
|
+
(Billing › Credits), signed in as the account that minted the delegation (**HUMAN**), then re-run.
|
|
167
180
|
If the engine already binds this did:web to a DIFFERENT address, it prints old → new and refuses:
|
|
168
181
|
that is an identity change — ask the owner before re-running with `--yes`.
|
|
169
182
|
|
|
170
183
|
Registering is the whole step for a provider: an oracle with no plan of its own runs at the
|
|
171
184
|
engine's **Standard** plan at once (payable by card and/or USDC on Base, into the oracle's earnings
|
|
172
|
-
addresses; listed in the Decision Console). PAY is for IXO-run oracles only. Nothing to request.
|
|
173
|
-
`GET /v1/oracles` (the console's list).
|
|
174
|
-
|
|
175
|
-
An oracle IXO runs for itself moves to an IXO plan only on IXO's side: ask IXO to move it
|
|
176
|
-
(**HUMAN**), and it is done when IXO confirms the plan.
|
|
185
|
+
addresses; listed in the Decision Console). PAY is for IXO-run oracles only. Nothing to request.
|
|
186
|
+
Done when the oracle appears in the engine's `GET /v1/oracles` (the console's list).
|
|
177
187
|
|
|
178
188
|
### 7. On chain — the full identity (mandatory)
|
|
179
189
|
|
|
180
190
|
Every oracle built with the kit has a full on-chain identity — the entity, its `#pro` profile, its
|
|
181
|
-
`#dmn` domain card and its `#acard` Agent Card (which lists the `decision-api` service at the lane's
|
|
182
|
-
price) — even when the API lane is all it serves. **Required, not optional:** a deployed
|
|
183
|
-
without `ORACLE_ENTITY_DID` fails `/health/ready` (its `identity` check). In order:
|
|
191
|
+
`#dmn` domain card and its `#acard` Agent Card (which lists the `decision-api` service at the lane's
|
|
192
|
+
published price) — even when the API lane is all it serves. **Required, not optional:** a deployed
|
|
193
|
+
oracle without `ORACLE_ENTITY_DID` fails `/health/ready` (its `identity` check). In order:
|
|
184
194
|
|
|
185
|
-
1. Run `create-oracle` step 8 — `bunx decision-oracle oracle init --protocol <did> --kit-url https
|
|
195
|
+
1. Run `create-oracle` step 8 — `bunx decision-oracle oracle init --protocol <did> --kit-url https://<host> --logo <url|file> --network devnet`
|
|
186
196
|
as a dry run, show the plan, and re-run the identical line with `--yes` only on the owner's OK.
|
|
187
|
-
|
|
188
|
-
placeholder) — ask the owner for one.
|
|
197
|
+
Ask the owner for a logo (without it the portal and the console show a placeholder).
|
|
189
198
|
2. Pin the entity DID it prints as `ORACLE_ENTITY_DID` in `wrangler.jsonc` — the top-level `vars`
|
|
190
199
|
AND every `env.<name>.vars` (named envs don't inherit).
|
|
191
200
|
3. `bunx wrangler secret put ORACLE_MATRIX_ACCESS_TOKEN --env <env>` with the value it wrote into
|
|
@@ -198,16 +207,16 @@ The oracle is not done until readiness passes. A 503 there names nothing; the op
|
|
|
198
207
|
|
|
199
208
|
### 8. Operator: a source, its protocol, its billing, a key
|
|
200
209
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
`org fund` check and top up its admin account):
|
|
210
|
+
A source belongs to an organisation entity (`--entity`): the partner's, or one made with
|
|
211
|
+
`org create` (`org balance` / `org fund` check and top up its admin account):
|
|
204
212
|
|
|
205
213
|
```bash
|
|
206
214
|
bunx decision-oracle org create --name "<partner>" --network devnet --yes # → { entityDid } — once, idempotent by name
|
|
207
|
-
K="--kit https
|
|
215
|
+
K="--kit https://<host> --network devnet"
|
|
208
216
|
bunx decision-oracle api source create --name "<partner>" --contact <email> --entity <org did> $K # as a controller of the org; pending-link; secret shown ONCE
|
|
209
|
-
bunx decision-oracle api
|
|
210
|
-
bunx decision-oracle api admin
|
|
217
|
+
bunx decision-oracle api source link <sourceId> $K # dry run → --yes; pending-link → active
|
|
218
|
+
bunx decision-oracle api admin protocols <sourceId> <protocol did> $K # dry run → --yes
|
|
219
|
+
bunx decision-oracle api admin billing-mode <sourceId> none $K # only for a partner who must not pay; dry run → --yes
|
|
211
220
|
bunx decision-oracle api key create --source <sourceId> --scopes claims:write,claims:read,webhooks:manage $K
|
|
212
221
|
bunx decision-oracle api review-billing enable --source <sourceId> $K # --cap <PAY> (default 1000); "Pay for reviews": as the org's CHAIN OWNER; dry run → --yes
|
|
213
222
|
bunx decision-oracle api review-billing status --source <sourceId> $K
|
|
@@ -217,32 +226,36 @@ bunx decision-oracle api review-billing disable --source <sourceId> $K
|
|
|
217
226
|
`review-billing` exists only while IXO's plan bills the oracle's reviews per review (`/v1/info`
|
|
218
227
|
`reviewBilling.mode: "x402"`; `enable` refuses otherwise), and needs `API_LANE` AND `API_PAYMENTS`
|
|
219
228
|
on, a published `pricing.api` price, and the organisation's collections created with
|
|
220
|
-
`--eval-amount 0` (an empty evaluation leg — `create-collection`). The login must be the
|
|
221
|
-
owner on chain — the reviews come out of ITS credits — and `--cap` (PAY) is a
|
|
222
|
-
never refunded: raise it by running `enable` again with a higher `--cap`.
|
|
229
|
+
`--eval-amount 0` (an empty evaluation leg — `create-collection`). The login must be the
|
|
230
|
+
organisation's owner on chain — the reviews come out of ITS credits — and `--cap` (PAY) is a
|
|
231
|
+
lifetime total, never refunded: raise it by running `enable` again with a higher `--cap`.
|
|
223
232
|
|
|
224
233
|
The source is `pending-link` (no keys, no claims) until a controller of the organisation signs its
|
|
225
234
|
`SourceCompanyLink`, PUTs it to the engine and syncs — the Decision Console's "add oracle" does all
|
|
226
|
-
three, and so does `
|
|
227
|
-
session should not be one where its oracle's
|
|
228
|
-
through the oracle's owner).
|
|
235
|
+
three, and so does `api source link <sourceId> --yes`. The session runs `source create` /
|
|
236
|
+
`key create` only as a controller; the operator's own session should not be one where its oracle's
|
|
237
|
+
reviews matter (the engine refuses a review chain through the oracle's owner).
|
|
229
238
|
|
|
230
239
|
A new source allows exactly the oracle's `supportedProtocols` (empty until step 1's DID is in
|
|
231
|
-
`src/
|
|
232
|
-
|
|
233
|
-
### 9. Verify —
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
240
|
+
`src/index.ts` and deployed) — `admin protocols` REPLACES the list.
|
|
241
|
+
|
|
242
|
+
### 9. Verify — one claim per outcome
|
|
243
|
+
|
|
244
|
+
With step 8's key (`Authorization: Bearer <key>` on `https://<host>/v1/api`), or the
|
|
245
|
+
`@ixo/decision-oracle-client` SDK/CLI (`decision-api-client`):
|
|
246
|
+
|
|
247
|
+
1. `GET /v1/api/protocols/<protocol did>` — the work order and the price.
|
|
248
|
+
2. `PUT /v1/api/uploads` — a file, if the protocol takes one (needs step 4's bucket). An upload
|
|
249
|
+
attachment carries the `cid` its upload returned — `attachments[].{ question, uploadId, cid }`;
|
|
250
|
+
accept refuses one without it or with another upload's. Without uploads, attach
|
|
251
|
+
`{ question, url }` with an https link.
|
|
252
|
+
3. `POST /v1/api/claims` with `Prefer: wait=60` — one claim per outcome the rubric can reach
|
|
253
|
+
(approve, reject, review), each with its own answers so the duplicate lane never sees a repeat.
|
|
254
|
+
Send `submitter` (your stable id for the person) when the rubric has a `frequency` rule.
|
|
255
|
+
4. Answer a `review` claim in the Decision Console as a reviewer (below), and watch it fold to a
|
|
256
|
+
final decision.
|
|
257
|
+
5. With `API_PAYMENTS` on and a source in billing mode `x402`, one paid claim — the
|
|
258
|
+
`decision-api-client` skill has the payer side.
|
|
246
259
|
|
|
247
260
|
Three people, three keys — never the same account twice:
|
|
248
261
|
|
|
@@ -250,37 +263,21 @@ Three people, three keys — never the same account twice:
|
|
|
250
263
|
`api admin billing-mode` and `api admin protocols`. It must NOT be the organisation's
|
|
251
264
|
controller or the reviewer: the engine refuses any review grant chain through the oracle's
|
|
252
265
|
identities, its owner included.
|
|
253
|
-
- **the organisation's controller
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
`
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
fixtures, decides one stop per row of the example's outcome table (approve prepaid · approve COD ·
|
|
269
|
-
review cash short · review GPS far · partial refused · reject wrong code · reject unknown order ·
|
|
270
|
-
review reused photo, held on `unique:SD-DUP-1` with its `duplicates[]` naming the earlier claim ·
|
|
271
|
-
reject the same order claimed again by its courier (same `submitter`) · review dispatch down), answers the cash review (and the
|
|
272
|
-
claim-level `SD-RT-CHECKS` asked beside it) as a member does in the Decision Console (never an
|
|
273
|
-
oracle operator's account, which the engine refuses in a reviewer grant), waits for the engine's fold (`decided partial`), walks
|
|
274
|
-
the rest of the surface, then revokes its keys and DELETEs its sources as the controller (`--keep`
|
|
275
|
-
keeps both). Each run spends ~13 engine decisions (and one review fold) of the owner's
|
|
276
|
-
credits. Then, on the owner's OK:
|
|
277
|
-
|
|
278
|
-
- `--frequency` — one courier over the daily limit (26 more decisions);
|
|
279
|
-
- `--paid` — one x402 claim paid from the session's own devnet deed. Needs a **subscription deed**
|
|
280
|
-
whose admin holds PAY and has granted the payments facilitator's address a `SendAuthorization` —
|
|
281
|
-
the subscriptions service's devnet purchase / top-up does both (**HUMAN** if absent). Check:
|
|
282
|
-
`curl "https://devnet.ixo.earth/rest/cosmos/authz/v1beta1/grants?granter=<deed admin>&grantee=<facilitator address>&msg_type_url=/cosmos.bank.v1beta1.MsgSend"`.
|
|
283
|
-
Spends one decision price of PAY (60¢ in the example).
|
|
266
|
+
- **the organisation's controller**: a source belongs to an organisation, so the controller
|
|
267
|
+
creates it (`POST /v1/api/sources { entityDid }` → `pending-link`), signs the `SourceCompanyLink`
|
|
268
|
+
(kid = the did:key of the Ed25519 key on its IID, canonical payload) and PUTs it to the ENGINE
|
|
269
|
+
(`PUT /v1/sources/:oracleDid/:sourceId/company`, UCAN `company/link`), syncs
|
|
270
|
+
(`POST /v1/api/sources/:id/company/sync` → `active`), mints and revokes the keys, and invites the
|
|
271
|
+
reviewer. `api source link` does the link and the sync. Its key needs no chain write: it already
|
|
272
|
+
controls the entity.
|
|
273
|
+
- **the reviewer** (a third account that is neither of the two): a MEMBER. The controller invites
|
|
274
|
+
it by DID with role `reviewer` and PUTs its role grant (≤ 30 days); the member registers a
|
|
275
|
+
`did:key` reviewer key, grants it `claims/review` on the source and answers through the API with
|
|
276
|
+
that two-link `reviewerGrant` (controller → member → key). The Decision Console does all of this
|
|
277
|
+
for the member.
|
|
278
|
+
|
|
279
|
+
Each decision spends one of the owner's engine credits; a review fold is free. Revoke test keys
|
|
280
|
+
afterwards (`api key revoke`).
|
|
284
281
|
|
|
285
282
|
### 10. Verify — the six member / review emails
|
|
286
283
|
|
|
@@ -294,27 +291,34 @@ With the owner's email as the test inbox, in the console (**HUMAN** clicks; you
|
|
|
294
291
|
| 1 | "<inviter> invited you to <source>" | the owner invites a reviewer by email (Members → Invite) |
|
|
295
292
|
| 2 | "<member> accepted your invitation to <source>" | the invitee accepts — to the inviter (a source owner without a member email gets it at the source's contact) |
|
|
296
293
|
| 3 | "Your access to <source> is ready" | the acceptance grants the delegation — to the member |
|
|
297
|
-
| 4 | "Review needed: <protocol> claim for <source>" | a claim goes `pending_review`
|
|
294
|
+
| 4 | "Review needed: <protocol> claim for <source>" | a claim goes `pending_review` — to each member with `claims/review`, an email and `notifyReviews` on |
|
|
298
295
|
| 5 | "<n> claims need your review for <source>" | a second review claim inside 15 min is held, then sent as one batch by the minute tick |
|
|
299
296
|
| 6 | "Still waiting" / "Two weeks waiting: …" | reminders at 3 and 14 days for an unanswered claim — confirm it is scheduled, don't wait |
|
|
300
297
|
|
|
301
|
-
### 11. Payments on
|
|
298
|
+
### 11. Payments on
|
|
299
|
+
|
|
300
|
+
In the env's vars:
|
|
301
|
+
|
|
302
|
+
- `API_PAYMENTS: "true"`;
|
|
303
|
+
- `X402_FACILITATOR_URL` — IXO's facilitator for the env's network: devnet
|
|
304
|
+
`https://facilitator.devnet.ixo.earth`, testnet `https://facilitator.testnet.ixo.earth`, mainnet
|
|
305
|
+
`https://facilitator.ixo.earth`;
|
|
306
|
+
- `X402_NETWORK` — `cosmos:devnet-1` / `cosmos:pandora-8` / `cosmos:ixo-5`;
|
|
307
|
+
- the payees: `EARNINGS_ADDRESS` (your own `ixo1…`, each offer's `payTo` for PAY and USDC on IXO;
|
|
308
|
+
unset ⇒ no PAY) and `EARNINGS_BASE_WALLET` (your `0x…`, for USDC on Base).
|
|
302
309
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
in the env's vars, a price (`pricing.api` in `defineOracle`, whole cents), and the
|
|
307
|
-
oracle registered with the engine (step 6) — the facilitator reads the oracle's terms from its plan
|
|
308
|
-
(Standard unless IXO set another). `API_PAYMENTS:
|
|
309
|
-
"false"` is the kill switch: new x402 submits answer 503 `payments-disabled`, `none` sources keep
|
|
310
|
-
deciding. The example's devnet env ships with payments on.
|
|
310
|
+
Plus a price (below) and the oracle registered with the engine (step 6) — the facilitator reads the
|
|
311
|
+
oracle's terms from its plan (Standard unless IXO set another). `API_PAYMENTS: "false"` is the kill
|
|
312
|
+
switch: new x402 submits answer 503 `payments-disabled`, `none` sources keep deciding.
|
|
311
313
|
|
|
312
314
|
### Pricing and card
|
|
313
315
|
|
|
314
|
-
The price per decision is `pricing.api` in `src/
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
316
|
+
The price per decision is `pricing.api` in `defineOracle` in `src/index.ts`:
|
|
317
|
+
`pricing: { api: { amountMicroUsd: 600_000, byProtocol?: { "<protocol did>": … } } }` — micro-USD
|
|
318
|
+
(1 USD = 1 000 000), a whole number of cents (a multiple of 10 000). With no `pricing.api`, the
|
|
319
|
+
`API_PRICE_USD_CENTS` var is the price. Change it and redeploy; the facilitator picks it up within
|
|
320
|
+
~5 min — the `update-pricing` skill walks the change, the card refresh included. **Card needs ≥ 50¢
|
|
321
|
+
per decision** (Stripe's minimum, checked per claim), so a cheaper price offers PAY only. The
|
|
318
322
|
Decision Console shows the price and card eligibility read-only.
|
|
319
323
|
|
|
320
324
|
## Troubleshooting
|
|
@@ -325,17 +329,18 @@ Decision Console shows the price and card eligibility read-only.
|
|
|
325
329
|
| Worker 500s on every request after deploy; logs say `WEBHOOK_ENCRYPTION_KEY: required when API_LANE is on` | the secret is missing in that env — `bunx wrangler secret put WEBHOOK_ENCRYPTION_KEY --env <env>` (for a new deployment only; never replace an existing one) |
|
|
326
330
|
| Worker 500s on every request; logs say `CHAIN_LANE is off and API_LANE is not on` | an oracle with no lane — set `"API_LANE": "true"` in that env's `vars` (with its secrets), or remove `CHAIN_LANE` |
|
|
327
331
|
| `register` / `scan` / an OpenHands webhook answers 409 `chain_lane_off` | the oracle is API-only (`CHAIN_LANE` `"false"`) — by design; set it back to `"true"` and redeploy to take chain hires again |
|
|
332
|
+
| `PUT /v1/api/uploads` answers 501 | no `API_UPLOADS` bucket bound in that env — step 4 |
|
|
328
333
|
| `PROTOCOL_NOT_ALLOWED` on submit | the source's allow-list lacks it — empty means NONE; `api admin protocols <src> <did> --yes` |
|
|
329
334
|
| 503 `SOURCE_PAUSED` `payments-disabled` | `API_PAYMENTS` is off and the source bills `x402` — turn payments on, or `api admin billing-mode <src> none` |
|
|
330
335
|
| 402 again after a `PAYMENT-SIGNATURE` | the facilitator refused the payer: no deed / no `SendAuthorization` / not enough PAY — the fresh 402's error says which |
|
|
331
336
|
| offers refused `plan_inactive`, `accepts: []` | the facilitator finds no active plan for the oracle: it is not registered with the engine (step 6 — registered, it runs on Standard at once), or, for an IXO-run oracle, IXO has not activated its plan (**HUMAN**: ask IXO). A way to pay whose payee is unset (`EARNINGS_ADDRESS` for PAY / USDC on IXO, `EARNINGS_BASE_WALLET` for USDC on Base) is simply not offered |
|
|
332
|
-
| Not listed in the console | not registered with the engine (step 6), or not payable (the row above); or `https://<ORACLE_HOSTNAME>/.well-known/did.json` does not answer; or `CORS_ORIGINS` lacks
|
|
333
|
-
| Every API claim rejects with `frequency` | the caller sends no `submitter` — an uncountable window fails closed; send
|
|
337
|
+
| Not listed in the console | not registered with the engine (step 6), or not payable (the row above); or `https://<ORACLE_HOSTNAME>/.well-known/did.json` does not answer; or `CORS_ORIGINS` lacks the console's origin |
|
|
338
|
+
| Every API claim rejects with `frequency` | the caller sends no `submitter` — an uncountable window fails closed; send a stable id for the person |
|
|
334
339
|
| 409 `REVIEW_EXPIRED` on a review answer | the paid claim's `answerBy` (its x402 reservation's expiry: extended up to 30 days for the review, else 24 h after verify) has passed — it can no longer be paid, and nothing was written. Answer paid reviews before the date the emails state |
|
|
335
|
-
| Claims go `pending_review` on
|
|
340
|
+
| Claims go `pending_review` on a claim-level review task you did not expect | an errored fact (onError `review`) held them for a person: the AI provider unavailable (no gateway credits, or `OPENROUTER_API_KEY` missing on `openrouter`), or an `ext` endpoint unreachable — read the claim's report; fix it, and new claims decide again (the held ones are answered in the console). A rubric without a claim-level ReviewTask parks such claims `review` with NO task — give it one |
|
|
336
341
|
| `engine register` refuses: "registered … as <old> — this would re-bind it to <new>" | the did:web is bound to another key on the engine — the configured `ORACLE_SECP_MNEMONIC` is not the one it registered with. Restore the old key, or (owner's OK) `--yes` |
|
|
337
342
|
| No emails at all | the `EMAIL_FROM_ADDRESS` domain is not onboarded, or `CONSOLE_URL` is unset — the lane logs and skips |
|
|
338
|
-
| `engine register` 402 | the owner's credits —
|
|
343
|
+
| `engine register` 402 | the owner's engine credits — buy them in the Decision Console (Billing › Credits), re-run |
|
|
339
344
|
| `/health/ready` 503 while the worker otherwise serves | `GET /v1/admin/status` names the failed check; `identity` ⇒ no `ORACLE_ENTITY_DID` on a deployed host — finish step 7 (`oracle init`, pin it in every `vars` block, redeploy) |
|
|
340
345
|
| `engine register` fails resolving the did:web | the env is not deployed yet, or `--env <env>` was left off (it registered `localhost`) |
|
|
341
346
|
|
|
@@ -344,5 +349,5 @@ Decision Console shows the price and card eligibility read-only.
|
|
|
344
349
|
Say plainly what is done and what is owed: the protocol DID and where it was written, the
|
|
345
350
|
worker's host and did:web, the oracle entity DID (`/v1/info` `entityDid`) and the `/health/ready`
|
|
346
351
|
answer, which ids and secrets were set (names only), the engine registration and
|
|
347
|
-
billing status, whether the oracle is listed/payable,
|
|
348
|
-
|
|
352
|
+
billing status, whether the oracle is listed/payable, each verify claim's outcome, the email rows
|
|
353
|
+
seen, and every HUMAN step still open.
|