@immediately-run/preauth-core 0.1.6 → 0.1.7
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/dist/bootConsent.d.ts +12 -1
- package/dist/bootConsent.js +29 -2
- package/dist/capabilities.d.ts +8 -0
- package/dist/capabilities.js +12 -1
- package/dist/docLayout.d.ts +10 -0
- package/dist/docLayout.js +17 -1
- package/dist/m1PreAuth.d.ts +11 -2
- package/dist/m1PreAuth.js +15 -3
- package/dist/port.d.ts +20 -0
- package/package.json +1 -1
package/dist/bootConsent.d.ts
CHANGED
|
@@ -20,6 +20,11 @@ export interface MintResult {
|
|
|
20
20
|
* requested) — the post-boot caller lifts the frame cap on this alone, even
|
|
21
21
|
* if a mount selection failed (matching its historical behavior). */
|
|
22
22
|
netFetchOk: boolean;
|
|
23
|
+
/** Whether the plain app-scoped capability grant succeeded (vacuously true when
|
|
24
|
+
* none was requested), R3-233. False when caps were requested but the store has
|
|
25
|
+
* no `grantAppCapabilities` (fail-loud, never validate-then-drop) or the write
|
|
26
|
+
* threw — the caller lifts those frame caps only on `true`. */
|
|
27
|
+
capabilitiesOk: boolean;
|
|
23
28
|
/** Successfully minted per-selection space ids (for post-boot provisioning). */
|
|
24
29
|
minted: {
|
|
25
30
|
selection: ConsentSelection;
|
|
@@ -39,4 +44,10 @@ export type MintErrorSink = (ctx: string, err: unknown) => void;
|
|
|
39
44
|
* records which §11.4 declared mount it satisfies, so a later boot re-provisions
|
|
40
45
|
* it without re-consent.
|
|
41
46
|
*/
|
|
42
|
-
export declare function mintConsentedGrants(store: MintStore, uid: string, appKey: string, selections: readonly ConsentSelection[], netFetchHosts: readonly NetFetchHost[], mintPath?: MintPath, onError?: MintErrorSink
|
|
47
|
+
export declare function mintConsentedGrants(store: MintStore, uid: string, appKey: string, selections: readonly ConsentSelection[], netFetchHosts: readonly NetFetchHost[], mintPath?: MintPath, onError?: MintErrorSink,
|
|
48
|
+
/** PLAIN app-scoped on/off capabilities to grant (R3-233) — `task:invoke`,
|
|
49
|
+
* `llm:chat`, `contribute:self`, `diagnostics:read`. NOT `net:fetch` (host-
|
|
50
|
+
* parameterized — granted via `netFetchHosts` above); the caller (`applyPreAuth`)
|
|
51
|
+
* filters host-parameterized caps out. Defaults to none, so existing callers that
|
|
52
|
+
* only mint mounts + hosts are unaffected. */
|
|
53
|
+
capabilities?: readonly string[]): Promise<MintResult>;
|
package/dist/bootConsent.js
CHANGED
|
@@ -28,7 +28,13 @@ exports.mintConsentedGrants = mintConsentedGrants;
|
|
|
28
28
|
* records which §11.4 declared mount it satisfies, so a later boot re-provisions
|
|
29
29
|
* it without re-consent.
|
|
30
30
|
*/
|
|
31
|
-
async function mintConsentedGrants(store, uid, appKey, selections, netFetchHosts, mintPath = 'interactive', onError
|
|
31
|
+
async function mintConsentedGrants(store, uid, appKey, selections, netFetchHosts, mintPath = 'interactive', onError,
|
|
32
|
+
/** PLAIN app-scoped on/off capabilities to grant (R3-233) — `task:invoke`,
|
|
33
|
+
* `llm:chat`, `contribute:self`, `diagnostics:read`. NOT `net:fetch` (host-
|
|
34
|
+
* parameterized — granted via `netFetchHosts` above); the caller (`applyPreAuth`)
|
|
35
|
+
* filters host-parameterized caps out. Defaults to none, so existing callers that
|
|
36
|
+
* only mint mounts + hosts are unaffected. */
|
|
37
|
+
capabilities = []) {
|
|
32
38
|
let ok = true;
|
|
33
39
|
let netFetchOk = true;
|
|
34
40
|
if (netFetchHosts.length > 0) {
|
|
@@ -41,6 +47,27 @@ async function mintConsentedGrants(store, uid, appKey, selections, netFetchHosts
|
|
|
41
47
|
netFetchOk = false;
|
|
42
48
|
}
|
|
43
49
|
}
|
|
50
|
+
// Plain app-scoped capability grants (R3-233). Fail LOUD if asked to mint caps but
|
|
51
|
+
// the adapter has no `grantAppCapabilities` — a silent skip would resurrect the
|
|
52
|
+
// exact validate-then-drop bug this fixes.
|
|
53
|
+
let capabilitiesOk = true;
|
|
54
|
+
if (capabilities.length > 0) {
|
|
55
|
+
if (!store.grantAppCapabilities) {
|
|
56
|
+
onError?.('capability grant unsupported by this store', new Error('grantAppCapabilities not implemented'));
|
|
57
|
+
ok = false;
|
|
58
|
+
capabilitiesOk = false;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
try {
|
|
62
|
+
await store.grantAppCapabilities({ uid, appKey, capabilities, mintPath });
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
onError?.('capability grant failed', err);
|
|
66
|
+
ok = false;
|
|
67
|
+
capabilitiesOk = false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
44
71
|
const minted = [];
|
|
45
72
|
for (const sel of selections) {
|
|
46
73
|
try {
|
|
@@ -63,5 +90,5 @@ async function mintConsentedGrants(store, uid, appKey, selections, netFetchHosts
|
|
|
63
90
|
ok = false;
|
|
64
91
|
}
|
|
65
92
|
}
|
|
66
|
-
return { ok, netFetchOk, minted };
|
|
93
|
+
return { ok, netFetchOk, capabilitiesOk, minted };
|
|
67
94
|
}
|
package/dist/capabilities.d.ts
CHANGED
|
@@ -49,6 +49,14 @@ export declare function isBaseline(cap: Capability): boolean;
|
|
|
49
49
|
* per decision #1.) */
|
|
50
50
|
export declare const APP_SCOPED_CAPABILITIES: readonly Capability[];
|
|
51
51
|
export declare function isAppScoped(cap: Capability): boolean;
|
|
52
|
+
/** App-scoped caps whose durable authority is a PARAMETER SET minted on its own
|
|
53
|
+
* path — today only `net:fetch` (its granted host set, §5.11). These are granted
|
|
54
|
+
* by that path, never as a bare on/off capability: a bare `net:fetch` grant would
|
|
55
|
+
* be UNBOUNDED (every origin), so the plain-capability mint (R3-233) MUST exclude
|
|
56
|
+
* them. `task:invoke` is `parameterized` too but its bound is the app's manifest
|
|
57
|
+
* `invokes` (§5.8), not a durable grant param, so it IS a plain on/off grant. */
|
|
58
|
+
export declare const HOST_PARAMETERIZED_CAPABILITIES: readonly Capability[];
|
|
59
|
+
export declare function isHostParameterized(cap: Capability): boolean;
|
|
52
60
|
/** Compare dotted numeric versions: <0 if a<b, 0 if equal, >0 if a>b. Missing
|
|
53
61
|
* segments are treated as 0 ("1.2" === "1.2.0"); non-numeric segments as 0. */
|
|
54
62
|
export declare function compareVersions(a: string, b: string): number;
|
package/dist/capabilities.js
CHANGED
|
@@ -9,11 +9,12 @@
|
|
|
9
9
|
// with a view() projection on a channel (§8.3); actions are gated before the
|
|
10
10
|
// handler (§8.4). Parameterized capabilities additionally bound an argument set.
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.APP_SCOPED_CAPABILITIES = exports.BASELINE_CAPABILITIES = exports.REGISTRY_VERSION = exports.CAPABILITIES = void 0;
|
|
12
|
+
exports.HOST_PARAMETERIZED_CAPABILITIES = exports.APP_SCOPED_CAPABILITIES = exports.BASELINE_CAPABILITIES = exports.REGISTRY_VERSION = exports.CAPABILITIES = void 0;
|
|
13
13
|
exports.isKnownCapability = isKnownCapability;
|
|
14
14
|
exports.tierOf = tierOf;
|
|
15
15
|
exports.isBaseline = isBaseline;
|
|
16
16
|
exports.isAppScoped = isAppScoped;
|
|
17
|
+
exports.isHostParameterized = isHostParameterized;
|
|
17
18
|
exports.compareVersions = compareVersions;
|
|
18
19
|
exports.isSupportedCapability = isSupportedCapability;
|
|
19
20
|
exports.unsupportedCapabilities = unsupportedCapabilities;
|
|
@@ -234,6 +235,16 @@ exports.APP_SCOPED_CAPABILITIES = Object.keys(exports.CAPABILITIES).filter((c) =
|
|
|
234
235
|
function isAppScoped(cap) {
|
|
235
236
|
return exports.CAPABILITIES[cap].appScoped === true;
|
|
236
237
|
}
|
|
238
|
+
/** App-scoped caps whose durable authority is a PARAMETER SET minted on its own
|
|
239
|
+
* path — today only `net:fetch` (its granted host set, §5.11). These are granted
|
|
240
|
+
* by that path, never as a bare on/off capability: a bare `net:fetch` grant would
|
|
241
|
+
* be UNBOUNDED (every origin), so the plain-capability mint (R3-233) MUST exclude
|
|
242
|
+
* them. `task:invoke` is `parameterized` too but its bound is the app's manifest
|
|
243
|
+
* `invokes` (§5.8), not a durable grant param, so it IS a plain on/off grant. */
|
|
244
|
+
exports.HOST_PARAMETERIZED_CAPABILITIES = ['net:fetch'];
|
|
245
|
+
function isHostParameterized(cap) {
|
|
246
|
+
return exports.HOST_PARAMETERIZED_CAPABILITIES.includes(cap);
|
|
247
|
+
}
|
|
237
248
|
// ── §5.11 capability version gate (threat T26) ──────────────────────────────
|
|
238
249
|
//
|
|
239
250
|
// Each capability declares the lowest registry version that knows it (`since`).
|
package/dist/docLayout.d.ts
CHANGED
|
@@ -59,3 +59,13 @@ export declare const mergeNetFetchHosts: (existing: readonly NetFetchHost[], inc
|
|
|
59
59
|
* grant time is stamped ONCE, on first mint, and `netFetchLastUsedAt` refreshes
|
|
60
60
|
* on every (re-)consent). */
|
|
61
61
|
export declare const netFetchGrantFields: (mergedHosts: readonly NetFetchHost[], hadGrantedAt: boolean, s: MintSentinels) => Record<string, unknown>;
|
|
62
|
+
/** Union granted PLAIN app-scoped capability names (set semantics; sorted for a
|
|
63
|
+
* stable, byte-faithful document) — the "consent accumulates" merge for the
|
|
64
|
+
* R3-233 capability grant, mirroring {@link mergeNetFetchHosts}. */
|
|
65
|
+
export declare const mergeCapabilities: (existing: readonly string[], incoming: readonly string[]) => string[];
|
|
66
|
+
/** `user-app-spaces/{uid}/apps/{appKey}` — the durable granted PLAIN app-scoped
|
|
67
|
+
* capability set (merge), R3-233. Lives on the SAME appKey doc as the net:fetch
|
|
68
|
+
* grant so one read (`getAppGrantDoc`) yields both. `capabilitiesGrantedAt` is
|
|
69
|
+
* stamped ONCE (first mint); `capabilitiesLastUsedAt` refreshes on every
|
|
70
|
+
* (re-)consent — the §8.15 90-day-unused expiry clock, identical to net:fetch. */
|
|
71
|
+
export declare const appCapabilitiesGrantFields: (mergedCaps: readonly string[], hadGrantedAt: boolean, s: MintSentinels) => Record<string, unknown>;
|
package/dist/docLayout.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// `.set()`/`.update()` is the only thing each adapter does itself. Drift is then
|
|
17
17
|
// impossible without editing a helper both consume.
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.netFetchGrantFields = exports.mergeNetFetchHosts = exports.appSpaceGrantFields = exports.appKeyTouchFields = exports.appCountFields = exports.userCountFields = exports.ownerUserSpaceFields = exports.ownerMemberFields = exports.spaceDocFields = exports.appCountPath = exports.userCountPath = exports.appSpacePath = exports.appKeyPath = exports.userSpacePath = exports.memberPath = exports.spacePath = exports.defined = exports.granteeId = exports.GRANT_EXPIRY_MS = exports.grantKey = void 0;
|
|
19
|
+
exports.appCapabilitiesGrantFields = exports.mergeCapabilities = exports.netFetchGrantFields = exports.mergeNetFetchHosts = exports.appSpaceGrantFields = exports.appKeyTouchFields = exports.appCountFields = exports.userCountFields = exports.ownerUserSpaceFields = exports.ownerMemberFields = exports.spaceDocFields = exports.appCountPath = exports.userCountPath = exports.appSpacePath = exports.appKeyPath = exports.userSpacePath = exports.memberPath = exports.spacePath = exports.defined = exports.granteeId = exports.GRANT_EXPIRY_MS = exports.grantKey = void 0;
|
|
20
20
|
/** Stable per-user identifier for a grant `(appKey, spaceId)`, used as the value
|
|
21
21
|
* of a delegated grant's `parentGrantId`. `::` is delimiter-safe: `appKey` uses
|
|
22
22
|
* `__` separators and a Firestore `spaceId` is alphanumeric. */
|
|
@@ -158,3 +158,19 @@ const netFetchGrantFields = (mergedHosts, hadGrantedAt, s) => (0, exports.define
|
|
|
158
158
|
netFetchLastUsedAt: s.serverTimestamp(),
|
|
159
159
|
});
|
|
160
160
|
exports.netFetchGrantFields = netFetchGrantFields;
|
|
161
|
+
/** Union granted PLAIN app-scoped capability names (set semantics; sorted for a
|
|
162
|
+
* stable, byte-faithful document) — the "consent accumulates" merge for the
|
|
163
|
+
* R3-233 capability grant, mirroring {@link mergeNetFetchHosts}. */
|
|
164
|
+
const mergeCapabilities = (existing, incoming) => [...new Set([...existing, ...incoming])].sort();
|
|
165
|
+
exports.mergeCapabilities = mergeCapabilities;
|
|
166
|
+
/** `user-app-spaces/{uid}/apps/{appKey}` — the durable granted PLAIN app-scoped
|
|
167
|
+
* capability set (merge), R3-233. Lives on the SAME appKey doc as the net:fetch
|
|
168
|
+
* grant so one read (`getAppGrantDoc`) yields both. `capabilitiesGrantedAt` is
|
|
169
|
+
* stamped ONCE (first mint); `capabilitiesLastUsedAt` refreshes on every
|
|
170
|
+
* (re-)consent — the §8.15 90-day-unused expiry clock, identical to net:fetch. */
|
|
171
|
+
const appCapabilitiesGrantFields = (mergedCaps, hadGrantedAt, s) => (0, exports.defined)({
|
|
172
|
+
grantedCapabilities: [...mergedCaps],
|
|
173
|
+
capabilitiesGrantedAt: hadGrantedAt ? undefined : s.serverTimestamp(),
|
|
174
|
+
capabilitiesLastUsedAt: s.serverTimestamp(),
|
|
175
|
+
});
|
|
176
|
+
exports.appCapabilitiesGrantFields = appCapabilitiesGrantFields;
|
package/dist/m1PreAuth.d.ts
CHANGED
|
@@ -46,8 +46,17 @@ export interface PreAuthResult {
|
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
48
48
|
* The M1 write path: validate the requested capabilities against the §8.9 target
|
|
49
|
-
* check, then — only if clean — mint the mounts
|
|
50
|
-
*
|
|
49
|
+
* check, then — only if clean — mint the mounts, net:fetch hosts, AND the plain
|
|
50
|
+
* app-scoped on/off capabilities (`task:invoke`, `llm:chat`, `contribute:self`,
|
|
51
|
+
* `diagnostics:read`) as durable grants with `policy` provenance, through the same
|
|
52
|
+
* `mintConsentedGrants` M3 uses.
|
|
53
|
+
*
|
|
54
|
+
* R3-233: the `grantable` app-scoped caps used to be VALIDATED and then silently
|
|
55
|
+
* DROPPED (only mounts + hosts were minted), so pre-authorizing `task:invoke` /
|
|
56
|
+
* `llm:chat` reported success but granted nothing and the gate kept refusing. They
|
|
57
|
+
* are now actually minted. `net:fetch` is excluded from the plain-cap mint — it is
|
|
58
|
+
* host-parameterized and granted via `netFetchHosts` (a bare grant would be
|
|
59
|
+
* unbounded).
|
|
51
60
|
*
|
|
52
61
|
* Refusal is terminal and silent of side effects: when any requested capability
|
|
53
62
|
* is broad-elevated or unknown, the function mints NOTHING and returns the
|
package/dist/m1PreAuth.js
CHANGED
|
@@ -69,8 +69,17 @@ const isPreAuthClean = (plan) => plan.refused.length === 0;
|
|
|
69
69
|
exports.isPreAuthClean = isPreAuthClean;
|
|
70
70
|
/**
|
|
71
71
|
* The M1 write path: validate the requested capabilities against the §8.9 target
|
|
72
|
-
* check, then — only if clean — mint the mounts
|
|
73
|
-
*
|
|
72
|
+
* check, then — only if clean — mint the mounts, net:fetch hosts, AND the plain
|
|
73
|
+
* app-scoped on/off capabilities (`task:invoke`, `llm:chat`, `contribute:self`,
|
|
74
|
+
* `diagnostics:read`) as durable grants with `policy` provenance, through the same
|
|
75
|
+
* `mintConsentedGrants` M3 uses.
|
|
76
|
+
*
|
|
77
|
+
* R3-233: the `grantable` app-scoped caps used to be VALIDATED and then silently
|
|
78
|
+
* DROPPED (only mounts + hosts were minted), so pre-authorizing `task:invoke` /
|
|
79
|
+
* `llm:chat` reported success but granted nothing and the gate kept refusing. They
|
|
80
|
+
* are now actually minted. `net:fetch` is excluded from the plain-cap mint — it is
|
|
81
|
+
* host-parameterized and granted via `netFetchHosts` (a bare grant would be
|
|
82
|
+
* unbounded).
|
|
74
83
|
*
|
|
75
84
|
* Refusal is terminal and silent of side effects: when any requested capability
|
|
76
85
|
* is broad-elevated or unknown, the function mints NOTHING and returns the
|
|
@@ -81,6 +90,9 @@ async function applyPreAuth(store, uid, appKey, request, onError) {
|
|
|
81
90
|
if (!(0, exports.isPreAuthClean)(plan)) {
|
|
82
91
|
return { ok: false, refused: plan.refused };
|
|
83
92
|
}
|
|
84
|
-
|
|
93
|
+
// The plain on/off caps to mint: every grantable cap EXCEPT the host-parameterized
|
|
94
|
+
// ones (net:fetch), which are minted as their host set via `netFetchHosts`.
|
|
95
|
+
const plainCaps = plan.grantable.filter((c) => !(0, capabilities_1.isHostParameterized)(c));
|
|
96
|
+
const mint = await (0, bootConsent_1.mintConsentedGrants)(store, uid, appKey, request.mounts, request.netFetchHosts, 'policy', onError, plainCaps);
|
|
85
97
|
return { ok: mint.ok, refused: [], mint };
|
|
86
98
|
}
|
package/dist/port.d.ts
CHANGED
|
@@ -68,6 +68,19 @@ export interface GrantNetFetchParams {
|
|
|
68
68
|
appKey: string;
|
|
69
69
|
hosts: readonly NetFetchHost[];
|
|
70
70
|
}
|
|
71
|
+
/** Parameters for `MintStore.grantAppCapabilities` — the durable §8.7 grant of a
|
|
72
|
+
* set of PLAIN (on/off) app-scoped elevated capabilities for one (user, app):
|
|
73
|
+
* `task:invoke`, `llm:chat`, `contribute:self`, `diagnostics:read` (R3-233). NOT
|
|
74
|
+
* `net:fetch` — that is host-parameterized and granted via {@link GrantNetFetchParams};
|
|
75
|
+
* a bare `net:fetch` grant would be unbounded. The set unions into the app's
|
|
76
|
+
* granted-capability set (consent accumulates), mirroring net:fetch hosts. */
|
|
77
|
+
export interface GrantAppCapabilitiesParams {
|
|
78
|
+
uid: string;
|
|
79
|
+
appKey: string;
|
|
80
|
+
capabilities: readonly string[];
|
|
81
|
+
/** §8.15 provenance; defaults to `interactive` when omitted. */
|
|
82
|
+
mintPath?: MintPath;
|
|
83
|
+
}
|
|
71
84
|
/**
|
|
72
85
|
* The mint port: exactly the methods `mintConsentedGrants` calls. Every backend
|
|
73
86
|
* (browser Firestore, admin Firestore, the in-memory test double) implements
|
|
@@ -81,4 +94,11 @@ export interface MintStore {
|
|
|
81
94
|
grantSpaceToApp(params: GrantSpaceParams): Promise<void>;
|
|
82
95
|
/** Union the given net:fetch hosts into the app's consented host set. */
|
|
83
96
|
grantNetFetchHosts(params: GrantNetFetchParams): Promise<void>;
|
|
97
|
+
/** Union the given PLAIN app-scoped capabilities into the app's granted set
|
|
98
|
+
* (R3-233). **Optional** so existing {@link MintStore} implementers (the backend
|
|
99
|
+
* `AdminMintStore`) keep compiling; when a caller asks to mint plain caps and an
|
|
100
|
+
* adapter does not implement this, `mintConsentedGrants` fails LOUD (reports
|
|
101
|
+
* `capabilitiesOk:false`) rather than silently dropping them — the exact
|
|
102
|
+
* validate-then-drop bug R3-233 fixes. */
|
|
103
|
+
grantAppCapabilities?(params: GrantAppCapabilitiesParams): Promise<void>;
|
|
84
104
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@immediately-run/preauth-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "The shared §8.9 pre-auth target check + the single grant-mint path (mintConsentedGrants) + the capability vocabulary + the byte-faithful grant/space/net-fetch document layout. Consumed by site-main (browser Firestore) and the backend (admin Firestore) so there is ONE gate, ONE mint path, ONE wire layout.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|