@substrat-run/kernel 0.1.0 → 0.2.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 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/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { ConsumerHandler, HostAdmin, ModuleRegistration, OperationContext, OperationHandler, ProvisionScopeInput, ScopedSql, ScopeHost, ScopeStub, SqlMigration, SqlValue, } from './scope-host.js';
1
+ export type { ConsumerHandler, GuardPredicate, HostAdmin, ModuleRegistration, OperationContext, OperationHandler, ProvisionScopeInput, ScopedSql, ScopeHost, ScopeStub, SqlMigration, SqlValue, } from './scope-host.js';
2
2
  export { assertAllowed, denyAllChecker, PermissionDenied, UNSAFE_allowAllChecker, } from './permission-checker.js';
3
3
  export type { PermissionChecker } from './permission-checker.js';
4
4
  export { ulid } from './ulid.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,SAAS,EACT,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,SAAS,EACT,SAAS,EACT,SAAS,EACT,YAAY,EACZ,QAAQ,GACT,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,cAAc,EACd,SAAS,EACT,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,SAAS,EACT,SAAS,EACT,SAAS,EACT,YAAY,EACZ,QAAQ,GACT,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,OAAO,EACL,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,EACL,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC"}
@@ -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
  *
@@ -65,24 +65,111 @@ export interface SqlMigration {
65
65
  * idempotent. Ordering is guaranteed only within (scope, module) — K-11.
66
66
  */
67
67
  export type ConsumerHandler = (ctx: OperationContext, event: DomainEvent) => void | Promise<void>;
68
+ /**
69
+ * A named, manifest-wired pre-condition on an operation (K-17; engine-protocol
70
+ * §6, open question 11). One module CONTRIBUTES a predicate under a name; a
71
+ * (usually different) module's manifest WIRES it to an operation via
72
+ * `guards: [{ before, predicate, config }]`. The kernel runs it inside the
73
+ * guarded operation's own transaction, immediately before the handler:
74
+ *
75
+ * throw → the operation is BLOCKED and the transaction rolls back (fail closed)
76
+ * return → the handler runs
77
+ *
78
+ * `config` is the manifest's config object, opaque to the kernel and parsed by
79
+ * the predicate itself; `input` is the (already structured-cloned) operation
80
+ * input. A predicate is a READ: it must not mutate — it is a gate, not a hook.
81
+ * Star topology holds — the guarded engine knows nothing of the guarding one.
82
+ */
83
+ export type GuardPredicate = (ctx: OperationContext, config: Record<string, unknown>, input: unknown) => void | Promise<void>;
68
84
  export interface ModuleRegistration {
69
85
  manifest: ModuleManifest;
70
86
  migrations?: SqlMigration[];
71
87
  operations?: Record<string, OperationHandler<never, unknown>>;
72
88
  /** eventType → handler; the types must appear in manifest.events.consumes. */
73
89
  consumers?: Record<string, ConsumerHandler>;
90
+ /**
91
+ * Named guard predicates this module contributes to the host — the code half
92
+ * of `manifest.guards`. Names are module-namespaced like operations
93
+ * ('protocol/all-signed'). Predicate names are global: two modules may not
94
+ * contribute the same name.
95
+ */
96
+ predicates?: Record<string, GuardPredicate>;
74
97
  }
75
98
  /**
76
- * Admin surface for enforcement input (design doc §4; testrun spec §9.2.5).
77
- * 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.)
78
119
  */
79
120
  export interface HostAdmin {
80
- defineRole(tenantId: TenantId, role: RoleDefinition): void;
81
- assignRole(assignment: RoleAssignment): void;
82
- 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>;
83
124
  /** Grant to an organization (portal customers); members reach it via membership tuples. */
84
- grantToOrg(orgId: string, permission: PermissionKey, node: Node, entity?: EntityRef): void;
85
- 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[]>;
86
173
  }
87
174
  export interface ProvisionScopeInput {
88
175
  tenantId: TenantId;
@@ -97,8 +184,13 @@ export interface ScopeHost {
97
184
  * resolves to another tenant's scope.
98
185
  */
99
186
  getScope(principal: PrincipalId, tenantId: TenantId, scopeId: ScopeId): Promise<ScopeStub>;
100
- /** Idempotent; journaled. Jurisdiction is fixed here forever (K-7). */
101
- 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>;
102
194
  /** Enforcement-input writes: roles, assignments, grants, membership. */
103
195
  readonly admin: HostAdmin;
104
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.1.0",
3
+ "version": "0.2.1",
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.1.0"
25
+ "@substrat-run/contracts": "^0.2.1"
26
26
  },
27
27
  "devDependencies": {
28
28
  "typescript": "^5.6.0"