@learncard/cli 3.5.1 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/README.md +230 -2
- package/dist/index.js +4337 -986
- package/examples/branded.network.yaml +20 -0
- package/examples/delegated-service-account.network.yaml +23 -0
- package/examples/minimal.network.yaml +7 -0
- package/examples/self-hosted-signing.network.yaml +10 -0
- package/examples/service-account.network.yaml +13 -0
- package/examples/state-districts.network.yaml +36 -0
- package/package.json +21 -17
- package/src/auth-grant.test.ts +54 -0
- package/src/auth-grant.ts +34 -0
- package/src/clr/validate.test.ts +65 -0
- package/src/clr/validate.ts +242 -0
- package/src/clr.ts +119 -0
- package/src/demo-inbox-refresh.test.ts +737 -0
- package/src/demo-inbox-refresh.ts +804 -0
- package/src/demo-refresh-command.test.ts +57 -0
- package/src/demo-refresh-command.ts +22 -0
- package/src/demo-refresh-ui.test.ts +66 -0
- package/src/demo-refresh-ui.ts +65 -0
- package/src/demo-refresh.test.ts +140 -0
- package/src/demo-refresh.ts +309 -0
- package/src/doctor/checks.test.ts +448 -0
- package/src/doctor/checks.ts +497 -0
- package/src/doctor.test.ts +67 -0
- package/src/doctor.ts +118 -0
- package/src/inbox.test.ts +257 -0
- package/src/inbox.ts +221 -0
- package/src/index.tsx +70 -8
- package/src/init.ts +1 -1
- package/src/open.ts +1 -1
- package/src/org/apply.test.ts +1108 -0
- package/src/org/apply.ts +924 -0
- package/src/org/branding.test.ts +60 -0
- package/src/org/diff.ts +14 -0
- package/src/org/load.ts +50 -0
- package/src/org/schema.test.ts +256 -0
- package/src/org/schema.ts +216 -0
- package/src/org.ts +124 -0
- package/src/project.test.ts +26 -1
- package/src/project.ts +105 -10
- package/src/promote.test.ts +142 -0
- package/src/promote.ts +202 -0
- package/src/refresh.test.ts +86 -0
- package/src/refresh.ts +93 -0
- package/src/send.test.ts +278 -2
- package/src/send.ts +152 -24
- package/src/setup-signing.ts +1 -1
- package/src/status.ts +2 -4
- package/src/whoami.test.ts +67 -0
- package/src/whoami.ts +129 -0
- package/tsconfig.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
# @learncard/cli
|
|
2
2
|
|
|
3
|
+
## 3.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#1598](https://github.com/learningeconomy/LearnCard/pull/1598) [`2991bd32b03e26736239dd8e586e2f720d9bcd45`](https://github.com/learningeconomy/LearnCard/commit/2991bd32b03e26736239dd8e586e2f720d9bcd45) Thanks [@Custard7](https://github.com/Custard7)! - Act as a managed profile. A request may carry `X-LearnCard-Act-As: <profileId>`; the network swaps the acting profile when the authenticated profile manages the target, keeps the token's scope unchanged, and records `onBehalfOf` on the resulting activity. API tokens must opt in via a new `actAs` field on the auth grant (`'*'` or a list of profile IDs; absent = no delegation).
|
|
8
|
+
|
|
9
|
+
- `@learncard/types`: `ACT_AS_HEADER`, `AuthGrant.actAs`.
|
|
10
|
+
- `@learncard/network-plugin` / `@learncard/init`: `actAs` option on `initLearnCard` and the network plugin; `learnCard.invoke.actAs(profileId)` returns a scoped instance.
|
|
11
|
+
- `@learncard/cli`: `serviceAccounts[].actAs` in the org spec is set on the grant at creation; like scope and expiry, it is compared on re-apply and any drift errors with a revoke hint (dry-run reports `drifted`). `actAs` lists are compared as sets, so reordering profile IDs is not drift. With a `learncard-hosted` signer, `org apply` also registers a hosted signing authority on each managed profile so tokens acting as it can sign. Shown by `doctor` and `whoami`; new `examples/delegated-service-account.network.yaml`.
|
|
12
|
+
|
|
13
|
+
- [#1590](https://github.com/learningeconomy/LearnCard/pull/1590) [`59d2c92fa58ce1b864e0bf4e2aa60c85d3f8a9a4`](https://github.com/learningeconomy/LearnCard/commit/59d2c92fa58ce1b864e0bf4e2aa60c85d3f8a9a4) Thanks [@Custard7](https://github.com/Custard7)! - CLI: add issuer-org tooling for partner integrations — `org apply` (declarative, idempotent org bootstrap), `doctor` (read-only preflight), `clr validate` (CLR 2.0 transcript lint), `inbox list`, `refresh history`, and `promote` (staging → production).
|
|
14
|
+
|
|
15
|
+
Credential library: the `clr/provisional-transcript` fixture now marks itself `partial: true` with a `validUntil`, and its in-progress result points at a `Status`-typed ResultDescription (was `RawScore`). `buildFinalTranscriptVariant` strips those provisional markers.
|
|
16
|
+
|
|
17
|
+
LCA API plugin: skip the encryption-key probe when the profile does not exist yet (it could only 401) and log initialization warnings as one line instead of a full stack dump.
|
|
18
|
+
|
|
19
|
+
- [#1586](https://github.com/learningeconomy/LearnCard/pull/1586) [`dac4695428e42ecc99b05c1774fce937c021c657`](https://github.com/learningeconomy/LearnCard/commit/dac4695428e42ecc99b05c1774fce937c021c657) Thanks [@Custard7](https://github.com/Custard7)! - `send` now prompts for the recipient when omitted (`npx @learncard/cli send`) and rejects placeholder addresses such as `you@example.com` instead of silently sending to an undeliverable domain. Non-interactive runs (`--yes`, no TTY) still require the recipient as an argument.
|
|
20
|
+
|
|
21
|
+
- [#1585](https://github.com/learningeconomy/LearnCard/pull/1585) [`0c1bf9a8a33e6392d5fd279479d9ab4fb0449b5e`](https://github.com/learningeconomy/LearnCard/commit/0c1bf9a8a33e6392d5fd279479d9ab4fb0449b5e) Thanks [@goblincore](https://github.com/goblincore)! - Add `learncard demo refresh`, a guided demonstration of sending a refreshable badge through `sendBoost`, publishing a new version from the returned receipt, and verifying the recipient's refreshed copy. Uses fresh demo accounts, defaults to the local network, and supports `--network staging`, `--yes`, and `--json`.
|
|
22
|
+
|
|
23
|
+
Add interactive `--ui` mode for the local LearnCard app: print a demo recipient sign-in link, deliver claim and update notifications, and wait for the app to save each version. Supports a custom local `--app-url` and validates that the app and CLI use matching local services.
|
|
24
|
+
|
|
25
|
+
Add `learncard demo refresh --inbox` for the deferred Universal Inbox path: issue a refreshable certificate to a random `@example.com` address with email delivery suppressed, publish a final version before any holder exists (`notification: "not-applicable"`), then claim with a real DIDAuth presentation and refresh to an honors version. `--inbox --ui` prints a claim link for the local app, waits for the human to claim, publishes an update to the bound holder, and verifies the app replaced (not duplicated) the same entry. Terminal mode supports `--yes`/`--json`, is local-only, and takes `--lca-url` (default `http://localhost:5100/trpc`) for the local signing service. The existing direct demo is unchanged.
|
|
26
|
+
|
|
27
|
+
Add opt-in `--inbox --ui --email [address]`: prompt for an address, send a provisional certificate through normal email delivery, let the recipient sign in and claim, then publish a visible final-results update. This mode never reads recipient keys or personal credential storage. Keep the pre-claim update in the separate disposable-account walkthrough.
|
|
28
|
+
|
|
29
|
+
Stop the email walkthrough before publishing if the claim has no recipient account, and only advertise queued notifications when publication confirms them.
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- Updated dependencies [[`2991bd32b03e26736239dd8e586e2f720d9bcd45`](https://github.com/learningeconomy/LearnCard/commit/2991bd32b03e26736239dd8e586e2f720d9bcd45), [`59d2c92fa58ce1b864e0bf4e2aa60c85d3f8a9a4`](https://github.com/learningeconomy/LearnCard/commit/59d2c92fa58ce1b864e0bf4e2aa60c85d3f8a9a4), [`0c1bf9a8a33e6392d5fd279479d9ab4fb0449b5e`](https://github.com/learningeconomy/LearnCard/commit/0c1bf9a8a33e6392d5fd279479d9ab4fb0449b5e), [`3aa4f5553ef92bc8a36a4bba50eeeba280d57441`](https://github.com/learningeconomy/LearnCard/commit/3aa4f5553ef92bc8a36a4bba50eeeba280d57441), [`3174c09b97fde6f7b5251390245038cf6b7ea9ba`](https://github.com/learningeconomy/LearnCard/commit/3174c09b97fde6f7b5251390245038cf6b7ea9ba), [`928e587378b3674766cf58a8bbe1cbd4d66d3a9f`](https://github.com/learningeconomy/LearnCard/commit/928e587378b3674766cf58a8bbe1cbd4d66d3a9f), [`928e587378b3674766cf58a8bbe1cbd4d66d3a9f`](https://github.com/learningeconomy/LearnCard/commit/928e587378b3674766cf58a8bbe1cbd4d66d3a9f)]:
|
|
34
|
+
- @learncard/types@5.21.0
|
|
35
|
+
- @learncard/init@2.5.0
|
|
36
|
+
- @learncard/lca-api-plugin@2.0.6
|
|
37
|
+
- @learncard/linked-claims-plugin@0.2.36
|
|
38
|
+
- @learncard/holder-continuity@0.2.19
|
|
39
|
+
- @learncard/core@9.4.36
|
|
40
|
+
- @learncard/network-brain-client@2.5.57
|
|
41
|
+
- @learncard/didkit-plugin@1.10.1
|
|
42
|
+
- @learncard/learn-cloud-plugin@2.3.42
|
|
43
|
+
- @learncard/ler-rs-plugin@0.1.27
|
|
44
|
+
- @learncard/open-badge-v2-plugin@1.1.37
|
|
45
|
+
- @learncard/render-method-plugin@8.0.0
|
|
46
|
+
|
|
3
47
|
## 3.5.1
|
|
4
48
|
|
|
5
49
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -21,10 +21,187 @@ https://docs.learncard.com
|
|
|
21
21
|
### Send your first credential
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
npx @learncard/cli send
|
|
24
|
+
npx @learncard/cli send
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Prompts for a recipient if omitted (or pass it: `send you@yourdomain.com`), generates a seed into `.env`, creates your issuer profile, sends a "Quickstart Complete" badge, and writes the code it ran to `./send.mjs`. The recipient can be an email or phone number (delivered through the Universal Inbox with a claim link) or an existing profile ID or DID (delivered straight into their wallet). Placeholder addresses like `you@example.com` are rejected — nobody would receive the badge. Pass `--yes` to accept defaults (the recipient must then be given as an argument); `--name "Acme"`, `--badge "Welcome to Acme"`, `--description`, and `--profile-id` set them explicitly. Walkthrough: https://docs.learncard.com/start-here/your-first-integration
|
|
28
|
+
|
|
29
|
+
### Demonstrate credential refresh
|
|
30
|
+
|
|
31
|
+
Run a guided, real-network demonstration without writing code:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
learncard demo refresh
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Press Enter between each step: send a **Provisional Course Certificate**, publish a
|
|
38
|
+
**Final Course Certificate**, then refresh the recipient's copy. The CLI shows the
|
|
39
|
+
before and after and verifies that the credential keeps its identity. It uses the
|
|
40
|
+
existing `sendBoost` flow with `enableRefresh: true` and publishes using the receipt
|
|
41
|
+
returned by that send. No second badge is sent.
|
|
42
|
+
|
|
43
|
+
The default network is `http://localhost:4000/trpc`. A developer must first start
|
|
44
|
+
the local services with managed refresh enabled. To present against staging once
|
|
45
|
+
LC-2198's service and SDK changes are deployed, use `learncard demo refresh --network staging`.
|
|
46
|
+
This command requires a CLI build containing this feature; it is not available in
|
|
47
|
+
older published versions. From this branch's repository root, run:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
bun --cwd packages/learn-card-cli start demo refresh
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
By default, each run creates two demo accounts and a badge on the selected network. Keys stay
|
|
54
|
+
in memory, and those demo records remain on the network after the command exits.
|
|
55
|
+
The demo does not use your `.env` identity or send email. The refreshed copy is
|
|
56
|
+
shown in the CLI session; it is not saved to the LearnCard app.
|
|
57
|
+
|
|
58
|
+
Use `--yes` to run without pauses, or `--json` for a machine-readable result.
|
|
59
|
+
|
|
60
|
+
#### Show the certificate and notifications in the local app
|
|
61
|
+
|
|
62
|
+
Start the normal local app stack first (from `apps/learn-card-app`, run
|
|
63
|
+
`bun run lc dev learncard local full`).
|
|
64
|
+
Once the app is available at `http://localhost:3000`, run this in a second terminal
|
|
65
|
+
from the repository root:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
bun --cwd packages/learn-card-cli start demo refresh --ui
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
1. Follow the printed sign-in link to enter a fresh **Refresh Demo Learner** account.
|
|
72
|
+
If already signed in, switch to the demo account when prompted.
|
|
73
|
+
2. Press Enter in the terminal to send the certificate. In the app, open **Alerts →
|
|
74
|
+
Claim → Accept**, skip the optional connection prompt, then view
|
|
75
|
+
**Provisional Course Certificate** in **Passport → Achievements**.
|
|
76
|
+
Open it to see **Provisional Results** and **Final grade: Pending** on the full certificate.
|
|
77
|
+
3. Press Enter in the terminal to publish the final certificate. The command checks that
|
|
78
|
+
the app has saved the original first.
|
|
79
|
+
4. Reload the app if needed and open **Alerts**. Select the notification that the school
|
|
80
|
+
updated a credential. The app retrieves and verifies **Final Course Certificate**,
|
|
81
|
+
replacing the existing copy. Open it to see **Final Results** and **Final grade: A**.
|
|
82
|
+
5. Press Enter in the terminal to confirm. The CLI checks that the updated credential
|
|
83
|
+
was actually saved by the app and verifies it.
|
|
84
|
+
|
|
85
|
+
Keep the terminal open throughout. The sign-in link grants access to this disposable demo
|
|
86
|
+
account; keep it private and use it only for test data. The CLI does not write account keys
|
|
87
|
+
to files, but the recipient remains signed into the browser after the demo. Rerunning
|
|
88
|
+
creates fresh accounts. UI mode requires an interactive terminal and cannot be combined
|
|
89
|
+
with `--yes`, `--json`, or `LC_YES=1`.
|
|
90
|
+
|
|
91
|
+
For a different local app port, pass `--app-url http://localhost:3001`. The CLI reads
|
|
92
|
+
the app's served `tenant-config.json` to use the same LearnCloud and notification services.
|
|
93
|
+
Both the app and all these services must use loopback URLs, and `--network` must match
|
|
94
|
+
the app's Brain service. Local refresh trust is automatic in the development app.
|
|
95
|
+
If using the E2E service stack instead of the normal app stack, set the generated app
|
|
96
|
+
config's `apis.lcaApi` to `http://localhost:5200/trpc` and `apis.notificationsEndpoint`
|
|
97
|
+
to `http://localhost:5200/api/notifications/send` before starting the demo. Do not run
|
|
98
|
+
database-resetting E2E tests during a demonstration.
|
|
99
|
+
|
|
100
|
+
#### Receive the claim at your own email (opt-in real email)
|
|
101
|
+
|
|
102
|
+
Use `--email` to run the Universal Inbox lifecycle against an address you own. The CLI asks
|
|
103
|
+
the locally configured delivery service to email a **provisional** claim link, you claim it
|
|
104
|
+
in the app, then the school publishes a single visible **final** update.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
bun --cwd packages/learn-card-cli start demo refresh --inbox --ui --email you@example.com
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`--email` without a value prompts for the address. It requires `--inbox`, `--ui`, and an
|
|
111
|
+
interactive terminal, and the address is validated before any account is created. The
|
|
112
|
+
walkthrough is:
|
|
113
|
+
|
|
114
|
+
1. **Issue provisional results.** The CLI asks the delivery service to email a claim link
|
|
115
|
+
for a **Provisional Course Certificate** to your address. It reports only that delivery
|
|
116
|
+
was requested; it never claims the email was sent or received.
|
|
117
|
+
2. **Claim in the app.** Open the mailbox link, sign in or create an account **using that
|
|
118
|
+
same address**, and claim the provisional certificate. Keep the app open.
|
|
119
|
+
3. **Publish final results.** Return to the terminal and press Enter. The CLI reads the
|
|
120
|
+
issuer's inbox-credential record for the bound holder DID, then publishes
|
|
121
|
+
**Final Certificate / Final grade: A** as the single visible update (version 2). That
|
|
122
|
+
publication requests an in-app notification and a update email with the issuer and certificate name prompting you to
|
|
123
|
+
log in and view notifications.
|
|
124
|
+
4. **View the update.** Open the app notifications to see the final certificate. The CLI
|
|
125
|
+
never creates, signs in as, or reads the recipient wallet, so it does not verify the
|
|
126
|
+
wallet contents and cannot confirm that any email or notification was delivered.
|
|
127
|
+
|
|
128
|
+
Real email mode is local-only like the rest of the inbox demo. It never suppresses
|
|
129
|
+
delivery, and it never publishes a hidden pre-claim version: version 2 is the only update.
|
|
130
|
+
|
|
131
|
+
> **Operator setup.** `--email` only requests delivery. The operator must enable and
|
|
132
|
+
> configure the local email adapter/delivery service first; otherwise no email is sent. In
|
|
133
|
+
> the local test mode the adapter logs the message instead of sending it, so open the link
|
|
134
|
+
> on the same computer that runs the app. The CLI cannot verify that an address is
|
|
135
|
+
> deliverable, and it masks the address in `--json` output.
|
|
136
|
+
|
|
137
|
+
#### Advanced: refresh for a recipient who has no account yet (disposable preclaim path)
|
|
138
|
+
|
|
139
|
+
This path exercises the deferred Universal Inbox mechanics — including publishing an update
|
|
140
|
+
_before_ any holder exists — with a fake `@example.com` address and suppressed delivery. It
|
|
141
|
+
is not the recommended mail walkthrough; use `--email` above for that.
|
|
142
|
+
|
|
143
|
+
The direct demo sends to a profile that already exists. Add `--inbox` to exercise the
|
|
144
|
+
deferred Universal Inbox path: the school issues a refreshable certificate to a random
|
|
145
|
+
`@example.com` address with no LearnCard account, publishes a new version **before**
|
|
146
|
+
anyone claims, and only then is a real holder bound and sent an update. Email delivery
|
|
147
|
+
is suppressed, so the CLI shows the claim link instead of mailing it.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
bun --cwd packages/learn-card-cli start demo refresh --inbox
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Press Enter through four stages:
|
|
154
|
+
|
|
155
|
+
1. **Issue provisional results.** Queues a **Provisional Course Certificate** for the
|
|
156
|
+
demo address. Nothing is emailed and there is no recipient to notify.
|
|
157
|
+
2. **Publish final results before claim.** The school publishes **Final Results /
|
|
158
|
+
Final grade: A**. Because no holder exists yet, the publication reports
|
|
159
|
+
`notification: "not-applicable"`; nothing is announced.
|
|
160
|
+
3. **Claim.** The CLI claims with a fresh local wallet using a real DIDAuth
|
|
161
|
+
presentation, exactly like the app. It receives the newest version (final results),
|
|
162
|
+
verifies the proof, and confirms the same credential identity.
|
|
163
|
+
4. **Publish honors results.** Now that the claim has bound the holder DID, the school
|
|
164
|
+
publishes **Honors Results / Final grade: A+**. The holder refreshes and verifies
|
|
165
|
+
that the same credential now shows the honors version.
|
|
166
|
+
|
|
167
|
+
`--yes` runs every stage without pausing; `--json` prints one machine-readable result
|
|
168
|
+
with no seeds, tokens, or claim links. Terminal mode is local-only (it rejects a
|
|
169
|
+
non-loopback `--network`) and needs the local LCA signing service. `--lca-url` defaults
|
|
170
|
+
to `http://localhost:5100/trpc`; pass `--lca-url http://localhost:5200/trpc` when
|
|
171
|
+
presenting against the E2E stack on port 5200.
|
|
172
|
+
|
|
173
|
+
For the guided app experience, start the normal local app stack as above and run:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
bun --cwd packages/learn-card-cli start demo refresh --inbox --ui
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The CLI reads Brain, LearnCloud, LCA, and notification services from the app's
|
|
180
|
+
`tenant-config.json` and requires them all to be loopback, just like the direct demo.
|
|
181
|
+
|
|
182
|
+
1. Press Enter to start, then follow the terminal prompts to issue provisional results
|
|
183
|
+
and publish **Final Results / Final grade: A** before the recipient account exists.
|
|
184
|
+
2. Press Enter to create the demo recipient. Open the printed **sign-in link** and choose
|
|
185
|
+
**Switch account** if prompted. It takes you directly to the claim screen; the
|
|
186
|
+
separate claim link is also printed. Keep both links private.
|
|
187
|
+
3. Click **Claim my credential → Accept**. The first certificate already shows final
|
|
188
|
+
results, not provisional results. Find it in **Passport → Achievements**.
|
|
189
|
+
This first delivery uses a claim link because there was no account to notify at issue time.
|
|
190
|
+
4. Return to the terminal and press Enter to confirm the certificate is saved, then
|
|
191
|
+
Enter again to publish the honors update. Early confirmation keeps the demo waiting.
|
|
192
|
+
5. Open **Alerts → “Inbox Demo School updated one of your credentials” → View Credential**.
|
|
193
|
+
Reload if the notification is not visible yet. The certificate now shows
|
|
194
|
+
**Honors Results / Final grade: A+**.
|
|
195
|
+
6. Press Enter in the terminal to finish. The CLI verifies that the app replaced the
|
|
196
|
+
same entry (not a duplicate) and that the saved certificate has a valid signature.
|
|
197
|
+
|
|
198
|
+
In `--ui` mode the CLI never claims, accepts, refreshes, or saves on the app's behalf;
|
|
199
|
+
a human performs every app action. Rerunning creates fresh accounts. This mode requires
|
|
200
|
+
an interactive terminal and cannot be combined with `--yes`, `--json`, or `LC_YES=1`.
|
|
201
|
+
|
|
202
|
+
The unknown email is simulated with suppressed delivery, but the claim link, signing
|
|
203
|
+
authority, publication, claim binding, and refresh are all real. The direct
|
|
204
|
+
`demo refresh` (without `--inbox`) is unchanged.
|
|
28
205
|
|
|
29
206
|
### Interactive REPL
|
|
30
207
|
|
|
@@ -72,6 +249,57 @@ await exportLearnCardBundle({ out: './learncard-export.zip', password });
|
|
|
72
249
|
|
|
73
250
|
See `@learncard/holder-continuity` `BUNDLE_SPEC.md` for the ZIP layout and manifest hashing rules.
|
|
74
251
|
|
|
252
|
+
## Bootstrap an organization
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
npx @learncard/cli org apply ./org.yaml
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Reconciles a declarative YAML/JSON spec (issuer profile, signing authority, districts/managed profiles, service-account tokens) against the network. With a `learncard-hosted` signer, every managed profile also gets its own hosted signing authority registered, so API tokens acting as a district can sign through it. Idempotent — re-running with the same file makes no changes. Use `--dry-run` to preview, and `--secrets-out ./secrets.env` to save any newly created service-account tokens (required the first time a `serviceAccounts` entry is created; each is written as `NAME=token` with the account name upper-cased and hyphens replaced by underscores, e.g. `EA_CLR_ISSUER=…`). `branding` (on the issuer and on each managed profile) sets the profile image, hero image, bios, website, type, and wallet display colours; it is diffed field-by-field, so re-running only sends what changed and `display` colours are merged, never wiped. Images must be https URLs for now — host them and paste the link. Webhooks are not registered on the network — LearnCard calls `configuration.webhookUrl` per issuance — so the first `webhooks[].url` is saved as `WEBHOOK_URL` in `.env` for `doctor` and your issuer code to default to. Start from one of `examples/*.network.yaml` — `minimal`, `service-account`, `branded`, `self-hosted-signing`, `state-districts`, or `delegated-service-account` — each shows one idea.
|
|
259
|
+
|
|
260
|
+
```yaml
|
|
261
|
+
issuer:
|
|
262
|
+
profileId: scde
|
|
263
|
+
displayName: South Carolina Department of Education
|
|
264
|
+
branding:
|
|
265
|
+
image: https://cdn.example.org/scde-logo.png
|
|
266
|
+
shortBio: State education agency.
|
|
267
|
+
websiteLink: https://ed.sc.gov
|
|
268
|
+
display: { backgroundColor: '#18224E', accentColor: '#2E7D32' }
|
|
269
|
+
signingAuthority: { type: learncard-hosted, name: scde-clr }
|
|
270
|
+
profileManager:
|
|
271
|
+
displayName: SC Districts
|
|
272
|
+
managed:
|
|
273
|
+
- { profileId: sc-greenville, displayName: Greenville County Schools }
|
|
274
|
+
serviceAccounts:
|
|
275
|
+
- name: ea-clr-issuer
|
|
276
|
+
scopes: [inbox:write, inbox:read, credentials:write, credentials:read]
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
A service account's `actAs` controls whether its token can act as a managed profile: `'*'` allows any profile under `profileManager.managed`, or list specific profile IDs (e.g. `actAs: [sc-greenville, sc-north]`) to limit it to those. Omit `actAs` and the token cannot act as anyone — delegation is deny-by-default, so add it explicitly wherever a backend needs to issue on a district's behalf. See `examples/delegated-service-account.network.yaml`.
|
|
280
|
+
|
|
281
|
+
Once the org exists, `send` picks up its pieces automatically: with a signing authority registered it signs through that authority by default (`--no-template` opts back out to the local key), and `--as <managedProfileId>` sends as one of the managed profiles — signed with that profile's own did:web, no separate seed or folder needed. `inbox list --as <managedProfileId>` shows what that profile has sent. `LEARNCARD_AS=<profileId>` does the same for a whole shell session without persisting anything, and `whoami` shows the folder's identity plus every profile `--as` can target (plus any service accounts and what they may act as). A folder is one identity; passing a different `--profile-id` is an error that points you at `--as`. Backends using an API token act as a managed profile by sending the `X-LearnCard-Act-As` header (SDK: `initLearnCard({ apiKey, actAs })`) — allowed only if the token's `actAs` permits it.
|
|
282
|
+
|
|
283
|
+
Spec objects are strict: an unknown or misspelled key fails validation rather than being dropped. Display names on the issuer, profile manager, and managed profiles are updated in place when they differ; a self-hosted signer whose registered DID differs from the spec is reported as drift. Service-account scopes, expiry, and `actAs` are all compared with the spec. If any of them drifts, apply errors with a revoke command: revoke the grant and re-run to create a replacement (dry-run reports `drifted`). With `--secrets-out`, a matching grant whose normalized key is missing from the secrets file gets a re-issued token. Tokens are written only to that file, never printed or stored elsewhere by the CLI.
|
|
284
|
+
|
|
285
|
+
New `send --as` scripts save `MANAGED_DID` in `.env` and use it with `didWeb` on the selected network. They reject missing or changed managed identities. Existing scripts are left untouched, with a warning to use the CLI if their issuer differs.
|
|
286
|
+
|
|
287
|
+
## Preflight with doctor
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
npx @learncard/cli doctor
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Checks this project's issuer setup against the network — identity, network reachability, API token scopes, signing authority (with a real test-sign + verify), did:web resolution, an optional `--webhook-url` POST ping, and whether managed credential refresh is enabled — printing one line per check with a remediation command for anything that fails. It calls network services but does not deliver credentials or provision resources. Trusted Registry verification is a manual check reported as skipped. Pass `--strict` to also exit non-zero on warnings, or `--scopes "..."` to check different permissions than the default Universal Inbox set.
|
|
294
|
+
|
|
295
|
+
## Promote staging → production
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
npx @learncard/cli promote --from staging --to production --org ./org.yaml
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Re-applies your org spec in its own `.learncard/production` folder (each network needs its own `.env`), writes fresh service-account tokens to `<target>/secrets.env` (or `--secrets-out`), and runs `doctor` — then prints the per-network checklist (profile, tokens, signing authority, templates, ConsentFlow contracts, credentials) that doesn't carry over; only your seed's `did:key` does. Add `--dry-run` to preview or `--skip-doctor` to skip the preflight.
|
|
302
|
+
|
|
75
303
|
## Contributing
|
|
76
304
|
|
|
77
305
|
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
|