@astram/guide 0.2.7 → 0.2.8
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/CAPABILITIES.md +65 -3
- package/package.json +1 -1
package/CAPABILITIES.md
CHANGED
|
@@ -17,12 +17,12 @@ below is wired by the two `forRoot` calls in your `app.module.ts`; you write no
|
|
|
17
17
|
## Wiring (the ONE seam, plus the identity module)
|
|
18
18
|
|
|
19
19
|
```ts
|
|
20
|
-
import { ShieldModule } from '@astram/guard';
|
|
20
|
+
import { ShieldModule, rootIdsFromEnv } from '@astram/guard';
|
|
21
21
|
import {
|
|
22
22
|
IdentityFeatureModule, identityAuthActionKeys, identityControlPlaneKeys,
|
|
23
23
|
} from '@astram/identity';
|
|
24
24
|
|
|
25
|
-
const rootIds =
|
|
25
|
+
const rootIds = rootIdsFromEnv(process.env); // SHIELD_ROOT_IDS; never from a request
|
|
26
26
|
|
|
27
27
|
imports: [
|
|
28
28
|
ShieldModule.forRoot({
|
|
@@ -62,6 +62,10 @@ imports: [
|
|
|
62
62
|
HERE ONLY: the module builds the record authorization from them and `ShieldModule` decides with
|
|
63
63
|
it. Passing `recordAuthorization`/`decisionAuthority` to `ShieldModule` as well fails the boot.
|
|
64
64
|
Without `rootIds` it logs a boot warning.
|
|
65
|
+
- **`rootIdsFromEnv(env)`** (`@astram/guard`) reads `SHIELD_ROOT_IDS` (comma-separated user
|
|
66
|
+
uuids, lower-case canonical form). Any invalid or duplicate entry fails boot (the value is never
|
|
67
|
+
echoed); unset returns an empty list. `npm run bootstrap:org` prints the line to set. An app that
|
|
68
|
+
reads its own variable keeps working; root is never inferred from `u_is_platform_superuser`.
|
|
65
69
|
- `ShieldModule.forRoot()` with no options and no identity module = enforcement only (no DB, no
|
|
66
70
|
Redis, boots without `.env`); it binds a stub that denies every `@Classified` route.
|
|
67
71
|
|
|
@@ -131,7 +135,62 @@ imports: [
|
|
|
131
135
|
customer business data.
|
|
132
136
|
- Migrations for `shield_identity`, `shield_authz`, `shield_app` identity tables ship in
|
|
133
137
|
`@astram/identity` and `@astram/guard` and run through `@astram/migrate` automatically. Dev seeds
|
|
134
|
-
(099/100) run only with `NODE_ENV=development` or `ASTRAM_SEED_DEV=1`.
|
|
138
|
+
(099/100) run only with `NODE_ENV=development` or `ASTRAM_SEED_DEV=1`. Anywhere else, create the
|
|
139
|
+
first users with `npm run bootstrap:org` (below).
|
|
140
|
+
- **API-created users can be reached by reset.** `POST /identity/users` with an email and a
|
|
141
|
+
company creates the user, an active membership and a **locked** credential in one transaction
|
|
142
|
+
(no password works until the user sets one). The response is `{ id, credential: 'locked' }`, or
|
|
143
|
+
`{ id, credential: 'none', reason }` (`no_email`, `no_company`; `no_provisioner` only when the
|
|
144
|
+
service is built without `IdentityFeatureModule.forRoot`) when no credential was made.
|
|
145
|
+
Once your `EmailSender` works, `POST /auth/forgot-password` reaches the user. A user whose email
|
|
146
|
+
is set later stays locked until the next `bootstrap:org` run gives them a credential.
|
|
147
|
+
- **Role owner.** Every role records an owner company when it is created: a non-root admin's
|
|
148
|
+
active company; none when root creates it in the admin UI (a shared, root-only role). It cannot
|
|
149
|
+
be changed by update. Deleting a company clears the owner of its roles. This round records
|
|
150
|
+
ownership only: which roles an admin can see or administer is unchanged.
|
|
151
|
+
|
|
152
|
+
## Organisation bootstrap (`npm run bootstrap:org`, `@astram/identity` `runOrgBootstrap`, ADR-0042)
|
|
153
|
+
- **What it does.** Creates your organisation from one JSON file at deploy time: platform roots,
|
|
154
|
+
a company tree (tenants and `children`), departments and branches per company, roles with their
|
|
155
|
+
permission keys, and users with one active membership and their role postings. It runs against
|
|
156
|
+
your own `AppModule` (`NestFactory.createApplicationContext`), so keys of your `extraObjects` are
|
|
157
|
+
grantable. Every write goes through the framework's services and `decide()`, as the declared
|
|
158
|
+
operation `shield:org-bootstrap`; audit rows carry `operation`, `step` and `onBehalfOf` (the
|
|
159
|
+
operator). It needs auth on and `IdentityFeatureModule` wired; without the module it exits with
|
|
160
|
+
"wire IdentityFeatureModule in AppModule before running bootstrap:org".
|
|
161
|
+
- **File.** `{ "version": 1, "roots": [{ username, email, homeCompany }], "tenants": [company] }`;
|
|
162
|
+
`company` = `{ name, departments?, branches?, roles?: [{ name, permissions }], users?:
|
|
163
|
+
[{ username, email, company?, postings?: [{ role, department?, branch? }] }], children? }`.
|
|
164
|
+
Unknown fields, unknown permission keys (allowed: catalog keys plus `<feature>:*` and
|
|
165
|
+
`<feature>:<type>:*`), duplicate usernames or emails and references to undeclared roles,
|
|
166
|
+
departments or branches are refused before any write. No password, uuid or secret in the file.
|
|
167
|
+
A user with no postings is allowed (signs in, holds nothing).
|
|
168
|
+
- **Run.** `npm run bootstrap:org -- --file org.json` is a dry run (prints `create` / `unchanged` /
|
|
169
|
+
`drift` / `not-in-file` / `conflict`). Add `--apply` to write; it needs
|
|
170
|
+
`ASTRAM_BOOTSTRAP_OPERATOR`. Root passwords: `ASTRAM_BOOTSTRAP_ROOT_PASSWORD_<USERNAME>` (a
|
|
171
|
+
`secret-ref:` outside development/test, so a `SECRETS_PROVIDER` is needed) or `--interactive`.
|
|
172
|
+
Other flags: `--reissue-links`, `--link-ttl-hours <1..168>` (default 72), `--links-to-stdout`,
|
|
173
|
+
`--claim-unowned-roles`. Runs hold a database lock.
|
|
174
|
+
- **First sign-in.** Non-root users get a locked credential and a one-time set-password link,
|
|
175
|
+
printed once, to a terminal only (`--links-to-stdout` sends them to a pipe, where logs can keep
|
|
176
|
+
them). The operator delivers each link; the user redeems it at the reset page. Outside
|
|
177
|
+
development/test `RESET_LINK_BASE_URL` must be set and not localhost.
|
|
178
|
+
- **Roots.** The run prints each root's uuid and a `SHIELD_ROOT_IDS=…` line: set it and restart
|
|
179
|
+
(`rootIdsFromEnv`, above).
|
|
180
|
+
- **Re-runs and new tenants.** Additive and idempotent: a second `--apply` writes nothing. A file
|
|
181
|
+
covers only the tenants it declares, so onboard a new tenant with a new file. A role name owned by
|
|
182
|
+
another tenant is a `conflict` (roles are still global by name); an unowned role needs
|
|
183
|
+
`--claim-unowned-roles`.
|
|
184
|
+
- **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.
|
|
188
|
+
- **Existing apps** (scaffolded before this release) have no `bootstrap:org`. Upgrade
|
|
189
|
+
`@astram/identity` and `@astram/guard` (`npm update @astram/identity @astram/guard`), copy
|
|
190
|
+
`src/bootstrap-org.ts` and `src/bootstrap-org.args.ts` from a freshly scaffolded app (or from
|
|
191
|
+
`create-astram-app/template/server/src/` in the framework repo) into `server/src/`, and add
|
|
192
|
+
`"bootstrap:org": "node --env-file-if-exists=.env dist/bootstrap-org.js"` to the `scripts` of
|
|
193
|
+
`server/package.json`. `npm run build` compiles both files; no other change is needed.
|
|
135
194
|
|
|
136
195
|
## The record pipe (`@astram/engine`)
|
|
137
196
|
- **`RecordEngine`** (token `RECORD_ENGINE`) / `RecordsService` — create/get/update/delete/list,
|
|
@@ -423,5 +482,8 @@ upload(@Req() req: Record<string, unknown>) {
|
|
|
423
482
|
- External identity providers (OIDC, SAML, LDAP, Entra ID, Cognito, Okta, Google Workspace) and
|
|
424
483
|
WebAuthn ceremonies.
|
|
425
484
|
- 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.
|
|
426
488
|
|
|
427
489
|
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.
|
|
3
|
+
"version": "0.2.8",
|
|
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",
|