@credda/cli 0.1.2 → 0.1.4

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Credda, Inc.
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,220 +1,304 @@
1
- # @credda/cli
2
-
3
- The official Credda CLI — portable trust from the terminal. A thin client
4
- over [`@credda/js`](../sdk)'s headless export; **no scoring logic lives
5
- here** (the deterministic score is computed only by the API's
6
- `score.service`).
7
-
8
- ## Install / run
9
-
10
- Not yet published to npm (same status as `@credda/mcp-server`). Run from a
11
- checkout:
12
-
13
- ```sh
14
- pnpm --filter @credda/js run build # build the SDK first (dist/ is gitignored)
15
- pnpm --filter @credda/cli run build
16
- node packages/cli/dist/index.js help
17
- ```
18
-
19
- ## Commands
20
-
21
- Public no API key:
22
-
23
- ```sh
24
- credda lookup <token> # trust check for a share token (GET /verify/:token)
25
- credda export <token> # full self-verifying trust export bundle
26
- credda verify <file|-> # OFFLINE-verify a credential someone handed you:
27
- # a W3C VC-JWT, a compact Trust Credential, or a
28
- # saved trust-export bundle auto-detected.
29
- # '-' reads stdin. Exit 0 valid / 2 invalid.
30
- credda registry # federated trust registry (/.well-known)
31
- credda did # issuer DID document
32
- credda benchmarks # cohort-benchmark catalog: the dimensions you can
33
- # benchmark on and the k-anonymity floor below
34
- # which no cohort is disclosed
35
- credda reason-codes # adverse-action reason-code catalog (ECOA / Reg B).
36
- # Credda supplies the attribution only — it is not
37
- # a creditor and issues no notice.
38
- credda badges list # the closed set of Open Badges 3.0 achievements
39
- credda badges get <badgeId> # this issuer will sign, and one definition
40
- credda professional-record public <token>
41
- # the professional record behind a share token
42
- # the token IS the subject's consent to present it
43
- ```
44
-
45
- Platform set `CREDDA_API_KEY` (a `crd_live_…` platform key):
46
-
47
- ```sh
48
- credda score <userId> # current score
49
- credda explain <userId> # factor-level explanation
50
- credda components <userId> # six named 0-100 components
51
- credda risk <userId> # advisory risk signals
52
- credda trust-summary <userId> [--narrative]
53
- # deterministic, evidence-based summary + strengths
54
- # + risks. It explains; it is never a verdict.
55
- # --narrative adds an advisory AI retelling.
56
- credda benchmark <userId> [--dimension <d>]
57
- # where the subject sits in its cohort: percentile
58
- # + the cohort distribution. `available:false`
59
- # when the cohort is below the k-anonymity floor
60
- # (insufficient_data) or the subject has no score
61
- # yet (no_score).
62
- credda distribution [--dimension <d>] [--cohort <c>]
63
- # aggregate, k-anonymised cohort distribution.
64
- # Omit --cohort for every cohort on the dimension.
65
- credda users [--score-min <n>] [--score-max <n>] [--band <b>]
66
- [--subject-type <PERSON|AGENT>] [--active-since <iso>]
67
- [--verified] [--min-verified <n>]
68
- [--sort <score|lastActivity|registered|externalId>]
69
- [--order <asc|desc>] [--cursor <c>] [--limit <n>]
70
- # query + export your book of subjects. The filter
71
- # set is closed and validated — no query DSL.
72
- credda usage [days] # your platform's metered usage (trailing window)
73
- credda usage --from 2026-06-01 --to 2026-06-30
74
- # explicit statement range (mutually exclusive
75
- # with [days])
76
- credda usage --csv usage.csv
77
- # write the flat CSV statement to a file
78
- # (raw ?format=csv fetch; combines with either window)
79
- credda activity [--action <A>] [--from <t>] [--to <t>] [--cursor <c>] [--limit <n>]
80
- # your platform's own activity/audit log,
81
- # newest-first, cursor-paginated
82
- credda verified-profile <userId>
83
- # how much of a subject's CLAIMED record
84
- # (education/skills/certifications/employment) is
85
- # third-party verified. Counts WHETHER a claim is
86
- # verified, never how prestigious it is — and can
87
- # never move the Reliability Score.
88
- credda qualify <userId> --category <education|skill|certification|employment>
89
- [--label <l>] [--issuer <i>] [--verified-by <witness>]
90
- # record a qualification claim. Always recorded;
91
- # counts as VERIFIED only with a genuine
92
- # third-party --verified-by witness.
93
- credda professional-record get <userId>
94
- # résumé-shaped summary of a VERIFIED work record.
95
- # Describes a record not a hiring verdict, a
96
- # background check, or a consumer report.
97
- credda professional-record credential <userId> [--ttl <seconds>]
98
- # mint the signed, offline-verifiable credential
99
- # (+ an "Add to LinkedIn" certification link)
100
- credda mint <userId> # mint a share token
101
- credda revoke <userId> # revoke a share token
102
- ```
103
-
104
- Confirmation requests the counterparty-confirmation primitive. You propose an
105
- outcome and deliver the one-time token yourself; the event is written, verified,
106
- only when that distinct party confirms:
107
-
108
- ```sh
109
- credda confirmations create --user worker_7 --type CONTRACT_FULFILLED \
110
- --counterparty client_42 --counterparty-name "Acme Ltd" \
111
- --description "Kitchen refit" [--stake HIGH] [--value 1200] \
112
- [--due <iso>] [--completed <iso>] [--return-url <url>] \
113
- [--expires-in 14] [--idempotency-key <k>]
114
- # needs CREDDA_API_KEY. The token is shown ONCE;
115
- # creating a request writes no event.
116
- credda confirmations list [--status PENDING] [--cursor <c>] [--limit <n>]
117
- credda confirmations get <id>
118
- credda confirmations cancel <id> # only while PENDING
119
-
120
- # ⚠️ These two are the COUNTERPARTY's calls and take NO API key — they hold a
121
- # token, not a Credda account:
122
- credda confirmations preview <id> --token <t>
123
- credda confirmations respond <id> --token <t> --confirm
124
- credda confirmations respond <id> --token <t> --decline
125
- # --confirm writes the verified event; --decline
126
- # writes nothing. Single-use either way, and
127
- # there is no default: you must say which.
128
- ```
129
-
130
- Threshold policies declarative "tell me when this line is crossed", delivered
131
- as `policy.threshold_crossed` through your webhooks. Config only: a policy never
132
- reads into, blocks, or changes a score:
133
-
134
- ```sh
135
- credda policies create --name "Watch 60" --user worker_7 \
136
- --metric score --direction down --threshold 60
137
- credda policies create --name "Anyone entering High Risk" --all \
138
- --metric band --direction enter --band "High Risk"
139
- credda policies list [--cursor <c>] [--limit <n>]
140
- credda policies get <id>
141
- credda policies update <id> [--threshold <n>] [--direction <d>] [--band <b>]
142
- [--component <c>] [--name <n>] [--activate | --deactivate]
143
- # the metric is immutable — delete and recreate
144
- credda policies delete <id>
145
- ```
146
-
147
- Score monitors — set `CREDDA_API_KEY`. Edge-triggered threshold/band watches
148
- that deliver `monitor.triggered` through your subscribed webhooks;
149
- notification config only a monitor never affects a score:
150
-
151
- ```sh
152
- credda monitors list [--cursor <c>] [--limit <n>]
153
- credda monitors get <id>
154
- credda monitors create --user <externalId> --below 40
155
- # at least one condition required:
156
- # --below <score> downward crossing (also fires
157
- # on a FIRST score already below)
158
- # --above <score> upward crossing
159
- # --band-change any band change
160
- credda monitors delete <id>
161
- ```
162
-
163
- Bulk screenings set `CREDDA_API_KEY`. Async batch score reads (up to
164
- 10,000 ids per job), strictly read-only:
165
-
166
- ```sh
167
- credda screen u1,u2 u3 # ids inline, comma/space separated
168
- credda screen --file roster.csv
169
- # one id per line, or a CSV whose FIRST column is
170
- # the id (a leading id/userId/externalId header
171
- # row is skipped; no quoted-CSV handling)
172
- credda screen u1,u2 --wait # poll until the job finishes, print the summary
173
- # (exit 1 if the job FAILED)
174
- credda screenings list [--cursor <c>] [--limit <n>]
175
- credda screenings get <id> # job status + summary
176
- credda screenings results <id> # per-user results as JSON
177
- credda screenings results <id> --csv out.csv
178
- # write the CSV attachment instead (raw fetch)
179
- ```
180
-
181
- Webhooks set `CREDDA_API_KEY`:
182
-
183
- ```sh
184
- credda webhooks list
185
- credda webhooks create https://hooks.you/credda score.updated score.band_changed
186
- # signing secret shown ONCE
187
- credda webhooks delete <id>
188
- credda webhooks test <id> # synthetic signed delivery
189
- credda webhooks deliveries <id> # recent attempts, incl. retries
190
- ```
191
-
192
- Local development:
193
-
194
- ```sh
195
- CREDDA_WEBHOOK_SECRET=whsec_... credda listen 4141
196
- ```
197
-
198
- `credda listen` runs a local receiver that HMAC-verifies each delivery (the
199
- same check your production handler must do) and pretty-prints the payload.
200
- Credda delivers to public HTTPS only, so expose the port with your own tunnel
201
- (e.g. `cloudflared tunnel --url http://localhost:4141`) and register the
202
- tunnel URL as the webhook the Stripe-CLI-style local loop without Credda
203
- running a tunneling service.
204
-
205
- Environment: `CREDDA_API_URL` overrides the API base (default
206
- `https://api.credda.io`); `CREDDA_WEBHOOK_SECRET` enables signature
207
- verification in `credda listen`.
208
-
209
- ## Design
210
-
211
- - `src/cli.ts` is the pure command router — no `process`, `fs`, or env access,
212
- so the whole surface is unit-tested with a mocked `CreddaClient` (same
213
- pattern as `packages/mcp`'s `tools.ts`).
214
- - `src/index.ts` only wires the real environment (env vars, stdin/file
215
- reading, exit codes).
216
- - `verify` uses the SDK's offline verifiers (WebCrypto Ed25519 + StatusList
217
- revocation) — the point is that a received credential can be checked
218
- without trusting the wire it arrived on.
219
- - Every command is read-only against the score. `mint`/`revoke` manage a
220
- share token a capability, not a score write.
1
+ > Source mirror for [`@credda/cli`](https://www.npmjs.com/package/@credda/cli). Install from npm: `npm install -g @credda/cli`. Canonical development happens in Credda internal tooling; this repo is for source and issues.
2
+
3
+ # @credda/cli
4
+
5
+ The official Credda CLI: portable trust from the terminal. A thin client over
6
+ [`@credda/js`](https://www.npmjs.com/package/@credda/js)'s headless export;
7
+ **no scoring logic lives here** (the deterministic score is computed only by
8
+ the API's `score.service`).
9
+
10
+ ## Install
11
+
12
+ ```sh
13
+ npm install -g @credda/cli # or: pnpm add -g @credda/cli
14
+ credda help
15
+ ```
16
+
17
+ Node 20 or newer. Nothing else to configure for the public commands below.
18
+
19
+ Prefer not to install globally? `npx @credda/cli help` works the same way.
20
+
21
+ `credda help` always lists exactly what the version you installed can do, so
22
+ treat it, not this file, as the authority on your copy. `credda --version`
23
+ prints that version.
24
+
25
+ ## Commands
26
+
27
+ Start here, if you hold a sandbox key (`CREDDA_API_KEY` of the `crd_test_`
28
+ kind; a live key is refused before anything happens):
29
+
30
+ ```sh
31
+ credda quickstart # seed the sandbox with synthetic subjects, print
32
+ # their real scores, then close the counterparty-
33
+ # confirmation loop so you finish holding a real
34
+ # VERIFIED event rather than a number you read
35
+ --no-confirm # stop after the seed; skip the confirmation loop
36
+ ```
37
+
38
+ Public no API key:
39
+
40
+ ```sh
41
+ credda lookup <token> # trust check for a share token (GET /verify/:token)
42
+ credda export <token> # full self-verifying trust export bundle
43
+ credda verify <file|-> # OFFLINE-verify a credential someone handed you:
44
+ # a W3C VC-JWT, a compact Trust Credential, or a
45
+ # saved trust-export bundle auto-detected.
46
+ # '-' reads stdin. Exit 0 valid / 2 invalid.
47
+ credda registry # federated trust registry (/.well-known)
48
+ credda did # issuer DID document
49
+ credda benchmarks # cohort-benchmark catalog: the dimensions you can
50
+ # benchmark on and the k-anonymity floor below
51
+ # which no cohort is disclosed
52
+ credda reason-codes # adverse-action reason-code catalog (ECOA / Reg B).
53
+ # Credda supplies the attribution only — it is not
54
+ # a creditor and issues no notice.
55
+ credda badges list # the closed set of Open Badges 3.0 achievements
56
+ credda badges get <badgeId> # this issuer will sign, and one definition
57
+ credda outcome-templates [industry]
58
+ # how a business maps its work to Credda events, and
59
+ # WHO confirms each outcome. Guidance only.
60
+ credda professional-record public <token>
61
+ # the professional record behind a share token —
62
+ # the token IS the subject's consent to present it
63
+ credda career-export --token <token>
64
+ # the whole verified record as a JSON Resume document,
65
+ # behind a share token (no API key sent)
66
+ ```
67
+
68
+ Platform — set `CREDDA_API_KEY` (a `crd_live_…` platform key):
69
+
70
+ ```sh
71
+ credda score <userId> # current score
72
+ credda explain <userId> # factor-level explanation
73
+ credda components <userId> # six named 0-100 components
74
+ credda risk <userId> # advisory risk signals
75
+ credda trust-summary <userId> [--narrative]
76
+ # deterministic, evidence-based summary + strengths
77
+ # + risks. It explains; it is never a verdict.
78
+ # --narrative adds an advisory AI retelling.
79
+ credda benchmark <userId> [--dimension <d>]
80
+ # where the subject sits in its cohort: percentile
81
+ # + the cohort distribution. `available:false`
82
+ # when the cohort is below the k-anonymity floor
83
+ # (insufficient_data) or the subject has no score
84
+ # yet (no_score).
85
+ credda distribution [--dimension <d>] [--cohort <c>]
86
+ # aggregate, k-anonymised cohort distribution.
87
+ # Omit --cohort for every cohort on the dimension.
88
+ credda users [--score-min <n>] [--score-max <n>] [--band <b>]
89
+ [--subject-type <PERSON|AGENT|ORGANIZATION>]
90
+ [--scored|--unscored] [--frozen]
91
+ [--active-since <iso>] [--registered-since <iso>]
92
+ [--registered-before <iso>] [--verified] [--min-verified <n>]
93
+ [--sort <score|lastActivity|registered|externalId>]
94
+ [--order <asc|desc>] [--cursor <c>] [--limit <n>]
95
+ # query + export your book of subjects. The filter
96
+ # set is closed and validated — no query DSL.
97
+ # A subject with no score yet reports null, never
98
+ # a placeholder; list those with --unscored.
99
+ credda book-summary [same filters as "users"]
100
+ # size a segment WITHOUT paging it: how many match,
101
+ # how many are scored, band mix, median/mean.
102
+ # Null (not 0) when nothing in it is scored.
103
+ credda usage [days] # your platform's metered usage (trailing window)
104
+ credda usage --from 2026-06-01 --to 2026-06-30
105
+ # explicit statement range (mutually exclusive
106
+ # with [days])
107
+ credda usage --csv usage.csv
108
+ # write the flat CSV statement to a file
109
+ # (raw ?format=csv fetch; combines with either window)
110
+ credda activity [--action <A>] [--from <t>] [--to <t>] [--cursor <c>] [--limit <n>]
111
+ # your platform's own activity/audit log,
112
+ # newest-first, cursor-paginated
113
+ credda verified-profile <userId>
114
+ # how much of a subject's CLAIMED record
115
+ # (education/skills/certifications/employment) is
116
+ # third-party verified. Counts WHETHER a claim is
117
+ # verified, never how prestigious it is — and can
118
+ # never move the Reliability Score.
119
+ credda qualify <userId> --category <education|skill|certification|employment>
120
+ [--label <l>] [--issuer <i>] [--verified-by <witness>]
121
+ # record a qualification claim. Always recorded;
122
+ # counts as VERIFIED only with a genuine
123
+ # third-party --verified-by witness.
124
+ credda professional-record get <userId>
125
+ # résumé-shaped summary of a VERIFIED work record.
126
+ # Describes a record not a hiring verdict, a
127
+ # background check, or a consumer report.
128
+ credda professional-record credential <userId> [--ttl <seconds>]
129
+ # mint the signed, offline-verifiable credential
130
+ # (+ an "Add to LinkedIn" certification link)
131
+ credda reliability-report <userId> [--recent <n>] [--benchmark]
132
+ # the consolidated worker reliability report a
133
+ # staffing agency or employer weighs. EVIDENCE, not
134
+ # a hire / place / rank verdict or a consumer report.
135
+ # Use --token <token> for the public worker-consent
136
+ # route (NO API key).
137
+ credda career-export <userId>
138
+ # the whole verified record as an open JSON Resume
139
+ # document. Use --token <token> for the public route.
140
+ credda mint <userId> # mint a share token
141
+ credda revoke <userId> # revoke a share token
142
+ ```
143
+
144
+ Confirmation requests the counterparty-confirmation primitive. You propose an
145
+ outcome and deliver the one-time token yourself; the event is written, verified,
146
+ only when that distinct party confirms:
147
+
148
+ ```sh
149
+ credda confirmations create --user worker_7 --type CONTRACT_FULFILLED \
150
+ --counterparty client_42 --counterparty-name "Acme Ltd" \
151
+ --description "Kitchen refit" [--stake HIGH] [--value 1200] \
152
+ [--due <iso>] [--completed <iso>] [--return-url <url>] \
153
+ [--expires-in 14] [--idempotency-key <k>]
154
+ # needs CREDDA_API_KEY. The token is shown ONCE;
155
+ # creating a request writes no event.
156
+ credda confirmations batch <file.json> [--idempotency-key <k>]
157
+ # the ACTIVATION ENGINE bulk-create up to 100
158
+ # requests from a JSON file (an array of request
159
+ # bodies, or { "requests": [...] }), warming a cold
160
+ # ledger from your book. Needs CREDDA_API_KEY; each
161
+ # ok item's token is shown ONCE.
162
+ credda confirmations list [--status PENDING] [--cursor <c>] [--limit <n>]
163
+ credda confirmations get <id>
164
+ credda confirmations cancel <id> # only while PENDING
165
+
166
+ # ⚠️ These two are the COUNTERPARTY's calls and take NO API key — they hold a
167
+ # token, not a Credda account:
168
+ credda confirmations preview <id> --token <t>
169
+ credda confirmations respond <id> --token <t> --confirm
170
+ credda confirmations respond <id> --token <t> --decline
171
+ # --confirm writes the verified event; --decline
172
+ # writes nothing. Single-use either way, and
173
+ # there is no default: you must say which.
174
+ ```
175
+
176
+ Reference requests the qualifications-half sibling of confirmations. A résumé
177
+ claim (employment / education / certification / skill) becomes verified when the
178
+ named third party who was there confirms it; a reference never moves the score:
179
+
180
+ ```sh
181
+ credda references create --user worker_7 --category employment \
182
+ --counterparty manager_42 --label "Senior Engineer" \
183
+ --issuer "Acme Ltd" [--jurisdiction US-CA] [--reference EMP-9910] \
184
+ [--counterparty-name "Dana Lee"] [--description <d>] \
185
+ [--return-url <url>] [--expires-in 14] [--idempotency-key <k>]
186
+ # needs CREDDA_API_KEY. The token is shown ONCE;
187
+ # creating a request records no qualification.
188
+ credda references list [--status PENDING] [--cursor <c>] [--limit <n>]
189
+ credda references get <id>
190
+ credda references cancel <id> # only while PENDING
191
+
192
+ # ⚠️ These two are the REFERENCE's calls and take NO API key — they hold a
193
+ # token, not a Credda account:
194
+ credda references preview <id> --token <t>
195
+ credda references respond <id> --token <t> --confirm
196
+ credda references respond <id> --token <t> --decline
197
+ # --confirm records the verified qualification;
198
+ # --decline writes nothing. Single-use either way,
199
+ # and there is no default: you must say which.
200
+ ```
201
+
202
+ Threshold policies declarative "tell me when this line is crossed", delivered
203
+ as `policy.threshold_crossed` through your webhooks. Config only: a policy never
204
+ reads into, blocks, or changes a score:
205
+
206
+ ```sh
207
+ credda policies create --name "Watch 60" --user worker_7 \
208
+ --metric score --direction down --threshold 60
209
+ credda policies create --name "Anyone entering High Risk" --all \
210
+ --metric band --direction enter --band "High Risk"
211
+ credda policies list [--cursor <c>] [--limit <n>]
212
+ credda policies get <id>
213
+ credda policies update <id> [--threshold <n>] [--direction <d>] [--band <b>]
214
+ [--component <c>] [--name <n>] [--activate | --deactivate]
215
+ # the metric is immutable — delete and recreate
216
+ credda policies delete <id>
217
+ ```
218
+
219
+ Score monitors set `CREDDA_API_KEY`. Edge-triggered threshold/band watches
220
+ that deliver `monitor.triggered` through your subscribed webhooks;
221
+ notification config only — a monitor never affects a score:
222
+
223
+ ```sh
224
+ credda monitors list [--cursor <c>] [--limit <n>]
225
+ credda monitors get <id>
226
+ credda monitors create --user <externalId> --below 40
227
+ # at least one condition required:
228
+ # --below <score> downward crossing (also fires
229
+ # on a FIRST score already below)
230
+ # --above <score> upward crossing
231
+ # --band-change any band change
232
+ credda monitors delete <id>
233
+ ```
234
+
235
+ Bulk screenings — set `CREDDA_API_KEY`. Async batch score reads (up to
236
+ 10,000 ids per job), strictly read-only:
237
+
238
+ ```sh
239
+ credda screen u1,u2 u3 # ids inline, comma/space separated
240
+ credda screen --file roster.csv
241
+ # one id per line, or a CSV whose FIRST column is
242
+ # the id (a leading id/userId/externalId header
243
+ # row is skipped; no quoted-CSV handling)
244
+ credda screen u1,u2 --wait # poll until the job finishes, print the summary
245
+ # (exit 1 if the job FAILED)
246
+ credda screenings list [--cursor <c>] [--limit <n>]
247
+ credda screenings get <id> # job status + summary
248
+ credda screenings results <id> # per-user results as JSON
249
+ credda screenings results <id> --csv out.csv
250
+ # write the CSV attachment instead (raw fetch)
251
+ ```
252
+
253
+ Webhooks — set `CREDDA_API_KEY`:
254
+
255
+ ```sh
256
+ credda webhooks list
257
+ credda webhooks create https://hooks.you/credda score.updated score.band_changed
258
+ # signing secret shown ONCE
259
+ credda webhooks delete <id>
260
+ credda webhooks test <id> # synthetic signed delivery
261
+ credda webhooks deliveries <id> # recent attempts, incl. retries
262
+ ```
263
+
264
+ Local development:
265
+
266
+ ```sh
267
+ CREDDA_WEBHOOK_SECRET=whsec_... credda listen 4141
268
+ ```
269
+
270
+ `credda listen` runs a local receiver that HMAC-verifies each delivery (the
271
+ same check your production handler must do) and pretty-prints the payload.
272
+ Credda delivers to public HTTPS only, so expose the port with your own tunnel
273
+ (e.g. `cloudflared tunnel --url http://localhost:4141`) and register the
274
+ tunnel URL as the webhook — the Stripe-CLI-style local loop without Credda
275
+ running a tunneling service.
276
+
277
+ Environment: `CREDDA_API_URL` overrides the API base (default
278
+ `https://api.credda.io`); `CREDDA_WEBHOOK_SECRET` enables signature
279
+ verification in `credda listen`.
280
+
281
+ ## Design
282
+
283
+ - `src/cli.ts` is the pure command router — no `process`, `fs`, or env access,
284
+ so the whole surface is unit-tested with a mocked `CreddaClient` (same
285
+ pattern as `packages/mcp`'s `tools.ts`).
286
+ - `src/index.ts` only wires the real environment (env vars, stdin/file
287
+ reading, exit codes).
288
+ - `verify` uses the SDK's offline verifiers (WebCrypto Ed25519 + StatusList
289
+ revocation) — the point is that a received credential can be checked
290
+ without trusting the wire it arrived on.
291
+ - Every command is read-only against the score. `mint`/`revoke` manage a
292
+ share token — a capability, not a score write.
293
+
294
+ ## License
295
+
296
+ MIT © Credda. See [LICENSE](LICENSE).
297
+
298
+ ---
299
+
300
+ Part of the Credda SDK family:
301
+ [`@credda/js`](https://github.com/Credda-io/credda-js) ·
302
+ [`credda-go`](https://github.com/Credda-io/credda-go) ·
303
+ [`@credda/cli`](https://github.com/Credda-io/credda-cli) ·
304
+ [`@credda/mcp-server`](https://github.com/Credda-io/credda-mcp)