@astram/guide 0.2.28 → 0.2.30

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 CHANGED
@@ -89,7 +89,13 @@ imports: [
89
89
  `isUniqueConflict(err, '<constraint>')`, never by message text. Inside `transaction(fn)` the
90
90
  transaction is then rollback-only (a further write is a 503): re-read in a fresh transaction for
91
91
  the idempotent answer. On a tenant type, lead every UNIQUE index with the tenant column, or a
92
- 409 reveals that another tenant holds the value.
92
+ 409 reveals that another tenant holds the value (the engine now warns at boot, naming the index).
93
+ Likewise give a `subtreeSubquery` type's scope column and its parent key the same character set
94
+ and collation: a mismatch is also warned at boot, and its reads fail with errno 1267.
95
+ - **Moving a row out of scope** — a non-root update that sets a `readScope` type's scope column to a
96
+ value outside the actor's subtree answers `update_outside_write_scope` (403) before any write.
97
+ Mark the scope column `immutable: true` if rows must never move; otherwise in-subtree moves stay
98
+ allowed. A patch that re-sends the scope column costs one extra probe query.
93
99
 
94
100
  ## Authentication and sessions (`@astram/guard`, `auth: true`)
95
101
  - `POST /auth/login` `{ identityHint, password }` → `{ session, tokens }`, or an MFA challenge.
@@ -248,7 +254,8 @@ imports: [
248
254
  `company` = `{ name, departments?, branches?, roles?: [{ name, permissions }], users?:
249
255
  [{ username, email, company?, postings?: [{ role, department?, branch? }] }], agent?: { mode },
250
256
  children? }`. `agent.mode` (`off` | `synthetic`, identity 0.26) sets that company's AI assistant
251
- mode (below); no `agent` key leaves it unchanged.
257
+ mode (below); no `agent` key leaves it unchanged. A file can never set `live`, and a run refuses
258
+ (`agent_mode_live_locked`) to change a company whose admins approved client data.
252
259
  Unknown fields, unknown permission keys (allowed: catalog keys plus `<feature>:*` and
253
260
  `<feature>:<type>:*`), duplicate usernames or emails and references to undeclared roles,
254
261
  departments or branches are refused before any write. No password, uuid or secret in the file.
@@ -915,7 +922,8 @@ grant a tenant-wide `stored_file` rule for this.
915
922
  server executes. The runtime never calls your app; each capability call it asks for runs inside the
916
923
  user's own streaming request, as that user, through the record pipe and `decide()`, recorded in
917
924
  `agent_action` before it runs. M1: **`read` and `compute` capabilities only**; no workers, no
918
- writes or proposals (M3), no chat UI in the template, synthetic data only (Rule 8).
925
+ writes or proposals (M3), no chat UI in the template, synthetic data by default (Rule 8; real
926
+ client data is a separate gated path, see "Client data to a model provider" below).
919
927
  - **Capabilities.** `defineCapability({ id, version, title, description, effect: 'read' |
920
928
  'compute', dataClass: 'internal' | 'confidential' | 'restricted', requires: [keys], input,
921
929
  output, limits?, handler, toEvidence?, partial? })`, or `inject` + `factory` for app services.
@@ -979,6 +987,8 @@ grant a tenant-wide `stored_file` rule for this.
979
987
  - Files enter a conversation as `attachments` (0–8 `stored_file` uuids your own `FileIntake` route returned)
980
988
  on `POST /agent/tasks` and on `continue` with a message. The runtime only ever gets task handles; any file
981
989
  the user cannot open answers one `404 attachment_unavailable`; a bad list `400 invalid_request`.
990
+ Attaching reads no file content (metadata only); a user may start or continue with attachments at most
991
+ 30 times a minute, then `429 attach_rate_limited` (retryable).
982
992
  - Every byte is parsed by the isolated analyser `@astram/file-analyser` (Python): never formulas, macros,
983
993
  links or embedded objects. Run it as the sidecar container (`docker-compose.analyser.yml` in the
984
994
  scaffold: no network, read-only, non-root) or as the hardened systemd unit in the package's `deploy/`.
@@ -1053,6 +1063,76 @@ grant a tenant-wide `stored_file` rule for this.
1053
1063
  opts out and adds a caution to the kind's `review` / `apply` permission text. Terminal proposals
1054
1064
  are deleted after `PROPOSAL_RETENTION_DAYS` (365, 30–3650); their audit rows stay.
1055
1065
  - Not yet: a review page in the scaffolded template.
1066
+ - **Client-data approval routes (model-provider-client-data phase P2; guard, identity).** A tenant
1067
+ admin approves sending this company's real data to ONE deployment-approved model provider profile.
1068
+ The routes, the stored approvals and the audit trail are described here; how a run uses an approval
1069
+ is in "Client data to a model provider" below. All routes are gated
1070
+ on the new key `identity:agent:client_data_approve`, which is **assigned to no role by default**;
1071
+ note that a role holding `identity:*` or `identity:agent:*` covers it, like `restricted_data`.
1072
+ - `GET /identity/agent/client-data`: the deployment approvals (profile, provider, region, retention,
1073
+ data classes, file content, contract reference, approved by / on; no secrets), whether two-person
1074
+ approval is on, and this company's approvals with history, newest first (≤ 200; people shown by
1075
+ name, never by id).
1076
+ - `POST /identity/agent/client-data/approvals` `{ profile, dataClasses, fileContent, reason, code }`:
1077
+ request an approval. `code` is the admin's own current authenticator code (step-up). With
1078
+ two-person approval (the default) the request is `pending`; without it the approval is `active`
1079
+ at once. `internal` is always included in the classes; the classes and the file flag cannot go
1080
+ beyond the deployment approval.
1081
+ - `POST …/approvals/:uuid/confirm` `{ code }`: a **different** administrator confirms a pending
1082
+ request (step-up again); the deployment approval must still match.
1083
+ - `POST …/approvals/:uuid/revoke` `{ reason }`: revoke a pending or active approval. No step-up
1084
+ (revoking must be fast). The revocation is recorded in Redis first and then in the database; if
1085
+ Redis cannot take it, nothing changes (`503 revoke_unavailable`, retry).
1086
+ - Fixed error codes (`{ statusCode, code, message }`): `profile_not_client_data`,
1087
+ `classes_exceed_deployment`, `file_content_not_allowed`, `invalid_request` (400);
1088
+ `step_up_required`, `confirmer_is_requester`, `forbidden` (403); `approval_not_found` (404, also
1089
+ another company's approval); `approval_pending_exists`, `approval_active_exists`,
1090
+ `approval_not_pending`, `approval_not_revocable`, `approval_state_changed`, `deployment_changed`
1091
+ (409); `too_many_requests` (429); `revoke_unavailable`, `authentication_unavailable`,
1092
+ `unavailable` (503).
1093
+ - An approval writes the company's mode `live`; revoking the last active one writes `off` (never
1094
+ `synthetic`). Org bootstrap still sets `off` / `synthetic` only, and **refuses to change a `live`
1095
+ company** (`agent_mode_live_locked`; revoke in the application first). The refusal comes with
1096
+ `--apply`; a dry run shows the company as `live → <file mode>` and writes nothing. A company that
1097
+ turns `live` after the plan was made is refused again under the row lock, so earlier steps of that
1098
+ run may already have been applied.
1099
+ - Every request, approval and revocation is an `admin_action` audit row
1100
+ (`agent.client_data.requested` / `.approved` / `.revoked`) with the profile, provider, contract
1101
+ reference, classes, file flag and reason; never data. Rows are never deleted.
1102
+ - `@astram/agent` binds `SHIELD_AGENT_CLIENT_DATA_PROFILES` to the booted `client_data` profiles. A
1103
+ profile that did not boot is not approvable: a request answers `400 profile_not_client_data`.
1104
+ - **Client data to a model provider (`@astram/agent`, model-provider-client-data).** Two approvals,
1105
+ neither a code constant. (1) The deployment: a model profile with
1106
+ `providerApproval: { scope: 'client_data', reference, region, retentionDays, dataClasses,
1107
+ approvedBy, approvedOn, fileContent, providerSmoke: { id, date }, runtimeConformance: { id, date,
1108
+ runtimeVersion } }` and the env `AGENT_CLIENT_DATA=1`. (2) The company: its administrators approve
1109
+ the profile through the routes above, which writes mode `live`. Nothing changes unless
1110
+ `AGENT_CLIENT_DATA=1` AND a `client_data` profile are both present. The process must not run with
1111
+ `NODE_TLS_REJECT_UNAUTHORIZED=0` (or `false`): boot refuses it for a `client_data` profile and the
1112
+ runtime client pins certificate verification.
1113
+ - Env, read only with `AGENT_ENABLED=1` (with the agent disabled `AGENT_CLIENT_DATA` is ignored and
1114
+ one fixed boot line says so): `AGENT_CLIENT_DATA` (`0` default | `1`) and
1115
+ `AGENT_EGRESS_POLICY_ACK` (`<verificationId>@<YYYY-MM-DD>`, at most 180 days old).
1116
+ `AgentModule.forRoot({ clientData: { twoPerson } })` defaults to `true`; `false` logs a warning at
1117
+ every boot.
1118
+ - A `client_data` profile boots only when, in this order, `env`, `masking`, `outbound_audit`,
1119
+ `egress_verification`, `runtime_conformance`, `provider_smoke` and `https` hold (boot error
1120
+ `modelProfiles.<name>: client_data_prerequisite (<which>)`; an `http:` runtime URL is refused in
1121
+ every environment, loopback included). With all of them met the profile boots.
1122
+ - A client-data run sends `dataScope: "client_data"` in its signed run request and only to a
1123
+ runtime whose verified `GET /v0/info` lists `client_data` and the `data-scope` feature at an
1124
+ agent-protocol minor at least the app's; otherwise the run ends `data_scope_unsupported` before
1125
+ anything is sent (the runtime must be on agent-protocol 0.5 or later, over `https:`, ideally with
1126
+ its durable state backend). Synthetic runs are unchanged. When the runtime lists `answers.v1`, an
1127
+ answers turn also sends the structured answers (free text masked; the flattened message copy is
1128
+ not). The app sends `history` unchanged: the runtime treats it as an untrusted data block
1129
+ (agent-protocol 0.5, a runtime requirement).
1130
+ - A run on a `live` company needs a booted `client_data` profile with an active approval, else
1131
+ `403 agent_client_data_not_approved`. The approval is read on every call and every send (revoke
1132
+ marker first, never cached); when it ends or narrows the task stops with `client_data_revoked`.
1133
+ A capability's data class must be in the approval's classes. File content goes to the provider
1134
+ only if the approval allows it (decided per run); org bootstrap still refuses a non-empty
1135
+ `agent.fileProviders` outside development/test. Masking is always on.
1056
1136
 
1057
1137
  ## Available, but NOT wired to a data path (do not assume they protect data yet)
1058
1138
  - **Envelope encryption / per-tenant DEKs** (`createEncryptionService`, `LocalKekCrypto`): the
@@ -180,9 +180,11 @@ makes every model call and never calls your app.
180
180
  lists rows returns how many exist, not only the page.
181
181
  - **No uuids to the model.** Return handles (`ctx.handle(uuid, typeKey)`) and declare handle inputs
182
182
  with `"x-astram-handle"`; never put a uuid, id or hex digest in a capability's output.
183
- - **Synthetic data only.** Enable a tenant (`agent: { mode: "synthetic" }` in the org bootstrap
184
- file) only when it holds synthetic data. Never set a model profile's `providerApproval.scope` to
185
- `client_data`; the framework refuses it until real data is approved.
183
+ - **Synthetic data by default.** Enable a tenant (`agent: { mode: "synthetic" }` in the org bootstrap
184
+ file) only when it holds synthetic data. Do not set a model profile's `providerApproval.scope` to
185
+ `client_data` or `AGENT_CLIENT_DATA=1` unless the platform owner has approved real data for that
186
+ provider; the company's administrators then approve it in the application (see
187
+ "Client data to a model provider" in CAPABILITIES.md). Never write a `live` mode yourself.
186
188
 
187
189
  ---
188
190
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astram/guide",
3
- "version": "0.2.28",
3
+ "version": "0.2.30",
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",