@astram/guide 0.2.8 → 0.2.9

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.
Files changed (2) hide show
  1. package/CAPABILITIES.md +34 -11
  2. package/package.json +1 -1
package/CAPABILITIES.md CHANGED
@@ -27,7 +27,7 @@ const rootIds = rootIdsFromEnv(process.env); // SHIELD_ROOT_IDS; never fro
27
27
  imports: [
28
28
  ShieldModule.forRoot({
29
29
  auth: true,
30
- emailSender: myEmailSender, // or 'disabled' — REQUIRED outside dev/test
30
+ emailSender: 'env', // or your EmailSender, or 'disabled' — REQUIRED outside dev/test
31
31
  rootIds,
32
32
  identityActionKeys: identityAuthActionKeys,
33
33
  identityControlPlaneKeys,
@@ -40,7 +40,7 @@ imports: [
40
40
  - **`ShieldModule.forRoot(options?)`** (`@astram/guard`; `forRootAsync` for DI-computed options).
41
41
  Options: `auth?`, `recordAuthorization?` (or a pre-built `decisionAuthority?`; only for an app
42
42
  WITHOUT `IdentityFeatureModule`, never both),
43
- `emailSender?: EmailSender | 'disabled'`, `rootIds?`, `identityActionKeys?`,
43
+ `emailSender?: EmailSender | 'disabled' | 'env'`, `rootIds?`, `identityActionKeys?`,
44
44
  `identityControlPlaneKeys?`, `config?`, and optional overrides `credentialResolver?`,
45
45
  `profileResolver?`, `credentialWriter?`, `resetTokenStore?`, `recoveryCodeStore?`,
46
46
  `profileWriter?`, `sessionSweeper?`, `auditSink?`, `mfaDeviceStore?`, `totpVerifier?`,
@@ -106,9 +106,30 @@ imports: [
106
106
  drops their roles at once.
107
107
  - **Silent recovery**: after a Redis loss, the next refresh rebuilds the session from MariaDB
108
108
  (no forced re-login); a restricted (recovery-code) session rebuilds restricted.
109
- - **Email**: reset links go through your `EmailSender` (`send({ to, subject, html, text })`).
110
- Outside `NODE_ENV=development|test`, `auth: true` without `emailSender` fails boot;
111
- `'disabled'` sends nothing and `/auth/forgot-password` still answers 204.
109
+ - **Email** (G-010, ADR-0043): `emailSender: 'env'` picks the provider from configuration —
110
+ `EMAIL_PROVIDER` = `smtp` | `brevo` | `sendgrid` | `mailgun` | `disabled` (no default), plus
111
+ `EMAIL_FROM_ADDRESS`, `EMAIL_FROM_NAME` and the provider's variables (the MESSAGING block in the
112
+ template's `server/_env.example`; runbook §6). It needs the `@astram/messaging` package installed;
113
+ write no mail code. Outside `NODE_ENV=development|test` credentials must be `secret-ref:` values, so
114
+ a `SECRETS_PROVIDER` is required first; bad config fails boot naming the variable;
115
+ `MESSAGE_BOOT_PROBE=1` checks credentials at boot without sending. Your own `EmailSender`
116
+ (`send({ to, subject, html, text })`) still works. Either way every framework email (password
117
+ reset, invite, email-change confirmation, security notices) is bounded (`MESSAGE_SEND_TIMEOUT_MS`,
118
+ default 10 s, one retry on a temporary failure) and audited (`message.sent` / `message.failed`:
119
+ provider, purpose, masked recipient; never the link or body). Outside `NODE_ENV=development|test`,
120
+ `auth: true` without `emailSender` fails boot; `'disabled'` sends nothing and
121
+ `/auth/forgot-password` still answers 204. SMS and WhatsApp are ports only (`SmsProvider`,
122
+ `WhatsAppProvider` in `@astram/core`); no adapter yet, and `SMS_PROVIDER` / `WHATSAPP_PROVIDER`
123
+ accept only `disabled`.
124
+ - **Administrator invite** (G-010): `POST /identity/users/:uuid/invite` (permission
125
+ `identity:user:invite`; wildcard `identity:*` / `identity:user:*` holders have it after the upgrade)
126
+ emails a one-time set-password link to a user whose sign-in is still pending. It is tenant-walled and
127
+ dominance-checked like update; 409 for a user who already set a password or has no credential, 429
128
+ over 3 per user or `INVITE_RATE_PER_ADMIN` per admin per hour, 501 with mail disabled and no link
129
+ return. `IdentityFeatureModule.forRoot({ invite: { returnLinkToAdmin: true } })` also returns the
130
+ link once. User reads carry a top-level `signInStatus` (`pending` | `active` | `none`). Frontend:
131
+ `identity.inviteUser(token, uuid)` (`@astram/frontend-core`); the scaffolded Users page has the
132
+ Invite button, and the reset page shows "Set your password" for `purpose=invite` links.
112
133
  - **Service-account API keys** (`Authorization: Bearer ask_…`) authenticate agents and machine
113
134
  clients through the same `decide()` path; scope, status and roles are read live; issuing a key
114
135
  needs an explicit expiry (`expiresAtMs`) or `neverExpires: true`.
@@ -182,9 +203,10 @@ imports: [
182
203
  another tenant is a `conflict` (roles are still global by name); an unowned role needs
183
204
  `--claim-unowned-roles`.
184
205
  - **What it does NOT do.** No deletes, renames, moves or revokes (differences are reported only);
185
- it never activates an inactive membership or updates a changed email. No upload from the UI. No
186
- administrator invite route or emailed link yet (G-010); department and branch admin routes are
187
- separate too.
206
+ it never activates an inactive membership or updates a changed email. No upload from the UI.
207
+ Department and branch admin routes are separate. With `emailSender` configured, `--email-links`
208
+ emails each non-root set-password link instead of printing it; root links are never emailed and
209
+ follow the terminal-only print rule (it cannot be combined with `--links-to-stdout`).
188
210
  - **Existing apps** (scaffolded before this release) have no `bootstrap:org`. Upgrade
189
211
  `@astram/identity` and `@astram/guard` (`npm update @astram/identity @astram/guard`), copy
190
212
  `src/bootstrap-org.ts` and `src/bootstrap-org.args.ts` from a freshly scaffolded app (or from
@@ -482,8 +504,9 @@ upload(@Req() req: Record<string, unknown>) {
482
504
  - External identity providers (OIDC, SAML, LDAP, Entra ID, Cognito, Okta, Google Workspace) and
483
505
  WebAuthn ceremonies.
484
506
  - Tenant- or role-mandated MFA (MFA is enforced for users who enrolled a device).
485
- - An administrator invite (admin-console route that emails a set-password link) and framework
486
- email/SMS delivery adapters (framework item G-010). Today first sign-in links come from
487
- `npm run bootstrap:org` only.
507
+ - SMS and WhatsApp delivery adapters, phone numbers on users, phone verification and SMS/WhatsApp
508
+ MFA (the ports exist in `@astram/core`; adapters come with a phone-number identity feature).
509
+ - A durable outbox or queue for guaranteed delivery (sends are best-effort with one retry; re-issue
510
+ an invite), bounce or webhook processing, and admin-editable message templates.
488
511
 
489
512
  If you need one of these, STOP and flag it in `.astram/guide/gaps.md` (Rule 4).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astram/guide",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "license": "MIT",
5
5
  "description": "Astram framework — the AI/dev GUIDE layer (ADR-0025). Ships the framework's hard rules + capabilities manifest as installed, upgrade-refreshed docs. A consumer app's CLAUDE.md references FRAMEWORK_RULES.md from this package; `npm update` replaces it wholesale so new rules arrive cleanly, while the app's own .astram/guide/ layer is never touched. This is the framework-owned FLOOR: an app rule may tighten it, never weaken it.",
6
6
  "type": "commonjs",