@hediet/linkrpc 0.0.3-next.20260922.2 → 0.0.3-next.20260924.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/README.md +133 -0
- package/dist/chunks/{hub.interfaces-Deslgtgr.js → hub.interfaces-CN-Bz7Kj.js} +2 -2
- package/dist/chunks/{hub.interfaces-Deslgtgr.js.map → hub.interfaces-CN-Bz7Kj.js.map} +1 -1
- package/dist/chunks/{hubAccess-BmPvNNaM.d.ts → hubAccess-BHfWpT7f.d.ts} +2 -2
- package/dist/chunks/{hubAccess-BmPvNNaM.d.ts.map → hubAccess-BHfWpT7f.d.ts.map} +1 -1
- package/dist/chunks/{hubFacade-BKTQXCdF.js → hubFacade-BK0LDuxF.js} +2 -2
- package/dist/chunks/{hubFacade-BKTQXCdF.js.map → hubFacade-BK0LDuxF.js.map} +1 -1
- package/dist/chunks/{hubFacade-Bmh-SFWd.d.ts → hubFacade-CmacIwKr.d.ts} +2 -2
- package/dist/chunks/{hubFacade-Bmh-SFWd.d.ts.map → hubFacade-CmacIwKr.d.ts.map} +1 -1
- package/dist/chunks/{inspectionHost-BIkLKvr1.js → inspectionHost-DfyuUt6V.js} +627 -64
- package/dist/chunks/inspectionHost-DfyuUt6V.js.map +1 -0
- package/dist/chunks/{linkRpcConnection-Bbq20hQG.d.ts → linkRpcConnection-CzF2DvFS.d.ts} +446 -28
- package/dist/chunks/linkRpcConnection-CzF2DvFS.d.ts.map +1 -0
- package/dist/chunks/{src-DKjTceek.js → src-Cmt04YKN.js} +133 -20
- package/dist/chunks/src-Cmt04YKN.js.map +1 -0
- package/dist/hub/client/index.d.ts +3 -3
- package/dist/hub/client/index.js +3 -3
- package/dist/hub/common/index.d.ts +612 -78
- package/dist/hub/common/index.d.ts.map +1 -1
- package/dist/hub/common/index.js +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/inspection/index.d.ts +1 -1
- package/dist/inspection/index.js +1 -1
- package/dist/node.d.ts +3 -3
- package/dist/node.js +4 -4
- package/dist/web.d.ts +2 -2
- package/dist/web.js +3 -3
- package/package.json +2 -2
- package/dist/chunks/inspectionHost-BIkLKvr1.js.map +0 -1
- package/dist/chunks/linkRpcConnection-Bbq20hQG.d.ts.map +0 -1
- package/dist/chunks/src-DKjTceek.js.map +0 -1
package/README.md
CHANGED
|
@@ -7,6 +7,139 @@ message on the wire is a valid JSON-RPC message, and linkrpc adds just enough on
|
|
|
7
7
|
strongly-typed services share one connection — an addressing grammar, built-in reflection,
|
|
8
8
|
content-addressed interface identity, and optional layers for signing and capabilities.
|
|
9
9
|
|
|
10
|
+
## Interface templates
|
|
11
|
+
|
|
12
|
+
This bounded v1 adds TypeScript authoring groups over ordinary concrete RPC methods.
|
|
13
|
+
Imported and generated TypeScript/Rust contracts remain flat; Rust generic factory
|
|
14
|
+
authoring is not part of this API. `x-interface-templates` is optional,
|
|
15
|
+
non-authoritative explanation: it can disappear without changing an interface's
|
|
16
|
+
hash, dispatch, permissions, or compatibility.
|
|
17
|
+
|
|
18
|
+
When reflection sees multiple documents with the same validated `id@hash`, it
|
|
19
|
+
prefers valid template metadata over none, or a compatible superset preserving
|
|
20
|
+
existing templates and instances. Otherwise the first candidate wins. Selection
|
|
21
|
+
retains one whole document, never merges fragments, and malformed optional
|
|
22
|
+
metadata is not ranked. Template-aware import/codegen still validates explanations
|
|
23
|
+
explicitly. No metadata revision or completeness guarantee is implied.
|
|
24
|
+
|
|
25
|
+
Author structural interface templates as **generic callable factories**, with ordinary Zod schemas and
|
|
26
|
+
`requestType` (classic Zod and `zod/mini` are both supported):
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { z } from 'zod';
|
|
30
|
+
import {
|
|
31
|
+
defineInterfaceTemplate, defineInterface, requestType, applicationError,
|
|
32
|
+
type InterfaceTemplateClient, type Schema,
|
|
33
|
+
} from '@hediet/linkrpc';
|
|
34
|
+
|
|
35
|
+
const Store = defineInterfaceTemplate(
|
|
36
|
+
{ id: 'example.store', parameters: ['Value'] },
|
|
37
|
+
<V>({ Value }: { Value: Schema<V> }) => {
|
|
38
|
+
const Conflict = applicationError('conflict', { data: z.object({ current: Value }) });
|
|
39
|
+
return {
|
|
40
|
+
get: requestType(z.object({}), Value),
|
|
41
|
+
set: requestType(z.object({ value: Value }), z.object({})).withErrors([Conflict]),
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
const numbers = Store({ Value: z.number() });
|
|
46
|
+
// numbers.members.get has result type number, not any.
|
|
47
|
+
// numbers.members.set.errors[0].create({ current: 42 }) creates a checked error.
|
|
48
|
+
const api = defineInterface({ id: 'example.api' }, {
|
|
49
|
+
numbers,
|
|
50
|
+
backup: Store({ Value: z.number() }).mapMembers({ get: 'fetchBackup', set: 'writeBackup' }),
|
|
51
|
+
ping: requestType(z.object({}), z.string()),
|
|
52
|
+
});
|
|
53
|
+
connection.register(api, {
|
|
54
|
+
numbers: {
|
|
55
|
+
get: () => 42,
|
|
56
|
+
set: ({ value }) => numbers.members.set.errors[0].create({ current: value }),
|
|
57
|
+
},
|
|
58
|
+
backup: {
|
|
59
|
+
get: () => 100,
|
|
60
|
+
set: () => ({}),
|
|
61
|
+
},
|
|
62
|
+
ping: () => 'pong',
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// The consumer depends on the bound template, not the containing interface or wire names.
|
|
66
|
+
function readStore(store: InterfaceTemplateClient<typeof numbers>): Promise<number> {
|
|
67
|
+
return store.get({});
|
|
68
|
+
}
|
|
69
|
+
const client = connection.get(api);
|
|
70
|
+
await readStore(client.numbers); // 42
|
|
71
|
+
await readStore(client.backup); // 100, sent as example.api::fetchBackup
|
|
72
|
+
await client.numbers.set({ value: 43 }); // retains the checked conflict result
|
|
73
|
+
await connection.getResultClient(api).backup.get({}); // includes generic failures as values
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Direct groups derive their instance names from declaration keys and lower to
|
|
77
|
+
ordinary flat wire members: `numbers$get`, `numbers$set`, `fetchBackup`, `writeBackup` and `ping` here. Registration **must**
|
|
78
|
+
use the corresponding nested group shape; missing groups/members, non-functions,
|
|
79
|
+
extra members, and flat aliases are rejected before anything is registered.
|
|
80
|
+
Handlers keep inferred params, context, results, notifications and streams.
|
|
81
|
+
Clients use the same nested shape: `connection.get(api).numbers.get({})`.
|
|
82
|
+
Both `get` and `getResultClient` expose **only** the declared groups and ordinary members,
|
|
83
|
+
not flat aliases. `InterfaceClient<typeof api>` and `InterfaceResultClient<typeof api>`
|
|
84
|
+
describe these shapes. `InterfaceTemplateClient<typeof numbers>` and
|
|
85
|
+
`InterfaceTemplateResultClient<typeof numbers>` describe reusable group consumers.
|
|
86
|
+
Group methods retain their materialized call functions: checked `.result()` calls
|
|
87
|
+
and streaming `send`, `cancel`, `ping`, and `dispose` are not hidden by an async wrapper.
|
|
88
|
+
Service-scoped, qualified/default-target, and bare clients follow the same grouping;
|
|
89
|
+
bare routes still do not support streaming.
|
|
90
|
+
|
|
91
|
+
For arbitrary wire names, use a mapped binding **directly as a group**:
|
|
92
|
+
`{ numbers: numbers.mapMembers({ get: 'fetchNumber', set: 'writeNumber' }) }`.
|
|
93
|
+
Its implementation is still `{ numbers: { get: ..., set: ... } }` and its client
|
|
94
|
+
still calls `numbers.get`; only the dispatched wire name becomes `fetchNumber`.
|
|
95
|
+
Compatible nonstreaming groups can also be passed to another registration:
|
|
96
|
+
`connection.register(otherApi, { store: client.numbers })`. This is not a general
|
|
97
|
+
stream-forwarding adapter: handlers receive `(params, context, stream)`, while
|
|
98
|
+
clients receive `(params, streamOptions?)`. Context variance and stream lifetimes
|
|
99
|
+
must be handled explicitly when the types are not compatible.
|
|
100
|
+
|
|
101
|
+
Calling `Store({ Value: schema })` specializes TypeScript payload types and creates
|
|
102
|
+
real method and error descriptors. The explicitly generic factory runs once with
|
|
103
|
+
symbolic schemas to export the portable template, then once per call with concrete
|
|
104
|
+
schemas. Factories **must be declarative and side-effect free**: do not inspect or
|
|
105
|
+
branch on schemas, capture changing state, or perform I/O. Every instantiation is
|
|
106
|
+
checked against the reflected template; structurally inconsistent specializations fail.
|
|
107
|
+
The wrapper has a bare generic call signature (no metadata properties); access
|
|
108
|
+
`numbers.template` and `numbers.arguments` on a bound instance. Metadata is snapshotted.
|
|
109
|
+
Arguments retain their original validators, including refinements and recursive
|
|
110
|
+
schemas. Concrete members can also be authored independently: `defineInterface`
|
|
111
|
+
validates their normalized wire contracts against its optional third argument,
|
|
112
|
+
`{ templates: { instanceName: boundTemplate.mapMembers(mapping) } }`, rather than
|
|
113
|
+
requiring JavaScript object identity. It never replaces them.
|
|
114
|
+
|
|
115
|
+
Mappings must supply every template member exactly once, with no extra keys.
|
|
116
|
+
Names can be arbitrary method names, including identity mappings. For the
|
|
117
|
+
optional metadata-only third argument, the concrete methods must already exist
|
|
118
|
+
and both registration and clients stay flat; metadata never invents local groups.
|
|
119
|
+
Every concrete method can be claimed at most once, within or across instances;
|
|
120
|
+
ordinary unclaimed methods are allowed. Unknown/missing parameters, missing
|
|
121
|
+
methods and inconsistent wire shapes fail explicitly.
|
|
122
|
+
Instances use the `x-interface-templates` extension and survive schema
|
|
123
|
+
import and code generation without changing the concrete interface hash.
|
|
124
|
+
Each instance has an explicit `members: { templateMember: concreteMember }`
|
|
125
|
+
mapping; the wire format has no prefix convention or implicit handler registration.
|
|
126
|
+
Schema import and generated code expose flat handlers and flat clients, preserving
|
|
127
|
+
the metadata verbatim. Local grouped declaration shape is not reconstructed from
|
|
128
|
+
reflection: independently authored flat interfaces may publish identical metadata.
|
|
129
|
+
|
|
130
|
+
Template schemas use the same representable, normalized structural JSON Schema
|
|
131
|
+
subset as ordinary interfaces: objects, arrays, tuples, unions, optional/nullable
|
|
132
|
+
values, primitives, and supported recursive/component references. Refinements are
|
|
133
|
+
retained in the concrete Zod validators but are not compared as validation code.
|
|
134
|
+
Unrepresentable transforms and unsupported structural constructs such as intersections
|
|
135
|
+
(`allOf`) fail explicitly; there is no fallback to an unknown schema.
|
|
136
|
+
Checked errors support both `applicationError` data and `rpcError` body schemas,
|
|
137
|
+
including generic parameters, nested payloads, and concrete `.create`/`.is` types.
|
|
138
|
+
Arguments must not themselves be
|
|
139
|
+
optional: express optionality with `z.optional(Value)` in the template. Validation
|
|
140
|
+
of instances uses LinkRPC's normalized structural JSON Schema subset, not
|
|
141
|
+
equivalence of arbitrary validation code or refinements.
|
|
142
|
+
|
|
10
143
|
## How linkrpc works
|
|
11
144
|
|
|
12
145
|
```mermaid
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
|
-
import {
|
|
5
|
+
import { mt as requestType, z as defineInterface } from "./inspectionHost-DfyuUt6V.js";
|
|
6
6
|
import { array, boolean, discriminatedUnion, enum as enum$1, extend, literal, minLength, number, object, optional, record, string, union, unknown } from "zod/mini";
|
|
7
7
|
//#region src/hub/common/hub.interfaces.ts
|
|
8
8
|
const zMemberPattern = union([object({ exact: string() }), object({ prefix: string() })]);
|
|
@@ -523,4 +523,4 @@ const hubAccessManifestInterface = defineInterface({
|
|
|
523
523
|
//#endregion
|
|
524
524
|
export { hubServiceIdRegistryInterface as i, hubAccessManifestInterface as n, hubGrantedServiceIdInterface as r, hubAccessInterface as t };
|
|
525
525
|
|
|
526
|
-
//# sourceMappingURL=hub.interfaces-
|
|
526
|
+
//# sourceMappingURL=hub.interfaces-CN-Bz7Kj.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hub.interfaces-Deslgtgr.js","names":["zEnum"],"sources":["../../src/hub/common/hub.interfaces.ts"],"sourcesContent":["import { array, boolean, discriminatedUnion, enum as zEnum, extend, literal, minLength, number, object, optional, record, string, union, unknown } from 'zod/mini';\nimport { defineInterface, requestType, type InterfaceClient } from '../../index';\n\nconst zMemberPattern = union([\n object({ exact: string() }),\n object({ prefix: string() }),\n]);\n\n/**\n * Optional per-permission invocation preview the requester suggests for\n * \"Allow once\" consent UX. When present, the user can bind the resulting\n * capability to this exact (method, params, interfaceHash) tuple.\n *\n * `nonce` and `signedAtMs` are consumer-chosen and identify the exact call\n * attempt the cap will authorise. The host hashes the same canonical\n * bytes the consumer will sign; the gate re-derives the hash from the\n * inbound call and compares. The consumer MUST use the same\n * nonce+signedAtMs when it actually issues the call (`@hediet/linkrpc`'s\n * {@link JsonRpcChannel.setCapProvider} hook does this automatically).\n *\n * Everything in `params` is shown to the user verbatim.\n */\nconst zCallIntent = object({\n /** Fully-qualified method name the requester intends to call. */\n method: string(),\n /** Exact params the requester intends to send. */\n params: optional(unknown()),\n /** Optional schema hash assertion the call carries inside the signed payload. */\n interfaceHash: optional(string()),\n /** base64url-encoded nonce bytes the consumer will sign with. */\n nonce: string(),\n /** Unix milliseconds the consumer will sign with. */\n signedAtMs: number(),\n /** Optional one-line summary the shell displays next to params. */\n summary: optional(string()),\n /** Requester's preferred default action (`once` if omitted). */\n suggestion: optional(zEnum(['once', 'shortLived', 'longLived', 'persistent'])),\n});\n\nconst zParamMatcher = union([\n object({ exact: unknown() }),\n object({ enum: array(unknown()) }),\n object({ prefix: string() }),\n object({ subsetOf: array(string()) }),\n object({ any: literal(true) }),\n]);\n\nconst zCallBindHash = object({\n alg: literal('sha256'),\n payloadHash: string(),\n});\n\n/** Wire shape of `TargetPattern` from `@hediet/linkrpc`. */\nconst zTargetPattern = object({\n serviceId: zMemberPattern,\n interfaceId: zMemberPattern,\n interfaceHash: optional(string()),\n members: array(zMemberPattern),\n});\n\n/**\n * Wire shape of `Permission` from `@hediet/linkrpc/identity/capability`.\n * Used by `hubAccess::requestAccess` so the consumer can describe exactly\n * what authority it wants — including wildcard service ids\n * (`target.serviceId: { prefix: \"\" }`). `canInvoke`/`canDelegate` default\n * to `false` (fail closed).\n */\nconst zPermission = object({\n target: zTargetPattern,\n canInvoke: optional(boolean()),\n canDelegate: optional(boolean()),\n params: optional(record(string(), zParamMatcher)),\n callBind: optional(zCallBindHash),\n});\n\n/**\n * A `requestAccess` permission carrying the consent-only {@link zCallIntent}\n * preview alongside the authority it requests. The host strips `callIntent`\n * before minting the signed capability.\n */\nconst zRequestPermission = extend(zPermission, {\n callIntent: optional(zCallIntent),\n});\n\n/**\n * Who is asking, plus the `principal` that becomes the minted capability's\n * `audience`/target. The hub uses `principal` directly (it is no longer derived\n * from the call's verified signer): a cap minted for a `principal` is only usable\n * by the holder of that principal's key, enforced at call time\n * (`permits` checks `audience === call.signer`).\n */\nconst zConsumer = object({\n name: string(),\n /** Capability audience/target — the consumer's own PrincipalId. */\n principal: string(),\n origin: optional(string()),\n purpose: optional(string()),\n});\n\n/**\n * Wire shape of `SignedCapability` from `@hediet/linkrpc`. Mirrors the\n * structural type without pulling zod into the identity package. Keep in\n * sync with `Capability` / `SignedCapability` there.\n *\n * A capability is now a flat object (its fields) plus a `$hubrpcSignature`\n * map carrying the issuer's `capability` signature. Delegation links to a\n * single `parentHash` (the parent's `signedHash(\"capability\", ...)`).\n */\nconst zSignedCapability = object({\n issuer: string(),\n audience: string(),\n permissions: array(zPermission),\n expiresAtMs: optional(number()),\n parentHash: optional(string()),\n nonce: string(),\n $hubrpcSignature: object({\n capability: optional(object({ keyId: string(), sig: string() })),\n call: optional(object({ keyId: string(), sig: string() })),\n }),\n});\n\n/** Lifetime hint for a minted capability. Shared across hubAccess + the manifest. */\nconst zDuration = zEnum(['once', 'shortLived', 'longLived', 'persistent']);\n\n// ---- discovery slot shapes (shared by hubAccess.request and hubAccessManifest) ----\n\n/** One interface a slot's chosen service must implement. */\nconst zInterfaceRef = object({\n id: string(),\n hash: optional(string()),\n /** Default true. */\n required: optional(boolean()),\n});\n\n/** One member (method) the consumer intends to call on a slot's interface. */\nconst zMemberRequest = object({\n interfaceId: string(),\n member: zMemberPattern,\n /** Default true. */\n required: optional(boolean()),\n});\n\n/**\n * A consumer's need for ONE service: the interfaces it must speak and the\n * members it intends to call. The discovery side of both `hubAccess::request`\n * (sent as a call param) and `hubAccessManifest` (published as state).\n */\nconst zSlotRequest = object({\n interfaces: array(zInterfaceRef),\n members: optional(array(zMemberRequest)),\n});\n\n/** The resolution of one slot: exactly one chosen service. */\nconst zResolvedSlot = object({\n serviceId: string(),\n satisfiedInterfaces: array(string()),\n});\n\n/**\n * Minimal JSON-document patch op, addressed by an RFC 6901 JSON Pointer.\n * `path: \"\"` targets the whole document; a pointer like `/granted/secretStore`\n * targets one entry. Used by `hubAccessManifest::setCurrent` to replace the\n * entire granted document or a single entry.\n */\nconst zManifestPatch = discriminatedUnion('op', [\n object({ op: literal('set'), path: string(), value: unknown() }),\n object({ op: literal('remove'), path: string() }),\n]);\n\n/**\n * `hubServiceIdRegistry::registerServiceId` — a participant claims a prefix\n * **outside** its provenance-granted namespace.\n *\n * Reached under the hub's own service id (form-3\n * `<hubServiceId>::hubServiceIdRegistry::registerServiceId`) and gated by an\n * admin-rooted capability. For claims **within** the connection's granted\n * namespace, use the cheaper, capability-free `hubGrantedServiceId::register`\n * instead.\n */\nexport const hubServiceIdRegistryInterface = defineInterface(\n {\n id: 'hubServiceIdRegistry',\n description: 'Claim a serviceId prefix on the hub.',\n },\n {\n registerServiceId: requestType(\n object({\n requestedPrefix: string().check(minLength(1)),\n }),\n object({}),\n ),\n },\n);\n\n\n/**\n * `hubGrantedServiceId` — the ungated connection surface, served at the\n * connection root on each participant's overlay (so it is reachable directly,\n * never forwarded, and needs no capability of its own). Two jobs:\n *\n * - `get` (unsigned) reports the topology facts the hub decided for this\n * connection — where it is and what it may claim.\n * - `register` (provenance-gated) claims a prefix **within**\n * this connection's granted namespace — the capability-free claim path.\n * Claims outside the namespace go through the admin-gated\n * `<hubServiceId>::hubServiceIdRegistry::registerServiceId` door instead.\n * - `getHubServiceId` (unsigned) reports the serviceId prefix the hub mounts\n * its own global services under — the prefix to address the admin-gated\n * registry and reflection endpoints through. Served at the connection root\n * so a participant can discover it **before** it knows where the hub lives.\n *\n * The hub's consent front door (`hubAccess::*`) is served at the connection\n * root too, so it needs no bootstrap capability and is reached directly.\n */\nexport const hubGrantedServiceIdInterface = defineInterface(\n {\n id: 'hubGrantedServiceId',\n description: 'Connection facts and capability-free serviceId claims.',\n },\n {\n /**\n * Connection facts (unsigned). The participant pulls, on each\n * (re)connect, the topology facts the hub has decided for this\n * connection:\n *\n * - `grantedServiceIdNamespace` — the absolute serviceId region this\n * connection's provenance may claim (anything at/under it). May be\n * the empty string, meaning \"claim nothing freely\".\n */\n get: requestType(\n object({}),\n object({\n grantedServiceIdNamespace: string(),\n }),\n ),\n\n /**\n * The serviceId prefix the hub mounts its own global services under\n * (default `'hub'`). Use it to address the admin-gated\n * `<hubServiceId>::hubServiceIdRegistry::registerServiceId` door and the\n * hub's reflection endpoints. Served at the connection root (unsigned),\n * so a participant can learn it without first knowing where the hub\n * lives.\n */\n getHubServiceId: requestType(\n object({}),\n object({\n hubServiceId: string(),\n }),\n ),\n\n /**\n * Claim a serviceId prefix **within** this connection's provenance-\n * granted namespace (see `get().grantedServiceIdNamespace`). No\n * capability needed — the grant already happened out-of-band at attach\n * time. The requested `serviceId` must equal the granted namespace or be\n * nested beneath it; anything else is rejected (claim it through the\n * admin-gated `<hubServiceId>::hubServiceIdRegistry::registerServiceId`\n * door instead).\n */\n register: requestType(\n object({ serviceId: string().check(minLength(1)) }),\n object({}),\n ),\n },\n);\n\n/**\n * `hubAccess::request` — a consumer (e.g. a sandboxed web editor) asks the\n * hub for scoped access to one or more services.\n *\n * Consumers describe their needs as named *dependencies* (\"slots\"): each\n * slot lists the interfaces the chosen service must implement and the\n * methods the consumer wants to call. The hub resolves candidate services\n * from its participant directory, then forwards the bundle to a host-\n * supplied handler (see `Hub` options) that shows the user a single\n * prompt. The handler picks a concrete service per slot and approves a\n * subset of the requested methods.\n *\n * In v1 the response carries only the resolution (which serviceId was\n * chosen per slot). Once the hub holds a signing identity it will also\n * return a `SignedCapability` the consumer attaches to subsequent calls.\n * Until then, dispatch is not gated on the grant — see plan-access.md.\n */\nexport const hubAccessInterface = defineInterface(\n {\n id: 'hubAccess',\n description: 'Consumer requests scoped access to services on the hub.',\n },\n {\n request: requestType(\n object({\n consumer: zConsumer,\n dependencies: record(string(), zSlotRequest),\n duration: optional(zDuration),\n }),\n discriminatedUnion('status', [\n object({\n status: literal('granted'),\n slots: record(string(), zResolvedSlot),\n /**\n * One or more `SignedCapability`s issued by the hub\n * (audience = consumer NodeId). Consumers attach them\n * via `$hubrpc.capabilities` on subsequent calls. Empty\n * array is legal (hub with no signing identity / tests).\n *\n * Plural so a handler can return per-slot caps with\n * different `exp`/caveats; typical handlers return a\n * single cap covering every granted member.\n */\n capabilities: array(zSignedCapability),\n }),\n object({\n status: literal('denied'),\n reason: optional(string()),\n }),\n object({\n status: literal('noCandidates'),\n /** Slot ids that have zero matching candidates. */\n slots: array(string()),\n }),\n ]),\n ),\n\n /**\n * Service-pinned widening of an existing grant. The consumer asks\n * the hub for additional members on a `serviceId` they already\n * deal with — same audience (their NodeId) and (intended) same\n * `rootIssuer` as the prior grant. The hub never picks the service\n * for the consumer here: `serviceId` is an input, not a result.\n *\n * On grant the response carries a fresh `SignedCapability` whose\n * attenuations cover **only** the granted delta. Bag-compatible\n * with the prior cap; combine via `merge` (when it exists) or\n * just keep both in `$hubrpc.capabilities`.\n *\n * Distinguished from `request` so the consent UI can render a\n * different affordance (\"X already has read access on `github`,\n * grant `update` as well?\" instead of from-zero selection).\n *\n * TODO(hub-ledger): enforce \"consumer has prior history on this\n * service\" — see `hub.ts:_handleAccessExtend`. v1 forwards\n * directly to the host's `onAccessExtend` without consulting the\n * `_grants` ledger; this is by design while we settle on the\n * persistence model.\n */\n extend: requestType(\n object({\n consumer: zConsumer,\n /**\n * Service to widen the grant on. MUST equal the\n * `serviceId` of a previously-issued attenuation for this\n * consumer NodeId. v1 does not validate this.\n */\n serviceId: string(),\n /**\n * The delta. Same shape as `request`'s slot.members. Each\n * entry MUST refer to an interface the consumer has been\n * introduced to on `serviceId` via a prior `request`.\n */\n added: array(zMemberRequest),\n duration: optional(zDuration),\n }),\n discriminatedUnion('status', [\n object({\n status: literal('granted'),\n serviceId: string(),\n /** Members actually granted (handler may approve a subset). */\n granted: array(\n object({\n interfaceId: string(),\n member: zMemberPattern,\n }),\n ),\n /**\n * Capability covering only the granted delta. Audience\n * = consumer NodeId. Bag-compatible with the prior\n * cap on this service.\n */\n capabilities: optional(array(zSignedCapability)),\n }),\n object({\n status: literal('denied'),\n reason: optional(string()),\n }),\n // TODO(hub-ledger): additional variants once the host's\n // grant ledger is persisted:\n // - { status: \"unknownService\", serviceId }\n // - { status: \"unknownInterface\", serviceId, interfaceIds }\n // Reject without ever prompting the user when the consumer\n // is asking to widen a service it has no prior grant on.\n ]),\n ),\n\n /**\n * `hubAccess::requestAccess` — direct capability request. The\n * consumer specifies the exact attenuations it wants. No\n * service-discovery, no candidate resolution: the consumer\n * already knows which `(serviceId, interfaceId, members)` it\n * needs, including wildcards (e.g. `serviceId: { prefix: \"\" }`\n * to ask for an interface anywhere).\n *\n * Compared to `request`:\n * - `request` does directory-based discovery, picks one service\n * per slot, and returns a cap pinned to that service. Use\n * when the consumer says \"give me SOME service that does X\".\n * - `requestAccess` is verbatim. Use when the consumer says\n * \"give me exactly these attenuations\". Especially useful for\n * reflection (`hubrpc.directory::list` on any service) and\n * for on-demand per-method grants from an explorer-style UI.\n *\n * The user prompt shows the exact `Capability` the hub will sign\n * on Allow, same byte-equality guarantee as `request`/`extend`.\n */\n requestAccess: requestType(\n object({\n consumer: zConsumer,\n permissions: array(zRequestPermission).check(minLength(1)),\n duration: optional(zDuration),\n }),\n discriminatedUnion('status', [\n object({\n status: literal('granted'),\n capabilities: array(zSignedCapability),\n }),\n object({\n status: literal('denied'),\n reason: optional(string()),\n }),\n ]),\n ),\n },\n);\n\n// ════════════════════════════════════════════════════════════════════════\n// hubAccessManifest — the declarative twin of `hubAccess`.\n// ════════════════════════════════════════════════════════════════════════\n\n/**\n * The root issuer(s) whose signature the participant will accept on the minted\n * capability. A capability is only usable by its holder if it chains to a root\n * that the *target* service trusts — and not every service trusts every root.\n * So the participant declares, per entry, which minting root(s) would produce a\n * cap it can actually use; the admin must mint with one of them (or decline).\n *\n * Tri-state, by design:\n * - **omitted** (`undefined`) — unspecified. The participant doesn't know /\n * doesn't constrain the root; the admin picks. (The common, lenient default.)\n * - **non-empty** (`[\"id:abc\", …]`) — mint with one of *these* roots; a cap\n * rooted elsewhere is useless to the participant.\n * - **empty** (`[]`) — \"no root is acceptable\", i.e. nothing can satisfy this.\n * Almost certainly a bug (it can never be granted); kept representable so a\n * tool can detect and flag it rather than silently treating it as \"any\".\n */\nconst zAcceptableRootIds = array(string());\n\n/**\n * A desired access entry the participant publishes. Two kinds, mirroring the\n * two discovery-vs-verbatim halves of `hubAccess`:\n *\n * - `discover` — \"find me a service implementing X\" (same shape as a\n * `hubAccess::request` slot). The admin resolves one concrete service.\n * - `direct` — \"grant exactly these permissions\" (same shape as\n * `hubAccess::requestAccess`). The serviceId is already in each permission's\n * target; no discovery.\n */\nconst zManifestRequest = discriminatedUnion('kind', [\n extend(zSlotRequest, {\n kind: literal('discover'),\n /** Who is asking; `principal` is the audience minted caps must target. */\n consumer: zConsumer,\n /** Human rationale shown to the admin (\"seal tokens at rest\"). */\n reason: optional(string()),\n duration: optional(zDuration),\n /** Root issuer(s) that must mint this cap. See {@link zAcceptableRootIds}. */\n acceptableRootIds: optional(zAcceptableRootIds),\n /**\n * Host-authored provenance bag, stamped at `registerHubAccessAtRoot`\n * (NOT taken from the consumer's request params — so it cannot be\n * spoofed). An open `record<string, unknown>` so a host can attach\n * whatever origin info it observed about the requesting transport (e.g.\n * `{ sourceTransportId }` to route a local consent UI). Meaningful only\n * to a consumer that KNOWS this manifest is local; an aggregating\n * manifest MUST re-scope or omit it.\n */\n origin: optional(record(string(), unknown())),\n }),\n object({\n kind: literal('direct'),\n /** Who is asking; `principal` is the audience minted caps must target. */\n consumer: zConsumer,\n reason: optional(string()),\n permissions: array(zRequestPermission).check(minLength(1)),\n duration: optional(zDuration),\n /** Root issuer(s) that must mint this cap. See {@link zAcceptableRootIds}. */\n acceptableRootIds: optional(zAcceptableRootIds),\n /** Host-authored provenance bag. See the `discover` variant. */\n origin: optional(record(string(), unknown())),\n }),\n]);\n\n/**\n * The current (granted) value of one entry. For `discover` it carries the single\n * chosen service (mirroring `hubAccess::request`'s resolved slot) plus the caps;\n * for `direct` just the caps (the serviceId is in each permission's target).\n */\nconst zGrantedEntry = discriminatedUnion('kind', [\n extend(zResolvedSlot, {\n kind: literal('discover'),\n capabilities: array(zSignedCapability),\n }),\n object({\n kind: literal('direct'),\n capabilities: array(zSignedCapability),\n }),\n]);\n\n/**\n * The current value of one entry: `granted` (caps, plus the chosen service for\n * `discover`) or `denied` (with an optional reason). An entry *absent* from the\n * current document is **undecided** — distinct from a terminal `denied`, which\n * lets an approver say \"no\" (and why) instead of silently withholding.\n */\nconst zCurrentEntry = discriminatedUnion('status', [\n object({ status: literal('granted'), granted: zGrantedEntry }),\n object({ status: literal('denied'), reason: optional(string()) }),\n]);\n\n/**\n * `hubAccessManifest` — the declarative twin of {@link hubAccessInterface}.\n *\n * Where `hubAccess` is the imperative, just-in-time door a consumer *calls* (and\n * the hub serves at the connection root), `hubAccessManifest` is **served by the\n * participant** under its own serviceId, so it appears in `hubrpc.directory::list`\n * — its very presence is the request. A participant publishes its DESIRED access\n * entries (keyed by id, like `request`'s `dependencies`); an admin discovers\n * them via the directory, mints capabilities **with its own identity** (a\n * configured hub capability root), and writes the CURRENT/granted state back via\n * patches. The participant reads the granted entries and attaches the caps to\n * its later calls.\n *\n * Reconcile model (desired vs. current), aligned with the hub's other surfaces:\n * - `getDesired` / `watchDesired` — the participant's declared needs.\n * - `getCurrent` / `setCurrent` / `watchCurrent` — the admin-written grants.\n *\n * `watch*` follows the coarse empty-tick convention of `hubrpc.directory::watch`:\n * a tick means \"re-`get` now\", keeping the server stateless (no per-item deltas).\n */\nexport const hubAccessManifestInterface = defineInterface(\n {\n id: 'hubAccessManifest',\n description:\n 'Participant-served declarative access: publish DESIRED access entries '\n + '(discover | direct), an admin mints capabilities and writes the CURRENT '\n + 'granted state back. The reconcile twin of hubAccess::{request,requestAccess}.',\n },\n {\n // ---- desired (participant-authored; the admin reads) ----------------\n /** The full desired document: who is asking and the entries it wants. */\n getDesired: requestType(\n object({}),\n object({\n /**\n * entryId → desired entry, each carrying its own `consumer`. A\n * per-participant manifest's entries all share one consumer; a hub\n * broker aggregates entries from many consumers in one flat map.\n */\n requested: record(string(), zManifestRequest),\n /** Bumped whenever `requested` changes; lets watchers dedupe ticks. */\n revision: number(),\n }),\n ).withStream({ client: object({}) }),\n /**\n * Coarse change tap on the desired document. Emits an empty tick when\n * `requested` may have changed; the caller re-`getDesired`. Resolves when\n * the caller cancels. Mirrors `hubrpc.directory::watch`.\n */\n watchDesired: requestType(\n object({}),\n object({}),\n ).withStream({ server: object({}) }),\n\n // ---- current / granted (admin-authored; the participant reads) ------\n /** The full current document: entryId → granted/denied (absent ⇒ undecided). */\n getCurrent: requestType(\n object({}),\n object({\n /** entryId → current value (granted | denied). */\n current: record(string(), zCurrentEntry),\n /** Bumped on every successful `setCurrent`. */\n revision: number(),\n }),\n ),\n /**\n * Apply patches to the current document. Replace the whole document with\n * `{ op: 'set', path: '', value }`, or a single entry with\n * `{ op: 'set', path: '/current/<entryId>', value }` (a `zCurrentEntry`:\n * granted or denied). Admin-only in practice (gated by a capability\n * rooted at an accepted issuer).\n */\n setCurrent: requestType(\n object({ patches: array(zManifestPatch) }),\n object({ revision: number() }),\n ).withStream({ client: object({}) }),\n /**\n * Coarse change tap on the current document. Emits an empty tick when\n * `current` may have changed; the participant re-`getCurrent` and applies\n * any new capabilities. Resolves when the caller cancels.\n */\n watchCurrent: requestType(\n object({}),\n object({}),\n ).withStream({ server: object({}) }),\n },\n);\n\n/**\n * The typed client shape of {@link hubAccessManifestInterface} — the exact\n * object `connection.get(hubAccessManifestInterface)` (or\n * `connection.service(id).get(...)`) returns. Approvers depend on this contract,\n * not on a concrete connection: the same approver runs against a local in-memory\n * host (loopback connection), a remote hub's served manifest, or a future\n * aggregating implementation with no code change.\n */\nexport type IHubAccessManifest = InterfaceClient<typeof hubAccessManifestInterface>;\n\n/** One typed entry from `hubAccessManifest::getDesired().requested`. */\nexport type HubAccessManifestRequest =\n Awaited<ReturnType<IHubAccessManifest['getDesired']>>['requested'][string];\n\n/** One typed value accepted at `/current/<entryId>` by `setCurrent`. */\nexport type HubAccessManifestDecision =\n Awaited<ReturnType<IHubAccessManifest['getCurrent']>>['current'][string];\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hub.interfaces-CN-Bz7Kj.js","names":["zEnum"],"sources":["../../src/hub/common/hub.interfaces.ts"],"sourcesContent":["import { array, boolean, discriminatedUnion, enum as zEnum, extend, literal, minLength, number, object, optional, record, string, union, unknown } from 'zod/mini';\nimport { defineInterface, requestType, type InterfaceClient } from '../../index';\n\nconst zMemberPattern = union([\n object({ exact: string() }),\n object({ prefix: string() }),\n]);\n\n/**\n * Optional per-permission invocation preview the requester suggests for\n * \"Allow once\" consent UX. When present, the user can bind the resulting\n * capability to this exact (method, params, interfaceHash) tuple.\n *\n * `nonce` and `signedAtMs` are consumer-chosen and identify the exact call\n * attempt the cap will authorise. The host hashes the same canonical\n * bytes the consumer will sign; the gate re-derives the hash from the\n * inbound call and compares. The consumer MUST use the same\n * nonce+signedAtMs when it actually issues the call (`@hediet/linkrpc`'s\n * {@link JsonRpcChannel.setCapProvider} hook does this automatically).\n *\n * Everything in `params` is shown to the user verbatim.\n */\nconst zCallIntent = object({\n /** Fully-qualified method name the requester intends to call. */\n method: string(),\n /** Exact params the requester intends to send. */\n params: optional(unknown()),\n /** Optional schema hash assertion the call carries inside the signed payload. */\n interfaceHash: optional(string()),\n /** base64url-encoded nonce bytes the consumer will sign with. */\n nonce: string(),\n /** Unix milliseconds the consumer will sign with. */\n signedAtMs: number(),\n /** Optional one-line summary the shell displays next to params. */\n summary: optional(string()),\n /** Requester's preferred default action (`once` if omitted). */\n suggestion: optional(zEnum(['once', 'shortLived', 'longLived', 'persistent'])),\n});\n\nconst zParamMatcher = union([\n object({ exact: unknown() }),\n object({ enum: array(unknown()) }),\n object({ prefix: string() }),\n object({ subsetOf: array(string()) }),\n object({ any: literal(true) }),\n]);\n\nconst zCallBindHash = object({\n alg: literal('sha256'),\n payloadHash: string(),\n});\n\n/** Wire shape of `TargetPattern` from `@hediet/linkrpc`. */\nconst zTargetPattern = object({\n serviceId: zMemberPattern,\n interfaceId: zMemberPattern,\n interfaceHash: optional(string()),\n members: array(zMemberPattern),\n});\n\n/**\n * Wire shape of `Permission` from `@hediet/linkrpc/identity/capability`.\n * Used by `hubAccess::requestAccess` so the consumer can describe exactly\n * what authority it wants — including wildcard service ids\n * (`target.serviceId: { prefix: \"\" }`). `canInvoke`/`canDelegate` default\n * to `false` (fail closed).\n */\nconst zPermission = object({\n target: zTargetPattern,\n canInvoke: optional(boolean()),\n canDelegate: optional(boolean()),\n params: optional(record(string(), zParamMatcher)),\n callBind: optional(zCallBindHash),\n});\n\n/**\n * A `requestAccess` permission carrying the consent-only {@link zCallIntent}\n * preview alongside the authority it requests. The host strips `callIntent`\n * before minting the signed capability.\n */\nconst zRequestPermission = extend(zPermission, {\n callIntent: optional(zCallIntent),\n});\n\n/**\n * Who is asking, plus the `principal` that becomes the minted capability's\n * `audience`/target. The hub uses `principal` directly (it is no longer derived\n * from the call's verified signer): a cap minted for a `principal` is only usable\n * by the holder of that principal's key, enforced at call time\n * (`permits` checks `audience === call.signer`).\n */\nconst zConsumer = object({\n name: string(),\n /** Capability audience/target — the consumer's own PrincipalId. */\n principal: string(),\n origin: optional(string()),\n purpose: optional(string()),\n});\n\n/**\n * Wire shape of `SignedCapability` from `@hediet/linkrpc`. Mirrors the\n * structural type without pulling zod into the identity package. Keep in\n * sync with `Capability` / `SignedCapability` there.\n *\n * A capability is now a flat object (its fields) plus a `$hubrpcSignature`\n * map carrying the issuer's `capability` signature. Delegation links to a\n * single `parentHash` (the parent's `signedHash(\"capability\", ...)`).\n */\nconst zSignedCapability = object({\n issuer: string(),\n audience: string(),\n permissions: array(zPermission),\n expiresAtMs: optional(number()),\n parentHash: optional(string()),\n nonce: string(),\n $hubrpcSignature: object({\n capability: optional(object({ keyId: string(), sig: string() })),\n call: optional(object({ keyId: string(), sig: string() })),\n }),\n});\n\n/** Lifetime hint for a minted capability. Shared across hubAccess + the manifest. */\nconst zDuration = zEnum(['once', 'shortLived', 'longLived', 'persistent']);\n\n// ---- discovery slot shapes (shared by hubAccess.request and hubAccessManifest) ----\n\n/** One interface a slot's chosen service must implement. */\nconst zInterfaceRef = object({\n id: string(),\n hash: optional(string()),\n /** Default true. */\n required: optional(boolean()),\n});\n\n/** One member (method) the consumer intends to call on a slot's interface. */\nconst zMemberRequest = object({\n interfaceId: string(),\n member: zMemberPattern,\n /** Default true. */\n required: optional(boolean()),\n});\n\n/**\n * A consumer's need for ONE service: the interfaces it must speak and the\n * members it intends to call. The discovery side of both `hubAccess::request`\n * (sent as a call param) and `hubAccessManifest` (published as state).\n */\nconst zSlotRequest = object({\n interfaces: array(zInterfaceRef),\n members: optional(array(zMemberRequest)),\n});\n\n/** The resolution of one slot: exactly one chosen service. */\nconst zResolvedSlot = object({\n serviceId: string(),\n satisfiedInterfaces: array(string()),\n});\n\n/**\n * Minimal JSON-document patch op, addressed by an RFC 6901 JSON Pointer.\n * `path: \"\"` targets the whole document; a pointer like `/granted/secretStore`\n * targets one entry. Used by `hubAccessManifest::setCurrent` to replace the\n * entire granted document or a single entry.\n */\nconst zManifestPatch = discriminatedUnion('op', [\n object({ op: literal('set'), path: string(), value: unknown() }),\n object({ op: literal('remove'), path: string() }),\n]);\n\n/**\n * `hubServiceIdRegistry::registerServiceId` — a participant claims a prefix\n * **outside** its provenance-granted namespace.\n *\n * Reached under the hub's own service id (form-3\n * `<hubServiceId>::hubServiceIdRegistry::registerServiceId`) and gated by an\n * admin-rooted capability. For claims **within** the connection's granted\n * namespace, use the cheaper, capability-free `hubGrantedServiceId::register`\n * instead.\n */\nexport const hubServiceIdRegistryInterface = defineInterface(\n {\n id: 'hubServiceIdRegistry',\n description: 'Claim a serviceId prefix on the hub.',\n },\n {\n registerServiceId: requestType(\n object({\n requestedPrefix: string().check(minLength(1)),\n }),\n object({}),\n ),\n },\n);\n\n\n/**\n * `hubGrantedServiceId` — the ungated connection surface, served at the\n * connection root on each participant's overlay (so it is reachable directly,\n * never forwarded, and needs no capability of its own). Two jobs:\n *\n * - `get` (unsigned) reports the topology facts the hub decided for this\n * connection — where it is and what it may claim.\n * - `register` (provenance-gated) claims a prefix **within**\n * this connection's granted namespace — the capability-free claim path.\n * Claims outside the namespace go through the admin-gated\n * `<hubServiceId>::hubServiceIdRegistry::registerServiceId` door instead.\n * - `getHubServiceId` (unsigned) reports the serviceId prefix the hub mounts\n * its own global services under — the prefix to address the admin-gated\n * registry and reflection endpoints through. Served at the connection root\n * so a participant can discover it **before** it knows where the hub lives.\n *\n * The hub's consent front door (`hubAccess::*`) is served at the connection\n * root too, so it needs no bootstrap capability and is reached directly.\n */\nexport const hubGrantedServiceIdInterface = defineInterface(\n {\n id: 'hubGrantedServiceId',\n description: 'Connection facts and capability-free serviceId claims.',\n },\n {\n /**\n * Connection facts (unsigned). The participant pulls, on each\n * (re)connect, the topology facts the hub has decided for this\n * connection:\n *\n * - `grantedServiceIdNamespace` — the absolute serviceId region this\n * connection's provenance may claim (anything at/under it). May be\n * the empty string, meaning \"claim nothing freely\".\n */\n get: requestType(\n object({}),\n object({\n grantedServiceIdNamespace: string(),\n }),\n ),\n\n /**\n * The serviceId prefix the hub mounts its own global services under\n * (default `'hub'`). Use it to address the admin-gated\n * `<hubServiceId>::hubServiceIdRegistry::registerServiceId` door and the\n * hub's reflection endpoints. Served at the connection root (unsigned),\n * so a participant can learn it without first knowing where the hub\n * lives.\n */\n getHubServiceId: requestType(\n object({}),\n object({\n hubServiceId: string(),\n }),\n ),\n\n /**\n * Claim a serviceId prefix **within** this connection's provenance-\n * granted namespace (see `get().grantedServiceIdNamespace`). No\n * capability needed — the grant already happened out-of-band at attach\n * time. The requested `serviceId` must equal the granted namespace or be\n * nested beneath it; anything else is rejected (claim it through the\n * admin-gated `<hubServiceId>::hubServiceIdRegistry::registerServiceId`\n * door instead).\n */\n register: requestType(\n object({ serviceId: string().check(minLength(1)) }),\n object({}),\n ),\n },\n);\n\n/**\n * `hubAccess::request` — a consumer (e.g. a sandboxed web editor) asks the\n * hub for scoped access to one or more services.\n *\n * Consumers describe their needs as named *dependencies* (\"slots\"): each\n * slot lists the interfaces the chosen service must implement and the\n * methods the consumer wants to call. The hub resolves candidate services\n * from its participant directory, then forwards the bundle to a host-\n * supplied handler (see `Hub` options) that shows the user a single\n * prompt. The handler picks a concrete service per slot and approves a\n * subset of the requested methods.\n *\n * In v1 the response carries only the resolution (which serviceId was\n * chosen per slot). Once the hub holds a signing identity it will also\n * return a `SignedCapability` the consumer attaches to subsequent calls.\n * Until then, dispatch is not gated on the grant — see plan-access.md.\n */\nexport const hubAccessInterface = defineInterface(\n {\n id: 'hubAccess',\n description: 'Consumer requests scoped access to services on the hub.',\n },\n {\n request: requestType(\n object({\n consumer: zConsumer,\n dependencies: record(string(), zSlotRequest),\n duration: optional(zDuration),\n }),\n discriminatedUnion('status', [\n object({\n status: literal('granted'),\n slots: record(string(), zResolvedSlot),\n /**\n * One or more `SignedCapability`s issued by the hub\n * (audience = consumer NodeId). Consumers attach them\n * via `$hubrpc.capabilities` on subsequent calls. Empty\n * array is legal (hub with no signing identity / tests).\n *\n * Plural so a handler can return per-slot caps with\n * different `exp`/caveats; typical handlers return a\n * single cap covering every granted member.\n */\n capabilities: array(zSignedCapability),\n }),\n object({\n status: literal('denied'),\n reason: optional(string()),\n }),\n object({\n status: literal('noCandidates'),\n /** Slot ids that have zero matching candidates. */\n slots: array(string()),\n }),\n ]),\n ),\n\n /**\n * Service-pinned widening of an existing grant. The consumer asks\n * the hub for additional members on a `serviceId` they already\n * deal with — same audience (their NodeId) and (intended) same\n * `rootIssuer` as the prior grant. The hub never picks the service\n * for the consumer here: `serviceId` is an input, not a result.\n *\n * On grant the response carries a fresh `SignedCapability` whose\n * attenuations cover **only** the granted delta. Bag-compatible\n * with the prior cap; combine via `merge` (when it exists) or\n * just keep both in `$hubrpc.capabilities`.\n *\n * Distinguished from `request` so the consent UI can render a\n * different affordance (\"X already has read access on `github`,\n * grant `update` as well?\" instead of from-zero selection).\n *\n * TODO(hub-ledger): enforce \"consumer has prior history on this\n * service\" — see `hub.ts:_handleAccessExtend`. v1 forwards\n * directly to the host's `onAccessExtend` without consulting the\n * `_grants` ledger; this is by design while we settle on the\n * persistence model.\n */\n extend: requestType(\n object({\n consumer: zConsumer,\n /**\n * Service to widen the grant on. MUST equal the\n * `serviceId` of a previously-issued attenuation for this\n * consumer NodeId. v1 does not validate this.\n */\n serviceId: string(),\n /**\n * The delta. Same shape as `request`'s slot.members. Each\n * entry MUST refer to an interface the consumer has been\n * introduced to on `serviceId` via a prior `request`.\n */\n added: array(zMemberRequest),\n duration: optional(zDuration),\n }),\n discriminatedUnion('status', [\n object({\n status: literal('granted'),\n serviceId: string(),\n /** Members actually granted (handler may approve a subset). */\n granted: array(\n object({\n interfaceId: string(),\n member: zMemberPattern,\n }),\n ),\n /**\n * Capability covering only the granted delta. Audience\n * = consumer NodeId. Bag-compatible with the prior\n * cap on this service.\n */\n capabilities: optional(array(zSignedCapability)),\n }),\n object({\n status: literal('denied'),\n reason: optional(string()),\n }),\n // TODO(hub-ledger): additional variants once the host's\n // grant ledger is persisted:\n // - { status: \"unknownService\", serviceId }\n // - { status: \"unknownInterface\", serviceId, interfaceIds }\n // Reject without ever prompting the user when the consumer\n // is asking to widen a service it has no prior grant on.\n ]),\n ),\n\n /**\n * `hubAccess::requestAccess` — direct capability request. The\n * consumer specifies the exact attenuations it wants. No\n * service-discovery, no candidate resolution: the consumer\n * already knows which `(serviceId, interfaceId, members)` it\n * needs, including wildcards (e.g. `serviceId: { prefix: \"\" }`\n * to ask for an interface anywhere).\n *\n * Compared to `request`:\n * - `request` does directory-based discovery, picks one service\n * per slot, and returns a cap pinned to that service. Use\n * when the consumer says \"give me SOME service that does X\".\n * - `requestAccess` is verbatim. Use when the consumer says\n * \"give me exactly these attenuations\". Especially useful for\n * reflection (`hubrpc.directory::list` on any service) and\n * for on-demand per-method grants from an explorer-style UI.\n *\n * The user prompt shows the exact `Capability` the hub will sign\n * on Allow, same byte-equality guarantee as `request`/`extend`.\n */\n requestAccess: requestType(\n object({\n consumer: zConsumer,\n permissions: array(zRequestPermission).check(minLength(1)),\n duration: optional(zDuration),\n }),\n discriminatedUnion('status', [\n object({\n status: literal('granted'),\n capabilities: array(zSignedCapability),\n }),\n object({\n status: literal('denied'),\n reason: optional(string()),\n }),\n ]),\n ),\n },\n);\n\n// ════════════════════════════════════════════════════════════════════════\n// hubAccessManifest — the declarative twin of `hubAccess`.\n// ════════════════════════════════════════════════════════════════════════\n\n/**\n * The root issuer(s) whose signature the participant will accept on the minted\n * capability. A capability is only usable by its holder if it chains to a root\n * that the *target* service trusts — and not every service trusts every root.\n * So the participant declares, per entry, which minting root(s) would produce a\n * cap it can actually use; the admin must mint with one of them (or decline).\n *\n * Tri-state, by design:\n * - **omitted** (`undefined`) — unspecified. The participant doesn't know /\n * doesn't constrain the root; the admin picks. (The common, lenient default.)\n * - **non-empty** (`[\"id:abc\", …]`) — mint with one of *these* roots; a cap\n * rooted elsewhere is useless to the participant.\n * - **empty** (`[]`) — \"no root is acceptable\", i.e. nothing can satisfy this.\n * Almost certainly a bug (it can never be granted); kept representable so a\n * tool can detect and flag it rather than silently treating it as \"any\".\n */\nconst zAcceptableRootIds = array(string());\n\n/**\n * A desired access entry the participant publishes. Two kinds, mirroring the\n * two discovery-vs-verbatim halves of `hubAccess`:\n *\n * - `discover` — \"find me a service implementing X\" (same shape as a\n * `hubAccess::request` slot). The admin resolves one concrete service.\n * - `direct` — \"grant exactly these permissions\" (same shape as\n * `hubAccess::requestAccess`). The serviceId is already in each permission's\n * target; no discovery.\n */\nconst zManifestRequest = discriminatedUnion('kind', [\n extend(zSlotRequest, {\n kind: literal('discover'),\n /** Who is asking; `principal` is the audience minted caps must target. */\n consumer: zConsumer,\n /** Human rationale shown to the admin (\"seal tokens at rest\"). */\n reason: optional(string()),\n duration: optional(zDuration),\n /** Root issuer(s) that must mint this cap. See {@link zAcceptableRootIds}. */\n acceptableRootIds: optional(zAcceptableRootIds),\n /**\n * Host-authored provenance bag, stamped at `registerHubAccessAtRoot`\n * (NOT taken from the consumer's request params — so it cannot be\n * spoofed). An open `record<string, unknown>` so a host can attach\n * whatever origin info it observed about the requesting transport (e.g.\n * `{ sourceTransportId }` to route a local consent UI). Meaningful only\n * to a consumer that KNOWS this manifest is local; an aggregating\n * manifest MUST re-scope or omit it.\n */\n origin: optional(record(string(), unknown())),\n }),\n object({\n kind: literal('direct'),\n /** Who is asking; `principal` is the audience minted caps must target. */\n consumer: zConsumer,\n reason: optional(string()),\n permissions: array(zRequestPermission).check(minLength(1)),\n duration: optional(zDuration),\n /** Root issuer(s) that must mint this cap. See {@link zAcceptableRootIds}. */\n acceptableRootIds: optional(zAcceptableRootIds),\n /** Host-authored provenance bag. See the `discover` variant. */\n origin: optional(record(string(), unknown())),\n }),\n]);\n\n/**\n * The current (granted) value of one entry. For `discover` it carries the single\n * chosen service (mirroring `hubAccess::request`'s resolved slot) plus the caps;\n * for `direct` just the caps (the serviceId is in each permission's target).\n */\nconst zGrantedEntry = discriminatedUnion('kind', [\n extend(zResolvedSlot, {\n kind: literal('discover'),\n capabilities: array(zSignedCapability),\n }),\n object({\n kind: literal('direct'),\n capabilities: array(zSignedCapability),\n }),\n]);\n\n/**\n * The current value of one entry: `granted` (caps, plus the chosen service for\n * `discover`) or `denied` (with an optional reason). An entry *absent* from the\n * current document is **undecided** — distinct from a terminal `denied`, which\n * lets an approver say \"no\" (and why) instead of silently withholding.\n */\nconst zCurrentEntry = discriminatedUnion('status', [\n object({ status: literal('granted'), granted: zGrantedEntry }),\n object({ status: literal('denied'), reason: optional(string()) }),\n]);\n\n/**\n * `hubAccessManifest` — the declarative twin of {@link hubAccessInterface}.\n *\n * Where `hubAccess` is the imperative, just-in-time door a consumer *calls* (and\n * the hub serves at the connection root), `hubAccessManifest` is **served by the\n * participant** under its own serviceId, so it appears in `hubrpc.directory::list`\n * — its very presence is the request. A participant publishes its DESIRED access\n * entries (keyed by id, like `request`'s `dependencies`); an admin discovers\n * them via the directory, mints capabilities **with its own identity** (a\n * configured hub capability root), and writes the CURRENT/granted state back via\n * patches. The participant reads the granted entries and attaches the caps to\n * its later calls.\n *\n * Reconcile model (desired vs. current), aligned with the hub's other surfaces:\n * - `getDesired` / `watchDesired` — the participant's declared needs.\n * - `getCurrent` / `setCurrent` / `watchCurrent` — the admin-written grants.\n *\n * `watch*` follows the coarse empty-tick convention of `hubrpc.directory::watch`:\n * a tick means \"re-`get` now\", keeping the server stateless (no per-item deltas).\n */\nexport const hubAccessManifestInterface = defineInterface(\n {\n id: 'hubAccessManifest',\n description:\n 'Participant-served declarative access: publish DESIRED access entries '\n + '(discover | direct), an admin mints capabilities and writes the CURRENT '\n + 'granted state back. The reconcile twin of hubAccess::{request,requestAccess}.',\n },\n {\n // ---- desired (participant-authored; the admin reads) ----------------\n /** The full desired document: who is asking and the entries it wants. */\n getDesired: requestType(\n object({}),\n object({\n /**\n * entryId → desired entry, each carrying its own `consumer`. A\n * per-participant manifest's entries all share one consumer; a hub\n * broker aggregates entries from many consumers in one flat map.\n */\n requested: record(string(), zManifestRequest),\n /** Bumped whenever `requested` changes; lets watchers dedupe ticks. */\n revision: number(),\n }),\n ).withStream({ client: object({}) }),\n /**\n * Coarse change tap on the desired document. Emits an empty tick when\n * `requested` may have changed; the caller re-`getDesired`. Resolves when\n * the caller cancels. Mirrors `hubrpc.directory::watch`.\n */\n watchDesired: requestType(\n object({}),\n object({}),\n ).withStream({ server: object({}) }),\n\n // ---- current / granted (admin-authored; the participant reads) ------\n /** The full current document: entryId → granted/denied (absent ⇒ undecided). */\n getCurrent: requestType(\n object({}),\n object({\n /** entryId → current value (granted | denied). */\n current: record(string(), zCurrentEntry),\n /** Bumped on every successful `setCurrent`. */\n revision: number(),\n }),\n ),\n /**\n * Apply patches to the current document. Replace the whole document with\n * `{ op: 'set', path: '', value }`, or a single entry with\n * `{ op: 'set', path: '/current/<entryId>', value }` (a `zCurrentEntry`:\n * granted or denied). Admin-only in practice (gated by a capability\n * rooted at an accepted issuer).\n */\n setCurrent: requestType(\n object({ patches: array(zManifestPatch) }),\n object({ revision: number() }),\n ).withStream({ client: object({}) }),\n /**\n * Coarse change tap on the current document. Emits an empty tick when\n * `current` may have changed; the participant re-`getCurrent` and applies\n * any new capabilities. Resolves when the caller cancels.\n */\n watchCurrent: requestType(\n object({}),\n object({}),\n ).withStream({ server: object({}) }),\n },\n);\n\n/**\n * The typed client shape of {@link hubAccessManifestInterface} — the exact\n * object `connection.get(hubAccessManifestInterface)` (or\n * `connection.service(id).get(...)`) returns. Approvers depend on this contract,\n * not on a concrete connection: the same approver runs against a local in-memory\n * host (loopback connection), a remote hub's served manifest, or a future\n * aggregating implementation with no code change.\n */\nexport type IHubAccessManifest = InterfaceClient<typeof hubAccessManifestInterface>;\n\n/** One typed entry from `hubAccessManifest::getDesired().requested`. */\nexport type HubAccessManifestRequest =\n Awaited<ReturnType<IHubAccessManifest['getDesired']>>['requested'][string];\n\n/** One typed value accepted at `/current/<entryId>` by `setCurrent`. */\nexport type HubAccessManifestDecision =\n Awaited<ReturnType<IHubAccessManifest['getCurrent']>>['current'][string];\n"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
|
-
import { Fr as SignedCapability } from "./linkRpcConnection-
|
|
5
|
+
import { Fr as SignedCapability } from "./linkRpcConnection-CzF2DvFS.js";
|
|
6
6
|
//#region src/hub/common/hubAccess.d.ts
|
|
7
7
|
/**
|
|
8
8
|
* The client-facing wire shapes of the hub's capability-negotiation protocol
|
|
@@ -76,4 +76,4 @@ declare function findCoveringCapabilities(capabilities: readonly SignedCapabilit
|
|
|
76
76
|
}): readonly SignedCapability[] | undefined;
|
|
77
77
|
//#endregion
|
|
78
78
|
export { HubAccessResult as a, HubAccessRequest as i, HubAccessPattern as n, findCoveringCapabilities as o, HubAccessPermission as r, HubAccessDuration as t };
|
|
79
|
-
//# sourceMappingURL=hubAccess-
|
|
79
|
+
//# sourceMappingURL=hubAccess-BHfWpT7f.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hubAccess-
|
|
1
|
+
{"version":3,"file":"hubAccess-BHfWpT7f.d.ts","names":[],"sources":["../../src/hub/common/hubAccess.ts"],"mappings":";;;;;;;;;;;;;KAqBY;;KAGA;WAA8B;;WAA8B;;;UAGvD;WACJ;aACI,WAAW;aACX,aAAa;aACb;aACA,kBAAkB;;;WAGtB;;WAEA;WACA,SAAS;;;UAIL;WACJ;aAAqB;aAAuB;aAA0B;;;WAEtE,sBAAsB;;WAEtB,WAAW;;;;;;;KAQZ;WAEK;;WAEA,uBAAuB;;WAEvB;;WAEA;WAA2B;;WAC3B;WAAyB;;;;;;;;;;iBAU1B,yBACZ,uBAAuB,oBACvB,oBAAoB,uBACpB;WACa;WACA;aAEL"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
|
-
import { r as hubGrantedServiceIdInterface } from "./hub.interfaces-
|
|
5
|
+
import { r as hubGrantedServiceIdInterface } from "./hub.interfaces-CN-Bz7Kj.js";
|
|
6
6
|
//#region src/hub/client/hubFacade.ts
|
|
7
7
|
/**
|
|
8
8
|
* Thin façade over the hub's own serviceId-routed services
|
|
@@ -82,4 +82,4 @@ function hubFromConnection(connection) {
|
|
|
82
82
|
//#endregion
|
|
83
83
|
export { hubFromConnection as n, Hub as t };
|
|
84
84
|
|
|
85
|
-
//# sourceMappingURL=hubFacade-
|
|
85
|
+
//# sourceMappingURL=hubFacade-BK0LDuxF.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hubFacade-
|
|
1
|
+
{"version":3,"file":"hubFacade-BK0LDuxF.js","names":[],"sources":["../../src/hub/client/hubFacade.ts"],"sourcesContent":["import type { LinkRpcConnection } from '../../connection/linkRpcConnection';\nimport { hubGrantedServiceIdInterface } from '../common/hub.interfaces';\n\n/**\n * Description of the services a consumer will need, presented to the user\n * up-front so access can be approved before the first call. Each named\n * *dependency* (\"slot\") lists the interfaces the chosen service must\n * implement and the members the consumer intends to call.\n */\nexport interface SlotPrerequest {\n readonly consumer: { readonly name: string; readonly purpose: string; };\n readonly dependencies: Readonly<\n Record<string, {\n readonly interfaces: readonly { readonly id: string; }[];\n readonly members: readonly {\n readonly interfaceId: string;\n readonly member: { readonly exact: string; };\n }[];\n }>\n >;\n}\n\n/** Handle for a set of slots reserved via {@link Hub.prerequestSlots}. */\nexport interface SlotReservation {\n /** Resolve the reserved slots into connected service clients. */\n resolve(): Promise<never>;\n}\n\n/**\n * Thin façade over the hub's own serviceId-routed services\n * (`hubGrantedServiceId`, `hubAccess`, …). The hub is not a single object on\n * the wire — it is a set of services the connection can `get`. This groups the\n * common connection calls so callers don't hand-route interface clients.\n */\nexport class Hub {\n constructor(private readonly _connection: LinkRpcConnection) { }\n\n /** `hubGrantedServiceId::get` — where am I and which serviceId namespace may I claim. */\n public getConnectionInfo() {\n return this._connection.get(hubGrantedServiceIdInterface).get({});\n }\n\n /**\n * Reserve the slots a consumer will need so the user can approve them\n * ahead of the first call. NOT YET IMPLEMENTED by the hub — the\n * returned reservation throws on {@link SlotReservation.resolve}.\n */\n public prerequestSlots(_request: SlotPrerequest): SlotReservation {\n return {\n resolve: async (): Promise<never> => {\n throw new Error('not implemented: hub.prerequestSlots / slot resolution');\n },\n };\n }\n\n /**\n * Claim a serviceId namespace for this connection so it may register\n * services under it, via `hubGrantedServiceId::register`.\n *\n * The claim is auto-granted from the connection's provenance when\n * `serviceIdNamespace` is at/under the connection's\n * `grantedServiceIdNamespace` (see {@link getConnectionInfo}) — no\n * signature or capability needed. Claims **outside** the granted namespace\n * are rejected here; route those through the admin-gated\n * `<hubServiceId>::hubServiceIdRegistry::registerServiceId` door instead.\n *\n * Resolves once the namespace is owned by this connection (idempotent for\n * a namespace this connection already owns). Rejects when the hub denies\n * the claim — e.g. the namespace is owned by another identity, or the\n * caller lacks admin authority outside its granted namespace.\n *\n * `requestPermissionIfDenied` is a hint for the future interactive\n * admin-approval flow; the hub does not yet expose one, so denial always\n * surfaces as a rejection regardless of the flag.\n */\n public async registerServiceIdNamespace(\n serviceIdNamespace: string,\n _opts?: { readonly requestPermissionIfDenied?: boolean; },\n ): Promise<void> {\n await this._connection\n .get(hubGrantedServiceIdInterface)\n .register({ serviceId: serviceIdNamespace });\n }\n\n /**\n * Claim this connection's provenance-granted serviceId namespace in one\n * step: read `grantedServiceIdNamespace` via {@link getConnectionInfo},\n * then {@link registerServiceIdNamespace} it, returning the claimed\n * `serviceId` so the caller can mount services under it.\n *\n * This is the batteries-included version of the\n * `hubGrantedServiceId::get` → `hubGrantedServiceId::register` dance a\n * service-exposing participant performs right after connecting.\n *\n * Throws when the hub granted this connection no namespace (an empty\n * `grantedServiceIdNamespace`, meaning \"claim nothing freely\") — there is\n * nothing to register, and any service prefix would have to be claimed\n * through the admin-gated\n * `<hubServiceId>::hubServiceIdRegistry::registerServiceId` door instead.\n */\n public async claimGrantedServiceIdNamespace(): Promise<{ readonly serviceId: string; }> {\n const { grantedServiceIdNamespace } = await this.getConnectionInfo();\n if (!grantedServiceIdNamespace) {\n throw new Error(\n 'hubGrantedServiceId::get returned an empty granted namespace; ' +\n 'nothing to claim (register a prefix through the admin-gated ' +\n 'hubServiceIdRegistry::registerServiceId door instead)',\n );\n }\n await this.registerServiceIdNamespace(grantedServiceIdNamespace);\n return { serviceId: grantedServiceIdNamespace };\n }\n}\n\n/** Wrap a connection in a {@link Hub} façade. */\nexport function hubFromConnection(connection: LinkRpcConnection): Hub {\n return new Hub(connection);\n}\n"],"mappings":";;;;;;;;;;;;AAkCA,IAAa,MAAb,MAAiB;CACgB;CAA7B,YAAY,aAAiD;EAAhC,KAAA,cAAA;CAAkC;;CAG/D,oBAA2B;EACvB,OAAO,KAAK,YAAY,IAAI,4BAA4B,CAAC,CAAC,IAAI,CAAC,CAAC;CACpE;;;;;;CAOA,gBAAuB,UAA2C;EAC9D,OAAO,EACH,SAAS,YAA4B;GACjC,MAAM,IAAI,MAAM,wDAAwD;EAC5E,EACJ;CACJ;;;;;;;;;;;;;;;;;;;;;CAsBA,MAAa,2BACT,oBACA,OACa;EACb,MAAM,KAAK,YACN,IAAI,4BAA4B,CAAC,CACjC,SAAS,EAAE,WAAW,mBAAmB,CAAC;CACnD;;;;;;;;;;;;;;;;;CAkBA,MAAa,iCAA2E;EACpF,MAAM,EAAE,8BAA8B,MAAM,KAAK,kBAAkB;EACnE,IAAI,CAAC,2BACD,MAAM,IAAI,MACN,iLAGJ;EAEJ,MAAM,KAAK,2BAA2B,yBAAyB;EAC/D,OAAO,EAAE,WAAW,0BAA0B;CAClD;AACJ;;AAGA,SAAgB,kBAAkB,YAAoC;CAClE,OAAO,IAAI,IAAI,UAAU;AAC7B"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
|
-
import { a as LinkRpcConnection } from "./linkRpcConnection-
|
|
5
|
+
import { a as LinkRpcConnection } from "./linkRpcConnection-CzF2DvFS.js";
|
|
6
6
|
//#region src/hub/client/hubFacade.d.ts
|
|
7
7
|
/**
|
|
8
8
|
* Description of the services a consumer will need, presented to the user
|
|
@@ -98,4 +98,4 @@ declare class Hub {
|
|
|
98
98
|
declare function hubFromConnection(connection: LinkRpcConnection): Hub;
|
|
99
99
|
//#endregion
|
|
100
100
|
export { hubFromConnection as i, SlotPrerequest as n, SlotReservation as r, Hub as t };
|
|
101
|
-
//# sourceMappingURL=hubFacade-
|
|
101
|
+
//# sourceMappingURL=hubFacade-CmacIwKr.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hubFacade-
|
|
1
|
+
{"version":3,"file":"hubFacade-CmacIwKr.d.ts","names":[],"sources":["../../src/hub/client/hubFacade.ts"],"mappings":";;;;;;;;;;;;UASiB;WACJ;aAAqB;aAAuB;;WAC5C,cAAc,SACnB;aACa;eAAgC;;aAChC;eACI;eACA;iBAAmB;;;;;;UAO3B;;EAEb,WAAW;;;;;;;;cASF;mBACoB;EAAA,YAAA,aAAa;;EAGnC,qBAAiB;;;;;;;;EASjB,gBAAgB,UAAU,iBAAiB;;;;;;;;;;;;;;;;;;;;;EA4BrC,2BACT,4BACA;aAAmB;MACpB;;;;;;;;;;;;;;;;;EAsBU,kCAAkC;aAAmB;;;;iBAetD,kBAAkB,YAAY,oBAAoB"}
|