@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 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
@@ -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; testrun spec §9.2.5).
100
- * v0 is host-level; the human-checkpoint review workflow wraps this later.
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
- /** Idempotent; journaled. Jurisdiction is fixed here forever (K-7). */
124
- provisionScope(input: ProvisionScopeInput): Promise<void>;
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. */
@@ -1 +1 @@
1
- {"version":3,"file":"scope-host.d.ts","sourceRoot":"","sources":["../src/scope-host.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scope-host.d.ts","sourceRoot":"","sources":["../src/scope-host.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ulid.d.ts","sourceRoot":"","sources":["../src/ulid.ts"],"names":[],"mappings":"AASA,wBAAgB,IAAI,CAAC,GAAG,GAAE,MAAmB,GAAG,MAAM,CAWrD"}
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 bits, Crockford base32.
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 = now;
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 b of rand)
14
- r += B32[b % 32]; // 256 % 32 === 0 → uniform
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,qEAAqE;AACrE,gEAAgE;AAMhE,MAAM,GAAG,GAAG,kCAAkC,CAAC;AAE/C,MAAM,UAAU,IAAI,CAAC,MAAc,IAAI,CAAC,GAAG,EAAE;IAC3C,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,IAAI,CAAC,GAAG,GAAG,CAAC;IACZ,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,MAAM,IAAI,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;IACxD,IAAI,CAAC,GAAG,EAAE,CAAC;IACX,KAAK,MAAM,CAAC,IAAI,IAAI;QAAE,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,2BAA2B;IACnE,OAAO,EAAE,GAAG,CAAC,CAAC;AAChB,CAAC"}
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.2.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.2.0"
25
+ "@substrat-run/contracts": "^0.3.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "typescript": "^5.6.0"