@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 +83 -3
- package/FRAMEWORK_RULES.md +5 -3
- package/package.json +1 -1
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
|
|
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
|
package/FRAMEWORK_RULES.md
CHANGED
|
@@ -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
|
|
184
|
-
file) only when it holds synthetic data.
|
|
185
|
-
`client_data
|
|
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.
|
|
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",
|