@cynodia/axiom 0.14.0-alpha.5 → 0.15.0-alpha.2

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/README.md CHANGED
@@ -33,7 +33,7 @@ Shorter forms of the same routing: [`AGENTS.md`](AGENTS.md) and [`llms.txt`](llm
33
33
  at this package's root.
34
34
 
35
35
  **Status: experimental / alpha.** The API may change between alpha releases. The
36
- documentation in `docs/` describes this exact version, `0.14.0-alpha.5`.
36
+ documentation in `docs/` describes this exact version, `0.15.0-alpha.2`.
37
37
 
38
38
  ## Installation
39
39
 
@@ -47,7 +47,7 @@ Every release of this project is a pre-release and npm's `latest` tag points at
47
47
  plain command above installs the current version. **There is no `alpha` dist-tag** — the tag
48
48
  was removed once it stopped tracking releases, and `npm install @cynodia/axiom@alpha` now
49
49
  fails with a 404. Pin the exact version instead when one is needed:
50
- `npm install @cynodia/axiom@0.14.0-alpha.5`.
50
+ `npm install @cynodia/axiom@0.15.0-alpha.2`.
51
51
 
52
52
  These are ES modules compiled to ES2022; import them with `import`, not `require`. There is
53
53
  no published Axiom CLI. `@cynodia/axiom-server`'s SQLite persistence adapter additionally
@@ -194,6 +194,7 @@ focused document when the reference is not specific enough for the question at h
194
194
  | Running N authority processes at once: ownership, leases, fencing, delivery guarantees, version skew | [`docs/DISTRIBUTED_AUTHORITY.md`](docs/DISTRIBUTED_AUTHORITY.md) |
195
195
  | Observing a `QueryDef` result over time: live deltas, reconnect, cursor, backpressure, transport | [`docs/LIVE_QUERIES.md`](docs/LIVE_QUERIES.md) |
196
196
  | Durable workflows: steps, bindings, event waits, timers, retries, cancellation, crash recovery | [`docs/WORKFLOWS.md`](docs/WORKFLOWS.md) |
197
+ | May this principal perform this operation: `AuthorizationPolicyDef`, closed scope, ALLOW/DENY, fail closed | [`docs/AUTHORIZATION.md`](docs/AUTHORIZATION.md) |
197
198
  | Machine queries, mutation impact and graph transformations | [`docs/AGENT_API.md`](docs/AGENT_API.md) |
198
199
 
199
200
  `docs/AGENT_REFERENCE.md` plus the `.d.ts` declarations are intended to be sufficient on
@@ -1,6 +1,6 @@
1
1
  # Actions and transactions
2
2
 
3
- Axiom 0.14.0-alpha.5. An action is behavior expressed as data, executed as a transaction.
3
+ Axiom 0.15.0-alpha.2. An action is behavior expressed as data, executed as a transaction.
4
4
 
5
5
  ```ts
6
6
  {
package/docs/AGENT_API.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Agent API
2
2
 
3
- Axiom 0.14.0-alpha.5. The machine-facing interface. Agents query semantics and apply
3
+ Axiom 0.15.0-alpha.2. The machine-facing interface. Agents query semantics and apply
4
4
  structural transformations; they never edit generated code.
5
5
 
6
6
  ```ts
@@ -1,6 +1,6 @@
1
1
  # Agent reference
2
2
 
3
- Axiom 0.14.0-alpha.5. Compressed operational contract. Read this plus the `.d.ts`
3
+ Axiom 0.15.0-alpha.2. Compressed operational contract. Read this plus the `.d.ts`
4
4
  declarations before authoring or modifying an Axiom application.
5
5
 
6
6
  Formal guarantees: [`SEMANTIC_CONTRACT.md`](SEMANTIC_CONTRACT.md). Mistakes that compile:
@@ -731,7 +731,8 @@ Portable artifacts, for a runtime written in another language:
731
731
  @cynodia/axiom-server/schema/server-ir.v5.schema.json JSON Schema for axiom.server.v5
732
732
  @cynodia/axiom-server/schema/server-ir.v6.schema.json JSON Schema for axiom.server.v6
733
733
  @cynodia/axiom-server/schema/server-ir.v7.schema.json JSON Schema for axiom.server.v7
734
- @cynodia/axiom-server/schema/server-ir.v8.schema.json JSON Schema for axiom.server.v8 (latest)
734
+ @cynodia/axiom-server/schema/server-ir.v8.schema.json JSON Schema for axiom.server.v8
735
+ @cynodia/axiom-server/schema/server-ir.v9.schema.json JSON Schema for axiom.server.v9 (latest)
735
736
  @cynodia/axiom-server/schema/protocol.v1.schema.json JSON Schema for the protocol
736
737
  @cynodia/axiom-server/conformance/queries/<name>.json one query conformance fixture (axiom.conformance.v4)
737
738
  @cynodia/axiom-server/conformance/migrations/<name>.json one migration conformance fixture (axiom.conformance.v5)
@@ -1231,6 +1232,86 @@ Diagnostics (validation): `WORKFLOW_ENTRY_NOT_FOUND` `WORKFLOW_STEP_NOT_FOUND`
1231
1232
  `WORKFLOW_UNREACHABLE_STEP` `WORKFLOW_NO_TERMINAL` `WORKFLOW_EXPRESSION_SCOPE`
1232
1233
  `WORKFLOW_NONDETERMINISTIC`.
1233
1234
 
1235
+ ## AUTHORIZATION
1236
+
1237
+ Full model: [`AUTHORIZATION.md`](AUTHORIZATION.md). **0.15 is phased.** Phase B added the
1238
+ vocabulary, `validateGraph` totality, the single semantic projection and `axiom.server.v9`.
1239
+ **Phase C (this build) enforces `ActionDef.authorizationPolicy`** — one `authorize()`
1240
+ evaluator on every `action.invoke` path (direct call, workflow action step, scheduler- and
1241
+ event-triggered action, retry, failover), conjoined with any legacy `ActionDef.authorization`
1242
+ expression, re-evaluated on every invocation against current policy. **Phase D enforces
1243
+ `QueryDef.authorizationPolicy` (`query.read`)** — the same evaluator gates a one-shot query,
1244
+ a `query` operation inside an action and a live-query open, before any provider call;
1245
+ `ReadPolicyDef` still filters rows, AND-ed into the effective filter so `filter`/`sort`/
1246
+ `limit`/aggregate see only the authorized dataset. **Phase E** — `WorkflowDef.startPolicy`
1247
+ decides `workflow.start` (a denied start creates no instance), `instanceAccessPolicy`
1248
+ decides `workflow.cancel` / `.inspect` / `.history` when declared; with none, cancel keeps
1249
+ the spec14pt6 owner-fingerprint baseline and `getWorkflow` / `inspectWorkflows` /
1250
+ `workflowHistory` stay operator-inspection APIs (an explicit trust boundary, not on the
1251
+ protocol). Unauthorized inspection ⇒ `undefined` / `[]` (no existence leak); terminal
1252
+ cancel stays idempotent for any caller. **Phase F** — a live query re-checks `query.read`
1253
+ against the **re-resolved** caller on every re-evaluation, so a revoked principal stops the
1254
+ stream (`{ kind: 'error', code: 'AUTHORIZATION_DENIED' }`); the current caller drives row
1255
+ filtering, so lost/gained access to a row is a `remove`/`insert` delta; `resumeLiveQuery`
1256
+ re-resolves + re-authorizes and refuses a cursor issued for a different principal.
1257
+ `subscription.open` (`SubscriptionDef`) is an infrastructure trust boundary — no graph
1258
+ policy, the adapter contract is the boundary. Every graph-defined `AuthorizationPolicyDef`
1259
+ reference is now enforced; `AUTHORIZATION_ENFORCEMENT_UNAVAILABLE` /
1260
+ `usesUnenforcedAuthorizationVocabulary` are the dormant fail-closed extension point
1261
+ (spec4 §4).
1262
+
1263
+ One authorization language. An `AuthorizationPolicyDef` (graph node kind
1264
+ `authorization-policy`) is a single boolean `allow` `Expression`. Exactly `true` ⇒ ALLOW;
1265
+ `false`, an absent policy, or **any** evaluation error ⇒ DENY (fail closed). **A missing
1266
+ PRINCIPAL / RESOURCE field never satisfies a rule** (spec15pt2): the policy evaluator is
1267
+ three-valued (concrete / security-absence / error), so `PRINCIPAL.role != "banned"`,
1268
+ `NOT(PRINCIPAL.role == "banned")` and `RESOURCE.ownerId == PRINCIPAL.id` all DENY when the
1269
+ named field is absent or the caller is anonymous — `neq` / `not` / `or` cannot turn absence
1270
+ into authority. `literal(true)` stays a genuine constant (explicit public still admits
1271
+ anonymous). Ordinary `Expression` semantics elsewhere are unchanged. Closed
1272
+ expression scope — the three reserved ids exported from core: `ref(PRINCIPAL)`
1273
+ (`'axiom_principal'`, the id `ActionDef.authorization` already uses), `ref(RESOURCE)`
1274
+ (`'axiom_resource'` — for `action.invoke`, a `{ id, kind }` descriptor), `ref(OPERATION)`
1275
+ (`'axiom_operation'` — resolves to the canonical operation string). **Not** a `StateDef`
1276
+ (`AUTHORIZATION_INVALID_SCOPE`), **not** a `QueryDef`, **not** `now`/`uuid`/`random`
1277
+ (`AUTHORIZATION_NONDETERMINISTIC`). Referenced by id: `ActionDef.authorizationPolicy`
1278
+ (`action.invoke`), `QueryDef.authorizationPolicy` (`query.read`, distinct from `readPolicyId`
1279
+ row filtering), `WorkflowDef.startPolicy` (`workflow.start`),
1280
+ `WorkflowDef.instanceAccessPolicy` (`workflow.inspect`/`.history`/`.cancel`). Legacy
1281
+ `ActionDef.authorization` (an `Expression`) and `ReadPolicyDef` coexist; when both a policy
1282
+ and a legacy expression are present the effective decision is their conjunction. Canonical
1283
+ operation ids: `AUTHORIZATION_OPERATIONS`. The pure ALLOW/DENY combiner is
1284
+ `decideAuthorization` (core).
1285
+
1286
+ `AuthorizationPolicyDef` is in `EXECUTABLE_KINDS` — editing `allow` from ALLOW to DENY moves
1287
+ `semanticFingerprint` and the `AuthorityCompatibilityKey`; a `name`/`description` change
1288
+ does not; a graph with **no** authorization vocabulary compiles to the byte-identical v1–v8
1289
+ document it always did. Totality: every validate/compile/analyze surface is total over a
1290
+ `null` / non-object policy / non-plain `allow` — structured diagnostic, never a native
1291
+ `TypeError`.
1292
+
1293
+ Static analysis: `AgentAPI.analyzeAuthorization()` → what protects every action / query /
1294
+ workflow surface, per-policy dependencies + a secret-free rule `summary`, the `unprotected`
1295
+ list (surfaces with no explicit boundary), and per-workflow `privilegeReviewActions`
1296
+ (policy-carrying action steps the start principal is not statically proven to satisfy). It
1297
+ never claims authorization it cannot prove and exposes no runtime secret. Primitive:
1298
+ `authorizationPolicyDependencies(policy)` (core).
1299
+
1300
+ Conformance: `axiom.conformance.v9` (`conformance/authorization/`),
1301
+ `runAuthorizationConformanceFixture` / `Suite` — a compiled Server IR + principals +
1302
+ provider rows + a deterministic driver script, each step carrying the independently-computed
1303
+ decision; verified over memory and SQLite.
1304
+
1305
+ Diagnostics (validation): `AUTHORIZATION_INVALID_POLICY` `AUTHORIZATION_INVALID_SCOPE`
1306
+ `AUTHORIZATION_NONDETERMINISTIC` `AUTHORIZATION_UNKNOWN_POLICY`. Runtime refusal:
1307
+ `AUTHORIZATION_DENIED` — `details.operation` (`action.invoke` / `query.read` /
1308
+ `workflow.start` / `workflow.cancel`) + `details.reason`
1309
+ (`policy-denied`/`policy-error`/`legacy-denied`/`legacy-error`/`owner-mismatch`), terminal,
1310
+ not retryable, carries no state value / credential / claim. A denied `query` operation
1311
+ inside an action surfaces as `QUERY_OPERATION_FAILED` with `details.code =
1312
+ 'AUTHORIZATION_DENIED'` and rolls the action back. A denied `workflow.inspect` /
1313
+ `workflow.history` returns `undefined` / `[]` (no existence leak).
1314
+
1234
1315
  ## Metadata classes
1235
1316
 
1236
1317
  ```ts
@@ -1,6 +1,6 @@
1
1
  # Anti-patterns
2
2
 
3
- Axiom 0.14.0-alpha.5. Each of these compiles. Each is wrong. Each is followed by the correct
3
+ Axiom 0.15.0-alpha.2. Each of these compiles. Each is wrong. Each is followed by the correct
4
4
  alternative.
5
5
 
6
6
  ## 1. Field names as entity runtime keys
package/docs/AUTHORITY.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Authority
2
2
 
3
- Axiom 0.14.0-alpha.5. How an application crosses the trust boundary.
3
+ Axiom 0.15.0-alpha.2. How an application crosses the trust boundary.
4
4
 
5
5
  Until 0.5.x an Axiom application executed locally. 0.6 adds an **authority**: a generic
6
6
  runtime that owns state, decides mutations and persists them. The same semantic graph
@@ -467,7 +467,7 @@ does for a local failure.
467
467
  | --- | --- |
468
468
  | `UNKNOWN_SERVER_ACTION` | The request named an action this authority does not execute. |
469
469
  | `ARGUMENT_TYPE_MISMATCH` | An argument did not conform to its declared parameter type, or is not a parameter at all. |
470
- | `AUTHORIZATION_DENIED` | The caller may not invoke this action, or the rule could not be evaluated. |
470
+ | `AUTHORIZATION_DENIED` | The caller may not invoke this action / read this query / start or cancel this workflow, or the rule could not be evaluated. spec15 Phase C covers `ActionDef.authorizationPolicy`, Phase D `QueryDef.authorizationPolicy` (`query.read` — including a live query whose caller is revoked mid-subscription, Phase F, delivered as a live-query error message carrying this code), Phase E `WorkflowDef.startPolicy` / `instanceAccessPolicy` (`workflow.start` / `.cancel`); `details.operation` names the canonical operation and `details.reason` is a non-secret machine reason (`policy-denied` / `policy-error` / `legacy-denied` / `legacy-error` / `owner-mismatch`). A denied `workflow.inspect` / `workflow.history` returns nothing instead (no existence leak). |
471
471
  | `CONCURRENCY_CONFLICT` | Another transaction committed the same state first. Nothing was applied. |
472
472
  | `MALFORMED_REQUEST` | The request was not an Axiom semantic request, or spoke an unknown protocol. |
473
473
  | `AUTHORITY_UNREACHABLE` | The authority could not be reached, timed out, or answered with a transport error. |
@@ -504,6 +504,7 @@ does for a local failure.
504
504
  | `LIVE_QUERY_CURSOR_INVALID` | An `axiom.live-query-cursor.v1` was tampered with, unsigned, or minted for a different query / principal / arguments / read policy. Fail-closed: continuing from it is refused and nothing is disclosed (spec13 §33-§35). |
505
505
  | `LIVE_QUERY_CURSOR_INCOMPATIBLE` | A live-query cursor was minted by an authority whose schema fingerprint, semantic fingerprint or server contract differs from this one's — the same fail-closed compatibility check the distributed work store applies. A presentation-only graph change does *not* trigger this (spec13 §79-§82). |
506
506
  | `LIVE_QUERY_EVALUATION_FAILED` | Re-evaluating the live query against the `DataProvider` after a committed change failed. Delivered as a `{ kind: 'error' }` message on the live stream; the last delivered result stands and the consumer may reconnect (spec13 §132). |
507
+ | `AUTHORIZATION_ENFORCEMENT_UNAVAILABLE` | The Server IR declares authorization vocabulary this build validates and fingerprints but does not yet enforce. Every `AuthorizationPolicyDef` reference the graph vocabulary defines is enforced through spec15 Phase E (`ActionDef` / `QueryDef` / `WorkflowDef`), so this is **dormant** for every valid IR — kept as the fail-closed extension point (`usesUnenforcedAuthorizationVocabulary`) for any later phase that adds authorization vocabulary ahead of its enforcement (spec4 §4, spec15 §128). |
507
508
  | `SCHEMA_MIGRATION_REQUIRED` | Persisted canonical data is at an older semantic schema than the graph requires; a migration must run before the authority will serve traffic (spec11 §12). |
508
509
  | `SCHEMA_INCOMPATIBLE` | Persisted data cannot be reconciled with the graph — a stored schema version ahead of the graph's, or no migration path to it. |
509
510
  | `MIGRATION_IN_PROGRESS` | A migration is already running: a migration lock is held with a valid lease, by this instance or another (spec11 §66). |
@@ -691,6 +692,7 @@ page plus the conformance fixtures.
691
692
  | `axiom.server.v6` | `QueryDef`, `RelationshipDef` and `ReadPolicyDef`, the `query` operation kind, and the `provider-record` location — the semantic data-access & query layer over large authoritative datasets | 0.10.0 |
692
693
  | `axiom.server.v7` | `MigrationDef` and the closed migration-operation vocabulary, plus the top-level `schemaVersion` and `schemaFingerprint` fields — semantic schema evolution over persisted canonical data | 0.11.0 |
693
694
  | `axiom.server.v8` | `WorkflowDef` and the closed workflow-step vocabulary (`action`, `wait-event`, `timer`, `branch`, `complete`, `fail`) — durable, portable long-running orchestration | 0.14.0 |
695
+ | `axiom.server.v9` | `AuthorizationPolicyDef` (one boolean `allow` expression over the closed scope `PRINCIPAL` / `RESOURCE` / `OPERATION`) and the `authorizationPolicy` / `startPolicy` / `instanceAccessPolicy` references from actions, queries and workflows — the canonical authorization model. See [`AUTHORIZATION.md`](AUTHORIZATION.md) | 0.15.0 |
694
696
 
695
697
  `SERVER_IR_CONTRACTS` enumerates all eight, and is the single source of truth this table is
696
698
  tested against — `packages/demo/test/documentation.test.ts` fails if a contract in
@@ -0,0 +1,261 @@
1
+ # Authorization
2
+
3
+ Axiom 0.15.0-alpha.2. The operational contract for **authorization completeness** — the 0.15
4
+ milestone (spec15). Whether a principal may perform a semantic operation is part of the
5
+ graph's executable meaning — not a runtime concern, not UI visibility, not something that
6
+ varies by transport, provider, process, retry path or authority topology. `axiom.server.v9`
7
+ when a graph carries authorization vocabulary; a graph with none is byte-identical to its
8
+ prior contract.
9
+
10
+ > **Authentication** answers *who is this?* — credential adapters and transports do that,
11
+ > outside portable application semantics. **Authorization** answers *may this principal
12
+ > perform this semantic operation?* — that is what this document defines.
13
+
14
+ 0.15 landed in nine phases (A–I); `0.15.0-alpha.2` (spec15pt2) is a corrective pass —
15
+ authorization **absent-value safety** (a missing PRINCIPAL / RESOURCE field never grants
16
+ authority), `validateGraph` totality over a malformed `allow` tree, and a fail-closed
17
+ `host.authenticate` exception boundary. This document describes the **model** (stable) and
18
+ marks which phase delivered each surface. External adversarial validation precedes the
19
+ semantic freeze (spec15 §134).
20
+
21
+ | Phase | Delivers | State |
22
+ | --- | --- | --- |
23
+ | A | Public-API authorization inventory (below) | ✅ landed |
24
+ | B | `AuthorizationPolicyDef` vocabulary, `validateGraph` totality, single semantic projection, `axiom.server.v9` | ✅ landed |
25
+ | **C** | One canonical `authorize()` evaluator; `ActionDef.authorizationPolicy` enforced on every `action.invoke` path (direct / workflow step / scheduler / event / retry / failover), conjoined with legacy `ActionDef.authorization`; state & provider-record mutation authorized through the action boundary | ✅ **enforced** |
26
+ | **D** | `QueryDef.authorizationPolicy` (`query.read`) enforced identically for a one-shot query, a `query` operation inside an action, and a live-query open — before any provider call. Row-level filtering stays `ReadPolicyDef`, AND-ed into the effective filter so `filter` / `sort` / `limit` / aggregation see only the authorized dataset | ✅ **enforced** |
27
+ | **E** | `WorkflowDef.startPolicy` decides `workflow.start` (discovering a workflow ≠ starting it). `instanceAccessPolicy` decides `workflow.cancel` / `.inspect` / `.history` when declared; with none, cancel keeps the 0.14 owner-fingerprint baseline and inspection stays an operator trust boundary. Unauthorized inspection is answered like a missing instance; terminal cancellation stays idempotent for any caller | ✅ **enforced** |
28
+ | **F** | A live query re-checks `query.read` against the **re-resolved** caller on every re-evaluation, so a revoked principal stops the stream (`{ kind: 'error', code: 'AUTHORIZATION_DENIED' }`). The current caller drives row filtering, so a claim / row change that removes access is a `remove` delta and the reverse an `insert`. `resumeLiveQuery` re-resolves + re-authorizes and refuses a cursor issued for a different principal. `subscription.open` (`SubscriptionDef`) is an infrastructure trust boundary — no graph policy, the adapter contract is the boundary | ✅ **enforced** |
29
+ | **G** | `AgentAPI.analyzeAuthorization()` — a graph-level coverage audit: what protects every action / query / workflow surface, what each policy depends on (`PRINCIPAL` / `RESOURCE` fields, `OPERATION`, a secret-free rule summary), which surfaces have **no** explicit boundary, and which workflow action steps run a policy the start principal is not statically proven to hold. `authorizationPolicyDependencies` in core | ✅ **landed** |
30
+ | **H** | `axiom.conformance.v9` — the portable authorization fixture tier (`conformance/authorization/`), `runAuthorizationConformanceFixture` / `Suite`; decisions verified over both memory and SQLite persistence | ✅ **landed** |
31
+ | **I** | The internal adversarial matrix (§74/§88/§136/§137 — every public surface × {owner, different, anonymous, role-equivalent, admin-like, malformed}, forbidden counters at zero), topology-independence over 1/2/8 authorities on shared SQLite, cross-principal race + contention (no unauthorized win, no raw SQLite error), and failover parity | ✅ **landed** |
32
+
33
+ Every `AuthorizationPolicyDef` reference the graph vocabulary defines is enforced across
34
+ `ActionDef` / `QueryDef` / `WorkflowDef` and every live-query re-evaluation.
35
+ `createAxiomServer` still **fails closed** (`AUTHORIZATION_ENFORCEMENT_UNAVAILABLE`) via
36
+ `usesUnenforcedAuthorizationVocabulary` — kept as the dormant extension point for any later
37
+ phase that introduces authorization vocabulary ahead of its enforcement (spec4 §4,
38
+ spec15 §128).
39
+
40
+ ---
41
+
42
+ ## The model
43
+
44
+ There is **one** authorization language. An `AuthorizationPolicyDef` is a graph node with a
45
+ single boolean `allow` expression:
46
+
47
+ ```ts
48
+ import { OPERATION, PRINCIPAL, RESOURCE } from '@cynodia/axiom-core';
49
+
50
+ graph.addNode<AuthorizationPolicyDef>({
51
+ id: POLICY_DOC_OWNER,
52
+ kind: 'authorization-policy',
53
+ // the owner, or anyone in the same tenant
54
+ allow: binary('or',
55
+ binary('eq', field(ref(RESOURCE), F_DOC_OWNER), field(ref(PRINCIPAL), F_PRINCIPAL_ID)),
56
+ binary('eq', field(ref(RESOURCE), F_DOC_TENANT), field(ref(PRINCIPAL), F_PRINCIPAL_TENANT))),
57
+ });
58
+ ```
59
+
60
+ - **Closed scope.** A policy expression's `ref` may resolve **only** the three reserved ids
61
+ exported from `@cynodia/axiom-core` — `PRINCIPAL` (`'axiom_principal'`, the canonical
62
+ caller, the *same* id `ActionDef.authorization` uses), `RESOURCE` (`'axiom_resource'`, the
63
+ semantic object the decision is about — for `action.invoke` a stable `{ id, kind }`
64
+ descriptor, since there is no per-record target), and `OPERATION` (`'axiom_operation'`;
65
+ `ref(OPERATION)` resolves to the canonical operation string, e.g. `'action.invoke'`). No
66
+ `StateDef`, no `QueryDef`, no `now` / `uuid` / `random`, no ambient runtime state, no
67
+ host-language callback — authorization is portable, deterministic, statically analyzable
68
+ data (spec15 §7, §34). Write a policy with the ordinary `field` / `ref` / `binary`
69
+ vocabulary: `binary('eq', field(ref(RESOURCE), F_OWNER), field(ref(PRINCIPAL), F_USER_ID))`.
70
+ - **ALLOW / DENY, fail closed.** `allow` evaluating to exactly `true` is **ALLOW**. `false`,
71
+ an absent policy field, or *any evaluation error* is **DENY** — the safe direction for a
72
+ failed access check is always refusal (spec15 §8, §123).
73
+ - **A missing PRINCIPAL / RESOURCE field never satisfies a rule** (spec15pt2 F1). The policy
74
+ evaluator is three-valued — a concrete value, **security-scope absence** (a field the
75
+ scope object does not carry), or an evaluation error. A comparison, `not`, `neq` or `or`
76
+ whose truth would depend on absence is *not satisfied*, so `PRINCIPAL.role != "banned"` /
77
+ `NOT(PRINCIPAL.role == "banned")` / `RESOURCE.ownerId == PRINCIPAL.id` all **DENY** when
78
+ the named field is absent or the caller is anonymous. This is authorization-evaluation
79
+ semantics only; ordinary `Expression` equality/nullish behaviour elsewhere is unchanged
80
+ (spec15pt2 §4). An `OR` branch that is *concretely* true still allows even if the other
81
+ branch is absent-dependent. `literal(true)` is a genuine constant — an explicitly public
82
+ policy still admits an anonymous caller.
83
+ - **Referenced by id.** A protected surface points at a policy:
84
+ - `ActionDef.authorizationPolicy` — `action.invoke`
85
+ - `QueryDef.authorizationPolicy` — `query.read` (distinct from `readPolicyId`, which
86
+ filters *which rows* the result contains)
87
+ - `WorkflowDef.startPolicy` — `workflow.start`
88
+ - `WorkflowDef.instanceAccessPolicy` — `workflow.inspect` / `workflow.history` /
89
+ `workflow.cancel` on a running instance
90
+ - **Same evaluator everywhere.** Actions, queries, workflows and live queries all make the
91
+ decision through one evaluator with the same `{ principal, operation, resource, policy }`
92
+ inputs — no per-surface policy engine (spec15 §5, §96).
93
+
94
+ ### Operation identity
95
+
96
+ Policies reason over canonical semantic operations (`AUTHORIZATION_OPERATIONS`), never over
97
+ transport method names:
98
+
99
+ ```
100
+ action.invoke query.read record.read record.mutate state.read state.mutate
101
+ workflow.start workflow.inspect workflow.history workflow.cancel
102
+ live.open live.resume subscription.open event.ingress
103
+ ```
104
+
105
+ ### Default when no policy is attached (`AUTHORIZATION_DEFAULT`)
106
+
107
+ One canonical rule, applied consistently, never left to the runtime (spec15 §9):
108
+
109
+ - a surface whose **pre-0.15 contract is public** (an unrestricted `ActionDef`, an
110
+ unrestricted `QueryDef`) stays public;
111
+ - a surface whose contract is **already restricted** — a legacy `ActionDef.authorization`
112
+ expression, a `QueryDef` with a `ReadPolicyDef`, a workflow instance operation (0.14
113
+ owner-fingerprint) — keeps that restriction;
114
+ - a **new privileged surface** with no policy fails closed.
115
+
116
+ A policy may broaden an owner-only default, but only explicitly — a role like `admin` never
117
+ bypasses owner-only semantics unless the policy says so (spec15 §14, §74).
118
+
119
+ ---
120
+
121
+ ## Authorization is semantic identity
122
+
123
+ `AuthorizationPolicyDef` is in `EXECUTABLE_KINDS` — the *single* projection both
124
+ `semanticFingerprint` and the authority-compatibility key derive from (spec14pt3's
125
+ architecture, preserved). Therefore:
126
+
127
+ - editing a policy from ALLOW to DENY **moves the semantic fingerprint**;
128
+ - a presentation-only change (`name`, `description`) does **not**;
129
+ - two authorities whose executable authorization meaning differs are **incompatible** — a
130
+ mixed-build authority with an incompatible policy fails closed before advancing a durable
131
+ workflow or serving a query, exactly as for any other semantic change (spec15 §45, §46,
132
+ §47). "Only security" is still semantic; it does not bypass compatibility.
133
+
134
+ A graph with **no** authorization vocabulary compiles to the byte-identical v1–v8 document
135
+ it always did and its fingerprint is unchanged (spec15 §39, §132).
136
+
137
+ **Evaluator version.** `0.15.0-alpha.1` and `0.15.0-alpha.2` evaluate the *same* policy
138
+ differently (absent-value safety, spec15pt2 F1), yet the graph — and its
139
+ `semanticFingerprint` — is identical. So the `AuthorityCompatibilityKey` carries an
140
+ `authorizationRuntime` discriminator, present only when the IR uses authorization
141
+ vocabulary: a mixed `alpha.1` / `alpha.2` cluster over an authorization-bearing graph is
142
+ fail-closed **incompatible** (spec15pt2 §35), while a graph with no policy rolls the
143
+ upgrade unaffected.
144
+
145
+ ---
146
+
147
+ ## Public-API authorization inventory (Phase A)
148
+
149
+ Every public `AxiomServer` semantic operation, classified. "Effective principal" is the
150
+ credential resolved at authority ingress; async work (workflow continuation, scheduler,
151
+ effect retry) reconstructs it from durable identity, never a process-local object
152
+ (spec15 §27).
153
+
154
+ | Operation | Kind | Resource | Effective principal | Pre-0.15 behaviour | 0.15 target |
155
+ | --- | --- | --- | --- | --- | --- |
156
+ | `handle({kind:'invoke'})` / `ActionDef` | execute | `ActionDef` (`{id,kind}`) | caller credential | `ActionDef.authorization` expr, or public | **C ✅** — `action.invoke` policy ∧ legacy expr, re-evaluated on every invocation against current policy |
157
+ | workflow `action` step | execute | `ActionDef` | **workflow's start principal** | inherits action's `authorization`, re-evaluated per step (0.14) | **C ✅** — same `authorize()` as a direct call; workflow start never amplifies privilege (§101); denial is terminal not retried (§109) |
158
+ | trigger / scheduler / event → action | execute | `ActionDef` | semantic object that scheduled it (no ambient SYSTEM) | `source: 'system'`, `invocation.allowedSources` | **C ✅** — policy under the canonical effective principal; `'system'` source is not privilege (§26, §102) |
159
+ | `handle({kind:'query'})` / `QueryDef` | read | `QueryDef` | caller credential | `ReadPolicyDef` row filter, or public | **D ✅** — `query.read` policy gates the whole query before any provider call; `ReadPolicyDef` still filters rows, AND-ed into the effective filter so `filter`/`sort`/`limit`/aggregate see the authorized dataset (§18, §81, §82) |
160
+ | `query` operation inside an `ActionDef` | read | `QueryDef` | the running action's caller | flows through the action | **D ✅** — `query.read` under `activePrincipal`; a denial fails the operation and rolls the action back (§54) |
161
+ | live query `openLiveQuery` (open) | read | `QueryDef` | caller credential | as one-shot query | **D ✅** — `query.read` decided once at open, identically to a one-shot query (§16) |
162
+ | live query update / `resumeLiveQuery` | read | `QueryDef` + rows | **re-resolved caller** | as one-shot query | **F ✅** — every re-evaluation re-resolves the caller and re-checks `query.read` (a revoked principal ⇒ `{ kind:'error', code:'AUTHORIZATION_DENIED' }`); row filtering tracks current claims so lost/gained access is a `remove`/`insert` delta (§19, §58, §79, §80); resume re-authorizes and rejects another principal's cursor (§20) |
163
+ | `SubscriptionDef` delivery (`subscription.open`) | execute | `SubscriptionDef` | event source identity | adapter-authenticated | **infrastructure trust boundary** — no graph policy; a `SubscriptionDef` is a world→Axiom inbound stream connected by an adapter, not a principal-facing subscribe. The adapter contract is the boundary (§24, §59) |
164
+ | provider-record mutation (via `ActionDef`) | mutate | provider record | caller credential | flows through the action's authorization | **C ✅** — authorized through the causing `ActionDef`; there is no public mutation path that bypasses `invokeCore` (§23) |
165
+ | authoritative `StateDef` write (via `ActionDef`) | mutate | `StateDef` | caller credential | flows through the action's authorization | **C ✅** — same; `hydrateState` stays administrative, not a semantic write (§21) |
166
+ | `startWorkflow` | execute | `WorkflowDef` (`{id,kind}`) | caller credential | any principal | **E ✅** — `WorkflowDef.startPolicy`; a denied start creates no instance; separate from the ActionDef auth a later step is subject to (§100, §101) |
167
+ | `getWorkflow` / `inspectWorkflows` / `workflowHistory` | read | workflow instance | caller credential | `getWorkflow` unauthenticated (0.14) | **E ✅** — gated by `instanceAccessPolicy` when declared (unauthorized ⇒ answered like a missing instance, no existence leak, §39); with none they stay **operator-inspection APIs**, an explicit trust boundary, not reachable through the principal-facing protocol (§15, §112-§113) |
168
+ | `cancelWorkflow` | mutate | workflow instance | caller credential | **owner-fingerprint** (spec14pt6 F4) | **E ✅** — `instanceAccessPolicy` decides when declared (explicit cross-principal, no implicit role bypass); with none the owner-fingerprint baseline is preserved. Unauthorized mutates nothing; terminal cancel stays idempotent for any caller (§14, §110) |
169
+ | `handle({kind:'event'})` ingress | execute | `EventDef` | event source identity | payload-validated, infra-trusted | explicit trust boundary per source; credentials, where accepted, affect authorization (F, §24) |
170
+ | `mutationLog` / `effectLog` / `subscriptionLog` / `revisionInspection` | read | authority internals | — | operator inspection | **operator / infrastructure APIs** — explicit trust boundary, not principal-facing (§112, §113) |
171
+
172
+ ---
173
+
174
+ ## Diagnostics
175
+
176
+ Validation (`validateGraph`): `AUTHORIZATION_INVALID_POLICY`, `AUTHORIZATION_INVALID_SCOPE`,
177
+ `AUTHORIZATION_NONDETERMINISTIC`, `AUTHORIZATION_UNKNOWN_POLICY` — see
178
+ [`VALIDATION.md`](VALIDATION.md). Every one is structured; a malformed policy never produces
179
+ a native exception (spec15 §37).
180
+
181
+ Runtime refusal: `AUTHORIZATION_DENIED` — the canonical code a denied `action.invoke`
182
+ (Phase C), `query.read` (Phase D — including a live query whose caller is revoked
183
+ mid-subscription, Phase F), `workflow.start` or `workflow.cancel` (Phase E) returns. On a
184
+ live subscription it arrives as `{ kind: 'error', code: 'AUTHORIZATION_DENIED' }` and the
185
+ stream serves no further data. Its `details` carry the canonical `operation`, a non-secret
186
+ machine `reason` —
187
+ `policy-denied` (the policy is not exactly `true`), `policy-error` (the policy threw — an
188
+ evaluation error is DENY, never ALLOW, spec15 §123), `legacy-denied` / `legacy-error` (the
189
+ legacy `authorization` expression), or (for workflow instance ops with no policy declared)
190
+ `owner-mismatch`, or (spec15pt2 F3, when `host.authenticate` itself threw)
191
+ `authentication-error` with `operation: 'authentication'` — plus `actionId` / `queryId`
192
+ where applicable. A denied `query` operation
193
+ inside an action surfaces as `QUERY_OPERATION_FAILED` with `details.code =
194
+ 'AUTHORIZATION_DENIED'` and rolls the action transaction back. A denied `workflow.inspect` /
195
+ `workflow.history` is answered like a missing instance (`undefined` / `[]`), never
196
+ `AUTHORIZATION_DENIED` — no existence leak (§39). No state value, credential, claim or token
197
+ crosses the boundary (spec15 §38, §66, §68). The refusal survives transport faithfully
198
+ (never a `500` / native exception). It is a **terminal** semantic refusal — not a retryable
199
+ infrastructure failure: a workflow action denied at a step routes to `onError` / fails, and
200
+ the attempt count does not grow (spec15 §56, §109).
201
+
202
+ `AUTHORIZATION_ENFORCEMENT_UNAVAILABLE` — `createAxiomServer` refuses at admission when the
203
+ IR carries authorization vocabulary a phase has **not yet** wired. Every
204
+ `AuthorizationPolicyDef` reference the graph vocabulary defines is now enforced, so the gate
205
+ is dormant; it is kept as the fail-closed extension point for a later phase.
206
+
207
+ ---
208
+
209
+ ## Static analysis (`AgentAPI.analyzeAuthorization`)
210
+
211
+ `AgentAPI.analyzeAuthorization()` returns an `AuthorizationAnalysis` over the graph — no
212
+ running authority, and it never claims a principal *is* authorized where it cannot prove it
213
+ (spec15 §42, §50). It exposes policy **structure**, never a runtime secret (spec15 §83):
214
+
215
+ - `policies[]` — per `AuthorizationPolicyDef`: `principalFields` / `resourceFields` (field
216
+ ids read off each scope), `readsOperation`, `constant` (`always-allow` / `always-deny` /
217
+ `null`), a secret-free one-line `summary` (`requires RESOURCE.field_doc_owner ==
218
+ PRINCIPAL.field_user_id`), and any `AUTHORIZATION_*` `problems`.
219
+ - `operations[]` — every `action.invoke` / `query.read` / `workflow.*` surface with its
220
+ `protection` (`policy` / `legacy-expression` / `policy+legacy` / `read-policy` /
221
+ `policy+read-policy` / `owner-fingerprint` / `public`) and an `unresolved` flag.
222
+ - `unprotected[]` — every surface with **no** explicit authorization boundary (a `public`
223
+ action or query; a workflow with no `startPolicy`). A workflow instance op with no
224
+ `instanceAccessPolicy` is *not* unresolved — owner-fingerprint is a defined default.
225
+ - `workflows[]` — `start` / `instanceAccess` mode, `actionDependencies` (each step's
226
+ `ActionDef` with its own protection), and `privilegeReviewActions` — action steps whose
227
+ `ActionDef` carries a policy that static analysis cannot prove the start principal
228
+ satisfies (the runtime enforces it per step, spec15 §10, §101).
229
+ - `usesAuthorizationVocabulary` — `true` ⇒ the graph requires `axiom.server.v9`.
230
+
231
+ `authorizationPolicyDependencies(policy)` (core) is the total, secret-free primitive the
232
+ analysis is built on.
233
+
234
+ ---
235
+
236
+ ## Conformance
237
+
238
+ `axiom.conformance.v9` (`conformance/authorization/`) is the portable authorization tier.
239
+ Each fixture is a compiled `axiom.server.v9` Server IR, the principal records each
240
+ credential resolves to, provider seed rows, and a deterministic driver script — every step
241
+ carrying the **decision the fixture author computed independently** (ALLOW / DENY, and for a
242
+ query the exact set of authorized row ids). `runAuthorizationConformanceFixture` /
243
+ `runAuthorizationConformanceSuite` (from `@cynodia/axiom-server`) run a fixture through a
244
+ real authority and assert the runtime matches and that a denied step changed nothing
245
+ (spec15 §115). The tier is verified over both memory and SQLite persistence (spec15 §114).
246
+ Covered categories (spec15 §71): allow, deny, anonymous, owner, cross-principal, role/claim
247
+ condition, resource-owner condition, tenant isolation, query filtering, action invocation,
248
+ workflow continuation / cancellation / inspection, live-query resume, and the fail-closed
249
+ behaviour of a constant-deny (mixed-build-incompatible) policy. Full semantic-fingerprint
250
+ divergence and real-process crash boundaries are the Phase I suite.
251
+
252
+ ---
253
+
254
+ ## Not in 0.15
255
+
256
+ Axiom consumes canonical principals from authentication infrastructure and defines
257
+ *application* authorization. Out of scope: OAuth/OIDC, user management, MFA, delegation /
258
+ impersonation (`actAs` / `sudo` / `assumeRole`), an ABAC language with arbitrary functions,
259
+ external policy engines, cryptographic capability tokens, row encryption, an audit-log
260
+ product (spec15 §51, §89). Stronger explainability and AI authoring build on this model in
261
+ 0.16.
@@ -1,6 +1,6 @@
1
1
  # Constraints
2
2
 
3
- Axiom 0.14.0-alpha.5. Two constructs, answering different questions. They are not
3
+ Axiom 0.15.0-alpha.2. Two constructs, answering different questions. They are not
4
4
  interchangeable.
5
5
 
6
6
  | | Question | Sees |
@@ -1,6 +1,6 @@
1
1
  # Distributed authority
2
2
 
3
- *This document describes Axiom `0.14.0-alpha.5`.*
3
+ *This document describes Axiom `0.15.0-alpha.2`.*
4
4
 
5
5
  The authoritative runtime (`docs/AUTHORITY.md`) may run as **more than one process at the
6
6
  same time**, over one shared persistence provider, without any change to the
package/docs/EFFECTS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Effects
2
2
 
3
- Axiom 0.14.0-alpha.5. External effects are not rollback-capable state mutations. This file
3
+ Axiom 0.15.0-alpha.2. External effects are not rollback-capable state mutations. This file
4
4
  is the delivery model; [`AUTHORITY.md`](AUTHORITY.md#external-effects) is the load-bearing
5
5
  statement of why, and [`INTEGRATIONS.md`](INTEGRATIONS.md) is the operation vocabulary this
6
6
  builds on.
package/docs/EVENTS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Events
2
2
 
3
- Axiom 0.14.0-alpha.5. An event is a typed fact — something that happened — never work
3
+ Axiom 0.15.0-alpha.2. An event is a typed fact — something that happened — never work
4
4
  itself. [`AUTHORITY.md`](AUTHORITY.md#external-events) is the load-bearing statement;
5
5
  this file is the vocabulary and the webhook delivery mechanism. A **subscription** is the
6
6
  other way an external fact becomes an `EventDef` payload — see
@@ -1,6 +1,6 @@
1
1
  # Expressions
2
2
 
3
- Axiom 0.14.0-alpha.5. An expression describes **what value is computed**. It is a tree of
3
+ Axiom 0.15.0-alpha.2. An expression describes **what value is computed**. It is a tree of
4
4
  plain data, never source text and never a callback. Evaluation is pure: an expression MUST
5
5
  NOT change state.
6
6
 
@@ -103,6 +103,14 @@ object([
103
103
  - `not` → `!truthy(operand)`.
104
104
  - `negate` → `-Number(operand ?? 0)`.
105
105
 
106
+ > **Authorization policies evaluate differently for a missing security field.** An
107
+ > `AuthorizationPolicyDef.allow` expression is run by a dedicated three-valued evaluator: a
108
+ > `field(ref(PRINCIPAL) | ref(RESOURCE), F)` read of a field the scope object does not
109
+ > carry is **security-scope absence**, not an ordinary `undefined`. A comparison, `not`,
110
+ > `neq` or `or` whose truth would depend on absence is *not satisfied*, so the policy
111
+ > DENIES (spec15pt2 F1). This is scoped to `allow` evaluation — the general expression
112
+ > semantics above are unchanged everywhere else, including in `ActionDef.authorization`.
113
+
106
114
  ### `call(fn, ...args)`
107
115
 
108
116
  Calls a built-in. See [Built-in functions](#built-in-functions).
@@ -1,6 +1,6 @@
1
1
  # Graph model
2
2
 
3
- Axiom 0.14.0-alpha.5. The `ApplicationGraph` is the authoritative representation of an
3
+ Axiom 0.15.0-alpha.2. The `ApplicationGraph` is the authoritative representation of an
4
4
  application. Everything else — the IR, the page, the DOM — is derived from it and is never
5
5
  edited.
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Integrations
2
2
 
3
- Axiom 0.14.0-alpha.5. How an application declares and calls an external system, without
3
+ Axiom 0.15.0-alpha.2. How an application declares and calls an external system, without
4
4
  embedding a transport, an SDK or a secret in the graph. The authority boundary this
5
5
  depends on is [`AUTHORITY.md`](AUTHORITY.md#external-systems); this file is the vocabulary.
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Realtime — live canonical queries
2
2
 
3
- Axiom 0.14.0-alpha.5. The operational contract for **observing a `QueryDef` result over
3
+ Axiom 0.15.0-alpha.2. The operational contract for **observing a `QueryDef` result over
4
4
  time**: subscribe once, receive an initial coherent result, then receive canonical changes
5
5
  as authoritative committed state moves — through any compatible authority, across
6
6
  reconnects. `axiom.server.v7` (0.13 adds no IR vocabulary).
package/docs/LOCATIONS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Locations
2
2
 
3
- Axiom 0.14.0-alpha.5.
3
+ Axiom 0.15.0-alpha.2.
4
4
 
5
5
  ```text
6
6
  Expression = a value
@@ -1,6 +1,6 @@
1
1
  # Schema evolution & semantic migrations
2
2
 
3
- Axiom 0.14.0-alpha.5. The operational contract for evolving a deployed application's
3
+ Axiom 0.15.0-alpha.2. The operational contract for evolving a deployed application's
4
4
  semantic model and its persisted canonical data over time — adding a required field,
5
5
  splitting one field into two, removing an obsolete one, migrating millions of
6
6
  provider-backed rows — **without** an application-authored SQL migration, an ORM migration,
@@ -1,6 +1,6 @@
1
1
  # Presentation
2
2
 
3
- Axiom 0.14.0-alpha.5. Presentation is **semantic UX intent**, expressed as data on a UI
3
+ Axiom 0.15.0-alpha.2. Presentation is **semantic UX intent**, expressed as data on a UI
4
4
  node. It names roles, tokens and device classes. It never names a colour, a length, a media
5
5
  query or a CSS property.
6
6
 
package/docs/QUERIES.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Semantic data access & the query layer
2
2
 
3
- Axiom 0.14.0-alpha.5. The operational contract for demand-driven reads over authoritative
3
+ Axiom 0.15.0-alpha.2. The operational contract for demand-driven reads over authoritative
4
4
  data that is too large to materialize as a `StateDef` — 500,000 orders, 5,000,000 order
5
5
  lines, years of audit rows. `axiom.server.v6`.
6
6
 
package/docs/RUNTIME.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Runtime
2
2
 
3
- Axiom 0.14.0-alpha.5. The runtime executes an `ApplicationIR`. It is domain-independent: it
3
+ Axiom 0.15.0-alpha.2. The runtime executes an `ApplicationIR`. It is domain-independent: it
4
4
  contains no knowledge of any application.
5
5
 
6
6
  ## Constructing
@@ -1,6 +1,6 @@
1
1
  # Semantic contract
2
2
 
3
- Axiom 0.14.0-alpha.5. Runtime guarantees, stated formally. This file defines behavior; it
3
+ Axiom 0.15.0-alpha.2. Runtime guarantees, stated formally. This file defines behavior; it
4
4
  does not teach. Where this file and any specification in `../specs/` disagree, this file
5
5
  describes the implementation and is authoritative.
6
6
 
package/docs/STATE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # State
2
2
 
3
- Axiom 0.14.0-alpha.5. A `StateDef` is a named application value: stored, or computed from
3
+ Axiom 0.15.0-alpha.2. A `StateDef` is a named application value: stored, or computed from
4
4
  other state.
5
5
 
6
6
  ```ts
package/docs/STORAGE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Storage and blobs
2
2
 
3
- Axiom 0.14.0-alpha.5. How an application stores, references, serves and deletes binary data —
3
+ Axiom 0.15.0-alpha.2. How an application stores, references, serves and deletes binary data —
4
4
  an attachment, a document, a photograph, a diagnostic log — with no filesystem path, no
5
5
  upload route and no download route anywhere in it.
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Subscriptions
2
2
 
3
- Axiom 0.14.0-alpha.5. How an application receives a stream of external events — an MQTT
3
+ Axiom 0.15.0-alpha.2. How an application receives a stream of external events — an MQTT
4
4
  topic, a WebSocket feed, a queue consumer, a filesystem watcher, a serial port — without a
5
5
  client, a socket or a callback anywhere in the graph.
6
6
 
package/docs/TRIGGERS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Triggers
2
2
 
3
- Axiom 0.14.0-alpha.5. A `TriggerDef` says **when** an action should be invoked, without
3
+ Axiom 0.15.0-alpha.2. A `TriggerDef` says **when** an action should be invoked, without
4
4
  embedding callback code. `docs/AUTHORITY.md`
5
5
  [§ Triggers](AUTHORITY.md#triggers) is the load-bearing statement of the execution model;
6
6
  this file is the vocabulary.
package/docs/UI.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # UI
2
2
 
3
- Axiom 0.14.0-alpha.5. Eleven semantic UI node kinds describe **what exists and what it does**.
3
+ Axiom 0.15.0-alpha.2. Eleven semantic UI node kinds describe **what exists and what it does**.
4
4
  How it looks is [presentation](PRESENTATION.md).
5
5
 
6
6
  All eleven share `UIBase`:
@@ -1,6 +1,6 @@
1
1
  # Validation
2
2
 
3
- Axiom 0.14.0-alpha.5. Validation is authoring-time structural checking. It is not the same
3
+ Axiom 0.15.0-alpha.2. Validation is authoring-time structural checking. It is not the same
4
4
  as runtime constraint evaluation — see [`CONSTRAINTS.md`](CONSTRAINTS.md) for the four
5
5
  layers of correctness.
6
6
 
@@ -36,7 +36,7 @@ non-numeric collections, and obviously incompatible assignments.
36
36
 
37
37
  ## Codes
38
38
 
39
- 121 codes, exported as `VALIDATION_CODES`. Every one is reachable.
39
+ 125 codes, exported as `VALIDATION_CODES`. Every one is reachable.
40
40
 
41
41
  ### Ids and references
42
42
 
@@ -229,6 +229,20 @@ permanently `running` workflow.
229
229
  | `WORKFLOW_EXPRESSION_SCOPE` | A workflow expression references an id outside the workflow expression scope: `ref(<input id>)`, `ref(<binding id>)`, `ref('EVENT')` (only inside a `wait-event` `where` / `bind`), `ref('PRINCIPAL')`. Not a `StateDef`, not a `QueryDef`. |
230
230
  | `WORKFLOW_NONDETERMINISTIC` | A workflow expression calls `now` / `uuid` / `random`. Workflow decisions must be deterministic and replayable. |
231
231
 
232
+ ### Authorization (0.15)
233
+
234
+ Full model: [`AUTHORIZATION.md`](AUTHORIZATION.md). Whether a principal may perform a
235
+ semantic operation is executable meaning; a malformed `AuthorizationPolicyDef` is rejected
236
+ by `validateGraph` before it can be compiled to `axiom.server.v9` or executed, always as a
237
+ structured diagnostic, never a native exception (spec15 §36, §37).
238
+
239
+ | Code | Raised when |
240
+ | --- | --- |
241
+ | `AUTHORIZATION_INVALID_POLICY` | An `AuthorizationPolicyDef` that is not an object, or has no boolean `allow` expression. |
242
+ | `AUTHORIZATION_INVALID_SCOPE` | An `AuthorizationPolicyDef.allow` expression references an id outside the closed policy scope: `ref('PRINCIPAL')`, `ref('RESOURCE')`, `ref('OPERATION')`. Not a `StateDef`, not a `QueryDef`, no ambient runtime state. |
243
+ | `AUTHORIZATION_NONDETERMINISTIC` | An `AuthorizationPolicyDef.allow` expression calls `now` / `uuid` / `random`. An authorization decision must be deterministic for the same semantic inputs. |
244
+ | `AUTHORIZATION_UNKNOWN_POLICY` | An `authorizationPolicy` (action / query), `startPolicy` or `instanceAccessPolicy` (workflow) id that does not resolve to an `authorization-policy` node. |
245
+
232
246
  ### UI and routing
233
247
 
234
248
  | Code | Raised when | Severity |
package/docs/WORKFLOWS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Durable workflows
2
2
 
3
- Axiom 0.14.0-alpha.5. The operational contract for **long-running semantic computations with
3
+ Axiom 0.15.0-alpha.2. The operational contract for **long-running semantic computations with
4
4
  a durable control position** — orchestration that survives process death, authority
5
5
  failover, retries, timer delivery, event delivery and ordinary distributed contention
6
6
  without application-owned infrastructure. `axiom.server.v8`.
@@ -255,7 +255,7 @@ step's argument expressions / `retry` policy, a `wait-event` step's `where` / `b
255
255
  or binding migration, and none is inferred ("closest step" recovery never happens).
256
256
  - A graph with **no** `WorkflowDef` compiles to the byte-identical `axiom.server.v1`–`v7`
257
257
  document it always did, and its `semanticFingerprint` / `schemaFingerprint` are unchanged.
258
- - Pre-`0.14.0-alpha.5` instances carry a compatibility key computed before `WorkflowDef`
258
+ - Pre-`0.15.0-alpha.2` instances carry a compatibility key computed before `WorkflowDef`
259
259
  participated; a corrected authority treats them as incompatible and fails closed (these
260
260
  are pre-freeze alpha releases — silent reinterpretation is the only unacceptable
261
261
  outcome).
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # Axiom
2
2
 
3
- > AI-native semantic application framework, version 0.14.0-alpha.5. An Axiom application is a
3
+ > AI-native semantic application framework, version 0.15.0-alpha.2. An Axiom application is a
4
4
  > typed semantic graph — state, behavior, constraints, UI structure, presentation and
5
5
  > authority as structured data — executed by generic runtimes. The JavaScript, HTML and CSS
6
6
  > that reach a browser are compiler output and are never authored or edited. The primary
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cynodia/axiom",
3
- "version": "0.14.0-alpha.5",
3
+ "version": "0.15.0-alpha.2",
4
4
  "description": "AI-native semantic web application framework.",
5
5
  "license": "MIT",
6
6
  "author": "AskTech AS",
@@ -34,10 +34,10 @@
34
34
  }
35
35
  },
36
36
  "dependencies": {
37
- "@cynodia/axiom-core": "0.14.0-alpha.5",
38
- "@cynodia/axiom-runtime": "0.14.0-alpha.5",
39
- "@cynodia/axiom-compiler": "0.14.0-alpha.5",
40
- "@cynodia/axiom-agent-api": "0.14.0-alpha.5"
37
+ "@cynodia/axiom-core": "0.15.0-alpha.2",
38
+ "@cynodia/axiom-runtime": "0.15.0-alpha.2",
39
+ "@cynodia/axiom-compiler": "0.15.0-alpha.2",
40
+ "@cynodia/axiom-agent-api": "0.15.0-alpha.2"
41
41
  },
42
42
  "scripts": {
43
43
  "build": "tsc -b tsconfig.json"