Skip to content

Commit 67a49fe

Browse files
authored
Merge pull request #4 from ixoworld/feat/new-skills
feat: add ixo-identity-verification and ixo-yellowcard skills
2 parents 82c5044 + c5e41dc commit 67a49fe

9 files changed

Lines changed: 2047 additions & 0 deletions

File tree

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
---
2+
name: ixo-identity-verification
3+
version: 2.0.0
4+
description: |
5+
Subscription-based KYC/AML identity verification using the ixo-kyc-server and ComplyCube.
6+
Orchestrates the full verification pipeline: initiate evaluation, get ComplyCube session URL,
7+
poll for completion, fetch credential, and mark complete. The credential is returned as a
8+
JSON string for the caller to store (e.g. in a Matrix room via a credential.store action block).
9+
author: ixo
10+
license: MIT
11+
compatibility: Node.js 18+
12+
allowed-tools: shell
13+
secrets:
14+
oracle:
15+
- KYC_PROVIDER_TOKEN: "Provider app token for KYC server API (originAuthorization)"
16+
- KYC_SERVER_URL: "Base URL of the ixo-kyc-server (e.g. https://kyc-server.ixo.earth)"
17+
user: []
18+
context:
19+
- _SKILL_CONTEXT_USER_DID
20+
- _SKILL_CONTEXT_USER_ADDRESS
21+
---
22+
23+
# IXO Identity Verification (Subscription-Based)
24+
25+
Orchestrates KYC/AML identity verification through the ixo-kyc-server + ComplyCube.
26+
This skill is a pure API orchestration layer — it talks to the KYC server, returns data,
27+
and leaves it to the caller (oracle agent) to decide how to present results to the user.
28+
29+
## How It Works
30+
31+
The skill runs 6 scripts sequentially. Each script calls the KYC server API, writes
32+
its result to `/workspace/output/<name>.json`, and prints the same JSON to stdout.
33+
The oracle reads the output and decides what to do with it.
34+
35+
```
36+
initiate-kyc → get-session-token → trigger-review → poll-status → fetch-credential → complete-kyc
37+
↑ ↑ ↑ ↑ ↑ ↑
38+
FORM_DATA PROTOCOL_DID TOKEN PROTOCOL_DID PROTOCOL_DID PROTOCOL_DID
39+
PROTOCOL_DID (from step 2)
40+
```
41+
42+
## Editor Flow Execution (REQUIRED when running inside a flow document)
43+
44+
When this skill is triggered from an editor flow (e.g. via a `form.submit` companion prompt),
45+
follow these steps EXACTLY. Do NOT deviate.
46+
47+
### Flow structure (3 blocks)
48+
49+
| Block | Type | Purpose |
50+
|-------|------|---------|
51+
| Block 1 | Action (`form.submit`) | Collects user data via SurveyJS (name, DOB, consent, etc.) |
52+
| Block 2 | FlowLink | Displays the ComplyCube liveness check URL (external link) |
53+
| Block 3 | Action (`credential.store`) | User stores the issued credential to their Matrix room |
54+
55+
### Step 0: Read flow context and blocks
56+
57+
1. Call `read_flow_context` to get flow-level metadata. Extract `protocolDid` — this is REQUIRED for all scripts. If missing, tell the user and stop.
58+
2. Call `list_blocks` to identify all blocks and their UUIDs. You need the FlowLink block UUID and the credential.store block UUID.
59+
60+
### Step 1: Initiate KYC (Phase 1)
61+
62+
Run `initiate-kyc.js` with:
63+
- `PROTOCOL_DID` = protocolDid from flow metadata
64+
- `FORM_DATA` = JSON string of the form answers (from the companion prompt trigger)
65+
66+
```bash
67+
PROTOCOL_DID="<protocolDid>" FORM_DATA='<form answers JSON>' node scripts/initiate-kyc.js
68+
```
69+
70+
### Step 2: Get liveness check URL and update FlowLink block (Phase 1)
71+
72+
Run `get-session-token.js`:
73+
```bash
74+
PROTOCOL_DID="<protocolDid>" node scripts/get-session-token.js
75+
```
76+
77+
This writes TWO output files:
78+
- `/workspace/output/session-token.json` — contains `token` (needed for step 4) and `kycUrl`
79+
- `/workspace/output/flowlink-update.json` — pre-formatted for the FlowLink block
80+
81+
**Update the FlowLink block using `apply_sandbox_output_to_block`:**
82+
```json
83+
{
84+
"filePath": "/workspace/output/flowlink-update.json",
85+
"blockId": "<flowlink-block-uuid>",
86+
"fieldMapping": { "links": "links" }
87+
}
88+
```
89+
90+
Do NOT use edit_block for this — use apply_sandbox_output_to_block.
91+
92+
Save the `token` value from session-token.json for step 4.
93+
94+
### Step 3: Wait for user to complete liveness check
95+
96+
Tell the user something like:
97+
> "Your identity verification link is ready. Please click the link in the verification block to complete the liveness check. Let me know when you're done."
98+
99+
**Do NOT proceed until the user explicitly tells you they have completed the liveness check.**
100+
Do NOT automatically continue. Wait for the user's message.
101+
102+
### Step 4: Trigger review
103+
104+
Run `trigger-review.js` with the token from step 2:
105+
```bash
106+
TOKEN="<token from step 2>" node scripts/trigger-review.js
107+
```
108+
109+
A 404 is normal (the webview may have already triggered it). Both success and `alreadyReviewed: true` mean you can proceed.
110+
111+
### Step 5: Poll for credential (max 2 minutes)
112+
113+
Run `poll-status.js` repeatedly:
114+
```bash
115+
PROTOCOL_DID="<protocolDid>" node scripts/poll-status.js
116+
```
117+
118+
**Polling rules:**
119+
- Poll every 10 seconds for the first minute, then every 20 seconds
120+
- **Stop after 2 minutes maximum**
121+
- If `phase` is `credential_ready` — proceed to step 6
122+
- If `phase` is `rejected` — tell user their verification was rejected, stop
123+
- If `phase` is `error` — tell user there was an error, stop
124+
- If 2 minutes pass without reaching a terminal phase — tell the user:
125+
> "Your credential is still being processed. Please wait a few minutes and then ask me to check the status again."
126+
Then STOP. Do not keep polling. The user will come back and ask you to check again.
127+
128+
### Step 6: Fetch credential and update credential.store block
129+
130+
Run `fetch-credential.js`:
131+
```bash
132+
PROTOCOL_DID="<protocolDid>" node scripts/fetch-credential.js
133+
```
134+
135+
**Update the credential.store block using `apply_sandbox_output_to_block`:**
136+
```json
137+
{
138+
"filePath": "/workspace/output/credential.json",
139+
"blockId": "<credential-store-block-uuid>",
140+
"fieldMapping": {
141+
"credential": "inputs.credential",
142+
"credentialKey": "inputs.credentialKey"
143+
}
144+
}
145+
```
146+
147+
Do NOT use edit_block for credentials — they are large JWT objects that WILL be truncated.
148+
You MUST use apply_sandbox_output_to_block with dot-notation fieldMapping to write into `inputs`.
149+
150+
### Step 7: Wait for user to store credential
151+
152+
Tell the user:
153+
> "Your credential is ready! Click the 'Store Credential' button to save it to your account."
154+
155+
Wait for the user to confirm or for the block state to change to `completed`.
156+
157+
### Step 8: Complete the evaluation
158+
159+
Run `complete-kyc.js`:
160+
```bash
161+
PROTOCOL_DID="<protocolDid>" node scripts/complete-kyc.js
162+
```
163+
164+
Tell the user their identity verification is complete.
165+
166+
---
167+
168+
## Conversation Mode (no flow blocks)
169+
170+
The oracle can also run these scripts outside of a flow context. In conversation mode, the oracle
171+
returns results directly to the user as messages (e.g. "Here is your KYC verification URL: ...").
172+
The credential can be returned as text or stored programmatically.
173+
174+
## Scripts
175+
176+
### Step 1: initiate-kyc.js — Create Evaluation
177+
178+
Creates a KYC evaluation on the server with the user's data.
179+
180+
```bash
181+
PROTOCOL_DID="did:ixo:entity:abc123" \
182+
FORM_DATA='{"firstName":"John","lastName":"Doe"}' \
183+
node scripts/initiate-kyc.js
184+
```
185+
186+
**Env:** `_SKILL_CONTEXT_USER_DID`, `_SKILL_CONTEXT_USER_ADDRESS`, `_ORACLE_SECRET_KYC_PROVIDER_TOKEN`, `_ORACLE_SECRET_KYC_SERVER_URL`
187+
188+
**Output:** `initiate-result.json`
189+
```json
190+
{ "success": true, "did": "...", "protocolDid": "...", "status": "verify", "evaluation": {...} }
191+
```
192+
193+
### Step 2: get-session-token.js — Get ComplyCube URL
194+
195+
Gets the ComplyCube webview URL for the user to complete the liveness check.
196+
197+
```bash
198+
PROTOCOL_DID="did:ixo:entity:abc123" \
199+
node scripts/get-session-token.js
200+
```
201+
202+
**Env:** `_SKILL_CONTEXT_USER_DID`, `_ORACLE_SECRET_KYC_PROVIDER_TOKEN`, `_ORACLE_SECRET_KYC_SERVER_URL`
203+
204+
**Output:** `session-token.json`
205+
```json
206+
{ "success": true, "token": "...", "kycUrl": "https://...", "expiresAt": "2026-03-05T..." }
207+
```
208+
209+
The `token` value is needed for step 3. The `kycUrl` is for the user to open.
210+
211+
### Step 3: trigger-review.js — Trigger Review
212+
213+
Triggers the review process after the user completes the ComplyCube webview.
214+
The webview often triggers this automatically, so a 404 response is treated as success.
215+
216+
```bash
217+
TOKEN="<token from step 2>" \
218+
node scripts/trigger-review.js
219+
```
220+
221+
**Env:** `_ORACLE_SECRET_KYC_SERVER_URL`
222+
223+
**Output:** `review-result.json`
224+
```json
225+
{ "success": true, "alreadyReviewed": false, "status": "review" }
226+
```
227+
228+
### Step 4: poll-status.js — Poll Verification Status
229+
230+
Polls the KYC server until a terminal state is reached. Run repeatedly with delays.
231+
232+
```bash
233+
PROTOCOL_DID="did:ixo:entity:abc123" \
234+
node scripts/poll-status.js
235+
```
236+
237+
**Env:** `_SKILL_CONTEXT_USER_DID`, `_ORACLE_SECRET_KYC_PROVIDER_TOKEN`, `_ORACLE_SECRET_KYC_SERVER_URL`
238+
239+
**Output:** `poll-result.json`
240+
```json
241+
{ "success": true, "serverStatus": "review", "phase": "verifying", "isTerminal": false, "checks": {...} }
242+
```
243+
244+
**Phase mapping:**
245+
- `verifying` — server is `verify` or `review` (keep polling)
246+
- `processing` — server is `clear`, `authorizing`, `authorized`, `issuing` (keep polling)
247+
- `credential_ready` — server is `issued` (proceed to step 5)
248+
- `rejected` — server rejected the verification (terminal)
249+
- `error` — something went wrong (terminal)
250+
251+
Recommended polling: every 10s initially, back off to 30s after 2 minutes.
252+
253+
### Step 5: fetch-credential.js — Fetch Verifiable Credential
254+
255+
Retrieves the issued credential from the KYC server.
256+
257+
```bash
258+
PROTOCOL_DID="did:ixo:entity:abc123" \
259+
node scripts/fetch-credential.js
260+
```
261+
262+
**Env:** `_SKILL_CONTEXT_USER_DID`, `_ORACLE_SECRET_KYC_PROVIDER_TOKEN`, `_ORACLE_SECRET_KYC_SERVER_URL`
263+
264+
**Output:** `credential.json`
265+
```json
266+
{ "success": true, "credentialKey": "kycamlattestation", "credential": { "@context": [...], ... } }
267+
```
268+
269+
The `credential` field is the full Verifiable Credential object. Pass it as the `credential`
270+
input to a `credential.store` action block — the handler takes care of double-stringifying
271+
it for Matrix storage (Matrix does not allow floats in state event values).
272+
273+
### Step 6: complete-kyc.js — Mark Complete
274+
275+
Marks the evaluation as complete on the KYC server. Run after the credential has been stored.
276+
277+
```bash
278+
PROTOCOL_DID="did:ixo:entity:abc123" \
279+
node scripts/complete-kyc.js
280+
```
281+
282+
**Env:** `_SKILL_CONTEXT_USER_DID`, `_ORACLE_SECRET_KYC_PROVIDER_TOKEN`, `_ORACLE_SECRET_KYC_SERVER_URL`
283+
284+
**Output:** `complete-result.json`
285+
```json
286+
{ "success": true, "status": "complete" }
287+
```
288+
289+
## Error Handling
290+
291+
All scripts output `{ "success": false, "error": "..." }` on failure. The oracle should
292+
communicate the error to the user and allow retry.
293+
294+
## KYC Server API Reference
295+
296+
Authenticated endpoints use the provider token as the `Authorization` header (raw string, not Bearer format).
297+
298+
| Endpoint | Method | Auth | Purpose |
299+
|----------|--------|------|---------|
300+
| `/kycaml/:did` | POST | Yes | Create evaluation |
301+
| `/tokens/:did/:protocolId` | GET | Yes | Get ComplyCube session URL + token |
302+
| `/tokens/byToken/:token/review` | POST | No | Trigger review after ComplyCube |
303+
| `/kycaml/:did/:protocolId` | GET | Yes | Get full evaluation details |
304+
| `/kycaml/:did/:protocolId/credential` | GET | Yes | Get Verifiable Credential |
305+
| `/kycaml/:did/:protocolId` | PATCH | Yes | Update status (e.g. complete) |

0 commit comments

Comments
 (0)