@agenticprimitives/fabric 0.0.0-alpha.1
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/CLAUDE.md +67 -0
- package/LICENSE +21 -0
- package/README.md +21 -0
- package/dist/case-machine.d.ts +76 -0
- package/dist/case-machine.d.ts.map +1 -0
- package/dist/case-machine.js +83 -0
- package/dist/case-machine.js.map +1 -0
- package/dist/client/index.d.ts +114 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +76 -0
- package/dist/client/index.js.map +1 -0
- package/dist/cloudflare/authority-verdict-cache.d.ts +27 -0
- package/dist/cloudflare/authority-verdict-cache.d.ts.map +1 -0
- package/dist/cloudflare/authority-verdict-cache.js +66 -0
- package/dist/cloudflare/authority-verdict-cache.js.map +1 -0
- package/dist/cloudflare/channel-store.d.ts +61 -0
- package/dist/cloudflare/channel-store.d.ts.map +1 -0
- package/dist/cloudflare/channel-store.js +124 -0
- package/dist/cloudflare/channel-store.js.map +1 -0
- package/dist/cloudflare/exchange-store.d.ts +16 -0
- package/dist/cloudflare/exchange-store.d.ts.map +1 -0
- package/dist/cloudflare/exchange-store.js +45 -0
- package/dist/cloudflare/exchange-store.js.map +1 -0
- package/dist/cloudflare/gateway-tools.d.ts +28 -0
- package/dist/cloudflare/gateway-tools.d.ts.map +1 -0
- package/dist/cloudflare/gateway-tools.js +45 -0
- package/dist/cloudflare/gateway-tools.js.map +1 -0
- package/dist/cloudflare/index.d.ts +13 -0
- package/dist/cloudflare/index.d.ts.map +1 -0
- package/dist/cloudflare/index.js +20 -0
- package/dist/cloudflare/index.js.map +1 -0
- package/dist/cloudflare/orchestration-runtime.d.ts +28 -0
- package/dist/cloudflare/orchestration-runtime.d.ts.map +1 -0
- package/dist/cloudflare/orchestration-runtime.js +35 -0
- package/dist/cloudflare/orchestration-runtime.js.map +1 -0
- package/dist/cloudflare/outbox-store.d.ts +24 -0
- package/dist/cloudflare/outbox-store.d.ts.map +1 -0
- package/dist/cloudflare/outbox-store.js +42 -0
- package/dist/cloudflare/outbox-store.js.map +1 -0
- package/dist/cloudflare/principal-gateway-do.d.ts +234 -0
- package/dist/cloudflare/principal-gateway-do.d.ts.map +1 -0
- package/dist/cloudflare/principal-gateway-do.js +369 -0
- package/dist/cloudflare/principal-gateway-do.js.map +1 -0
- package/dist/cloudflare/provenance-store.d.ts +9 -0
- package/dist/cloudflare/provenance-store.d.ts.map +1 -0
- package/dist/cloudflare/provenance-store.js +36 -0
- package/dist/cloudflare/provenance-store.js.map +1 -0
- package/dist/cloudflare/receipt-signer.d.ts +26 -0
- package/dist/cloudflare/receipt-signer.d.ts.map +1 -0
- package/dist/cloudflare/receipt-signer.js +69 -0
- package/dist/cloudflare/receipt-signer.js.map +1 -0
- package/dist/cloudflare/request-id-ledger.d.ts +13 -0
- package/dist/cloudflare/request-id-ledger.d.ts.map +1 -0
- package/dist/cloudflare/request-id-ledger.js +38 -0
- package/dist/cloudflare/request-id-ledger.js.map +1 -0
- package/dist/cloudflare/sql-vault.d.ts +10 -0
- package/dist/cloudflare/sql-vault.d.ts.map +1 -0
- package/dist/cloudflare/sql-vault.js +98 -0
- package/dist/cloudflare/sql-vault.js.map +1 -0
- package/dist/communicative-act.d.ts +47 -0
- package/dist/communicative-act.d.ts.map +1 -0
- package/dist/communicative-act.js +36 -0
- package/dist/communicative-act.js.map +1 -0
- package/dist/delivery-envelope.d.ts +38 -0
- package/dist/delivery-envelope.d.ts.map +1 -0
- package/dist/delivery-envelope.js +43 -0
- package/dist/delivery-envelope.js.map +1 -0
- package/dist/exchange-record.d.ts +131 -0
- package/dist/exchange-record.d.ts.map +1 -0
- package/dist/exchange-record.js +25 -0
- package/dist/exchange-record.js.map +1 -0
- package/dist/exchange-reducer.d.ts +25 -0
- package/dist/exchange-reducer.d.ts.map +1 -0
- package/dist/exchange-reducer.js +108 -0
- package/dist/exchange-reducer.js.map +1 -0
- package/dist/exchange-state.d.ts +56 -0
- package/dist/exchange-state.d.ts.map +1 -0
- package/dist/exchange-state.js +81 -0
- package/dist/exchange-state.js.map +1 -0
- package/dist/gateway-surface.d.ts +24 -0
- package/dist/gateway-surface.d.ts.map +1 -0
- package/dist/gateway-surface.js +33 -0
- package/dist/gateway-surface.js.map +1 -0
- package/dist/inbox-doc.d.ts +32 -0
- package/dist/inbox-doc.d.ts.map +1 -0
- package/dist/inbox-doc.js +57 -0
- package/dist/inbox-doc.js.map +1 -0
- package/dist/inbox-projection.d.ts +15 -0
- package/dist/inbox-projection.d.ts.map +1 -0
- package/dist/inbox-projection.js +28 -0
- package/dist/inbox-projection.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/interactions/audited.d.ts +12 -0
- package/dist/interactions/audited.d.ts.map +1 -0
- package/dist/interactions/audited.js +67 -0
- package/dist/interactions/audited.js.map +1 -0
- package/dist/interactions/builders.d.ts +24 -0
- package/dist/interactions/builders.d.ts.map +1 -0
- package/dist/interactions/builders.js +48 -0
- package/dist/interactions/builders.js.map +1 -0
- package/dist/interactions/cards.d.ts +32 -0
- package/dist/interactions/cards.d.ts.map +1 -0
- package/dist/interactions/cards.js +20 -0
- package/dist/interactions/cards.js.map +1 -0
- package/dist/interactions/index.d.ts +15 -0
- package/dist/interactions/index.d.ts.map +1 -0
- package/dist/interactions/index.js +23 -0
- package/dist/interactions/index.js.map +1 -0
- package/dist/interactions/machine.d.ts +19 -0
- package/dist/interactions/machine.d.ts.map +1 -0
- package/dist/interactions/machine.js +31 -0
- package/dist/interactions/machine.js.map +1 -0
- package/dist/interactions/mandates.d.ts +64 -0
- package/dist/interactions/mandates.d.ts.map +1 -0
- package/dist/interactions/mandates.js +62 -0
- package/dist/interactions/mandates.js.map +1 -0
- package/dist/interactions/relationship.d.ts +29 -0
- package/dist/interactions/relationship.d.ts.map +1 -0
- package/dist/interactions/relationship.js +31 -0
- package/dist/interactions/relationship.js.map +1 -0
- package/dist/interactions/risk.d.ts +39 -0
- package/dist/interactions/risk.d.ts.map +1 -0
- package/dist/interactions/risk.js +49 -0
- package/dist/interactions/risk.js.map +1 -0
- package/dist/interactions/runs.d.ts +26 -0
- package/dist/interactions/runs.d.ts.map +1 -0
- package/dist/interactions/runs.js +80 -0
- package/dist/interactions/runs.js.map +1 -0
- package/dist/interactions/store.d.ts +20 -0
- package/dist/interactions/store.d.ts.map +1 -0
- package/dist/interactions/store.js +68 -0
- package/dist/interactions/store.js.map +1 -0
- package/dist/interactions/tickets.d.ts +68 -0
- package/dist/interactions/tickets.d.ts.map +1 -0
- package/dist/interactions/tickets.js +49 -0
- package/dist/interactions/tickets.js.map +1 -0
- package/dist/interactions/types.d.ts +160 -0
- package/dist/interactions/types.d.ts.map +1 -0
- package/dist/interactions/types.js +7 -0
- package/dist/interactions/types.js.map +1 -0
- package/dist/messaging/board.d.ts +54 -0
- package/dist/messaging/board.d.ts.map +1 -0
- package/dist/messaging/board.js +90 -0
- package/dist/messaging/board.js.map +1 -0
- package/dist/messaging/conversation.d.ts +35 -0
- package/dist/messaging/conversation.d.ts.map +1 -0
- package/dist/messaging/conversation.js +72 -0
- package/dist/messaging/conversation.js.map +1 -0
- package/dist/messaging/delivery.d.ts +19 -0
- package/dist/messaging/delivery.d.ts.map +1 -0
- package/dist/messaging/delivery.js +56 -0
- package/dist/messaging/delivery.js.map +1 -0
- package/dist/messaging/directory.d.ts +32 -0
- package/dist/messaging/directory.d.ts.map +1 -0
- package/dist/messaging/directory.js +35 -0
- package/dist/messaging/directory.js.map +1 -0
- package/dist/messaging/envelope.d.ts +14 -0
- package/dist/messaging/envelope.d.ts.map +1 -0
- package/dist/messaging/envelope.js +71 -0
- package/dist/messaging/envelope.js.map +1 -0
- package/dist/messaging/hash.d.ts +19 -0
- package/dist/messaging/hash.d.ts.map +1 -0
- package/dist/messaging/hash.js +36 -0
- package/dist/messaging/hash.js.map +1 -0
- package/dist/messaging/index.d.ts +16 -0
- package/dist/messaging/index.d.ts.map +1 -0
- package/dist/messaging/index.js +29 -0
- package/dist/messaging/index.js.map +1 -0
- package/dist/messaging/projector.d.ts +58 -0
- package/dist/messaging/projector.d.ts.map +1 -0
- package/dist/messaging/projector.js +103 -0
- package/dist/messaging/projector.js.map +1 -0
- package/dist/messaging/receipts.d.ts +5 -0
- package/dist/messaging/receipts.d.ts.map +1 -0
- package/dist/messaging/receipts.js +19 -0
- package/dist/messaging/receipts.js.map +1 -0
- package/dist/messaging/rules.d.ts +84 -0
- package/dist/messaging/rules.d.ts.map +1 -0
- package/dist/messaging/rules.js +109 -0
- package/dist/messaging/rules.js.map +1 -0
- package/dist/messaging/store.d.ts +24 -0
- package/dist/messaging/store.d.ts.map +1 -0
- package/dist/messaging/store.js +120 -0
- package/dist/messaging/store.js.map +1 -0
- package/dist/messaging/types.d.ts +166 -0
- package/dist/messaging/types.d.ts.map +1 -0
- package/dist/messaging/types.js +7 -0
- package/dist/messaging/types.js.map +1 -0
- package/dist/messaging/vault-store.d.ts +37 -0
- package/dist/messaging/vault-store.d.ts.map +1 -0
- package/dist/messaging/vault-store.js +62 -0
- package/dist/messaging/vault-store.js.map +1 -0
- package/dist/record-catalog.d.ts +22 -0
- package/dist/record-catalog.d.ts.map +1 -0
- package/dist/record-catalog.js +35 -0
- package/dist/record-catalog.js.map +1 -0
- package/package.json +82 -0
- package/spec.md +15 -0
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# @agenticprimitives/fabric — Claude guide
|
|
2
|
+
|
|
3
|
+
The **Agentic Interaction Fabric** (spec 316): the A2A-native substrate that absorbs `messaging`
|
|
4
|
+
(envelope/inbox) + `interactions` (case/run) into ONE canonical exchange object over a two-level
|
|
5
|
+
exec/case FSM. Decentralized (per-principal gateway + vault), tiered (T0/T1/T2), authority
|
|
6
|
+
re-verified against the principal SA. Transport-agnostic core; the Cloudflare `PrincipalGatewayDO`
|
|
7
|
+
adapter ships from `./cloudflare`.
|
|
8
|
+
|
|
9
|
+
## Status (wave-by-wave build)
|
|
10
|
+
- **W1 DONE:** exec/case FSM spine + absorbed messaging/interactions reducers (`./messaging`,
|
|
11
|
+
`./interactions` subpaths) + unified reducer + inbox projection. Golden-vectored.
|
|
12
|
+
- **W2 DONE (`./cloudflare` adapter):** `PrincipalGatewayDO`, co-resident on one `state.storage.sql`
|
|
13
|
+
trust domain — `createDurableObjectSqlVault` (per-owner DEK, L2 envelope crypto); durable
|
|
14
|
+
single-writer `createDurableObjectExchangeStore` (fail-closed `applyExchangeEvent`); T0
|
|
15
|
+
`deliver`/`applyEvent`/`inbox`; T1 `AuthorityVerdictCache` + `RequestIdLedger` (§3a: cached verdict
|
|
16
|
+
A + single-use requestId); **co-resident MCP** (`invokeFast` runs the tool IN-PROCESS over the DO
|
|
17
|
+
vault after the A→B→requestId gate; heavy verify + KEK wrapper INJECTED via `GatewayDeps`); **T2
|
|
18
|
+
orchestration loop** (co-resident tools AS the `orchestration` ToolInvoker; only the LLM planner
|
|
19
|
+
injected); **signed `DeliveryReceiptV1`** (DO-local secp256k1). The ONLY subtree importing
|
|
20
|
+
`@cloudflare/workers-types`.
|
|
21
|
+
- **W4 DONE:** `GatewaySurfaceCommitmentV1` (gatewayRoot preimage) + peer `DeliveryEnvelopeV1` +
|
|
22
|
+
`receiveDelivery` + `OutboxStore` store-and-forward.
|
|
23
|
+
- **Next:** on-chain `atl:gatewayRoot` record (contracts, separate); `A2aTaskDO`/`SessionStoreDO`
|
|
24
|
+
absorption + `alarm()` drain; `fetch`/`alarm` e2e via vitest-pool-workers.
|
|
25
|
+
|
|
26
|
+
## What this package owns
|
|
27
|
+
- `ExchangeRecordV1` — the ONE canonical object (spine id `xch_`); two-level FSM: `ExecState`
|
|
28
|
+
(transport, reused from `fulfillment`) + optional `CaseState` (business substate).
|
|
29
|
+
- `EXEC_CASE_COUPLING` + `canExchangeTransition`/`isExecCaseConsistent` — exhaustive, fail-closed
|
|
30
|
+
coupling (`src/exchange-state.ts`). Communication (T0) carries NO case; `delivered`/`seen` are
|
|
31
|
+
inbox read-receipt facts, never case states (Blocker 6).
|
|
32
|
+
- The `IntentEnvelopeV1`/`MessageEnvelopeV1` wire object + inbox projection (from `messaging`), the
|
|
33
|
+
case reducer + typed payloads + runs (from `interactions`), delivery/admission/discovery.
|
|
34
|
+
|
|
35
|
+
## Hard rules
|
|
36
|
+
- **Authority is never satisfied by the exchange.** A request carries an `authorityRef`; approving a
|
|
37
|
+
case advances the lifecycle, it NEVER unlocks a resource (spec 316 §2, ADR-0041).
|
|
38
|
+
- **`ExecState` is `fulfillment`'s `TaskState`, unmodified** — `fulfillment` stays a pure leaf.
|
|
39
|
+
- **Fail-closed coupling.** An `(exec, case)` pair with no mapping is a typed rejection.
|
|
40
|
+
- **Transport-agnostic core.** No Cloudflare/MCP/A2A-SDK imports in `src/` — `./cloudflare` is the
|
|
41
|
+
only adapter and the only `@cloudflare/workers-types` consumer.
|
|
42
|
+
|
|
43
|
+
## Boundary
|
|
44
|
+
`types`, `fulfillment`, `vault`, `audit`, `delegation`, `chain-state`, `tool-policy`, `agent-naming`
|
|
45
|
+
(injected resolver) ← `fabric`; `fabric/cloudflare → orchestration` (adapter-only); `fabric →
|
|
46
|
+
provenance` (the PROV-O projector). No back-edges; nothing imports `fabric` except `./cloudflare` + apps.
|
|
47
|
+
|
|
48
|
+
## Validate
|
|
49
|
+
```bash
|
|
50
|
+
pnpm --filter @agenticprimitives/fabric typecheck
|
|
51
|
+
pnpm --filter @agenticprimitives/fabric test
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Capabilities this package participates in
|
|
55
|
+
- **Messaging + Interactions** ([`specs/309`](../../specs/309-messaging-and-interactions.md)) — fabric IS
|
|
56
|
+
the substrate the messaging (envelope/inbox) + interactions (case/run) capability was absorbed into
|
|
57
|
+
(spec 316 W1); demo guide: [`apps/demo-sso-next/docs/messaging-interactions/guide.md`](../../apps/demo-sso-next/docs/messaging-interactions/guide.md).
|
|
58
|
+
- **Home / agentic control plane + inbox** ([`specs/310`](../../specs/310-agentic-trust-home-control-plane-and-inbox.md)) —
|
|
59
|
+
the vault-resident inbox + control-plane projections read fabric wire types; demo guide:
|
|
60
|
+
[`apps/demo-sso-next/docs/home-inbox/guide.md`](../../apps/demo-sso-next/docs/home-inbox/guide.md).
|
|
61
|
+
- Index: [`docs/architecture/cross-cutting-capabilities.md`](../../docs/architecture/cross-cutting-capabilities.md).
|
|
62
|
+
|
|
63
|
+
## Spec + demo guides
|
|
64
|
+
`../../specs/316-agentic-interaction-fabric.md` (architect-of-record) +
|
|
65
|
+
`../../docs/architecture/agentic-interaction-fabric-target.md` + ADR-0047. Demo guides: spec 309
|
|
66
|
+
`../../apps/demo-sso-next/docs/messaging-interactions/guide.md` · spec 310
|
|
67
|
+
`../../apps/demo-sso-next/docs/home-inbox/guide.md`.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Agentic Trust Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# @agenticprimitives/fabric
|
|
2
|
+
|
|
3
|
+
The **Agentic Interaction Fabric** (spec 316): the A2A-native substrate that absorbs `messaging`
|
|
4
|
+
(envelope/inbox) and `interactions` (case/run) into ONE canonical exchange object over a two-level
|
|
5
|
+
exec/case state machine. Transport-agnostic core; the Cloudflare gateway ships as a `./cloudflare` adapter.
|
|
6
|
+
|
|
7
|
+
- **One object:** `ExchangeRecordV1` — an `ExecState` transport envelope carrying an optional 11-state
|
|
8
|
+
`CaseState` business substate, coupled by an exhaustive fail-closed table.
|
|
9
|
+
- **One reducer:** `applyExchangeEvent` over a single append-only event stream (message | case | artifact |
|
|
10
|
+
transport). The inbox is a projection (`projectInboxItem`).
|
|
11
|
+
- **Subpaths:** `@agenticprimitives/fabric/messaging` + `/interactions` re-export the absorbed surfaces.
|
|
12
|
+
|
|
13
|
+
Authority is never satisfied by the exchange — a request carries an `authorityRef`; approving a case advances
|
|
14
|
+
the lifecycle, it never unlocks a resource (ADR-0041).
|
|
15
|
+
|
|
16
|
+
See `CLAUDE.md` for the implementation guide and `../../specs/316-agentic-interaction-fabric.md` (architect
|
|
17
|
+
of record) + `../../docs/architecture/agentic-interaction-fabric-target.md` + ADR-0047.
|
|
18
|
+
Interaction-flow diagrams (person↔person, org channels, cases) + XMTP/Diode alignment:
|
|
19
|
+
[`docs/architecture.md`](docs/architecture.md) · commercial comparison:
|
|
20
|
+
[`docs/fabric-capabilities-vs-commercial.md`](docs/fabric-capabilities-vs-commercial.md) · gaps:
|
|
21
|
+
[`docs/fabric-capability-gaps.md`](docs/fabric-capability-gaps.md).
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { CanonicalAgentId } from '@agenticprimitives/types';
|
|
2
|
+
import type { VaultRef } from '@agenticprimitives/vault';
|
|
3
|
+
import type { AuthorityRef, InteractionKind } from './interactions/index.js';
|
|
4
|
+
import type { MessageId, ContextRefV1, SignatureProof } from './messaging/index.js';
|
|
5
|
+
import type { CaseState } from './exchange-state.js';
|
|
6
|
+
import type { ExchangeId } from './exchange-record.js';
|
|
7
|
+
/** The minimal fields the case FSM reads (roles/state/expiry) and writes (state/accumulators). The full
|
|
8
|
+
* `FabricCaseV1` and a lean record-derived view both satisfy this. */
|
|
9
|
+
export interface CaseFsmFields {
|
|
10
|
+
/** Case identity — a `string` so BOTH `FabricCaseV1` (`ExchangeId`) and the absorbed interactions
|
|
11
|
+
* `InteractionCaseV1` (`InteractionId`) satisfy the shared machine (13→11 reconciliation, D2). The machine
|
|
12
|
+
* uses `id` only for the wrong-case equality check. */
|
|
13
|
+
id: string;
|
|
14
|
+
state: CaseState;
|
|
15
|
+
requester: CanonicalAgentId;
|
|
16
|
+
responder: CanonicalAgentId;
|
|
17
|
+
resourceOwner?: CanonicalAgentId;
|
|
18
|
+
expiresAt?: string;
|
|
19
|
+
updatedAt: string;
|
|
20
|
+
latestMessageId?: MessageId;
|
|
21
|
+
evidenceRefs: VaultRef[];
|
|
22
|
+
authorityRefs: AuthorityRef[];
|
|
23
|
+
}
|
|
24
|
+
/** The full case record — the FSM facet plus the display/anchoring fields (kind/subject/context/root). */
|
|
25
|
+
export interface FabricCaseV1 extends CaseFsmFields {
|
|
26
|
+
version: 'ap.case.v1';
|
|
27
|
+
/** The fabric case narrows `id` to the exchange spine id. */
|
|
28
|
+
id: ExchangeId;
|
|
29
|
+
kind: InteractionKind;
|
|
30
|
+
subject: string;
|
|
31
|
+
/** Entities this case is ABOUT (spec 312) — about-ness, never authority. */
|
|
32
|
+
contextRefs?: ContextRefV1[];
|
|
33
|
+
createdAt: string;
|
|
34
|
+
rootMessageId: MessageId;
|
|
35
|
+
latestMessageId: MessageId;
|
|
36
|
+
}
|
|
37
|
+
/** Case transitions. `admit`/`view` from the absorbed machine are REMOVED (now inbox read-receipt events). */
|
|
38
|
+
export type CaseTransition = 'submit' | 'triage' | 'ask-info' | 'submit-info' | 'approve' | 'issue-credential' | 'deliver-credential' | 'deny' | 'expire' | 'revoke' | 'fail';
|
|
39
|
+
/** A signed lifecycle event (verification is the runtime's job; the machine checks the actor's ROLE). */
|
|
40
|
+
export interface CaseTransitionEventV1 {
|
|
41
|
+
version: 'ap.case.event.v1';
|
|
42
|
+
/** `string` for the same reason as `CaseFsmFields.id` (D2). */
|
|
43
|
+
caseId: string;
|
|
44
|
+
transition: CaseTransition;
|
|
45
|
+
actor: CanonicalAgentId;
|
|
46
|
+
at: string;
|
|
47
|
+
messageId?: MessageId;
|
|
48
|
+
reason?: string;
|
|
49
|
+
/** Replay dedupe — same key ⇒ same outcome, applied once. */
|
|
50
|
+
idempotencyKey?: string;
|
|
51
|
+
evidenceRef?: VaultRef;
|
|
52
|
+
authorityRef?: AuthorityRef;
|
|
53
|
+
signature?: SignatureProof;
|
|
54
|
+
}
|
|
55
|
+
export declare const TERMINAL_CASE_STATES: readonly CaseState[];
|
|
56
|
+
export declare function isTerminalCaseState(state: CaseState): boolean;
|
|
57
|
+
export type CaseRejection = 'wrong-case' | 'invalid-transition' | 'unauthorized-actor' | 'case-expired' | 'not-yet-due';
|
|
58
|
+
export type CaseResult = {
|
|
59
|
+
ok: true;
|
|
60
|
+
case: FabricCaseV1;
|
|
61
|
+
} | {
|
|
62
|
+
ok: false;
|
|
63
|
+
reason: CaseRejection;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Pure, fail-closed (spec 316 §2). Generic over the concrete case type: preserves whatever extra fields
|
|
67
|
+
* `T` carries beyond `CaseFsmFields`. Returns the next case or a typed rejection — never a silent no-op.
|
|
68
|
+
*/
|
|
69
|
+
export declare function applyCaseTransition<T extends CaseFsmFields>(current: T, event: CaseTransitionEventV1): {
|
|
70
|
+
ok: true;
|
|
71
|
+
case: T;
|
|
72
|
+
} | {
|
|
73
|
+
ok: false;
|
|
74
|
+
reason: CaseRejection;
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=case-machine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"case-machine.d.ts","sourceRoot":"","sources":["../src/case-machine.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACpF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD;uEACuE;AACvE,MAAM,WAAW,aAAa;IAC5B;;4DAEwD;IACxD,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS,CAAC;IACjB,SAAS,EAAE,gBAAgB,CAAC;IAC5B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,aAAa,CAAC,EAAE,gBAAgB,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,SAAS,CAAC;IAC5B,YAAY,EAAE,QAAQ,EAAE,CAAC;IACzB,aAAa,EAAE,YAAY,EAAE,CAAC;CAC/B;AAED,0GAA0G;AAC1G,MAAM,WAAW,YAAa,SAAQ,aAAa;IACjD,OAAO,EAAE,YAAY,CAAC;IACtB,6DAA6D;IAC7D,EAAE,EAAE,UAAU,CAAC;IACf,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,YAAY,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,SAAS,CAAC;IACzB,eAAe,EAAE,SAAS,CAAC;CAC5B;AAED,8GAA8G;AAC9G,MAAM,MAAM,cAAc,GACtB,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,aAAa,GACb,SAAS,GACT,kBAAkB,GAClB,oBAAoB,GACpB,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,MAAM,CAAC;AAEX,yGAAyG;AACzG,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,kBAAkB,CAAC;IAC5B,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,cAAc,CAAC;IAC3B,KAAK,EAAE,gBAAgB,CAAC;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,SAAS,CAAC,EAAE,cAAc,CAAC;CAC5B;AAED,eAAO,MAAM,oBAAoB,EAAE,SAAS,SAAS,EAA4D,CAAC;AAElH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAE7D;AA2BD,MAAM,MAAM,aAAa,GACrB,YAAY,GACZ,oBAAoB,GACpB,oBAAoB,GACpB,cAAc,GACd,aAAa,CAAC;AAElB,MAAM,MAAM,UAAU,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,CAAC;AAcjG;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,aAAa,EACzD,OAAO,EAAE,CAAC,EACV,KAAK,EAAE,qBAAqB,GAC3B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,CA4B9D"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// The case (business-substate) machine — ABSORBED from `@agenticprimitives/interactions` (spec 309 §6.1)
|
|
2
|
+
// and re-anchored on the fabric's corrected 11-state `CaseState` (spec 316 §2, Blocker 6). Fail-closed:
|
|
3
|
+
// - unknown transitions reject;
|
|
4
|
+
// - a correctly-signed but UNAUTHORIZED event does not change state (typed rejection, never a silent no-op);
|
|
5
|
+
// - operations on an expired case reject (only `expire`/`fail` may land).
|
|
6
|
+
// Signature VERIFICATION is the runtime's job; this machine enforces that the claimed actor holds the
|
|
7
|
+
// required ROLE for the move.
|
|
8
|
+
//
|
|
9
|
+
// The machine operates on the MINIMAL `CaseFsmFields` facet it actually reads/writes, so both the full case
|
|
10
|
+
// record (`FabricCaseV1`) and a lean `ExchangeRecordV1`-derived view (`caseViewFromRecord`) satisfy it — the
|
|
11
|
+
// generic `applyCaseTransition` preserves whatever extra fields the concrete type carries.
|
|
12
|
+
//
|
|
13
|
+
// Difference from the absorbed `interactions` machine: `delivered`/`seen` are NO LONGER case states — they
|
|
14
|
+
// are inbox read-receipt facts. So `admit`(submitted→delivered)/`view`(delivered→seen) are DROPPED (they
|
|
15
|
+
// become `ExchangeEventV1` `kind:'message'` events), and the moves that referenced `seen`/`delivered`
|
|
16
|
+
// re-anchor on `submitted`/`triaged`.
|
|
17
|
+
export const TERMINAL_CASE_STATES = ['denied', 'completed', 'expired', 'revoked', 'failed'];
|
|
18
|
+
export function isTerminalCaseState(state) {
|
|
19
|
+
return TERMINAL_CASE_STATES.includes(state);
|
|
20
|
+
}
|
|
21
|
+
const TRANSITIONS = {
|
|
22
|
+
submit: { from: ['draft'], to: 'submitted', signers: ['requester'] },
|
|
23
|
+
triage: { from: ['submitted'], to: 'triaged', signers: ['responder'] },
|
|
24
|
+
'ask-info': { from: ['submitted', 'triaged'], to: 'needs-info', signers: ['responder'] },
|
|
25
|
+
// needs-info → triaged (NOT submitted): the requester supplied info, the responder RESUMES working
|
|
26
|
+
// (couples to exec `working`), preserving the "responder engaged" phase across the info loop.
|
|
27
|
+
'submit-info': { from: ['needs-info'], to: 'triaged', signers: ['requester'] },
|
|
28
|
+
approve: { from: ['submitted', 'triaged'], to: 'approved', signers: ['responder', 'resource-owner'] },
|
|
29
|
+
'issue-credential': { from: ['approved'], to: 'issued', signers: ['resource-owner', 'responder'] },
|
|
30
|
+
'deliver-credential': { from: ['issued'], to: 'completed', signers: ['resource-owner', 'responder'] },
|
|
31
|
+
deny: { from: 'any-non-terminal', to: 'denied', signers: ['responder', 'resource-owner'] },
|
|
32
|
+
expire: { from: 'any-non-terminal', to: 'expired', signers: ['any'] },
|
|
33
|
+
revoke: { from: 'any-non-terminal', to: 'revoked', signers: ['responder', 'resource-owner'] },
|
|
34
|
+
fail: { from: 'any-non-terminal', to: 'failed', signers: ['any'] },
|
|
35
|
+
};
|
|
36
|
+
function actorRoles(c, actor) {
|
|
37
|
+
const roles = new Set(['any']);
|
|
38
|
+
if (actor === c.requester)
|
|
39
|
+
roles.add('requester');
|
|
40
|
+
if (actor === c.responder)
|
|
41
|
+
roles.add('responder');
|
|
42
|
+
if (c.resourceOwner !== undefined && actor === c.resourceOwner)
|
|
43
|
+
roles.add('resource-owner');
|
|
44
|
+
return roles;
|
|
45
|
+
}
|
|
46
|
+
function isPastExpiry(c, atIso) {
|
|
47
|
+
return c.expiresAt !== undefined && Date.parse(atIso) >= Date.parse(c.expiresAt);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Pure, fail-closed (spec 316 §2). Generic over the concrete case type: preserves whatever extra fields
|
|
51
|
+
* `T` carries beyond `CaseFsmFields`. Returns the next case or a typed rejection — never a silent no-op.
|
|
52
|
+
*/
|
|
53
|
+
export function applyCaseTransition(current, event) {
|
|
54
|
+
if (event.caseId !== current.id)
|
|
55
|
+
return { ok: false, reason: 'wrong-case' };
|
|
56
|
+
const spec = TRANSITIONS[event.transition];
|
|
57
|
+
if (!spec)
|
|
58
|
+
return { ok: false, reason: 'invalid-transition' };
|
|
59
|
+
const fromOk = spec.from === 'any-non-terminal' ? !isTerminalCaseState(current.state) : spec.from.includes(current.state);
|
|
60
|
+
if (!fromOk)
|
|
61
|
+
return { ok: false, reason: 'invalid-transition' };
|
|
62
|
+
// `expire` only lands once the deadline passed; everything else (except `fail`) rejects on an expired case.
|
|
63
|
+
if (event.transition === 'expire') {
|
|
64
|
+
if (current.expiresAt === undefined || !isPastExpiry(current, event.at))
|
|
65
|
+
return { ok: false, reason: 'not-yet-due' };
|
|
66
|
+
}
|
|
67
|
+
else if (event.transition !== 'fail' && isPastExpiry(current, event.at)) {
|
|
68
|
+
return { ok: false, reason: 'case-expired' };
|
|
69
|
+
}
|
|
70
|
+
const roles = actorRoles(current, event.actor);
|
|
71
|
+
if (!spec.signers.some((r) => roles.has(r)))
|
|
72
|
+
return { ok: false, reason: 'unauthorized-actor' };
|
|
73
|
+
const next = {
|
|
74
|
+
...current,
|
|
75
|
+
state: spec.to,
|
|
76
|
+
updatedAt: event.at,
|
|
77
|
+
latestMessageId: event.messageId ?? current.latestMessageId,
|
|
78
|
+
evidenceRefs: event.evidenceRef ? [...current.evidenceRefs, event.evidenceRef] : current.evidenceRefs,
|
|
79
|
+
authorityRefs: event.authorityRef ? [...current.authorityRefs, event.authorityRef] : current.authorityRefs,
|
|
80
|
+
};
|
|
81
|
+
return { ok: true, case: next };
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=case-machine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"case-machine.js","sourceRoot":"","sources":["../src/case-machine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { CanonicalAgentId } from '@agenticprimitives/types';
|
|
2
|
+
import type { DirectoryListingV1, ChannelV1, MessageEnvelopeV1 } from '../messaging/index.js';
|
|
3
|
+
export type FetchLike = (input: string, init?: {
|
|
4
|
+
method?: string;
|
|
5
|
+
headers?: Record<string, string>;
|
|
6
|
+
body?: string;
|
|
7
|
+
}) => Promise<{
|
|
8
|
+
ok: boolean;
|
|
9
|
+
status: number;
|
|
10
|
+
json: () => Promise<unknown>;
|
|
11
|
+
}>;
|
|
12
|
+
export interface FabricClientOptions {
|
|
13
|
+
/** The a2a substrate origin serving `/interactions/*` (e.g. the demo-a2a Worker). */
|
|
14
|
+
baseUrl: string;
|
|
15
|
+
/** The owner's broker session id_token (aud = the Home audience, iss = the broker). */
|
|
16
|
+
session: string;
|
|
17
|
+
/** Injected fetch (defaults to the global). Lets non-browser/relying hosts supply their own. */
|
|
18
|
+
fetch?: FetchLike;
|
|
19
|
+
}
|
|
20
|
+
/** A capability record the owner reads/writes self-only over the interactions grant (spec 323 W2). */
|
|
21
|
+
export type CapabilityRecordType = 'impact-profile' | 'skills.data' | 'home.manifest' | 'control-events.data';
|
|
22
|
+
export interface RelationshipEntry {
|
|
23
|
+
org: string;
|
|
24
|
+
relationship: 'member' | 'steward';
|
|
25
|
+
orgName?: string;
|
|
26
|
+
kind?: string;
|
|
27
|
+
parent?: string;
|
|
28
|
+
delegationHash?: string;
|
|
29
|
+
delegations?: unknown[];
|
|
30
|
+
updatedAt: string;
|
|
31
|
+
}
|
|
32
|
+
export interface RelationshipsDoc {
|
|
33
|
+
orgs: Record<string, RelationshipEntry>;
|
|
34
|
+
}
|
|
35
|
+
export declare class FabricError extends Error {
|
|
36
|
+
readonly status: number;
|
|
37
|
+
readonly op: string;
|
|
38
|
+
constructor(message: string, status: number, op: string);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A client bound to ONE owner session. `principal` on each call is the person (their own records) or
|
|
42
|
+
* an org they steward. Throws {@link FabricError} on a non-ok response (a 403/409 from a real gate —
|
|
43
|
+
* membership, plane-not-enabled — is surfaced with its status so callers can branch, e.g. show an
|
|
44
|
+
* "enable storage" affordance). `session` is sent in the body of every owner-facing op.
|
|
45
|
+
*/
|
|
46
|
+
export declare function createFabricClient(opts: FabricClientOptions): {
|
|
47
|
+
/** Storage liveness for a principal (open — no session needed). */
|
|
48
|
+
status: (principal: string) => Promise<{
|
|
49
|
+
granted: boolean;
|
|
50
|
+
current: boolean;
|
|
51
|
+
deliveryGranted: boolean;
|
|
52
|
+
}>;
|
|
53
|
+
relationships: {
|
|
54
|
+
get: (principal: string) => Promise<RelationshipsDoc>;
|
|
55
|
+
merge: (principal: string, entry: Partial<RelationshipEntry> & {
|
|
56
|
+
org: string;
|
|
57
|
+
}, remove?: boolean) => Promise<Record<string, unknown>>;
|
|
58
|
+
};
|
|
59
|
+
record: {
|
|
60
|
+
get: <T = unknown>(principal: string, recordType: CapabilityRecordType) => Promise<T | null>;
|
|
61
|
+
put: (principal: string, recordType: CapabilityRecordType, record: unknown) => Promise<Record<string, unknown>>;
|
|
62
|
+
};
|
|
63
|
+
memberProfile: {
|
|
64
|
+
put: (principal: string, org: string, profile: Record<string, unknown>) => Promise<Record<string, unknown>>;
|
|
65
|
+
};
|
|
66
|
+
directory: {
|
|
67
|
+
list: (principal: string, stewardship?: unknown) => Promise<{
|
|
68
|
+
listing: DirectoryListingV1;
|
|
69
|
+
label: string;
|
|
70
|
+
}[]>;
|
|
71
|
+
publish: (principal: string, listing: DirectoryListingV1, label?: string, subjectStewardship?: unknown) => Promise<Record<string, unknown>>;
|
|
72
|
+
revoke: (principal: string) => Promise<Record<string, unknown>>;
|
|
73
|
+
};
|
|
74
|
+
channels: {
|
|
75
|
+
list: (principal: string, stewardship?: unknown) => Promise<{
|
|
76
|
+
channels: ChannelV1[];
|
|
77
|
+
you: string;
|
|
78
|
+
steward: boolean;
|
|
79
|
+
}>;
|
|
80
|
+
read: (principal: string, channelId: string, stewardship?: unknown) => Promise<{
|
|
81
|
+
channels: ChannelV1[];
|
|
82
|
+
bodies: Record<string, string>;
|
|
83
|
+
you: string;
|
|
84
|
+
steward: boolean;
|
|
85
|
+
}>;
|
|
86
|
+
create: (principal: string, title: string, stewardship?: unknown) => Promise<{
|
|
87
|
+
channelId: string;
|
|
88
|
+
}>;
|
|
89
|
+
post: (principal: string, channelId: string, bodyText: string) => Promise<{
|
|
90
|
+
messageId: string;
|
|
91
|
+
}>;
|
|
92
|
+
};
|
|
93
|
+
inbox: {
|
|
94
|
+
get: <T = unknown>(principal: string) => Promise<T | null>;
|
|
95
|
+
put: (principal: string, doc: unknown) => Promise<Record<string, unknown>>;
|
|
96
|
+
readBody: (principal: string, resource: string) => Promise<{
|
|
97
|
+
b64: string;
|
|
98
|
+
contentType: string;
|
|
99
|
+
bodyHash: string;
|
|
100
|
+
} | null>;
|
|
101
|
+
};
|
|
102
|
+
/** The issuance ledger — self, or a steward presenting the stewardship wire. */
|
|
103
|
+
grants: {
|
|
104
|
+
list: (principal: string, stewardship?: unknown) => Promise<{
|
|
105
|
+
hash: string;
|
|
106
|
+
delegate: string;
|
|
107
|
+
resources: string[];
|
|
108
|
+
storedAt: string;
|
|
109
|
+
}[]>;
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
export type FabricClient = ReturnType<typeof createFabricClient>;
|
|
113
|
+
export type { CanonicalAgentId, DirectoryListingV1, ChannelV1, MessageEnvelopeV1 };
|
|
114
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export class FabricError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
op;
|
|
4
|
+
constructor(message, status, op) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.status = status;
|
|
7
|
+
this.op = op;
|
|
8
|
+
this.name = 'FabricError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const trimBase = (u) => u.replace(/\/$/, '');
|
|
12
|
+
const isAddress = (s) => /^0x[0-9a-fA-F]{40}$/.test(s);
|
|
13
|
+
/**
|
|
14
|
+
* A client bound to ONE owner session. `principal` on each call is the person (their own records) or
|
|
15
|
+
* an org they steward. Throws {@link FabricError} on a non-ok response (a 403/409 from a real gate —
|
|
16
|
+
* membership, plane-not-enabled — is surfaced with its status so callers can branch, e.g. show an
|
|
17
|
+
* "enable storage" affordance). `session` is sent in the body of every owner-facing op.
|
|
18
|
+
*/
|
|
19
|
+
export function createFabricClient(opts) {
|
|
20
|
+
const base = trimBase(opts.baseUrl);
|
|
21
|
+
const doFetch = opts.fetch ?? ((input, init) => fetch(input, init));
|
|
22
|
+
async function call(principal, op, extra = {}, withSession = true) {
|
|
23
|
+
if (!isAddress(principal))
|
|
24
|
+
throw new FabricError(`principal must be a 0x…40 address`, 400, op);
|
|
25
|
+
const resp = await doFetch(`${base}/interactions/${principal.toLowerCase()}/${op}`, {
|
|
26
|
+
method: 'POST',
|
|
27
|
+
headers: { 'content-type': 'application/json' },
|
|
28
|
+
body: JSON.stringify({ ...(withSession ? { session: opts.session } : {}), ...extra }),
|
|
29
|
+
});
|
|
30
|
+
const body = (await resp.json().catch(() => ({})));
|
|
31
|
+
if (!resp.ok || body.ok === false) {
|
|
32
|
+
throw new FabricError(body.error ?? `interactions.${op} failed (${resp.status})`, resp.status, op);
|
|
33
|
+
}
|
|
34
|
+
return body;
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
/** Storage liveness for a principal (open — no session needed). */
|
|
38
|
+
status: (principal) => call(principal, 'status', {}, false),
|
|
39
|
+
// ── Owner-own capability records (self-only; spec 323 W2) ──
|
|
40
|
+
relationships: {
|
|
41
|
+
get: (principal) => call(principal, 'relationships.get').then((r) => r.relationships),
|
|
42
|
+
merge: (principal, entry, remove = false) => call(principal, 'relationships.merge', { entry, ...(remove ? { remove: true } : {}) }),
|
|
43
|
+
},
|
|
44
|
+
record: {
|
|
45
|
+
get: (principal, recordType) => call(principal, 'record.get', { recordType }).then((r) => r.record),
|
|
46
|
+
put: (principal, recordType, record) => call(principal, 'record.put', { recordType, record }),
|
|
47
|
+
},
|
|
48
|
+
memberProfile: {
|
|
49
|
+
put: (principal, org, profile) => call(principal, 'member.profile.put', { org, profile }),
|
|
50
|
+
},
|
|
51
|
+
// ── Directory (membership) ──
|
|
52
|
+
directory: {
|
|
53
|
+
list: (principal, stewardship) => call(principal, 'directory.list', stewardship ? { stewardship } : {}).then((r) => r.listings),
|
|
54
|
+
publish: (principal, listing, label, subjectStewardship) => call(principal, 'directory.publish', { listing, ...(label ? { label } : {}), ...(subjectStewardship ? { subjectStewardship } : {}) }),
|
|
55
|
+
revoke: (principal) => call(principal, 'directory.revoke'),
|
|
56
|
+
},
|
|
57
|
+
// ── Channels ──
|
|
58
|
+
channels: {
|
|
59
|
+
list: (principal, stewardship) => call(principal, 'channels.list', stewardship ? { stewardship } : {}),
|
|
60
|
+
read: (principal, channelId, stewardship) => call(principal, 'channels.read', { channelId, ...(stewardship ? { stewardship } : {}) }),
|
|
61
|
+
create: (principal, title, stewardship) => call(principal, 'channels.create', { title, ...(stewardship ? { stewardship } : {}) }),
|
|
62
|
+
post: (principal, channelId, bodyText) => call(principal, 'channels.post', { channelId, bodyText }),
|
|
63
|
+
},
|
|
64
|
+
// ── Inbox (owner-facing residency; the W4 session path) ──
|
|
65
|
+
inbox: {
|
|
66
|
+
get: (principal) => call(principal, 'inbox.get').then((r) => r.doc),
|
|
67
|
+
put: (principal, doc) => call(principal, 'inbox.put', { doc }),
|
|
68
|
+
readBody: (principal, resource) => call(principal, 'inbox.body.get', { resource }).then((r) => r.record),
|
|
69
|
+
},
|
|
70
|
+
/** The issuance ledger — self, or a steward presenting the stewardship wire. */
|
|
71
|
+
grants: {
|
|
72
|
+
list: (principal, stewardship) => call(principal, 'grants.list', stewardship ? { stewardship } : {}).then((r) => r.grants),
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** The args-INDEPENDENT sub-verdict (A). Establishes WHO (principal) may act under WHICH delegation with
|
|
2
|
+
* WHICH session key, at a given epoch/manager — never WHAT args are permitted (that is the uncached B). */
|
|
3
|
+
export interface AuthorityVerdict {
|
|
4
|
+
principal: string;
|
|
5
|
+
sessionKey: string;
|
|
6
|
+
delegationHash: string;
|
|
7
|
+
/** The deployment epoch (spec 311) the verdict was created under — the epoch-invalidation key. */
|
|
8
|
+
epoch: string;
|
|
9
|
+
/** The DelegationManager address the verdict was validated against. */
|
|
10
|
+
manager: string;
|
|
11
|
+
/** ISO time the cold-path `verifyDelegationToken` produced this verdict. */
|
|
12
|
+
verifiedAt: string;
|
|
13
|
+
}
|
|
14
|
+
export type AuthorityVerdictKey = Pick<AuthorityVerdict, 'sessionKey' | 'delegationHash' | 'epoch' | 'manager'>;
|
|
15
|
+
export interface AuthorityVerdictCache {
|
|
16
|
+
/** Cold-path only: cache the full-verify verdict (A). */
|
|
17
|
+
put(verdict: AuthorityVerdict): Promise<void>;
|
|
18
|
+
/** Warm T1: the cached (A) verdict, or `null` (⇒ fall to the cold path). NEVER treat a hit as permission
|
|
19
|
+
* to act — the caller MUST still run the per-call caveat pass (B) + reserve the requestId. */
|
|
20
|
+
get(key: AuthorityVerdictKey): Promise<AuthorityVerdict | null>;
|
|
21
|
+
/** Revocation notice: drop every verdict for a delegation (monotonic invalidation). */
|
|
22
|
+
invalidateDelegation(delegationHash: string): Promise<void>;
|
|
23
|
+
/** Deployment-epoch change (spec 311): drop every verdict NOT on `currentEpoch`. */
|
|
24
|
+
sweepEpoch(currentEpoch: string): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export declare function createDurableObjectAuthorityVerdictCache(sql: SqlStorage): AuthorityVerdictCache;
|
|
27
|
+
//# sourceMappingURL=authority-verdict-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authority-verdict-cache.d.ts","sourceRoot":"","sources":["../../src/cloudflare/authority-verdict-cache.ts"],"names":[],"mappings":"AA6BA;4GAC4G;AAC5G,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,kGAAkG;IAClG,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,gBAAgB,EAAE,YAAY,GAAG,gBAAgB,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;AAYhH,MAAM,WAAW,qBAAqB;IACpC,yDAAyD;IACzD,GAAG,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C;mGAC+F;IAC/F,GAAG,CAAC,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAChE,uFAAuF;IACvF,oBAAoB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,oFAAoF;IACpF,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;AAKD,wBAAgB,wCAAwC,CAAC,GAAG,EAAE,UAAU,GAAG,qBAAqB,CA6C/F"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/// <reference types="@cloudflare/workers-types" />
|
|
2
|
+
// AuthorityVerdictCache (spec 316 §3a — the T1 fast-path verdict, L2).
|
|
3
|
+
//
|
|
4
|
+
// SECURITY MODEL — the §3a split (do not collapse it): the T1 authorization is TWO parts.
|
|
5
|
+
// (A) the ARGS-INDEPENDENT verdict cached here: at cold-path creation a FULL `verifyDelegationToken` ran
|
|
6
|
+
// and established — DEL-001 session-key↔delegation binding, delegation ERC-1271 validity, and
|
|
7
|
+
// `delegator === principal` — keyed by (sessionKeyPubkey, delegationHash, deploymentEpoch,
|
|
8
|
+
// delegationManager). It is monotonic-invalidated on revoke (drop the delegationHash) or epoch change
|
|
9
|
+
// (sweep everything not on the current epoch).
|
|
10
|
+
// (B) the PER-CALL caveat pass that is NEVER cached — the full off-chain caveat evaluator against the
|
|
11
|
+
// concrete args on EVERY T1 invoke, with the evaluated argsHash bound into a single-use requestId.
|
|
12
|
+
// Authorization = (A) PLUS (B), NEVER (A) alone. This cache stores ONLY (A). A hit means "the binding +
|
|
13
|
+
// validity held as of `verifiedAt` and has not been revoked/epoch-swept" — it is NOT permission to act; the
|
|
14
|
+
// caller MUST still run (B) + reserve the requestId. Never authorize from a revocation-only lookup.
|
|
15
|
+
//
|
|
16
|
+
// Stored on the DO's `state.storage.sql` (co-located with the vault + exchange stream, one trust domain).
|
|
17
|
+
const CREATE_TABLE = `CREATE TABLE IF NOT EXISTS authority_verdicts (
|
|
18
|
+
cache_key TEXT PRIMARY KEY,
|
|
19
|
+
principal TEXT NOT NULL,
|
|
20
|
+
session_key TEXT NOT NULL,
|
|
21
|
+
delegation_hash TEXT NOT NULL,
|
|
22
|
+
epoch TEXT NOT NULL,
|
|
23
|
+
manager TEXT NOT NULL,
|
|
24
|
+
verified_at TEXT NOT NULL
|
|
25
|
+
)`;
|
|
26
|
+
const CREATE_IDX_DELEG = `CREATE INDEX IF NOT EXISTS idx_authority_verdicts_deleg ON authority_verdicts (delegation_hash)`;
|
|
27
|
+
const CREATE_IDX_EPOCH = `CREATE INDEX IF NOT EXISTS idx_authority_verdicts_epoch ON authority_verdicts (epoch)`;
|
|
28
|
+
const keyOf = (k) => `${k.sessionKey.toLowerCase()}|${k.delegationHash.toLowerCase()}|${k.epoch}|${k.manager.toLowerCase()}`;
|
|
29
|
+
export function createDurableObjectAuthorityVerdictCache(sql) {
|
|
30
|
+
sql.exec(CREATE_TABLE);
|
|
31
|
+
sql.exec(CREATE_IDX_DELEG);
|
|
32
|
+
sql.exec(CREATE_IDX_EPOCH);
|
|
33
|
+
return {
|
|
34
|
+
async put(v) {
|
|
35
|
+
sql.exec(`INSERT INTO authority_verdicts (cache_key, principal, session_key, delegation_hash, epoch, manager, verified_at)
|
|
36
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
37
|
+
ON CONFLICT(cache_key) DO UPDATE SET
|
|
38
|
+
principal = excluded.principal, session_key = excluded.session_key,
|
|
39
|
+
delegation_hash = excluded.delegation_hash, epoch = excluded.epoch,
|
|
40
|
+
manager = excluded.manager, verified_at = excluded.verified_at`, keyOf(v), v.principal.toLowerCase(), v.sessionKey.toLowerCase(), v.delegationHash.toLowerCase(), v.epoch, v.manager.toLowerCase(), v.verifiedAt);
|
|
41
|
+
},
|
|
42
|
+
async get(key) {
|
|
43
|
+
const rows = sql
|
|
44
|
+
.exec('SELECT * FROM authority_verdicts WHERE cache_key = ?', keyOf(key))
|
|
45
|
+
.toArray();
|
|
46
|
+
const r = rows[0];
|
|
47
|
+
if (!r)
|
|
48
|
+
return null;
|
|
49
|
+
return {
|
|
50
|
+
principal: r.principal,
|
|
51
|
+
sessionKey: r.session_key,
|
|
52
|
+
delegationHash: r.delegation_hash,
|
|
53
|
+
epoch: r.epoch,
|
|
54
|
+
manager: r.manager,
|
|
55
|
+
verifiedAt: r.verified_at,
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
async invalidateDelegation(delegationHash) {
|
|
59
|
+
sql.exec('DELETE FROM authority_verdicts WHERE delegation_hash = ?', delegationHash.toLowerCase());
|
|
60
|
+
},
|
|
61
|
+
async sweepEpoch(currentEpoch) {
|
|
62
|
+
sql.exec('DELETE FROM authority_verdicts WHERE epoch != ?', currentEpoch);
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=authority-verdict-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authority-verdict-cache.js","sourceRoot":"","sources":["../../src/cloudflare/authority-verdict-cache.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,uEAAuE;AACvE,EAAE;AACF,0FAA0F;AAC1F,2GAA2G;AAC3G,oGAAoG;AACpG,iGAAiG;AACjG,4GAA4G;AAC5G,qDAAqD;AACrD,wGAAwG;AACxG,yGAAyG;AACzG,wGAAwG;AACxG,4GAA4G;AAC5G,oGAAoG;AACpG,EAAE;AACF,0GAA0G;AAE1G,MAAM,YAAY,GAAG;;;;;;;;EAQnB,CAAC;AACH,MAAM,gBAAgB,GAAG,iGAAiG,CAAC;AAC3H,MAAM,gBAAgB,GAAG,uFAAuF,CAAC;AAwCjH,MAAM,KAAK,GAAG,CAAC,CAAsB,EAAU,EAAE,CAC/C,GAAG,CAAC,CAAC,UAAU,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,cAAc,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;AAE1G,MAAM,UAAU,wCAAwC,CAAC,GAAe;IACtE,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACvB,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC3B,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAE3B,OAAO;QACL,KAAK,CAAC,GAAG,CAAC,CAAC;YACT,GAAG,CAAC,IAAI,CACN;;;;;0EAKkE,EAClE,KAAK,CAAC,CAAC,CAAC,EACR,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,EACzB,CAAC,CAAC,UAAU,CAAC,WAAW,EAAE,EAC1B,CAAC,CAAC,cAAc,CAAC,WAAW,EAAE,EAC9B,CAAC,CAAC,KAAK,EACP,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EACvB,CAAC,CAAC,UAAU,CACb,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,GAAG;YACX,MAAM,IAAI,GAAG,GAAG;iBACb,IAAI,CAAa,sDAAsD,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;iBACpF,OAAO,EAAE,CAAC;YACb,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,IAAI,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAC;YACpB,OAAO;gBACL,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,UAAU,EAAE,CAAC,CAAC,WAAW;gBACzB,cAAc,EAAE,CAAC,CAAC,eAAe;gBACjC,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,OAAO,EAAE,CAAC,CAAC,OAAO;gBAClB,UAAU,EAAE,CAAC,CAAC,WAAW;aAC1B,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,oBAAoB,CAAC,cAAc;YACvC,GAAG,CAAC,IAAI,CAAC,0DAA0D,EAAE,cAAc,CAAC,WAAW,EAAE,CAAC,CAAC;QACrG,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,YAAY;YAC3B,GAAG,CAAC,IAAI,CAAC,iDAAiD,EAAE,YAAY,CAAC,CAAC;QAC5E,CAAC;KACF,CAAC;AACJ,CAAC"}
|