@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 +3 -2
- package/docs/ACTIONS_TRANSACTIONS.md +1 -1
- package/docs/AGENT_API.md +1 -1
- package/docs/AGENT_REFERENCE.md +83 -2
- package/docs/ANTI_PATTERNS.md +1 -1
- package/docs/AUTHORITY.md +4 -2
- package/docs/AUTHORIZATION.md +261 -0
- package/docs/CONSTRAINTS.md +1 -1
- package/docs/DISTRIBUTED_AUTHORITY.md +1 -1
- package/docs/EFFECTS.md +1 -1
- package/docs/EVENTS.md +1 -1
- package/docs/EXPRESSIONS.md +9 -1
- package/docs/GRAPH_MODEL.md +1 -1
- package/docs/INTEGRATIONS.md +1 -1
- package/docs/LIVE_QUERIES.md +1 -1
- package/docs/LOCATIONS.md +1 -1
- package/docs/MIGRATIONS.md +1 -1
- package/docs/PRESENTATION.md +1 -1
- package/docs/QUERIES.md +1 -1
- package/docs/RUNTIME.md +1 -1
- package/docs/SEMANTIC_CONTRACT.md +1 -1
- package/docs/STATE.md +1 -1
- package/docs/STORAGE.md +1 -1
- package/docs/SUBSCRIPTIONS.md +1 -1
- package/docs/TRIGGERS.md +1 -1
- package/docs/UI.md +1 -1
- package/docs/VALIDATION.md +16 -2
- package/docs/WORKFLOWS.md +2 -2
- package/llms.txt +1 -1
- package/package.json +5 -5
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.
|
|
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.
|
|
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
|
package/docs/AGENT_API.md
CHANGED
package/docs/AGENT_REFERENCE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agent reference
|
|
2
2
|
|
|
3
|
-
Axiom 0.
|
|
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
|
|
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
|
package/docs/ANTI_PATTERNS.md
CHANGED
package/docs/AUTHORITY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Authority
|
|
2
2
|
|
|
3
|
-
Axiom 0.
|
|
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.
|
package/docs/CONSTRAINTS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Distributed authority
|
|
2
2
|
|
|
3
|
-
*This document describes Axiom `0.
|
|
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.
|
|
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.
|
|
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
|
package/docs/EXPRESSIONS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Expressions
|
|
2
2
|
|
|
3
|
-
Axiom 0.
|
|
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).
|
package/docs/GRAPH_MODEL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Graph model
|
|
2
2
|
|
|
3
|
-
Axiom 0.
|
|
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
|
|
package/docs/INTEGRATIONS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Integrations
|
|
2
2
|
|
|
3
|
-
Axiom 0.
|
|
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
|
|
package/docs/LIVE_QUERIES.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Realtime — live canonical queries
|
|
2
2
|
|
|
3
|
-
Axiom 0.
|
|
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
package/docs/MIGRATIONS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Schema evolution & semantic migrations
|
|
2
2
|
|
|
3
|
-
Axiom 0.
|
|
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,
|
package/docs/PRESENTATION.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Presentation
|
|
2
2
|
|
|
3
|
-
Axiom 0.
|
|
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.
|
|
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
|
# Semantic contract
|
|
2
2
|
|
|
3
|
-
Axiom 0.
|
|
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
package/docs/STORAGE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Storage and blobs
|
|
2
2
|
|
|
3
|
-
Axiom 0.
|
|
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
|
|
package/docs/SUBSCRIPTIONS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Subscriptions
|
|
2
2
|
|
|
3
|
-
Axiom 0.
|
|
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.
|
|
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.
|
|
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`:
|
package/docs/VALIDATION.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Validation
|
|
2
2
|
|
|
3
|
-
Axiom 0.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
38
|
-
"@cynodia/axiom-runtime": "0.
|
|
39
|
-
"@cynodia/axiom-compiler": "0.
|
|
40
|
-
"@cynodia/axiom-agent-api": "0.
|
|
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"
|