@substrat-run/kernel 0.2.0 → 0.3.0
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 +2 -0
- package/dist/scope-host.d.ts +79 -10
- package/dist/scope-host.d.ts.map +1 -1
- package/dist/ulid.d.ts.map +1 -1
- package/dist/ulid.js +44 -5
- package/dist/ulid.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -9,6 +9,8 @@ imports no platform APIs — Cloudflare specifics live only in adapters, and eve
|
|
|
9
9
|
adapter must pass the same conformance suite
|
|
10
10
|
([`@substrat-run/contract-tests`](https://npmjs.com/package/@substrat-run/contract-tests)).
|
|
11
11
|
|
|
12
|
+
**Full documentation: https://substrat.ahlstrand.es/reference/kernel**
|
|
13
|
+
|
|
12
14
|
## The scope-host contract
|
|
13
15
|
|
|
14
16
|
A *scope* is one isolation domain (a BRF, a filial, a brand) with its own SQLite
|
package/dist/scope-host.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CapabilityGrant, Decision, DomainEvent, DomainEventInput, EntityRef, Jurisdiction, ModuleManifest, Node, PermissionKey, PrincipalId, RoleAssignment, RoleDefinition, ScopeId, StorageShape, TenantId } from '@substrat-run/contracts';
|
|
1
|
+
import type { AdminLogEntry, CapabilityGrant, CreateTenantInput, Decision, DomainEvent, DomainEventInput, EntityRef, IdentityLink, Jurisdiction, ModuleManifest, Node, PermissionKey, PlatformActorId, PrincipalId, ResolvedIdentity, RoleAssignment, RoleDefinition, ScopeId, StorageShape, Tenant, TenantId, TenantStatus } from '@substrat-run/contracts';
|
|
2
2
|
/**
|
|
3
3
|
* The scope-host contract — the adapter seam (§5.1 of the design doc).
|
|
4
4
|
*
|
|
@@ -96,16 +96,80 @@ export interface ModuleRegistration {
|
|
|
96
96
|
predicates?: Record<string, GuardPredicate>;
|
|
97
97
|
}
|
|
98
98
|
/**
|
|
99
|
-
* Admin surface for enforcement input (design doc §4;
|
|
100
|
-
*
|
|
99
|
+
* Admin surface for enforcement input (design doc §4; control-plane.md §4.4).
|
|
100
|
+
*
|
|
101
|
+
* Every mutation is a control-plane action: it takes a `PlatformActorId` — the
|
|
102
|
+
* authenticated staff subject, typed distinctly from a tenant `PrincipalId` so
|
|
103
|
+
* the compiler refuses to confuse them — and writes an append-only audit row
|
|
104
|
+
* stamped platform-side (actor, action, target, before/after, timestamp). The
|
|
105
|
+
* actor is never a principal in any tenant, and the record is never supplied by
|
|
106
|
+
* the caller. This is the one surface that must not be retrofitted (K-20): a
|
|
107
|
+
* surface that can act without a durable record of who acted is worse than none.
|
|
108
|
+
*
|
|
109
|
+
* Locally the actor is a dev stub (control-plane.md §6); real staff auth (SSO,
|
|
110
|
+
* MFA) gates EXPOSING this surface, not building it — D-16 cashed in.
|
|
111
|
+
*
|
|
112
|
+
* The whole surface is ASYNCHRONOUS (every method returns a Promise) because a
|
|
113
|
+
* durable/remote control plane — e.g. a Cloudflare Durable Object — cannot be
|
|
114
|
+
* backed synchronously: reads may cross an RPC boundary and writes must await a
|
|
115
|
+
* durable record before returning. The second adapter surfaced this (D-14); a
|
|
116
|
+
* synchronous admin interface could not be honoured by anything but an in-memory
|
|
117
|
+
* store, so the contract is async everywhere. (`registerModule`/`defineOperation`
|
|
118
|
+
* stay sync — they are code-time bookkeeping, not control-plane state.)
|
|
101
119
|
*/
|
|
102
120
|
export interface HostAdmin {
|
|
103
|
-
defineRole(tenantId: TenantId, role: RoleDefinition): void
|
|
104
|
-
assignRole(assignment: RoleAssignment): void
|
|
105
|
-
grant(grant: CapabilityGrant): void
|
|
121
|
+
defineRole(actor: PlatformActorId, tenantId: TenantId, role: RoleDefinition): Promise<void>;
|
|
122
|
+
assignRole(actor: PlatformActorId, assignment: RoleAssignment): Promise<void>;
|
|
123
|
+
grant(actor: PlatformActorId, grant: CapabilityGrant): Promise<void>;
|
|
106
124
|
/** Grant to an organization (portal customers); members reach it via membership tuples. */
|
|
107
|
-
grantToOrg(orgId: string, permission: PermissionKey, node: Node, entity?: EntityRef): void
|
|
108
|
-
addMember(tenantId: TenantId, principal: PrincipalId, orgId: string): void
|
|
125
|
+
grantToOrg(actor: PlatformActorId, orgId: string, permission: PermissionKey, node: Node, entity?: EntityRef): Promise<void>;
|
|
126
|
+
addMember(actor: PlatformActorId, tenantId: TenantId, principal: PrincipalId, orgId: string): Promise<void>;
|
|
127
|
+
/**
|
|
128
|
+
* Persist a tenant. Idempotent on the id — re-creating an existing tenant is a
|
|
129
|
+
* no-op, not an error (control-plane.md §4.1). `status` starts `active` and
|
|
130
|
+
* `createdAt` is stamped host-side. This is what replaces "a tenant is a ULID
|
|
131
|
+
* nobody used before" with a real record.
|
|
132
|
+
*/
|
|
133
|
+
createTenant(actor: PlatformActorId, input: CreateTenantInput): Promise<void>;
|
|
134
|
+
/**
|
|
135
|
+
* Transition a tenant's status. `suspended` fails `getScope` closed for every
|
|
136
|
+
* scope under the tenant (K-3's path) — the containment lever for non-payment
|
|
137
|
+
* or an incident, reversible without deleting anything.
|
|
138
|
+
*/
|
|
139
|
+
setTenantStatus(actor: PlatformActorId, tenantId: TenantId, status: TenantStatus): Promise<void>;
|
|
140
|
+
/** The tenant registry — the directory's inventory (control-plane.md §4.5 console item 1). */
|
|
141
|
+
listTenants(): Promise<Tenant[]>;
|
|
142
|
+
getTenant(tenantId: TenantId): Promise<Tenant | undefined>;
|
|
143
|
+
/** active → suspended. Reversible containment (incident, dispute). */
|
|
144
|
+
suspendScope(actor: PlatformActorId, tenantId: TenantId, scopeId: ScopeId): Promise<void>;
|
|
145
|
+
/** suspended → active. */
|
|
146
|
+
unsuspendScope(actor: PlatformActorId, tenantId: TenantId, scopeId: ScopeId): Promise<void>;
|
|
147
|
+
/** active|suspended → archived. Stops the active-scope meter (§9). */
|
|
148
|
+
archiveScope(actor: PlatformActorId, tenantId: TenantId, scopeId: ScopeId): Promise<void>;
|
|
149
|
+
/**
|
|
150
|
+
* archived → active. A RESTORE, never a flag flip (control-plane.md §4.2):
|
|
151
|
+
* §9's meter can only charge on "active scope" if un-archiving is a deliberate,
|
|
152
|
+
* audited act. Jurisdiction is untouched — it is fixed at provisioning (K-7).
|
|
153
|
+
*/
|
|
154
|
+
unarchiveScope(actor: PlatformActorId, tenantId: TenantId, scopeId: ScopeId): Promise<void>;
|
|
155
|
+
/** Turn a SKU flag on for a tenant. Idempotent; audited. */
|
|
156
|
+
grantEntitlement(actor: PlatformActorId, tenantId: TenantId, entitlementKey: string): Promise<void>;
|
|
157
|
+
/** Turn it off. A tenant's scopes lose access to that module's operations. */
|
|
158
|
+
revokeEntitlement(actor: PlatformActorId, tenantId: TenantId, entitlementKey: string): Promise<void>;
|
|
159
|
+
/** The tenant's held SKU flags (control-plane.md §5 meter 2). */
|
|
160
|
+
listEntitlements(tenantId: TenantId): Promise<string[]>;
|
|
161
|
+
/** Bind an external identity to a principal + home node. Idempotent on (provider, externalId); audited. */
|
|
162
|
+
linkIdentity(actor: PlatformActorId, input: IdentityLink): Promise<void>;
|
|
163
|
+
/** Resolve an external identity to its principal + home node — the auth adapter's read path. */
|
|
164
|
+
resolveIdentity(provider: string, externalId: string): Promise<ResolvedIdentity | undefined>;
|
|
165
|
+
/**
|
|
166
|
+
* The append-only admin audit trail, newest-comparable last (ULID order is
|
|
167
|
+
* chronological). Read path for the console history and the permission-diff
|
|
168
|
+
* human checkpoint (control-plane.md §4.5).
|
|
169
|
+
*/
|
|
170
|
+
auditLog(filter?: {
|
|
171
|
+
tenantId?: TenantId;
|
|
172
|
+
}): Promise<AdminLogEntry[]>;
|
|
109
173
|
}
|
|
110
174
|
export interface ProvisionScopeInput {
|
|
111
175
|
tenantId: TenantId;
|
|
@@ -120,8 +184,13 @@ export interface ScopeHost {
|
|
|
120
184
|
* resolves to another tenant's scope.
|
|
121
185
|
*/
|
|
122
186
|
getScope(principal: PrincipalId, tenantId: TenantId, scopeId: ScopeId): Promise<ScopeStub>;
|
|
123
|
-
/**
|
|
124
|
-
|
|
187
|
+
/**
|
|
188
|
+
* The entry scope-lifecycle transition (control-plane.md §4.2): idempotent,
|
|
189
|
+
* journaled, audited. Requires an existing ACTIVE tenant — a scope with no
|
|
190
|
+
* tenant record is the "tenant is an FK string" hole §4.1 closes, so it fails
|
|
191
|
+
* closed. Jurisdiction is fixed here forever (K-7).
|
|
192
|
+
*/
|
|
193
|
+
provisionScope(actor: PlatformActorId, input: ProvisionScopeInput): Promise<void>;
|
|
125
194
|
/** Enforcement-input writes: roles, assignments, grants, membership. */
|
|
126
195
|
readonly admin: HostAdmin;
|
|
127
196
|
/** Register a module: validates the manifest, applies migrations lazily per scope. */
|
package/dist/scope-host.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope-host.d.ts","sourceRoot":"","sources":["../src/scope-host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,QAAQ,EACR,WAAW,EACX,gBAAgB,EAChB,SAAS,EACT,YAAY,EACZ,cAAc,EACd,IAAI,EACJ,aAAa,EACb,WAAW,EACX,cAAc,EACd,cAAc,EACd,OAAO,EACP,YAAY,EACZ,QAAQ,
|
|
1
|
+
{"version":3,"file":"scope-host.d.ts","sourceRoot":"","sources":["../src/scope-host.ts"],"names":[],"mappings":"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"}
|
package/dist/ulid.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ulid.d.ts","sourceRoot":"","sources":["../src/ulid.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ulid.d.ts","sourceRoot":"","sources":["../src/ulid.ts"],"names":[],"mappings":"AAqCA,wBAAgB,IAAI,CAAC,GAAG,GAAE,MAAmB,GAAG,MAAM,CAwBrD"}
|
package/dist/ulid.js
CHANGED
|
@@ -1,17 +1,56 @@
|
|
|
1
|
-
// Minimal ULID: 48-bit timestamp + 80 random
|
|
1
|
+
// Minimal ULID: 48-bit timestamp + 80-bit random, Crockford base32.
|
|
2
2
|
// Kept dependency-free; kernel IDs must be sortable and opaque.
|
|
3
|
+
//
|
|
4
|
+
// MONOTONIC within a process (the ULID spec's monotonic factory): two IDs minted
|
|
5
|
+
// in the same millisecond still sort in creation order — the low bits increment
|
|
6
|
+
// instead of being re-randomized. This is load-bearing: the audit log and the
|
|
7
|
+
// event outbox both document "ULID order is chronological" and order by id, so a
|
|
8
|
+
// non-monotonic id would make same-millisecond rows sort randomly.
|
|
3
9
|
const B32 = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
|
|
10
|
+
// Monotonic state (per process/isolate — exactly the scope where a single writer
|
|
11
|
+
// orders its own rows). The random part is held as 16 base32 digits (0–31).
|
|
12
|
+
let lastTime = -1;
|
|
13
|
+
const lastRand = new Array(16).fill(0);
|
|
14
|
+
function freshRandom() {
|
|
15
|
+
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
16
|
+
for (let i = 0; i < 16; i++)
|
|
17
|
+
lastRand[i] = bytes[i] % 32; // 256 % 32 === 0 → uniform
|
|
18
|
+
}
|
|
19
|
+
/** Step the 80-bit random part by one, with carry. Returns false on overflow. */
|
|
20
|
+
function incrementRandom() {
|
|
21
|
+
for (let i = 15; i >= 0; i--) {
|
|
22
|
+
if (lastRand[i] < 31) {
|
|
23
|
+
lastRand[i]++;
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
lastRand[i] = 0;
|
|
27
|
+
}
|
|
28
|
+
return false; // all digits were 31 — overflowed (astronomically rare)
|
|
29
|
+
}
|
|
4
30
|
export function ulid(now = Date.now()) {
|
|
31
|
+
let time = now;
|
|
32
|
+
if (time <= lastTime) {
|
|
33
|
+
// Same or backwards clock: keep the last timestamp and step the random part
|
|
34
|
+
// so the id still increases. On the (impossible) overflow, bump the ms.
|
|
35
|
+
time = lastTime;
|
|
36
|
+
if (!incrementRandom()) {
|
|
37
|
+
time = lastTime + 1;
|
|
38
|
+
freshRandom();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
freshRandom();
|
|
43
|
+
}
|
|
44
|
+
lastTime = time;
|
|
5
45
|
let ts = '';
|
|
6
|
-
let t =
|
|
46
|
+
let t = time;
|
|
7
47
|
for (let i = 0; i < 10; i++) {
|
|
8
48
|
ts = B32[t % 32] + ts;
|
|
9
49
|
t = Math.floor(t / 32);
|
|
10
50
|
}
|
|
11
|
-
const rand = crypto.getRandomValues(new Uint8Array(16));
|
|
12
51
|
let r = '';
|
|
13
|
-
for (const
|
|
14
|
-
r += B32[
|
|
52
|
+
for (const d of lastRand)
|
|
53
|
+
r += B32[d];
|
|
15
54
|
return ts + r;
|
|
16
55
|
}
|
|
17
56
|
//# sourceMappingURL=ulid.js.map
|
package/dist/ulid.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ulid.js","sourceRoot":"","sources":["../src/ulid.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"ulid.js","sourceRoot":"","sources":["../src/ulid.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,gEAAgE;AAChE,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAChF,8EAA8E;AAC9E,iFAAiF;AACjF,mEAAmE;AAMnE,MAAM,GAAG,GAAG,kCAAkC,CAAC;AAE/C,iFAAiF;AACjF,4EAA4E;AAC5E,IAAI,QAAQ,GAAG,CAAC,CAAC,CAAC;AAClB,MAAM,QAAQ,GAAa,IAAI,KAAK,CAAS,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAEzD,SAAS,WAAW;IAClB,MAAM,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;IACzD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE;QAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAE,GAAG,EAAE,CAAC,CAAC,2BAA2B;AACxF,CAAC;AAED,iFAAiF;AACjF,SAAS,eAAe;IACtB,KAAK,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,IAAI,QAAQ,CAAC,CAAC,CAAE,GAAG,EAAE,EAAE,CAAC;YACtB,QAAQ,CAAC,CAAC,CAAE,EAAE,CAAC;YACf,OAAO,IAAI,CAAC;QACd,CAAC;QACD,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC,CAAC,wDAAwD;AACxE,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,MAAc,IAAI,CAAC,GAAG,EAAE;IAC3C,IAAI,IAAI,GAAG,GAAG,CAAC;IACf,IAAI,IAAI,IAAI,QAAQ,EAAE,CAAC;QACrB,4EAA4E;QAC5E,wEAAwE;QACxE,IAAI,GAAG,QAAQ,CAAC;QAChB,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;YACvB,IAAI,GAAG,QAAQ,GAAG,CAAC,CAAC;YACpB,WAAW,EAAE,CAAC;QAChB,CAAC;IACH,CAAC;SAAM,CAAC;QACN,WAAW,EAAE,CAAC;IAChB,CAAC;IACD,QAAQ,GAAG,IAAI,CAAC;IAEhB,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,IAAI,CAAC,GAAG,IAAI,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;QACtB,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IACzB,CAAC;IACD,IAAI,CAAC,GAAG,EAAE,CAAC;IACX,KAAK,MAAM,CAAC,IAAI,QAAQ;QAAE,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;IACtC,OAAO,EAAE,GAAG,CAAC,CAAC;AAChB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@substrat-run/kernel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Substrat kernel contracts and services — pure TypeScript, no platform imports (D-14)",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"dist"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@substrat-run/contracts": "^0.
|
|
25
|
+
"@substrat-run/contracts": "^0.3.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"typescript": "^5.6.0"
|