@happyvertical/smrt-app-runtime 0.55.16 → 0.55.17
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/AGENTS.md +7 -0
- package/README.md +7 -0
- package/dist/manifest.json +1 -1
- package/dist/smrt-knowledge.json +5 -5
- package/dist/sveltekit/handlers.d.ts +12 -0
- package/dist/sveltekit/handlers.d.ts.map +1 -1
- package/dist/sveltekit/index.d.ts +3 -2
- package/dist/sveltekit/index.d.ts.map +1 -1
- package/dist/sveltekit/owner-setup.d.ts +9 -0
- package/dist/sveltekit/owner-setup.d.ts.map +1 -1
- package/dist/sveltekit/sign-out.d.ts +17 -0
- package/dist/sveltekit/sign-out.d.ts.map +1 -0
- package/dist/sveltekit.js +70 -6
- package/dist/sveltekit.js.map +1 -1
- package/package.json +8 -8
package/AGENTS.md
CHANGED
|
@@ -128,6 +128,13 @@ Application infrastructure composition for the validated runtime profiles in
|
|
|
128
128
|
immediately after the claim commits (before cookie serialization or hooks);
|
|
129
129
|
`onOwnerClaimed` is for additional cleanup and
|
|
130
130
|
`removeOnboardingHandoff: false` is the explicit opt-out.
|
|
131
|
+
- `createSessionLayoutLoad()` adds `session.user` (`{ id, email, name? }`
|
|
132
|
+
copied from `locals.user`, else `null`). `createSignOutHandler(runtime)` is
|
|
133
|
+
the `POST` handler for `src/routes/sign-out/+server.ts`: 405 for other
|
|
134
|
+
methods, 403 unless `Sec-Fetch-Site: same-origin` or `Origin` equals the
|
|
135
|
+
request origin, then `destroySessionCookie` with the runtime's cookie
|
|
136
|
+
name/path/domain (cookie cleared even if the session row is gone); 204, or
|
|
137
|
+
303 to `/` when the request accepts HTML.
|
|
131
138
|
- Owner setup re-checks loopback peer and loopback URL host per request and
|
|
132
139
|
returns only fixed `{ code, message }` failures.
|
|
133
140
|
- `verifyLocalMcpToken` (local only) and `resolveMcpPrincipal` (hosted only:
|
package/README.md
CHANGED
|
@@ -178,8 +178,15 @@ export const GET = createRuntimeDiagnosticsHandler({ runtime, toolNames });
|
|
|
178
178
|
export const { load, actions } = createOwnerSetupPage(runtime);
|
|
179
179
|
// src/routes/+layout.server.ts
|
|
180
180
|
export const load = createSessionLayoutLoad();
|
|
181
|
+
// src/routes/sign-out/+server.ts
|
|
182
|
+
export const POST = createSignOutHandler(runtime);
|
|
181
183
|
```
|
|
182
184
|
|
|
185
|
+
`createSessionLayoutLoad()` returns `session.user` (`{ id, email, name? }` or
|
|
186
|
+
`null`). `createSignOutHandler` accepts only same-origin `POST` (405 / 403
|
|
187
|
+
otherwise), destroys the session, clears the cookie, and answers 204 (303 to
|
|
188
|
+
`/` for HTML form posts).
|
|
189
|
+
|
|
183
190
|
Diagnostics authorize (owner role or `runtime_diagnostics.read` on an active,
|
|
184
191
|
session-matching membership) before reading the runtime and return only stable
|
|
185
192
|
`{ schemaVersion: 1, error: { code } }` failures. Owner setup is local-only and
|
package/dist/manifest.json
CHANGED
package/dist/smrt-knowledge.json
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
"sensitiveFieldsExcluded": true,
|
|
4
4
|
"generatedAt": "1970-01-01T00:00:00.000Z",
|
|
5
5
|
"packageName": "@happyvertical/smrt-app-runtime",
|
|
6
|
-
"packageVersion": "0.55.
|
|
6
|
+
"packageVersion": "0.55.17",
|
|
7
7
|
"sourceManifestPath": "dist/manifest.json",
|
|
8
8
|
"agentDocPath": "AGENTS.md",
|
|
9
9
|
"sourceHashes": {
|
|
10
|
-
"manifest": "
|
|
11
|
-
"packageJson": "
|
|
12
|
-
"agents": "
|
|
10
|
+
"manifest": "231b8589458153523e7220b1dc48708577254b971ee629819310b1f124c2c31d",
|
|
11
|
+
"packageJson": "6482fcdf7bb1b91cc62d6a05e9abb81e88144ec65253fe1b606ff660b7001a3b",
|
|
12
|
+
"agents": "1f1cb24cd04164dc7735b4b1688dddd1a48dc6c544e0652644561bbbf806324c"
|
|
13
13
|
},
|
|
14
14
|
"exports": [
|
|
15
15
|
".",
|
|
@@ -66,5 +66,5 @@
|
|
|
66
66
|
"polymorphicAssociations": 0,
|
|
67
67
|
"uuidColumns": 0
|
|
68
68
|
},
|
|
69
|
-
"agentDoc": "# @happyvertical/smrt-app-runtime\n\nApplication infrastructure composition for the validated runtime profiles in\n`@happyvertical/smrt-config`.\n\n## Local profile\n\n- `validateApplicationId()` is the canonical strict validator for explicit app\n IDs. `encodeApplicationId()` derives a length-bounded, collision-resistant\n ID from package names; generated operational surfaces must share these APIs.\n- `initializeLocalApplicationRuntime()` owns user data paths, SQLite tuning,\n local application-secret creation, and the single-use owner bootstrap flow.\n- SQLite is acquired through `@happyvertical/sql`'s explicit\n `{ driver: 'node:sqlite', custody: 'trusted-parent' }` boundary after the\n runtime establishes its user-owned mode-0700 data root.\n- Application migrations are explicit through `prepareDatabase`; runtime never\n creates application model tables implicitly. A rejected `prepareDatabase` is\n normalized into `LocalRuntimeError` with the stable `migration_failed` code\n and the fixed recovery message `MIGRATION_FAILED_MESSAGE` (\"run pnpm\n app:setup and inspect the private migration logs\"). The caller's error text\n is never surfaced — it is retained only as a non-enumerable `cause` for\n private logs — and the same application root stays retryable.\n- Standalone migration commands must call `prepareLocalDatabaseStorage()`\n before opening SQLite so custody is established without creating schema or\n bootstrap records.\n- Read-only/copy/import operator commands must call\n `validateLocalDatabaseStorage()` before opening SQLite; it verifies the\n app-bound marker, source separation, real path chain, ownership, and mode\n without creating or repairing storage.\n- Owner bootstrap creates normal `Person`, `User`, `Tenant`, owner `Role` /\n `Membership`, and `Session` records in one transaction.\n- Bootstrap is loopback-only. Only an HMAC of the short-lived token is stored.\n A token that cannot claim fails a read-only preflight before role/catalog\n seeding (#3323); the claim transaction's conditional UPDATE stays the\n authority for single use, expiry, and concurrency.\n- Local MCP tokens (`mcpTokens`, `openLocalMcpTokenStore`, `smrt app token`):\n owner-bound, scoped, expiring (default 30 d, max 365 d), revocable. Only a\n domain-separated HMAC (application secret) is stored, in the runtime-owned\n `_smrt_local_mcp_tokens` system table (created like the bootstrap table;\n never application schema). Issue refuses scopes the owner lacks; verify\n returns token scopes ∩ live permissions and `null` for anything else.\n- Bearer binding is explicit (`tenantBinding`, set by the auth adapter;\n `direct-membership.ts`): `direct` (local tokens, default hosted resolver)\n needs an active direct membership in exactly the tenant; the default\n `direct-or-inherited` (hosted app mappings) applies\n `isSessionTenantAuthorized`'s rule. The direct row, when present, is pinned\n into `PermissionResolver`, so inheritance never substitutes for it.\n `verify()` principals (and their `scopes` arrays) are frozen;\n `runAsPrincipal` takes a validated, deep-frozen, null-prototype copy of\n the principal before any await (`plain-data.ts`: own data only; poison\n keys `__proto__`/`constructor`/`prototype`, accessors, inherited or\n non-plain data, sparse arrays and functions rejected), authorizes from it,\n and hands `fn` that snapshot. Token `issue()` input is read the same way.\n They carry\n `tenantBinding: 'direct'` and the\n `LOCAL_MCP_TOKEN_PRINCIPAL` brand; `runAsPrincipal` rejects a copy that\n widens the binding, so the two public APIs compose safely.\n Never log or return the token outside `issue()`. The operator store opens an\n uncached connection and takes no writer lease (revocation is per request).\n- Background jobs and application-defined paid capabilities are default-off.\n- The embedded runner reuses `TaskRunner`; it is not a second job contract.\n\n## Deployed profiles\n\n- `initializeDeployedApplicationRuntime()` composes only `self-hosted` and\n `cloud`; it rejects local initialization and provider/binding mismatches.\n- Provider bindings own credentials, vendor clients, and readiness checks.\n Runtime diagnostics contain selectors and status only.\n- The database binding must expose a provider-owned `close` callback before\n `connect` can run. The runtime owns that cleanup boundary and runs the\n application's explicit, idempotent `prepareDatabase` hook.\n- A failed startup cleanup throws `DeployedRuntimeCleanupError`; callers retain\n and retry its idempotent `retryCleanup()` boundary until it succeeds.\n- Public authentication, asset storage, and secret bindings are mandatory and\n readiness-checked before startup succeeds.\n- `createTaskWorker()` and `createScheduleWorker()` initialize the normal jobs\n package runners against the shared PostgreSQL database. They are intended for\n separate processes; the web process does not start them automatically. The\n worker process must import the app's compiled registration\n (`.smrt/runtime/register.js`, #3117) first; runners resolve `objectType`\n through the registry and never load application source themselves.\n- `close()` drains in-flight readiness/session/worker initialization and\n serialized runner start/stop operations, stops every runner returned by the\n runtime, and then closes PostgreSQL. Returned runners must not be restarted\n after runtime shutdown begins.\n- A tracked operation may await a re-entrant `close()` request without\n deadlocking; external `close()` callers still await complete cleanup.\n- `health()` is process liveness. `readiness()` probes database/auth/assets/\n secrets; it does not claim that an external worker fleet is running.\n- Database-provider readiness is additive; a PostgreSQL-specific server-version\n probe always gates startup and live readiness.\n- Cloud must keep required tenant context and must never introduce a root or\n unscoped tenant fallback. RLS remains an explicit deployment/migration choice.\n\n## SvelteKit entry (`./sveltekit`)\n\n- Server-only subpath; `@sveltejs/kit` is an optional peer and is externalized\n in `vite.config.ts` (kit recognizes `redirect`/`fail` by class identity).\n The root entry must never import kit or Svelte.\n- Without a `runtime` option the profile comes from smrt-config's\n `resolveEffectiveApplicationRuntime(await loadConfig())`, the same rule as\n `smrt app` (#3446): no block → `local`; `null`/`false`/`0`/`''` fail closed.\n- `createSmrtSvelteKitRuntime()` order is fixed: readiness gate → URL tenant\n selection (`locals.selectedTenant*`, never tenant context, headers ignored) →\n verified session (tenant context entered only when\n `isSessionTenantAuthorized()` accepts it, else unauthenticated; never via\n `createSessionHandler`, which re-runs `resolve` on downstream errors) →\n publish `locals.tenantContext` only for that verified tenant.\n- `database-rls` isolation forces `postgresRls: true`, including on\n `skipPaths` (anonymous principal). `resolve` runs at most once;\n session-layer failure before it is a 500 with cleared locals.\n- In an RLS request `classOptions()`/`databaseConfig()` return the\n transaction-bound request db (class `db` overrides win). Anything that\n outlives a request (session service, tenant selector) must use the base\n config, never these.\n- The local writer lease defaults to `defaultWriterLease()` (root\n `acquireWriterLease` over `prepareApplicationStateRoot`, presenting\n `SMRT_OPERATION_INSTANCE`); `acquireWriterLease` replaces it, `false` opts\n out, deployed profiles never take it. Provider readiness stays injected:\n apps pass `createProviderReadinessProbe`; deployed startup fails closed\n without `providerReadiness`.\n- `getCollection()` builds from `classOptions()` on every call (RLS request db\n inside a request); each generated route's embedded prelude imports the\n app's `smrt.ts` and reaches it through core's\n `createGeneratedCollectionAccess()` (a legacy accessor export wins,\n deprecated for one release).\n- Owner setup removes the `smrt app` onboarding hand-off files by default,\n immediately after the claim commits (before cookie serialization or hooks);\n `onOwnerClaimed` is for additional cleanup and\n `removeOnboardingHandoff: false` is the explicit opt-out.\n- Owner setup re-checks loopback peer and loopback URL host per request and\n returns only fixed `{ code, message }` failures.\n- `verifyLocalMcpToken` (local only) and `resolveMcpPrincipal` (hosted only:\n issuer/subject → `oidc_identities` → exactly one active user with exactly\n one active direct membership, else `null`) are the runtime's MCP credential\n bindings; pass the runtime as `mountMcpAppRoute({ runtime })` (or\n `createHostedMcpResourceAuth({ runtime })`).\n Token claims never select a tenant.\n- `resolveApplicationId()` / `runtimeConfigurationFingerprint()` must stay\n byte-compatible with process managers (golden vectors in tests).\n\n## Operator state (root entry)\n\n- One owner for the state shared by the web process and `smrt app`\n (`@happyvertical/smrt-cli/app` re-exports these): `resolveApplicationStateRoot`\n / `prepareApplicationStateRoot` (private app-bound mode-0700 root and\n marker), `withOperationLock` (`operation.lock`), `acquireWriterLease` /\n `readActiveWriterLease` (`writer.lease`), `reclaimStaleRecord`, and\n `createProviderReadinessProbe` (`SMRT_*_READINESS_MODULE`, resolved from the\n app root with Node's own ESM rules). File names, record formats, and\n messages are a cross-process contract; never change them unilaterally.\n- Tests that hook `node:fs` interleavings must live in this package: from a\n consumer the package is an externalized dependency a mock cannot reach.\n\n## Public runtime diagnostics\n\n- `projectRuntimeDiagnostics()` is the only public diagnostic projection. It\n constructs the schema-version-1 allowlist and never serializes the private\n local or deployed diagnostics objects.\n- The allowlist is limited to active profile/coarse health, schema and\n migration readiness, six fixed capability statuses, a sorted/capped public\n tool inventory plus digest, explicit operational topology differences,\n coarse worker heartbeat state, and eight stable code/timestamp errors.\n- Callers supply an explicit clock and worker-heartbeat seam. A missing\n heartbeat is `unknown`; web-process liveness never proves worker liveness.\n- Error ingestion maps unknown codes to `runtime_error`, truncates timestamps\n to the minute, and ignores messages, stacks, logs, records, and nested state.\n- Authentication and diagnostics-read authorization belong before every call\n to the projector or its runtime probes.\n\n## Invariants\n\n- Never expose application-secret bytes or bootstrap token hashes in diagnostics.\n- Never include migration or provider error text in a local or deployed startup\n error message; migration driver text is a likely credential carrier.\n- Never include provider error text, URLs, tokens, or credentials in deployed\n errors, diagnostics, health, or readiness payloads.\n- Keep local data, assets, database, and secrets outside the source tree.\n- Do not reach into collection/database private fields. Add an upstream public\n API if composition cannot be expressed through exported surfaces.\n- Keep profile selection out of domain models, generated APIs, MCP/WebMCP\n surfaces, effects, approvals, and authorization records.\n\n## Validation\n\n```bash\npnpm --filter @happyvertical/smrt-app-runtime test\npnpm --filter @happyvertical/smrt-app-runtime typecheck\npnpm --filter @happyvertical/smrt-app-runtime build\n```\n"
|
|
69
|
+
"agentDoc": "# @happyvertical/smrt-app-runtime\n\nApplication infrastructure composition for the validated runtime profiles in\n`@happyvertical/smrt-config`.\n\n## Local profile\n\n- `validateApplicationId()` is the canonical strict validator for explicit app\n IDs. `encodeApplicationId()` derives a length-bounded, collision-resistant\n ID from package names; generated operational surfaces must share these APIs.\n- `initializeLocalApplicationRuntime()` owns user data paths, SQLite tuning,\n local application-secret creation, and the single-use owner bootstrap flow.\n- SQLite is acquired through `@happyvertical/sql`'s explicit\n `{ driver: 'node:sqlite', custody: 'trusted-parent' }` boundary after the\n runtime establishes its user-owned mode-0700 data root.\n- Application migrations are explicit through `prepareDatabase`; runtime never\n creates application model tables implicitly. A rejected `prepareDatabase` is\n normalized into `LocalRuntimeError` with the stable `migration_failed` code\n and the fixed recovery message `MIGRATION_FAILED_MESSAGE` (\"run pnpm\n app:setup and inspect the private migration logs\"). The caller's error text\n is never surfaced — it is retained only as a non-enumerable `cause` for\n private logs — and the same application root stays retryable.\n- Standalone migration commands must call `prepareLocalDatabaseStorage()`\n before opening SQLite so custody is established without creating schema or\n bootstrap records.\n- Read-only/copy/import operator commands must call\n `validateLocalDatabaseStorage()` before opening SQLite; it verifies the\n app-bound marker, source separation, real path chain, ownership, and mode\n without creating or repairing storage.\n- Owner bootstrap creates normal `Person`, `User`, `Tenant`, owner `Role` /\n `Membership`, and `Session` records in one transaction.\n- Bootstrap is loopback-only. Only an HMAC of the short-lived token is stored.\n A token that cannot claim fails a read-only preflight before role/catalog\n seeding (#3323); the claim transaction's conditional UPDATE stays the\n authority for single use, expiry, and concurrency.\n- Local MCP tokens (`mcpTokens`, `openLocalMcpTokenStore`, `smrt app token`):\n owner-bound, scoped, expiring (default 30 d, max 365 d), revocable. Only a\n domain-separated HMAC (application secret) is stored, in the runtime-owned\n `_smrt_local_mcp_tokens` system table (created like the bootstrap table;\n never application schema). Issue refuses scopes the owner lacks; verify\n returns token scopes ∩ live permissions and `null` for anything else.\n- Bearer binding is explicit (`tenantBinding`, set by the auth adapter;\n `direct-membership.ts`): `direct` (local tokens, default hosted resolver)\n needs an active direct membership in exactly the tenant; the default\n `direct-or-inherited` (hosted app mappings) applies\n `isSessionTenantAuthorized`'s rule. The direct row, when present, is pinned\n into `PermissionResolver`, so inheritance never substitutes for it.\n `verify()` principals (and their `scopes` arrays) are frozen;\n `runAsPrincipal` takes a validated, deep-frozen, null-prototype copy of\n the principal before any await (`plain-data.ts`: own data only; poison\n keys `__proto__`/`constructor`/`prototype`, accessors, inherited or\n non-plain data, sparse arrays and functions rejected), authorizes from it,\n and hands `fn` that snapshot. Token `issue()` input is read the same way.\n They carry\n `tenantBinding: 'direct'` and the\n `LOCAL_MCP_TOKEN_PRINCIPAL` brand; `runAsPrincipal` rejects a copy that\n widens the binding, so the two public APIs compose safely.\n Never log or return the token outside `issue()`. The operator store opens an\n uncached connection and takes no writer lease (revocation is per request).\n- Background jobs and application-defined paid capabilities are default-off.\n- The embedded runner reuses `TaskRunner`; it is not a second job contract.\n\n## Deployed profiles\n\n- `initializeDeployedApplicationRuntime()` composes only `self-hosted` and\n `cloud`; it rejects local initialization and provider/binding mismatches.\n- Provider bindings own credentials, vendor clients, and readiness checks.\n Runtime diagnostics contain selectors and status only.\n- The database binding must expose a provider-owned `close` callback before\n `connect` can run. The runtime owns that cleanup boundary and runs the\n application's explicit, idempotent `prepareDatabase` hook.\n- A failed startup cleanup throws `DeployedRuntimeCleanupError`; callers retain\n and retry its idempotent `retryCleanup()` boundary until it succeeds.\n- Public authentication, asset storage, and secret bindings are mandatory and\n readiness-checked before startup succeeds.\n- `createTaskWorker()` and `createScheduleWorker()` initialize the normal jobs\n package runners against the shared PostgreSQL database. They are intended for\n separate processes; the web process does not start them automatically. The\n worker process must import the app's compiled registration\n (`.smrt/runtime/register.js`, #3117) first; runners resolve `objectType`\n through the registry and never load application source themselves.\n- `close()` drains in-flight readiness/session/worker initialization and\n serialized runner start/stop operations, stops every runner returned by the\n runtime, and then closes PostgreSQL. Returned runners must not be restarted\n after runtime shutdown begins.\n- A tracked operation may await a re-entrant `close()` request without\n deadlocking; external `close()` callers still await complete cleanup.\n- `health()` is process liveness. `readiness()` probes database/auth/assets/\n secrets; it does not claim that an external worker fleet is running.\n- Database-provider readiness is additive; a PostgreSQL-specific server-version\n probe always gates startup and live readiness.\n- Cloud must keep required tenant context and must never introduce a root or\n unscoped tenant fallback. RLS remains an explicit deployment/migration choice.\n\n## SvelteKit entry (`./sveltekit`)\n\n- Server-only subpath; `@sveltejs/kit` is an optional peer and is externalized\n in `vite.config.ts` (kit recognizes `redirect`/`fail` by class identity).\n The root entry must never import kit or Svelte.\n- Without a `runtime` option the profile comes from smrt-config's\n `resolveEffectiveApplicationRuntime(await loadConfig())`, the same rule as\n `smrt app` (#3446): no block → `local`; `null`/`false`/`0`/`''` fail closed.\n- `createSmrtSvelteKitRuntime()` order is fixed: readiness gate → URL tenant\n selection (`locals.selectedTenant*`, never tenant context, headers ignored) →\n verified session (tenant context entered only when\n `isSessionTenantAuthorized()` accepts it, else unauthenticated; never via\n `createSessionHandler`, which re-runs `resolve` on downstream errors) →\n publish `locals.tenantContext` only for that verified tenant.\n- `database-rls` isolation forces `postgresRls: true`, including on\n `skipPaths` (anonymous principal). `resolve` runs at most once;\n session-layer failure before it is a 500 with cleared locals.\n- In an RLS request `classOptions()`/`databaseConfig()` return the\n transaction-bound request db (class `db` overrides win). Anything that\n outlives a request (session service, tenant selector) must use the base\n config, never these.\n- The local writer lease defaults to `defaultWriterLease()` (root\n `acquireWriterLease` over `prepareApplicationStateRoot`, presenting\n `SMRT_OPERATION_INSTANCE`); `acquireWriterLease` replaces it, `false` opts\n out, deployed profiles never take it. Provider readiness stays injected:\n apps pass `createProviderReadinessProbe`; deployed startup fails closed\n without `providerReadiness`.\n- `getCollection()` builds from `classOptions()` on every call (RLS request db\n inside a request); each generated route's embedded prelude imports the\n app's `smrt.ts` and reaches it through core's\n `createGeneratedCollectionAccess()` (a legacy accessor export wins,\n deprecated for one release).\n- Owner setup removes the `smrt app` onboarding hand-off files by default,\n immediately after the claim commits (before cookie serialization or hooks);\n `onOwnerClaimed` is for additional cleanup and\n `removeOnboardingHandoff: false` is the explicit opt-out.\n- `createSessionLayoutLoad()` adds `session.user` (`{ id, email, name? }`\n copied from `locals.user`, else `null`). `createSignOutHandler(runtime)` is\n the `POST` handler for `src/routes/sign-out/+server.ts`: 405 for other\n methods, 403 unless `Sec-Fetch-Site: same-origin` or `Origin` equals the\n request origin, then `destroySessionCookie` with the runtime's cookie\n name/path/domain (cookie cleared even if the session row is gone); 204, or\n 303 to `/` when the request accepts HTML.\n- Owner setup re-checks loopback peer and loopback URL host per request and\n returns only fixed `{ code, message }` failures.\n- `verifyLocalMcpToken` (local only) and `resolveMcpPrincipal` (hosted only:\n issuer/subject → `oidc_identities` → exactly one active user with exactly\n one active direct membership, else `null`) are the runtime's MCP credential\n bindings; pass the runtime as `mountMcpAppRoute({ runtime })` (or\n `createHostedMcpResourceAuth({ runtime })`).\n Token claims never select a tenant.\n- `resolveApplicationId()` / `runtimeConfigurationFingerprint()` must stay\n byte-compatible with process managers (golden vectors in tests).\n\n## Operator state (root entry)\n\n- One owner for the state shared by the web process and `smrt app`\n (`@happyvertical/smrt-cli/app` re-exports these): `resolveApplicationStateRoot`\n / `prepareApplicationStateRoot` (private app-bound mode-0700 root and\n marker), `withOperationLock` (`operation.lock`), `acquireWriterLease` /\n `readActiveWriterLease` (`writer.lease`), `reclaimStaleRecord`, and\n `createProviderReadinessProbe` (`SMRT_*_READINESS_MODULE`, resolved from the\n app root with Node's own ESM rules). File names, record formats, and\n messages are a cross-process contract; never change them unilaterally.\n- Tests that hook `node:fs` interleavings must live in this package: from a\n consumer the package is an externalized dependency a mock cannot reach.\n\n## Public runtime diagnostics\n\n- `projectRuntimeDiagnostics()` is the only public diagnostic projection. It\n constructs the schema-version-1 allowlist and never serializes the private\n local or deployed diagnostics objects.\n- The allowlist is limited to active profile/coarse health, schema and\n migration readiness, six fixed capability statuses, a sorted/capped public\n tool inventory plus digest, explicit operational topology differences,\n coarse worker heartbeat state, and eight stable code/timestamp errors.\n- Callers supply an explicit clock and worker-heartbeat seam. A missing\n heartbeat is `unknown`; web-process liveness never proves worker liveness.\n- Error ingestion maps unknown codes to `runtime_error`, truncates timestamps\n to the minute, and ignores messages, stacks, logs, records, and nested state.\n- Authentication and diagnostics-read authorization belong before every call\n to the projector or its runtime probes.\n\n## Invariants\n\n- Never expose application-secret bytes or bootstrap token hashes in diagnostics.\n- Never include migration or provider error text in a local or deployed startup\n error message; migration driver text is a likely credential carrier.\n- Never include provider error text, URLs, tokens, or credentials in deployed\n errors, diagnostics, health, or readiness payloads.\n- Keep local data, assets, database, and secrets outside the source tree.\n- Do not reach into collection/database private fields. Add an upstream public\n API if composition cannot be expressed through exported surfaces.\n- Keep profile selection out of domain models, generated APIs, MCP/WebMCP\n surfaces, effects, approvals, and authorization records.\n\n## Validation\n\n```bash\npnpm --filter @happyvertical/smrt-app-runtime test\npnpm --filter @happyvertical/smrt-app-runtime typecheck\npnpm --filter @happyvertical/smrt-app-runtime build\n```\n"
|
|
70
70
|
}
|
|
@@ -70,8 +70,20 @@ export interface SessionLayoutData {
|
|
|
70
70
|
readonly activeTenantId: string | null;
|
|
71
71
|
/** URL-selected tenant candidate; display only, never authorization. */
|
|
72
72
|
readonly selectedTenantSlug: string | null;
|
|
73
|
+
/** Display identity of the signed-in user (id, email, name only). */
|
|
74
|
+
readonly user: SessionLayoutUser | null;
|
|
73
75
|
};
|
|
74
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* The only user fields the layout data ever carries. `name` appears only when
|
|
79
|
+
* `locals.user` carries one (the `User` model itself has no name; it lives on
|
|
80
|
+
* the linked profile), so consumers fall back to `email`.
|
|
81
|
+
*/
|
|
82
|
+
export interface SessionLayoutUser {
|
|
83
|
+
readonly id: string;
|
|
84
|
+
readonly email: string;
|
|
85
|
+
readonly name?: string;
|
|
86
|
+
}
|
|
75
87
|
/**
|
|
76
88
|
* Mount as `src/routes/+layout.server.ts`:
|
|
77
89
|
* `export const load = createSessionLayoutLoad();`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/sveltekit/handlers.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/sveltekit/handlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
* export const { handle, init } = runtime;
|
|
14
14
|
* ```
|
|
15
15
|
*/
|
|
16
|
-
export { createRuntimeDiagnosticsHandler, createRuntimeHealthHandler, createSessionLayoutLoad, type DiagnosticsPrincipalLocals, RUNTIME_DIAGNOSTICS_READ_PERMISSION, type RuntimeDiagnosticsHandlerOptions, type SessionLayoutData, } from './handlers.js';
|
|
17
|
-
export { createOwnerSetupPage, DEFAULT_OWNER_SETUP_MESSAGES, isLoopbackAddress, isLoopbackHostname, isLoopbackRequest, OWNER_SETUP_FIELDS, type OwnerSetupActionEvent, type OwnerSetupErrorCode, type OwnerSetupEvent, type OwnerSetupFailure, type OwnerSetupPage, type OwnerSetupPageData, type OwnerSetupPageOptions, } from './owner-setup.js';
|
|
16
|
+
export { createRuntimeDiagnosticsHandler, createRuntimeHealthHandler, createSessionLayoutLoad, type DiagnosticsPrincipalLocals, RUNTIME_DIAGNOSTICS_READ_PERMISSION, type RuntimeDiagnosticsHandlerOptions, type SessionLayoutData, type SessionLayoutUser, } from './handlers.js';
|
|
17
|
+
export { createOwnerSetupPage, DEFAULT_OWNER_SETUP_MESSAGES, isLoopbackAddress, isLoopbackHostname, isLoopbackRequest, OWNER_SETUP_FIELDS, type OwnerSetupActionEvent, type OwnerSetupErrorCode, type OwnerSetupEvent, type OwnerSetupFailure, type OwnerSetupLinkState, type OwnerSetupPage, type OwnerSetupPageData, type OwnerSetupPageOptions, } from './owner-setup.js';
|
|
18
18
|
export { type ApplicationRuntimeDiagnosticsOptions, authorizedTenantLocalsHandle, composeHandles, createSmrtSvelteKitRuntime, defaultWriterLease, type ProviderReadinessComponent, type ProviderReadinessFactory, type RuntimeHealthBody, type SmrtRuntimeBoundPrincipal, type SmrtRuntimeBoundPrincipalSnapshot, type SmrtRuntimeDeepReadonly, type SmrtRuntimeLocals, type SmrtRuntimeMcpIdentity, type SmrtRuntimeMcpPrincipalMapping, type SmrtRuntimeSessionCookie, type SmrtRuntimeSessionOptions, type SmrtSvelteKitRuntime, type SmrtSvelteKitRuntimeOptions, type TenantBindingMode, type WriterLease, type WriterLeaseContext, } from './runtime.js';
|
|
19
|
+
export { createSignOutHandler } from './sign-out.js';
|
|
19
20
|
export { createSubdomainTenantSelector, normalizeTenantSelection, type SubdomainTenantSelectorOptions, selectTenantSlug, type TenantSelection, type TenantSelectionEvent, type TenantSelector, } from './tenancy.js';
|
|
20
21
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sveltekit/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EACL,+BAA+B,EAC/B,0BAA0B,EAC1B,uBAAuB,EACvB,KAAK,0BAA0B,EAC/B,mCAAmC,EACnC,KAAK,gCAAgC,EACrC,KAAK,iBAAiB,GACvB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,oBAAoB,EACpB,4BAA4B,EAC5B,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,GAC3B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,KAAK,oCAAoC,EACzC,4BAA4B,EAC5B,cAAc,EACd,0BAA0B,EAC1B,kBAAkB,EAClB,KAAK,0BAA0B,EAC/B,KAAK,wBAAwB,EAC7B,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,iCAAiC,EACtC,KAAK,uBAAuB,EAC5B,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,8BAA8B,EACnC,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,kBAAkB,GACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,6BAA6B,EAC7B,wBAAwB,EACxB,KAAK,8BAA8B,EACnC,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sveltekit/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EACL,+BAA+B,EAC/B,0BAA0B,EAC1B,uBAAuB,EACvB,KAAK,0BAA0B,EAC/B,mCAAmC,EACnC,KAAK,gCAAgC,EACrC,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,GACvB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,oBAAoB,EACpB,4BAA4B,EAC5B,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,GAC3B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,KAAK,oCAAoC,EACzC,4BAA4B,EAC5B,cAAc,EACd,0BAA0B,EAC1B,kBAAkB,EAClB,KAAK,0BAA0B,EAC/B,KAAK,wBAAwB,EAC7B,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,iCAAiC,EACtC,KAAK,uBAAuB,EAC5B,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,8BAA8B,EACnC,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,kBAAkB,GACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EACL,6BAA6B,EAC7B,wBAAwB,EACxB,KAAK,8BAA8B,EACnC,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC"}
|
|
@@ -16,7 +16,16 @@ export interface OwnerSetupPageData {
|
|
|
16
16
|
readonly available: boolean;
|
|
17
17
|
/** One-time token from `?token=` (empty unless available). */
|
|
18
18
|
readonly token: string;
|
|
19
|
+
/**
|
|
20
|
+
* State of the setup link, so the page can explain a dead link instead of
|
|
21
|
+
* offering a form that cannot succeed. `ready`: a token is present and the
|
|
22
|
+
* invitation has not expired (the token itself is verified on submit);
|
|
23
|
+
* `missing`: no `?token=`; `expired`: the invitation lapsed; `unavailable`:
|
|
24
|
+
* claimed, disabled, or not a loopback request.
|
|
25
|
+
*/
|
|
26
|
+
readonly link: OwnerSetupLinkState;
|
|
19
27
|
}
|
|
28
|
+
export type OwnerSetupLinkState = 'ready' | 'missing' | 'expired' | 'unavailable';
|
|
20
29
|
/** Stable failure codes returned by the setup action. */
|
|
21
30
|
export type OwnerSetupErrorCode =
|
|
22
31
|
/** Not the local profile (HTTP 404). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"owner-setup.d.ts","sourceRoot":"","sources":["../../src/sveltekit/owner-setup.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,KAAK,aAAa,EAAkB,MAAM,eAAe,CAAC;AACnE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEzD,8CAA8C;AAC9C,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"owner-setup.d.ts","sourceRoot":"","sources":["../../src/sveltekit/owner-setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sign-out route handler: destroys the current session and clears its cookie
|
|
3
|
+
* with the same cookie attributes the owner-setup action used to set it.
|
|
4
|
+
*/
|
|
5
|
+
import { type RequestHandler } from '@sveltejs/kit';
|
|
6
|
+
import type { SmrtSvelteKitRuntime } from './runtime.js';
|
|
7
|
+
/**
|
|
8
|
+
* Mount as `src/routes/sign-out/+server.ts`:
|
|
9
|
+
* `export const POST = createSignOutHandler(runtime);`
|
|
10
|
+
*
|
|
11
|
+
* `POST` only (other methods answer 405) and same-origin only (403 otherwise).
|
|
12
|
+
* The session row is destroyed best-effort and the cookie is cleared even when
|
|
13
|
+
* the row is already gone. Answers 204, or a 303 to `/` when the request
|
|
14
|
+
* accepts HTML so a no-JS form post lands on the home page.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createSignOutHandler(runtime: Pick<SmrtSvelteKitRuntime, 'sessionCookie' | 'classOptions'>): RequestHandler;
|
|
17
|
+
//# sourceMappingURL=sign-out.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign-out.d.ts","sourceRoot":"","sources":["../../src/sveltekit/sign-out.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,KAAK,cAAc,EAAY,MAAM,eAAe,CAAC;AAC9D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAUzD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,IAAI,CAAC,oBAAoB,EAAE,eAAe,GAAG,cAAc,CAAC,GACpE,cAAc,CAoChB"}
|
package/dist/sveltekit.js
CHANGED
|
@@ -6,6 +6,7 @@ import { MembershipCollection, PermissionResolver, RoleCollection, SessionServic
|
|
|
6
6
|
import { getDatabase } from "@happyvertical/sql";
|
|
7
7
|
import { fail, json, redirect } from "@sveltejs/kit";
|
|
8
8
|
import { ObjectRegistry } from "@happyvertical/smrt-core";
|
|
9
|
+
import { destroySessionCookie } from "@happyvertical/smrt-users/sveltekit";
|
|
9
10
|
//#region src/sveltekit/handlers.ts
|
|
10
11
|
var RUNTIME_DIAGNOSTICS_READ_PERMISSION = "runtime_diagnostics.read";
|
|
11
12
|
function createRuntimeHealthHandler(runtime) {
|
|
@@ -71,13 +72,24 @@ function stableError(status, code) {
|
|
|
71
72
|
error: { code }
|
|
72
73
|
}, { status });
|
|
73
74
|
}
|
|
75
|
+
function layoutUser(user) {
|
|
76
|
+
if (!user || typeof user !== "object") return null;
|
|
77
|
+
const { id, email, name } = user;
|
|
78
|
+
if (typeof id !== "string" || typeof email !== "string") return null;
|
|
79
|
+
return {
|
|
80
|
+
id,
|
|
81
|
+
email,
|
|
82
|
+
...typeof name === "string" && name ? { name } : {}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
74
85
|
function createSessionLayoutLoad() {
|
|
75
86
|
return ({ locals }) => {
|
|
76
87
|
const value = locals;
|
|
77
88
|
return { session: {
|
|
78
89
|
authenticated: Boolean(value.user),
|
|
79
90
|
activeTenantId: value.tenantId ?? null,
|
|
80
|
-
selectedTenantSlug: value.selectedTenantSlug ?? null
|
|
91
|
+
selectedTenantSlug: value.selectedTenantSlug ?? null,
|
|
92
|
+
user: layoutUser(value.user)
|
|
81
93
|
} };
|
|
82
94
|
};
|
|
83
95
|
}
|
|
@@ -110,12 +122,30 @@ function createOwnerSetupPage(runtime, options = {}) {
|
|
|
110
122
|
if (resolveUser(event)) redirect(303, redirectTo);
|
|
111
123
|
if ((await runtime.resolvedRuntime()).profile !== "local" || !isLoopbackRequest(event)) return {
|
|
112
124
|
available: false,
|
|
113
|
-
token: ""
|
|
125
|
+
token: "",
|
|
126
|
+
link: "unavailable"
|
|
127
|
+
};
|
|
128
|
+
const status = (await (await runtime.localRuntime()).diagnostics()).bootstrap.status;
|
|
129
|
+
if (status === "claimed") return {
|
|
130
|
+
available: false,
|
|
131
|
+
token: "",
|
|
132
|
+
link: "unavailable"
|
|
133
|
+
};
|
|
134
|
+
const token = event.url.searchParams.get(OWNER_SETUP_FIELDS.token) || "";
|
|
135
|
+
if (status === "expired") return {
|
|
136
|
+
available: true,
|
|
137
|
+
token: "",
|
|
138
|
+
link: "expired"
|
|
139
|
+
};
|
|
140
|
+
if (!token) return {
|
|
141
|
+
available: true,
|
|
142
|
+
token: "",
|
|
143
|
+
link: "missing"
|
|
114
144
|
};
|
|
115
|
-
const available = (await (await runtime.localRuntime()).diagnostics()).bootstrap.status !== "claimed";
|
|
116
145
|
return {
|
|
117
|
-
available,
|
|
118
|
-
token
|
|
146
|
+
available: true,
|
|
147
|
+
token,
|
|
148
|
+
link: "ready"
|
|
119
149
|
};
|
|
120
150
|
};
|
|
121
151
|
const claim = async (event) => {
|
|
@@ -810,6 +840,40 @@ function snapshotBoundPrincipal(principal) {
|
|
|
810
840
|
return snapshot;
|
|
811
841
|
}
|
|
812
842
|
//#endregion
|
|
813
|
-
|
|
843
|
+
//#region src/sveltekit/sign-out.ts
|
|
844
|
+
function isSameOriginRequest(request, url) {
|
|
845
|
+
const site = request.headers.get("sec-fetch-site");
|
|
846
|
+
if (site !== null) return site === "same-origin";
|
|
847
|
+
const origin = request.headers.get("origin");
|
|
848
|
+
return origin !== null && origin === url.origin;
|
|
849
|
+
}
|
|
850
|
+
function createSignOutHandler(runtime) {
|
|
851
|
+
return async (event) => {
|
|
852
|
+
if (event.request.method !== "POST") return new Response("Method not allowed", {
|
|
853
|
+
status: 405,
|
|
854
|
+
headers: { allow: "POST" }
|
|
855
|
+
});
|
|
856
|
+
if (!isSameOriginRequest(event.request, event.url)) return new Response("Forbidden", { status: 403 });
|
|
857
|
+
const cookie = runtime.sessionCookie;
|
|
858
|
+
await destroySessionCookie(event, {
|
|
859
|
+
...runtime.classOptions("Session"),
|
|
860
|
+
cookieName: cookie.name,
|
|
861
|
+
cookiePath: cookie.path,
|
|
862
|
+
...cookie.domain ? { cookieDomain: cookie.domain } : {}
|
|
863
|
+
});
|
|
864
|
+
event.cookies.set(cookie.name, "", {
|
|
865
|
+
path: cookie.path,
|
|
866
|
+
...cookie.domain ? { domain: cookie.domain } : {},
|
|
867
|
+
httpOnly: true,
|
|
868
|
+
secure: cookie.secure ?? event.url.protocol === "https:",
|
|
869
|
+
sameSite: cookie.sameSite,
|
|
870
|
+
maxAge: 0
|
|
871
|
+
});
|
|
872
|
+
if (event.request.headers.get("accept")?.includes("text/html")) redirect(303, "/");
|
|
873
|
+
return new Response(null, { status: 204 });
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
//#endregion
|
|
877
|
+
export { DEFAULT_OWNER_SETUP_MESSAGES, OWNER_SETUP_FIELDS, RUNTIME_DIAGNOSTICS_READ_PERMISSION, authorizedTenantLocalsHandle, composeHandles, createOwnerSetupPage, createRuntimeDiagnosticsHandler, createRuntimeHealthHandler, createSessionLayoutLoad, createSignOutHandler, createSmrtSvelteKitRuntime, createSubdomainTenantSelector, defaultWriterLease, isLoopbackAddress, isLoopbackHostname, isLoopbackRequest, normalizeTenantSelection, selectTenantSlug };
|
|
814
878
|
|
|
815
879
|
//# sourceMappingURL=sveltekit.js.map
|
package/dist/sveltekit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sveltekit.js","names":["name","resolved","tenantId"],"sources":["../src/sveltekit/handlers.ts","../src/sveltekit/owner-setup.ts","../src/sveltekit/tenancy.ts","../src/sveltekit/runtime.ts"],"sourcesContent":["/**\n * Mountable runtime routes: liveness health, authorized diagnostics, and the\n * server-owned session summary for the root layout.\n */\n\nimport {\n getRequestScopedDatabase,\n RoleCollection,\n} from '@happyvertical/smrt-users';\nimport { json, type RequestHandler } from '@sveltejs/kit';\nimport type { RuntimeDiagnostics } from '../runtime-diagnostics.js';\nimport type {\n ApplicationRuntimeDiagnosticsOptions,\n SmrtSvelteKitRuntime,\n} from './runtime.js';\n\n/** Explicit permission that grants diagnostics read without the owner role. */\nexport const RUNTIME_DIAGNOSTICS_READ_PERMISSION = 'runtime_diagnostics.read';\n\n/**\n * Mount as `src/routes/api/_runtime/health/+server.ts`:\n * `export const GET = createRuntimeHealthHandler(runtime);`\n *\n * Local responses add the application ID, managed process instance, and the\n * secret-free configuration fingerprint that process managers compare.\n * Deployed responses expose only schema version, status, and profile.\n */\nexport function createRuntimeHealthHandler(\n runtime: Pick<SmrtSvelteKitRuntime, 'health'>,\n): RequestHandler {\n return async () => json(await runtime.health());\n}\n\n/** The locals the diagnostics route authorizes against. */\nexport interface DiagnosticsPrincipalLocals {\n readonly user?: { readonly id?: unknown } | null;\n readonly membership?: {\n readonly userId?: unknown;\n readonly tenantId?: unknown;\n readonly roleId?: unknown;\n isActive?: () => boolean;\n } | null;\n readonly permissions?: readonly string[];\n readonly tenantId?: unknown;\n readonly sessionId?: unknown;\n}\n\ntype DiagnosticsInputValue<T> = T | (() => T);\n\nexport interface RuntimeDiagnosticsHandlerOptions {\n /** Supplies default diagnostics reading and owner-role lookup. */\n readonly runtime?: Pick<\n SmrtSvelteKitRuntime,\n 'readDiagnostics' | 'classOptions'\n >;\n /** Replace the runtime projection (must already be the public allowlist). */\n readonly readDiagnostics?: () => Promise<RuntimeDiagnostics>;\n /** Replace the owner-role lookup. Thrown errors deny. */\n readonly resolveRoleSlug?: (roleId: string) => Promise<string | null>;\n /** Override where the principal comes from. Defaults to `event.locals`. */\n readonly resolveLocals?: (event: {\n readonly locals: unknown;\n }) => DiagnosticsPrincipalLocals;\n /** Public tool inventory (sorted/capped by the projector). */\n readonly toolNames?: DiagnosticsInputValue<readonly string[]>;\n readonly schemaStatus?: DiagnosticsInputValue<\n ApplicationRuntimeDiagnosticsOptions['schemaStatus']\n >;\n readonly migrationStatus?: DiagnosticsInputValue<\n ApplicationRuntimeDiagnosticsOptions['migrationStatus']\n >;\n readonly workerHeartbeatAt?: () => Date | string | null | undefined;\n readonly recentErrors?: () => ApplicationRuntimeDiagnosticsOptions['recentErrors'];\n /** Testable clock. */\n readonly now?: () => Date;\n}\n\nfunction resolveInputValue<T>(\n value: DiagnosticsInputValue<T> | undefined,\n): T | undefined {\n return typeof value === 'function' ? (value as () => T)() : value;\n}\n\n/**\n * Mount as `src/routes/api/_runtime/diagnostics/+server.ts`:\n * `export const GET = createRuntimeDiagnosticsHandler({ runtime });`\n *\n * Authentication and authorization always complete before the private runtime\n * is read. Authorized callers hold `runtime_diagnostics.read` or the `owner`\n * role of their active, session-matching membership. Every failure is a\n * stable `{ schemaVersion: 1, error: { code } }` body.\n */\nexport function createRuntimeDiagnosticsHandler(\n options: RuntimeDiagnosticsHandlerOptions,\n): RequestHandler {\n const runtime = options.runtime;\n const readDiagnostics =\n options.readDiagnostics ??\n (runtime\n ? () =>\n runtime.readDiagnostics({\n toolNames: resolveInputValue(options.toolNames) ?? [],\n observedAt: options.now?.() ?? new Date(),\n schemaStatus: resolveInputValue(options.schemaStatus) ?? 'unknown',\n migrationStatus:\n resolveInputValue(options.migrationStatus) ?? 'unknown',\n workerHeartbeatAt: options.workerHeartbeatAt?.() ?? null,\n recentErrors: options.recentErrors?.() ?? [],\n })\n : undefined);\n const resolveRoleSlug =\n options.resolveRoleSlug ??\n (runtime\n ? async (roleId: string) => {\n const config = runtime.classOptions('Role');\n const requestDb = getRequestScopedDatabase();\n const roles = await RoleCollection.create(\n requestDb\n ? { ...config, db: requestDb as typeof config.db }\n : config,\n );\n return (await roles.findById(roleId))?.slug ?? null;\n }\n : undefined);\n if (!readDiagnostics || !resolveRoleSlug) {\n throw new TypeError(\n 'createRuntimeDiagnosticsHandler requires `runtime`, or both `readDiagnostics` and `resolveRoleSlug`.',\n );\n }\n const resolveLocals =\n options.resolveLocals ??\n ((event: { readonly locals: unknown }) =>\n event.locals as DiagnosticsPrincipalLocals);\n\n return async (event) => {\n const locals = resolveLocals(event);\n const principal = authenticatedPrincipal(locals);\n if (!principal) return stableError(401, 'authentication_required');\n\n let authorized =\n locals.permissions?.includes(RUNTIME_DIAGNOSTICS_READ_PERMISSION) ===\n true;\n if (!authorized) {\n try {\n authorized = (await resolveRoleSlug(principal.roleId)) === 'owner';\n } catch {\n authorized = false;\n }\n }\n if (!authorized) return stableError(403, 'authorization_denied');\n\n try {\n return json(await readDiagnostics());\n } catch {\n return stableError(503, 'diagnostics_unavailable');\n }\n };\n}\n\nfunction authenticatedPrincipal(\n locals: DiagnosticsPrincipalLocals | null | undefined,\n): { roleId: string } | null {\n const userId = locals?.user?.id;\n const tenantId = locals?.tenantId;\n const sessionId = locals?.sessionId;\n const membership = locals?.membership;\n if (\n typeof userId !== 'string' ||\n !userId ||\n typeof tenantId !== 'string' ||\n !tenantId ||\n typeof sessionId !== 'string' ||\n !sessionId ||\n !membership ||\n membership.userId !== userId ||\n membership.tenantId !== tenantId ||\n typeof membership.roleId !== 'string' ||\n !membership.roleId\n ) {\n return null;\n }\n try {\n if (membership.isActive?.() !== true) return null;\n } catch {\n return null;\n }\n return { roleId: membership.roleId };\n}\n\nfunction stableError(status: number, code: string): Response {\n return json({ schemaVersion: 1, error: { code } }, { status });\n}\n\n/** Server-owned session summary for the root layout. */\nexport interface SessionLayoutData {\n readonly session: {\n readonly authenticated: boolean;\n /** Tenant authorized by the active session. */\n readonly activeTenantId: string | null;\n /** URL-selected tenant candidate; display only, never authorization. */\n readonly selectedTenantSlug: string | null;\n };\n}\n\n/**\n * Mount as `src/routes/+layout.server.ts`:\n * `export const load = createSessionLayoutLoad();`\n */\nexport function createSessionLayoutLoad(): (event: {\n readonly locals: unknown;\n}) => SessionLayoutData {\n return ({ locals }) => {\n const value = locals as {\n user?: unknown;\n tenantId?: string | null;\n selectedTenantSlug?: string | null;\n };\n return {\n session: {\n authenticated: Boolean(value.user),\n activeTenantId: value.tenantId ?? null,\n selectedTenantSlug: value.selectedTenantSlug ?? null,\n },\n };\n };\n}\n","/**\n * Server half of the local owner-setup page (`src/routes/setup/+page.server.ts`).\n *\n * Owner bootstrap is local-profile only, loopback-only, and single-use. The\n * runtime stores only an HMAC of the short-lived token; this module never\n * stores, logs, or returns claim errors, and it re-checks loopback custody on\n * every request instead of trusting the bind host alone.\n */\n\nimport type { Cookies } from '@sveltejs/kit';\nimport { type ActionFailure, fail, redirect } from '@sveltejs/kit';\nimport type { LocalOwnerClaimResult } from '../index.js';\nimport { removeOnboardingHandoff } from '../state-root.js';\nimport type { SmrtSvelteKitRuntime } from './runtime.js';\n\n/** Data returned by the setup page `load`. */\nexport interface OwnerSetupPageData {\n /** False when setup is claimed, disabled (deployed), or not loopback. */\n readonly available: boolean;\n /** One-time token from `?token=` (empty unless available). */\n readonly token: string;\n}\n\n/** Stable failure codes returned by the setup action. */\nexport type OwnerSetupErrorCode =\n /** Not the local profile (HTTP 404). */\n | 'setup_disabled'\n /** Request is not from a loopback client on a loopback host (HTTP 403). */\n | 'setup_unavailable'\n /** Missing token, blank name, or email without `@` (HTTP 400). */\n | 'setup_invalid_input'\n /** Token wrong, expired, or already used; owner already claimed (HTTP 400). */\n | 'setup_invalid';\n\n/** `form` data returned with `fail(status, …)`. */\nexport interface OwnerSetupFailure {\n readonly code: OwnerSetupErrorCode;\n /** Fixed, secret-free message; never claim error text. */\n readonly message: string;\n}\n\n/** Form field names posted to the setup action. */\nexport const OWNER_SETUP_FIELDS = Object.freeze({\n token: 'token',\n name: 'name',\n email: 'email',\n tenantName: 'tenantName',\n} as const);\n\nexport const DEFAULT_OWNER_SETUP_MESSAGES: Readonly<\n Record<OwnerSetupErrorCode, string>\n> = Object.freeze({\n setup_disabled: 'Local owner setup is disabled.',\n setup_unavailable: 'Local owner setup is available only from this device.',\n setup_invalid_input: 'Name, email, and a valid setup token are required.',\n setup_invalid:\n 'The setup invitation is invalid, expired, or already used. Run pnpm app:stop, pnpm app:recover, pnpm app:start, then pnpm app:open.',\n});\n\n/** The request fields the setup page reads. */\nexport interface OwnerSetupEvent {\n readonly url: URL;\n readonly locals: unknown;\n getClientAddress(): string;\n}\n\nexport interface OwnerSetupActionEvent extends OwnerSetupEvent {\n readonly request: Request;\n readonly cookies: Pick<Cookies, 'set'>;\n}\n\nexport interface OwnerSetupPageOptions {\n /** Where a signed-in visitor or a successful claim is sent. Default `/`. */\n readonly redirectTo?: string;\n /** Replace fixed failure messages (for example app-specific recovery steps). */\n readonly messages?: Partial<Record<OwnerSetupErrorCode, string>>;\n /** Override where the signed-in user comes from. Defaults to `locals.user`. */\n readonly resolveUser?: (event: OwnerSetupEvent) => unknown;\n /**\n * Set false to keep the `smrt app` onboarding hand-off files after a claim.\n * By default the runtime removes `onboarding.json` and\n * `onboarding-launch.html` from the private state root once the claim has\n * committed, so `pnpm app:open` stops offering the spent invitation.\n */\n readonly removeOnboardingHandoff?: boolean;\n /**\n * Best-effort extra cleanup after a successful claim (the hand-off files are\n * already removed by default). Errors are ignored: the owner and session are\n * already authoritative and a stale token now fails closed.\n */\n readonly onOwnerClaimed?: (\n result: LocalOwnerClaimResult,\n event: OwnerSetupActionEvent,\n ) => void | Promise<void>;\n}\n\nexport interface OwnerSetupPage {\n readonly load: (event: OwnerSetupEvent) => Promise<OwnerSetupPageData>;\n readonly actions: {\n readonly default: (\n event: OwnerSetupActionEvent,\n ) => Promise<ActionFailure<OwnerSetupFailure>>;\n };\n}\n\n/**\n * Mount as `src/routes/setup/+page.server.ts`:\n *\n * ```ts\n * import { createOwnerSetupPage } from '@happyvertical/smrt-app-runtime/sveltekit';\n * import { runtime } from '../../hooks.server';\n *\n * export const { load, actions } = createOwnerSetupPage(runtime);\n * ```\n *\n * The form posts to the `default` action with fields `token` (hidden),\n * `name`, `email`, and optional `tenantName`. Success sets the session cookie\n * and redirects 303; failures return `fail(status, { code, message })`.\n */\nexport function createOwnerSetupPage(\n runtime: Pick<\n SmrtSvelteKitRuntime,\n 'resolvedRuntime' | 'localRuntime' | 'sessionCookie'\n > &\n Partial<Pick<SmrtSvelteKitRuntime, 'applicationStateRoot'>>,\n options: OwnerSetupPageOptions = {},\n): OwnerSetupPage {\n const redirectTo = options.redirectTo ?? '/';\n const messages = { ...DEFAULT_OWNER_SETUP_MESSAGES, ...options.messages };\n const failure = (status: number, code: OwnerSetupErrorCode) =>\n fail(status, { code, message: messages[code] } as OwnerSetupFailure);\n const resolveUser =\n options.resolveUser ??\n ((event: OwnerSetupEvent) => (event.locals as { user?: unknown }).user);\n\n const load = async (event: OwnerSetupEvent): Promise<OwnerSetupPageData> => {\n if (resolveUser(event)) redirect(303, redirectTo);\n const resolved = await runtime.resolvedRuntime();\n if (resolved.profile !== 'local' || !isLoopbackRequest(event)) {\n return { available: false, token: '' };\n }\n const local = await runtime.localRuntime();\n const diagnostics = await local.diagnostics();\n const available = diagnostics.bootstrap.status !== 'claimed';\n return {\n available,\n token: available\n ? event.url.searchParams.get(OWNER_SETUP_FIELDS.token) || ''\n : '',\n };\n };\n\n const claim = async (\n event: OwnerSetupActionEvent,\n ): Promise<ActionFailure<OwnerSetupFailure>> => {\n const resolved = await runtime.resolvedRuntime();\n if (resolved.profile !== 'local') return failure(404, 'setup_disabled');\n if (!isLoopbackRequest(event)) return failure(403, 'setup_unavailable');\n\n let form: FormData;\n try {\n form = await event.request.formData();\n } catch {\n return failure(400, 'setup_invalid_input');\n }\n const field = (name: string) => {\n const value = form.get(name);\n return typeof value === 'string' ? value : '';\n };\n const token = field(OWNER_SETUP_FIELDS.token);\n const name = field(OWNER_SETUP_FIELDS.name);\n const email = field(OWNER_SETUP_FIELDS.email);\n const tenantName = field(OWNER_SETUP_FIELDS.tenantName).trim();\n if (!token || !name.trim() || !email.includes('@')) {\n return failure(400, 'setup_invalid_input');\n }\n\n const local = await runtime.localRuntime();\n let result: LocalOwnerClaimResult;\n try {\n result = await local.claimOwner({\n token,\n name,\n email,\n ...(tenantName ? { tenantName } : {}),\n userAgent: event.request.headers.get('user-agent') || undefined,\n ipAddress: event.getClientAddress(),\n });\n } catch {\n return failure(400, 'setup_invalid');\n }\n\n // claimOwner resolved, so the claim transaction has committed. Clean up\n // before cookie serialization, which can throw on a malformed config.\n if (\n options.removeOnboardingHandoff !== false &&\n runtime.applicationStateRoot\n ) {\n try {\n removeOnboardingHandoff(runtime.applicationStateRoot());\n } catch {\n // Best effort: the spent token already fails closed.\n }\n }\n const cookie = runtime.sessionCookie;\n event.cookies.set(cookie.name, result.sessionId, {\n path: cookie.path,\n ...(cookie.domain ? { domain: cookie.domain } : {}),\n httpOnly: true,\n secure: cookie.secure ?? event.url.protocol === 'https:',\n sameSite: cookie.sameSite,\n maxAge: cookie.maxAgeSeconds,\n });\n try {\n await options.onOwnerClaimed?.(result, event);\n } catch {\n // Best effort only; see OwnerSetupPageOptions.onOwnerClaimed.\n }\n redirect(303, redirectTo);\n };\n\n return Object.freeze({\n load,\n actions: Object.freeze({ default: claim }),\n });\n}\n\n/**\n * True only when both the peer address and the addressed host are loopback.\n * The host check rejects tunnels and DNS-rebinding hosts that forward to a\n * loopback listener; a throwing `getClientAddress` is treated as remote.\n */\nexport function isLoopbackRequest(event: {\n readonly url: URL;\n getClientAddress(): string;\n}): boolean {\n let address: string;\n try {\n address = event.getClientAddress();\n } catch {\n return false;\n }\n return isLoopbackAddress(address) && isLoopbackHostname(event.url.hostname);\n}\n\n/** Loopback IP literal: `127.0.0.0/8`, `::1`, or IPv4-mapped `127/8`. */\nexport function isLoopbackAddress(value: string | null | undefined): boolean {\n if (typeof value !== 'string') return false;\n let host = value\n .trim()\n .toLowerCase()\n .replace(/^\\[|\\]$/g, '');\n if (host === '::1' || host === '0:0:0:0:0:0:0:1') return true;\n if (host.startsWith('::ffff:')) host = host.slice('::ffff:'.length);\n const octets = host.split('.');\n return (\n octets.length === 4 &&\n octets.every((part) => /^\\d{1,3}$/.test(part) && Number(part) <= 255) &&\n Number(octets[0]) === 127\n );\n}\n\n/** Loopback host name as SvelteKit reports it in `url.hostname`. */\nexport function isLoopbackHostname(hostname: string): boolean {\n const host = hostname.trim().toLowerCase().replace(/\\.$/, '');\n return host === 'localhost' || isLoopbackAddress(host);\n}\n","/**\n * Tenant *selection* for SvelteKit requests.\n *\n * Selection and authorization are deliberately separate:\n *\n * - a selector maps a trusted URL shape (by default the leading subdomain) to\n * an active tenant record and publishes it as `locals.selectedTenant*`;\n * - the signed session (`createSessionHandler({ enterTenantContext: true })`)\n * authorizes the signed-in user's membership and establishes the request\n * tenant context.\n *\n * A selected tenant is useful for login and membership-gated switch flows, but\n * it never becomes ambient query authority by itself. The default selector\n * ignores client-provided tenant headers.\n */\n\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport { TenantCollection, TenantStatus } from '@happyvertical/smrt-users';\n\n/** The request fields a tenant selector may read. */\nexport interface TenantSelectionEvent {\n readonly url: URL;\n readonly request: { readonly headers: Headers };\n}\n\n/** URL-derived tenant candidate. Never authorization. */\nexport interface TenantSelection {\n /** Database ID for an active tenant, or null when none resolves. */\n readonly tenantId: string | null;\n /** URL-derived slug. Informational until membership authorizes a switch. */\n readonly tenantSlug: string | null;\n}\n\n/**\n * Replaceable selection extension point. Implementations must preserve the\n * rule that selection alone never establishes authorization context.\n */\nexport type TenantSelector = (\n event: TenantSelectionEvent,\n) => TenantSelection | Promise<TenantSelection>;\n\nconst ROOT_LIKE_HOSTS = new Set(['localhost', '127.0.0.1', '::1', '[::1]']);\nconst RESERVED_SUBDOMAINS = new Set(['www', 'api', 'app', 'admin']);\nconst NO_SELECTION: TenantSelection = Object.freeze({\n tenantId: null,\n tenantSlug: null,\n});\n\n/**\n * Pure URL parser used by the default selector.\n *\n * Set `TENANT_BASE_DOMAIN` (or pass `baseDomain`) in production so\n * multi-label public suffixes are never guessed. Without it, the fallback is\n * intended only for local domains such as `acme.demo.local`.\n */\nexport function selectTenantSlug(\n url: URL,\n baseDomain: string | undefined = process.env.TENANT_BASE_DOMAIN,\n): string | null {\n const hostname = url.hostname.toLowerCase();\n if (\n ROOT_LIKE_HOSTS.has(hostname) ||\n hostname.startsWith('[') ||\n /^\\d{1,3}(\\.\\d{1,3}){3}$/.test(hostname)\n ) {\n return null;\n }\n\n const normalizedBase = baseDomain\n ?.trim()\n .toLowerCase()\n .replace(/^\\.+|\\.+$/g, '');\n\n let candidate: string | undefined;\n if (normalizedBase) {\n if (hostname === normalizedBase) return null;\n const suffix = `.${normalizedBase}`;\n if (!hostname.endsWith(suffix)) return null;\n candidate = hostname.slice(0, -suffix.length).split('.')[0];\n } else {\n const labels = hostname.split('.');\n if (labels.length < 3) return null;\n candidate = labels[0];\n }\n\n return candidate && !RESERVED_SUBDOMAINS.has(candidate) ? candidate : null;\n}\n\nfunction isMissingTenantTable(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n const code = (error as Error & { code?: string }).code;\n return (\n code === '42P01' ||\n error.message.includes('relation \"tenants\" does not exist') ||\n error.message.includes('no such table: tenants')\n );\n}\n\nexport interface SubdomainTenantSelectorOptions {\n /** Explicit public base domain. Defaults to `TENANT_BASE_DOMAIN`. */\n readonly baseDomain?: string;\n /** Collection options for the `Tenant` lookup. */\n readonly classOptions: () => SmrtClassOptions;\n}\n\n/**\n * Default selector: the leading subdomain names an ACTIVE tenant.\n *\n * A missing `tenants` table (a fresh project before its first migration)\n * yields no selection; the runtime never creates that table.\n */\nexport function createSubdomainTenantSelector(\n options: SubdomainTenantSelectorOptions,\n): TenantSelector {\n return async (event) => {\n const tenantSlug = selectTenantSlug(event.url, options.baseDomain);\n if (!tenantSlug) return NO_SELECTION;\n let tenantId: string | null;\n try {\n const tenants = await TenantCollection.create(options.classOptions());\n const tenant = await tenants.findBySlug(tenantSlug);\n tenantId =\n tenant?.status === TenantStatus.ACTIVE ? (tenant.id ?? null) : null;\n } catch (error) {\n if (!isMissingTenantTable(error)) throw error;\n tenantId = null;\n }\n return { tenantId, tenantSlug };\n };\n}\n\n/** Coerce an application selector's result into the published shape. */\nexport function normalizeTenantSelection(\n selection: TenantSelection | null | undefined,\n): TenantSelection {\n const tenantId =\n typeof selection?.tenantId === 'string' && selection.tenantId\n ? selection.tenantId\n : null;\n const tenantSlug =\n typeof selection?.tenantSlug === 'string' && selection.tenantSlug\n ? selection.tenantSlug\n : null;\n return { tenantId, tenantSlug };\n}\n","/**\n * SvelteKit server composition for the validated runtime profiles.\n *\n * Request foundation, in order:\n *\n * 0. Wait for the fail-closed runtime startup gate (`init`).\n * 1. Resolve a URL tenant candidate into `locals.selectedTenant*`. This is\n * selection only and never enters AsyncLocalStorage tenant context.\n * 2. Load the signed session. Its tenant is accepted only for a verified\n * active direct or legitimately inherited membership\n * ({@link isSessionTenantAuthorized}, the deployed runtime's rule);\n * otherwise the request is unauthenticated. An accepted session enters the\n * tenant context and permission set for downstream code, inside the\n * request RLS transaction when the profile selects `database-rls`.\n * 3. Publish the authorized context on `locals.tenantContext` only when it\n * matches the verified session tenant.\n *\n * This ordering prevents a spoofed header or hostname from becoming query\n * authority. Membership-gated tenant switching belongs in an explicit action\n * using `switchSessionTenant()` from `@happyvertical/smrt-users/sveltekit`.\n */\n\nimport {\n loadConfig,\n type ResolvedApplicationRuntime,\n resolveEffectiveApplicationRuntime,\n} from '@happyvertical/smrt-config';\nimport {\n ObjectRegistry,\n type SmrtClassOptions,\n type SmrtCollection,\n type SmrtObject,\n} from '@happyvertical/smrt-core';\nimport { OidcIdentityCollection } from '@happyvertical/smrt-profiles';\nimport {\n enableTenancy,\n getCurrentTenant,\n isTenancyEnabled,\n type MinimalTenantContext,\n withSystemContext,\n} from '@happyvertical/smrt-tenancy';\nimport {\n getCurrentSessionPermissionContext,\n getRequestScopedDatabase,\n MembershipCollection,\n PermissionResolver,\n SessionService,\n type SessionServiceOptions,\n UserCollection,\n UserStatus,\n withPrincipalPermissionContext,\n withSessionPermissionContext,\n} from '@happyvertical/smrt-users';\nimport type {\n SessionHandlerOptions,\n SessionLocals,\n} from '@happyvertical/smrt-users/sveltekit';\nimport { type DatabaseInterface, getDatabase } from '@happyvertical/sql';\nimport type { Handle, ServerInit } from '@sveltejs/kit';\nimport {\n resolveApplicationId,\n runtimeConfigurationFingerprint,\n} from '../application-identity.js';\nimport {\n type DeployedApplicationRuntime,\n type DeployedApplicationRuntimeProfile,\n initializeDeployedApplicationRuntime,\n isSessionTenantAuthorized,\n type PublicAuthenticationProvider,\n} from '../deployed-runtime.js';\nimport {\n resolveBoundMembershipPermissions,\n type TenantBindingMode,\n} from '../direct-membership.js';\nimport {\n initializeLocalApplicationRuntime,\n isLocalMcpTokenPrincipal,\n type LocalApplicationRuntime,\n type LocalMcpTokenPrincipal,\n type LocalOwnerBootstrapInvitation,\n resolveLocalRuntimePaths,\n validateApplicationId,\n} from '../index.js';\nimport {\n deepFrozenPlainCopy,\n ownValue,\n PlainDataError,\n} from '../plain-data.js';\nimport {\n projectRuntimeDiagnostics,\n type RuntimeDiagnostics,\n type RuntimeDiagnosticsProjectionInput,\n} from '../runtime-diagnostics.js';\nimport {\n prepareApplicationStateRoot,\n resolveApplicationStateRoot,\n} from '../state-root.js';\nimport { acquireWriterLease, type WriterLease } from '../writer-lease.js';\nimport {\n createSubdomainTenantSelector,\n normalizeTenantSelection,\n type TenantSelector,\n} from './tenancy.js';\n\n/** `App.Locals` fields populated by the SMRT runtime `handle`. */\nexport interface SmrtRuntimeLocals extends SessionLocals {\n /**\n * URL-selected tenant candidate. Never use this as authorization; the\n * session-authorized tenant remains `tenantId` from {@link SessionLocals}.\n */\n selectedTenantId: string | null;\n selectedTenantSlug: string | null;\n /** Full context for the authorized session tenant, when present. */\n tenantContext?: MinimalTenantContext;\n}\n\n/** Provider component whose readiness a deployed web process must prove. */\nexport type ProviderReadinessComponent =\n | 'authentication'\n | 'assets'\n | 'secrets';\n\n/**\n * Build a provider-owned readiness probe. The probe must check the real\n * backing service and must never return or log credentials.\n */\nexport type ProviderReadinessFactory = (\n component: ProviderReadinessComponent,\n context: { readonly profile: string; readonly provider: string },\n) => () => Promise<void>;\n\n/** Context handed to an application-owned writer lease. */\nexport interface WriterLeaseContext {\n readonly appId: string;\n readonly dataDirectory: string | undefined;\n readonly sourceRoot: string;\n}\n\n/**\n * Held for the life of the local web process; released on failed startup.\n * The root entry's `acquireWriterLease()` returns one.\n */\nexport type { WriterLease } from '../writer-lease.js';\n\n/** Inputs to the public diagnostics projection that only the app can supply. */\nexport interface ApplicationRuntimeDiagnosticsOptions {\n readonly toolNames: readonly string[];\n readonly observedAt: Date;\n /** Application-owned schema verification seam; runtime liveness is not proof. */\n readonly schemaStatus?: 'not-ready' | 'ready' | 'unknown';\n /** Application-owned migration tracker seam; database liveness is not proof. */\n readonly migrationStatus?: 'current' | 'failed' | 'pending' | 'unknown';\n /** Bounded worker seam supplied by the deployment's lease/heartbeat adapter. */\n readonly workerHeartbeatAt?: Date | string | null;\n /** Stable code/timestamp pairs only; raw errors never cross this seam. */\n readonly recentErrors?: readonly {\n readonly code?: unknown;\n readonly at?: unknown;\n }[];\n}\n\n/** Liveness body served by the mounted health route. */\nexport interface RuntimeHealthBody {\n readonly schemaVersion: 1;\n readonly status: 'ready';\n readonly profile: ResolvedApplicationRuntime['profile'];\n /** Local profile only: canonical application ID. */\n readonly application?: string;\n /** Local profile only: managed process instance (`SMRT_PROCESS_INSTANCE`). */\n readonly instance?: string | null;\n /** Local profile only: {@link runtimeConfigurationFingerprint}. */\n readonly configuration?: string;\n}\n\n/** Session-handler options the application may tune. */\nexport type SmrtRuntimeSessionOptions = Omit<\n SessionHandlerOptions,\n 'enterTenantContext'\n>;\n\n/** App-specific options for {@link createSmrtSvelteKitRuntime}. */\nexport interface SmrtSvelteKitRuntimeOptions {\n /**\n * Explicit application ID. Defaults to `SMRT_APP_ID` (strictly validated)\n * or the encoded `package.json` name under `sourceRoot`.\n */\n readonly appId?: string;\n /** Application source checkout. Defaults to `process.cwd()`. */\n readonly sourceRoot?: string;\n /** Local data root. Defaults to `SMRT_DATA_DIR`, then the OS data directory. */\n readonly dataDirectory?: string;\n /** Environment to read. Defaults to `process.env`. */\n readonly env?: NodeJS.ProcessEnv;\n /**\n * Resolved runtime profile. Defaults to `smrt.config` `runtime` when\n * present, otherwise the `local` profile; a present value that is not a\n * runtime block (`null`, `false`, `0`, `''`) fails closed.\n */\n readonly runtime?:\n | ResolvedApplicationRuntime\n | (() => ResolvedApplicationRuntime | Promise<ResolvedApplicationRuntime>);\n /**\n * Explicit, idempotent application migration hook, run during `init`.\n * The runtime never synthesizes application tables itself.\n */\n readonly prepareDatabase?: (db: DatabaseInterface) => Promise<void>;\n /**\n * Local HTTP bind host. Defaults to `HOST`, or `127.0.0.1` when\n * `NODE_ENV=development`. Local production startup without one fails.\n */\n readonly bindHost?: string;\n /** Opt in to persisted local background jobs. Default `SMRT_BACKGROUND_JOBS === 'true'`. */\n readonly backgroundJobs?: boolean;\n /** Opt in to application-defined paid capabilities. Default false. */\n readonly paidCapabilities?: boolean;\n /** Owner-bootstrap token lifetime (1..900 seconds). */\n readonly bootstrapTtlSeconds?: number;\n /** Session lifetime for the owner-bootstrap session cookie. Default 7 days. */\n readonly sessionTtlSeconds?: number;\n /**\n * Local single-writer lease held for the web process, released when local\n * startup fails. Defaults to the state-root writer lease shared with\n * `smrt app` operations ({@link defaultWriterLease}: `writer.lease` under\n * `prepareApplicationStateRoot()`, presenting `SMRT_OPERATION_INSTANCE`).\n * Pass a factory to replace it, or `false` to hold no lease. Deployed\n * profiles never take it.\n */\n readonly acquireWriterLease?:\n | ((context: WriterLeaseContext) => WriterLease)\n | false;\n /**\n * Deployed provider readiness probes. Required by `self-hosted` and `cloud`;\n * deployed startup fails closed without them.\n */\n readonly providerReadiness?: ProviderReadinessFactory;\n /** Replace the default subdomain tenant selector. Selection only. */\n readonly selectTenant?: TenantSelector;\n /** Base domain for the default selector. Defaults to `TENANT_BASE_DOMAIN`. */\n readonly tenantBaseDomain?: string;\n /** Session handler tuning. `enterTenantContext` is always `true`. */\n readonly session?: SmrtRuntimeSessionOptions;\n /** Per-class overrides layered over the runtime database config. */\n readonly classOverrides?: Readonly<Record<string, Partial<SmrtClassOptions>>>;\n /** Call `enableTenancy()` if it is not already enabled. Default true. */\n readonly enableTenancy?: boolean;\n /**\n * Receives an owner-bootstrap invitation that this process's startup newly\n * issued (the plaintext is returned exactly once; only its HMAC is stored).\n * Use it to show a loopback `/setup?token=` URL. Errors are ignored so a\n * failing presenter never blocks startup. Default: not presented.\n */\n readonly onBootstrapInvitation?: (\n invitation: LocalOwnerBootstrapInvitation,\n context: { readonly bindHost: string },\n ) => void;\n /** Testable clock for the local runtime. */\n readonly now?: () => Date;\n}\n\n/** Cookie attributes for a session established outside the session handler. */\nexport interface SmrtRuntimeSessionCookie {\n readonly name: string;\n readonly path: string;\n readonly domain?: string;\n readonly sameSite: 'strict' | 'lax' | 'none';\n /** `undefined` means \"secure when the request is HTTPS\". */\n readonly secure?: boolean;\n readonly maxAgeSeconds: number;\n}\n\nexport type { TenantBindingMode } from '../direct-membership.js';\n\n/** The composed runtime; `handle` and `init` are bound and destructurable. */\n/** A principal a route authenticated itself, for {@link SmrtSvelteKitRuntime.runAsPrincipal}. */\nexport interface SmrtRuntimeBoundPrincipal {\n readonly id: string;\n readonly tenantId?: string | null;\n /** Granted scopes that cap the live permission set (e.g. token scopes). */\n readonly scopes?: readonly string[];\n /**\n * How tenant authority may be established, set by the authenticating\n * adapter (never request input). `direct` (local owner tokens, the default\n * hosted resolver) needs an active direct membership in exactly\n * `tenantId`. `direct-or-inherited` (the default; hosted application-owned\n * mappings) applies the session step's rule: that direct row, or with no\n * direct row, authority inherited from an active inheritable ancestor.\n */\n readonly tenantBinding?: TenantBindingMode;\n}\n\n/** A verified hosted access-token identity (see `McpVerifiedIdentity`). */\nexport interface SmrtRuntimeMcpIdentity {\n /** Exact issuer the token was verified against. */\n readonly issuer: string;\n /** Verified `sub` claim. */\n readonly subject: string;\n}\n\n/** The current account and tenant a hosted MCP identity maps to. */\nexport interface SmrtRuntimeMcpPrincipalMapping {\n readonly id: string;\n readonly tenantId: string;\n readonly kind: 'human';\n /** The default resolver maps only direct memberships. */\n readonly tenantBinding: 'direct';\n}\n\n/** `T` with every nested array and object field readonly. */\nexport type SmrtRuntimeDeepReadonly<T> = T extends readonly (infer U)[]\n ? readonly SmrtRuntimeDeepReadonly<U>[]\n : T extends object\n ? { readonly [K in keyof T]: SmrtRuntimeDeepReadonly<T[K]> }\n : T;\n\n/**\n * The frozen principal `runAsPrincipal` hands its callback: a deep, frozen\n * snapshot of the caller's principal taken before any await, with the\n * effective `tenantBinding` and effective (frozen) `scopes`.\n */\nexport type SmrtRuntimeBoundPrincipalSnapshot<P> = SmrtRuntimeDeepReadonly<\n Omit<P, 'scopes' | 'tenantBinding'>\n> & {\n readonly scopes: readonly string[];\n readonly tenantBinding: TenantBindingMode;\n};\n\nexport interface SmrtSvelteKitRuntime {\n /** `hooks.server.ts` `handle`. */\n readonly handle: Handle;\n /** `hooks.server.ts` `init`: the fail-closed startup gate. */\n readonly init: ServerInit;\n /** Session cookie attributes shared with owner setup. */\n readonly sessionCookie: SmrtRuntimeSessionCookie;\n /** Resolve the runtime profile (memoized; retried after failure). */\n resolvedRuntime(): Promise<ResolvedApplicationRuntime>;\n /** Await profile-specific startup (same promise as `init`). */\n ready(): Promise<void>;\n /** Canonical application ID. */\n applicationId(): string;\n /**\n * Private state root shared with the `smrt app` operator commands (writer\n * lease, operation lock, onboarding hand-off files). Resolves the path only;\n * it does not create it.\n */\n applicationStateRoot(): string;\n /** Secret-free configuration fingerprint. Requires a resolved runtime. */\n configurationFingerprint(): string;\n /**\n * Database for SMRT collections. Requires a resolved runtime. Inside a\n * request running in the RLS transaction (`database-rls` isolation, or\n * `session.postgresRls`) this is the transaction-bound request database;\n * otherwise the base configuration. Call it per request and never retain\n * the result (or collections built from it) beyond that request.\n */\n databaseConfig(): SmrtClassOptions['db'];\n /**\n * Collection options for a class: {@link databaseConfig} plus\n * `classOverrides`. A class whose override sets `db` keeps that database.\n * Same per-request rule as {@link databaseConfig}.\n */\n classOptions(className: string): SmrtClassOptions;\n /**\n * The registered collection for a class, built from {@link classOptions}\n * evaluated on this call: inside an RLS request it is bound to the request\n * transaction. Generated API routes resolve collections through it. Call it\n * per request and never retain the result beyond that request.\n */\n getCollection<T extends SmrtObject = SmrtObject>(\n className: string,\n ): Promise<SmrtCollection<T>>;\n /**\n * Run `fn` as a principal the route verified itself (for example a bearer\n * token mapped by an MCP route), replacing the cookie session's permission\n * context for that call. The principal's permissions are resolved live from\n * its membership in `tenantId` and, when `scopes` is given, capped to it;\n * under the RLS transaction rule of {@link databaseConfig} a fresh\n * transaction publishes that user, tenant and permission set, and\n * `databaseConfig()`/`classOptions()` return it inside `fn`. Rejects (before\n * `fn` runs) without a user id, a tenant, or a membership its\n * `tenantBinding` accepts: `direct` needs an active direct membership in\n * exactly that tenant; `direct-or-inherited` (default) also accepts\n * authority inherited from an active inheritable ancestor when no direct\n * row exists. A suspended or pending direct row is always authoritative.\n *\n * `principal` is read once, before any await: `fn` receives a frozen copy\n * of that snapshot (its effective `tenantBinding`, and `scopes` replaced by\n * the effective, frozen permission set: live permissions capped by the\n * given scopes), never the caller's object. Callers\n * that authorize in code (rather than through database policy, as under\n * `application` isolation) must authorize with these effective scopes so a\n * revoked permission takes effect even while a token still carries it.\n */\n runAsPrincipal<\n T,\n P extends SmrtRuntimeBoundPrincipal = SmrtRuntimeBoundPrincipal,\n >(\n principal: P,\n fn: (bound: SmrtRuntimeBoundPrincipalSnapshot<P>) => Promise<T>,\n ): Promise<T>;\n /**\n * Verify an owner-minted local MCP token (`smrt app token`). Resolves the\n * token's owner principal with its scopes capped to the owner's live\n * permissions, or `null` for anything else: a malformed, unknown, revoked\n * or expired token, an inactive owner or membership, or any non-`local`\n * profile. Pass the runtime as `createHostedMcpResourceAuth({ runtime })`\n * so `/mcp` accepts these tokens in the local profile.\n */\n verifyLocalMcpToken(token: string): Promise<LocalMcpTokenPrincipal | null>;\n /**\n * Default membership-backed hosted MCP principal mapping: the verified\n * issuer/subject must be linked (`oidc_identities`) to exactly one active\n * user with exactly one active direct tenant membership. Anything else\n * (unlinked, ambiguous, inactive, several tenants, the `local` profile)\n * resolves `null`, which denies the request. Token claims never select the\n * tenant. Applications with multi-tenant users pass their own\n * `resolvePrincipal`, which takes precedence.\n */\n resolveMcpPrincipal(\n identity: SmrtRuntimeMcpIdentity,\n ): Promise<SmrtRuntimeMcpPrincipalMapping | null>;\n /** The local runtime. Rejects outside the `local` profile. */\n localRuntime(): Promise<LocalApplicationRuntime>;\n /** The deployed runtime. Rejects in the `local` profile. */\n deployedRuntime(): Promise<DeployedApplicationRuntime>;\n /** Liveness body for the health route (awaits startup). */\n health(): Promise<RuntimeHealthBody>;\n /**\n * Read private runtime state and project it onto the public allowlist.\n * Call only after the route has authenticated and authorized its caller.\n */\n readDiagnostics(\n options: ApplicationRuntimeDiagnosticsOptions,\n ): Promise<RuntimeDiagnostics>;\n}\n\n/** @internal Test and composition seams; not part of the public entry. */\nexport interface SmrtSvelteKitRuntimeSeams {\n readonly initializeLocal?: typeof initializeLocalApplicationRuntime;\n readonly initializeDeployed?: typeof initializeDeployedApplicationRuntime;\n readonly loadRuntime?: () => Promise<ResolvedApplicationRuntime>;\n}\n\nconst DEFAULT_SESSION_TTL_SECONDS = 7 * 24 * 60 * 60;\n\n/**\n * Compose runtime startup, tenant selection, the signed session, and\n * authorized-tenant locals into a SvelteKit `{ handle, init }` pair.\n *\n * @example\n * ```ts\n * // src/hooks.server.ts\n * import { createSmrtSvelteKitRuntime } from '@happyvertical/smrt-app-runtime/sveltekit';\n *\n * export const runtime = createSmrtSvelteKitRuntime();\n * export const { handle, init } = runtime;\n * ```\n */\nexport function createSmrtSvelteKitRuntime(\n options: SmrtSvelteKitRuntimeOptions = {},\n): SmrtSvelteKitRuntime {\n return composeSmrtSvelteKitRuntime(options, {});\n}\n\n/** @internal */\nexport function composeSmrtSvelteKitRuntime(\n options: SmrtSvelteKitRuntimeOptions,\n seams: SmrtSvelteKitRuntimeSeams,\n): SmrtSvelteKitRuntime {\n const env = options.env ?? process.env;\n const sourceRoot = options.sourceRoot ?? process.cwd();\n const dataDirectory = options.dataDirectory ?? env.SMRT_DATA_DIR;\n const initializeLocal =\n seams.initializeLocal ?? initializeLocalApplicationRuntime;\n const initializeDeployed =\n seams.initializeDeployed ?? initializeDeployedApplicationRuntime;\n\n if (options.enableTenancy !== false && !isTenancyEnabled()) {\n enableTenancy();\n }\n\n let appId: string | undefined;\n const applicationId = (): string => {\n appId ??= options.appId\n ? validateApplicationId(options.appId)\n : resolveApplicationId({ sourceRoot, explicitId: env.SMRT_APP_ID });\n return appId;\n };\n\n let resolved: ResolvedApplicationRuntime | undefined;\n let resolvedPromise: Promise<ResolvedApplicationRuntime> | undefined;\n const resolvedRuntime = (): Promise<ResolvedApplicationRuntime> => {\n resolvedPromise ??= (async () => {\n const value =\n typeof options.runtime === 'function'\n ? await options.runtime()\n : (options.runtime ??\n (await (seams.loadRuntime ?? loadConfiguredRuntime)()));\n resolved = value;\n return value;\n })().catch((error: unknown) => {\n resolvedPromise = undefined;\n throw error;\n });\n return resolvedPromise;\n };\n\n const requireResolved = (): ResolvedApplicationRuntime => {\n if (!resolved) {\n throw new Error(\n 'The SMRT application runtime is not resolved yet; await init() first.',\n );\n }\n return resolved;\n };\n\n /** Base (connection-level) database config; never request-scoped. */\n const baseDatabaseConfig = (): SmrtClassOptions['db'] => {\n const runtime = requireResolved();\n if (runtime.profile === 'local') {\n const paths = resolveLocalRuntimePaths({\n appId: applicationId(),\n dataDirectory,\n sourceRoot,\n env,\n });\n return {\n type: 'sqlite',\n url: paths.database,\n secureFile: {\n driver: 'node:sqlite',\n custody: 'trusted-parent',\n root: paths.root,\n },\n };\n }\n const databaseUrl = env.DATABASE_URL;\n if (!databaseUrl) {\n throw new Error(`${runtime.profile} requires DATABASE_URL.`);\n }\n return { type: 'postgres', url: databaseUrl };\n };\n\n /** Base collection options; used for anything that outlives a request. */\n const baseClassOptions = (className: string): SmrtClassOptions => {\n const defaults: SmrtClassOptions = { db: baseDatabaseConfig() };\n const override = options.classOverrides?.[className];\n return override ? { ...defaults, ...override } : defaults;\n };\n\n /**\n * Inside a request running in the RLS transaction, the transaction-bound\n * request database (it carries the `smrt.*` session variables); otherwise\n * undefined. Never cache the returned handle beyond the request.\n */\n const rlsRequestDatabase = (): SmrtClassOptions['db'] | undefined => {\n const context = getCurrentSessionPermissionContext();\n if (context?.postgresRls !== true) return undefined;\n return (getRequestScopedDatabase() ?? undefined) as\n | SmrtClassOptions['db']\n | undefined;\n };\n\n const databaseConfig = (): SmrtClassOptions['db'] =>\n rlsRequestDatabase() ?? baseDatabaseConfig();\n\n const classOptions = (className: string): SmrtClassOptions => {\n const base = baseClassOptions(className);\n if (options.classOverrides?.[className]?.db !== undefined) return base;\n const requestDb = rlsRequestDatabase();\n return requestDb ? { ...base, db: requestDb } : base;\n };\n\n const getCollection = <T extends SmrtObject = SmrtObject>(\n className: string,\n ): Promise<SmrtCollection<T>> =>\n ObjectRegistry.getCollection<T>(className, classOptions(className));\n\n const writerLease =\n options.acquireWriterLease === false\n ? undefined\n : (options.acquireWriterLease ??\n ((context: WriterLeaseContext) => defaultWriterLease(context, env)));\n\n let localPromise: Promise<LocalApplicationRuntime> | undefined;\n let localLease: WriterLease | undefined;\n const localRuntime = async (): Promise<LocalApplicationRuntime> => {\n const runtime = await resolvedRuntime();\n if (runtime.profile !== 'local') {\n throw new Error(\n 'Owner bootstrap is available only in the local profile.',\n );\n }\n if (localPromise) return localPromise;\n const bindHost =\n options.bindHost ||\n env.HOST ||\n (env.NODE_ENV === 'development' ? '127.0.0.1' : null);\n if (!bindHost) {\n throw new Error(\n 'Local production startup requires an explicit loopback HOST; use pnpm app:start.',\n );\n }\n const id = applicationId();\n localLease ??= writerLease?.({\n appId: id,\n dataDirectory,\n sourceRoot,\n });\n localPromise = initializeLocal({\n appId: id,\n dataDirectory,\n sourceRoot,\n env,\n bindHost,\n providers: {\n database: runtime.providers.database,\n authentication: runtime.providers.authentication,\n tenancy: runtime.providers.tenancy,\n assets: runtime.providers.assets,\n secrets: runtime.providers.secrets,\n jobs: runtime.providers.jobs,\n network: runtime.providers.network,\n },\n backgroundJobs:\n options.backgroundJobs ?? env.SMRT_BACKGROUND_JOBS === 'true',\n paidCapabilities: options.paidCapabilities,\n bootstrapTtlSeconds: options.bootstrapTtlSeconds,\n sessionTtlSeconds: options.sessionTtlSeconds,\n prepareDatabase: options.prepareDatabase,\n now: options.now,\n })\n .then(({ runtime: initialized, bootstrap }) => {\n if (bootstrap) {\n try {\n options.onBootstrapInvitation?.(bootstrap, { bindHost });\n } catch {\n // Presentation is best effort; the invitation stays claimable.\n }\n }\n return initialized;\n })\n .catch((error: unknown) => {\n localLease?.release();\n localLease = undefined;\n localPromise = undefined;\n throw error;\n });\n return localPromise;\n };\n\n let deployedPromise: Promise<DeployedApplicationRuntime> | undefined;\n const deployedRuntime = async (): Promise<DeployedApplicationRuntime> => {\n const runtime = await resolvedRuntime();\n if (runtime.profile === 'local') {\n throw new Error('deployed_runtime_unavailable');\n }\n const profile = runtime.profile as DeployedApplicationRuntimeProfile;\n const databaseUrl = env.DATABASE_URL;\n if (!databaseUrl) {\n throw new Error(`${profile} requires DATABASE_URL.`);\n }\n const authenticationProvider = runtime.providers.authentication.provider;\n if (authenticationProvider === 'owner-bootstrap') {\n throw new Error('Deployed profiles require public authentication.');\n }\n const readiness = options.providerReadiness;\n if (!readiness) {\n throw new Error(\n `${profile} requires providerReadiness probes for authentication, assets, and secrets.`,\n );\n }\n deployedPromise ??= initializeDeployed({\n profile,\n providers: {\n database: runtime.providers.database,\n authentication: runtime.providers.authentication,\n tenancy: runtime.providers.tenancy,\n assets: runtime.providers.assets,\n secrets: runtime.providers.secrets,\n jobs: runtime.providers.jobs,\n network: runtime.providers.network,\n },\n database: {\n engine: 'postgres',\n connect: () => getDatabase({ type: 'postgres', url: databaseUrl }),\n close: async (db) => {\n await db.close?.();\n },\n },\n authentication: {\n provider: authenticationProvider as PublicAuthenticationProvider,\n readiness: readiness('authentication', {\n profile,\n provider: authenticationProvider,\n }),\n },\n assets: {\n provider: runtime.providers.assets.provider,\n readiness: readiness('assets', {\n profile,\n provider: runtime.providers.assets.provider,\n }),\n },\n secrets: {\n provider: runtime.providers.secrets.provider,\n readiness: readiness('secrets', {\n profile,\n provider: runtime.providers.secrets.provider,\n }),\n },\n prepareDatabase: options.prepareDatabase,\n });\n return deployedPromise;\n };\n\n const ready = async (): Promise<void> => {\n const runtime = await resolvedRuntime();\n if (runtime.profile === 'local') {\n await localRuntime();\n return;\n }\n await deployedRuntime();\n };\n\n const configurationFingerprint = (): string =>\n runtimeConfigurationFingerprint(requireResolved(), env);\n\n const health = async (): Promise<RuntimeHealthBody> => {\n await ready();\n const runtime = requireResolved();\n const publicHealth = {\n schemaVersion: 1 as const,\n status: 'ready' as const,\n profile: runtime.profile,\n };\n return runtime.profile === 'local'\n ? {\n ...publicHealth,\n application: applicationId(),\n instance: env.SMRT_PROCESS_INSTANCE || null,\n configuration: configurationFingerprint(),\n }\n : publicHealth;\n };\n\n const readDiagnostics = async (\n diagnosticsOptions: ApplicationRuntimeDiagnosticsOptions,\n ): Promise<RuntimeDiagnostics> => {\n await ready();\n const runtime = requireResolved();\n const schema = {\n status: diagnosticsOptions.schemaStatus ?? 'unknown',\n migrations: diagnosticsOptions.migrationStatus ?? 'unknown',\n } as const;\n\n if (runtime.profile === 'local') {\n const local = await localRuntime();\n const diagnostics = await local.diagnostics();\n return projectRuntimeDiagnostics({\n profile: 'local',\n health: 'healthy',\n schema,\n capabilities: {\n 'asset-storage': 'available',\n authentication: 'available',\n 'background-jobs': diagnostics.jobs.backgroundEnabled\n ? 'available'\n : 'disabled',\n database: 'available',\n 'paid-capabilities': diagnostics.paidCapabilitiesEnabled\n ? 'available'\n : 'disabled',\n 'secret-storage': 'available',\n },\n toolNames: diagnosticsOptions.toolNames,\n worker: {\n topology: diagnostics.jobs.topology,\n required: diagnostics.jobs.backgroundEnabled,\n heartbeatAt: diagnosticsOptions.workerHeartbeatAt,\n },\n recentErrors: diagnosticsOptions.recentErrors,\n observedAt: diagnosticsOptions.observedAt,\n });\n }\n\n const deployed = await deployedPromise;\n if (!deployed) throw new Error('deployed_runtime_unavailable');\n const readiness = await deployed.readiness();\n const componentStatus = (\n component: keyof typeof readiness.components,\n ): 'available' | 'unavailable' =>\n readiness.components[component].status === 'ready'\n ? 'available'\n : 'unavailable';\n const input: RuntimeDiagnosticsProjectionInput = {\n profile: runtime.profile,\n health:\n deployed.health().status === 'healthy'\n ? readiness.status === 'ready'\n ? 'healthy'\n : 'degraded'\n : 'stopped',\n schema,\n capabilities: {\n 'asset-storage': componentStatus('assets'),\n authentication: componentStatus('authentication'),\n 'background-jobs': 'available',\n database: componentStatus('database'),\n 'paid-capabilities': 'unknown',\n 'secret-storage': componentStatus('secrets'),\n },\n toolNames: diagnosticsOptions.toolNames,\n worker: {\n topology: deployed.resolvedRuntime.providers.jobs.topology,\n required: true,\n heartbeatAt: diagnosticsOptions.workerHeartbeatAt,\n },\n recentErrors: diagnosticsOptions.recentErrors,\n observedAt: diagnosticsOptions.observedAt,\n };\n return projectRuntimeDiagnostics(input);\n };\n\n const selectTenant: TenantSelector =\n options.selectTenant ??\n createSubdomainTenantSelector({\n baseDomain: options.tenantBaseDomain ?? env.TENANT_BASE_DOMAIN,\n classOptions: () => baseClassOptions('Tenant'),\n });\n\n const readinessHandle: Handle = async ({ event, resolve }) => {\n await ready();\n return resolve(event);\n };\n\n const tenantSelectionHandle: Handle = async ({ event, resolve }) => {\n const locals = runtimeLocals(event.locals);\n const selection = normalizeTenantSelection(\n await selectTenant({ url: event.url, request: event.request }),\n );\n locals.selectedTenantId = selection.tenantId;\n locals.selectedTenantSlug = selection.tenantSlug;\n return resolve(event);\n };\n\n const sessionCookieName = options.session?.cookieName ?? 'sid';\n const sessionSkipPaths = options.session?.skipPaths ?? [];\n const sessionServiceOptions = (): SessionServiceOptions => ({\n ...baseClassOptions('Session'),\n ...options.session,\n defaultTTL: options.session?.ttl ?? DEFAULT_SESSION_TTL_SECONDS,\n autoExtend: options.session?.autoExtend ?? false,\n });\n /** `database-rls` isolation always runs the request RLS transaction. */\n const postgresRls = (): boolean | undefined =>\n requireResolved().providers.tenancy.isolation === 'database-rls'\n ? true\n : options.session?.postgresRls;\n let permissionResolverPromise: Promise<PermissionResolver> | undefined;\n const permissionResolver = (): Promise<PermissionResolver> => {\n permissionResolverPromise ??= PermissionResolver.create(\n baseClassOptions('Permission'),\n ).catch((error: unknown) => {\n permissionResolverPromise = undefined;\n throw error;\n });\n return permissionResolverPromise;\n };\n\n const runAsPrincipal = async <\n T,\n P extends SmrtRuntimeBoundPrincipal = SmrtRuntimeBoundPrincipal,\n >(\n principal: P,\n fn: (bound: SmrtRuntimeBoundPrincipalSnapshot<P>) => Promise<T>,\n ): Promise<T> => {\n // One complete snapshot before the first await: authorization, the\n // permission/RLS context and the principal handed to `fn` all come from\n // it, and the caller's object is never read again (a caller mutating it\n // mid-flight cannot change the bound identity, tenant, binding or scopes).\n if (!principal || typeof principal !== 'object') {\n throw new Error('A bound principal is required.');\n }\n const snapshot = snapshotBoundPrincipal(principal) as P;\n // The snapshot has a null prototype: every read below is own data.\n const tokenDerived = isLocalMcpTokenPrincipal(snapshot);\n const userId = ownValue(snapshot, 'id') as P['id'];\n const tenantId = ownValue(snapshot, 'tenantId') as P['tenantId'];\n const snapshotScopes = ownValue(snapshot, 'scopes') as\n | readonly string[]\n | undefined;\n const requestedTenantBinding = ownValue(snapshot, 'tenantBinding') as\n | TenantBindingMode\n | undefined;\n const requestedScopes =\n snapshotScopes === undefined\n ? undefined\n : Object.freeze([...snapshotScopes]);\n if (\n requestedScopes !== undefined &&\n !requestedScopes.every((scope) => typeof scope === 'string')\n ) {\n throw new Error('A bound principal has malformed scopes.');\n }\n if (typeof userId !== 'string' || userId.length === 0) {\n throw new Error('A bound principal requires a user id.');\n }\n if (typeof tenantId !== 'string' || tenantId.length === 0) {\n throw new Error('A bound principal requires a tenant.');\n }\n // Resolution reads the base connection, before any RLS transaction opens.\n // A token-derived principal is direct-only. A copy that tries to widen\n // its binding is refused rather than silently narrowed.\n if (\n tokenDerived &&\n requestedTenantBinding !== undefined &&\n requestedTenantBinding !== 'direct'\n ) {\n throw new Error(\n 'A local MCP token principal binds only through its direct membership.',\n );\n }\n const binding: TenantBindingMode = tokenDerived\n ? 'direct'\n : (requestedTenantBinding ?? 'direct-or-inherited');\n if (binding !== 'direct' && binding !== 'direct-or-inherited') {\n throw new Error('Unknown tenant binding mode for a bound principal.');\n }\n const resolved = await resolveBoundMembershipPermissions({\n memberships: await MembershipCollection.create(\n baseClassOptions('Membership'),\n ),\n resolver: await permissionResolver(),\n userId,\n tenantId,\n binding,\n });\n if (!resolved) {\n throw new Error(\n binding === 'direct'\n ? 'The bound principal has no active direct membership in its tenant.'\n : 'The bound principal has no authorized membership in its tenant.',\n );\n }\n const cap = requestedScopes ? new Set(requestedScopes) : undefined;\n const permissions = [...resolved.permissions].filter(\n (permission) => !cap || cap.has(permission),\n );\n return withPrincipalPermissionContext(\n {\n ...baseClassOptions('Session'),\n userId,\n tenantId,\n permissions,\n enterTenantContext: true,\n postgresRls: postgresRls(),\n },\n () =>\n fn(\n // Null prototype like the snapshot: a field the principal lacks\n // (say `allowCrossTenant`) never resolves to an inherited value.\n Object.freeze(\n Object.assign(Object.create(null), snapshot, {\n id: userId,\n tenantId,\n tenantBinding: binding,\n scopes: Object.freeze([...permissions]),\n }),\n ) as SmrtRuntimeBoundPrincipalSnapshot<P>,\n ),\n );\n };\n\n const verifyLocalMcpToken = async (\n token: string,\n ): Promise<LocalMcpTokenPrincipal | null> => {\n if ((await resolvedRuntime()).profile !== 'local') return null;\n const local = await localRuntime();\n // A runtime without a token store has no tokens: fail closed.\n return (await local.mcpTokens?.verify(token)) ?? null;\n };\n\n const resolveMcpPrincipal = async (\n identity: SmrtRuntimeMcpIdentity,\n ): Promise<SmrtRuntimeMcpPrincipalMapping | null> => {\n if ((await resolvedRuntime()).profile === 'local') return null;\n if (!identity || typeof identity !== 'object') return null;\n const issuer = ownValue(identity, 'issuer');\n const subject = ownValue(identity, 'subject');\n if (\n typeof issuer !== 'string' ||\n issuer.length === 0 ||\n typeof subject !== 'string' ||\n subject.length === 0\n ) {\n return null;\n }\n // Every read is keyed by the verified issuer/subject and the ids derived\n // from it, and only ids leave this block (the PermissionResolver rule).\n return withSystemContext(async () => {\n const identities = await OidcIdentityCollection.create(\n baseClassOptions('OidcIdentity'),\n );\n let linked: Awaited<ReturnType<typeof identities.findBySubject>>;\n try {\n linked = await identities.findBySubject(issuer, subject);\n } catch {\n return null; // Ambiguous identity: deny.\n }\n if (!linked?.profileId) return null;\n const users = await UserCollection.create(baseClassOptions('User'));\n const owners = await users.list({\n where: { profileId: linked.profileId },\n limit: 2,\n });\n const user = owners.length === 1 ? owners[0] : undefined;\n if (\n typeof user?.id !== 'string' ||\n user.id.length === 0 ||\n user.status !== UserStatus.ACTIVE\n ) {\n return null;\n }\n const memberships = await MembershipCollection.create(\n baseClassOptions('Membership'),\n );\n const tenants = new Set(\n (await memberships.findActiveByUser(user.id))\n .map((membership) => membership.tenantId)\n .filter(\n (tenantId): tenantId is string =>\n typeof tenantId === 'string' && tenantId.length > 0,\n ),\n );\n if (tenants.size !== 1) return null;\n const [tenantId] = tenants;\n return Object.freeze({\n id: user.id,\n tenantId,\n kind: 'human' as const,\n tenantBinding: 'direct' as const,\n });\n });\n };\n\n let sessionServicePromise: Promise<SessionService> | undefined;\n const sessionService = (): Promise<SessionService> => {\n sessionServicePromise ??= (async () => {\n const service = new TenantAuthorizingSessionService(\n sessionServiceOptions(),\n requireResolved().providers.tenancy.context === 'required',\n );\n await service.initialize();\n return service;\n })().catch((error: unknown) => {\n sessionServicePromise = undefined;\n throw error;\n });\n return sessionServicePromise;\n };\n\n /**\n * Step 2. Downstream code runs at most once: a downstream throw (or a\n * failure after `resolve` was entered, such as an RLS commit) propagates\n * unchanged and `resolve` is never re-entered. A session-layer failure\n * before `resolve` fails closed with 500 and no authenticated locals, for\n * both RLS and non-RLS isolation.\n */\n const sessionStep: Handle = async ({ event, resolve }) => {\n const locals = runtimeLocals(event.locals);\n clearSessionLocals(locals);\n const rls = postgresRls();\n // A skipped path skips session loading only. Under RLS it still runs in\n // the request transaction, as an anonymous principal.\n const skipped = sessionSkipPaths.some((path) =>\n event.url.pathname.startsWith(path),\n );\n if (skipped && !rls) return resolve(event);\n const sessionId = skipped\n ? undefined\n : event.cookies.get(sessionCookieName);\n if (!sessionId && !rls) return resolve(event);\n\n let resolveEntered = false;\n let downstream: { error: unknown } | undefined;\n try {\n const service = await sessionService();\n return await withSessionPermissionContext(\n {\n ...sessionServiceOptions(),\n enterTenantContext: true,\n postgresRls: rls,\n sessionId: sessionId ?? null,\n sessionService: service,\n },\n async (context) => {\n if (context.session) {\n locals.user = context.user;\n locals.membership = context.membership ?? null;\n locals.permissions = context.permissions;\n locals.tenantId = context.tenantId;\n locals.sessionId = context.sessionId;\n if (context.tenantId) {\n verifiedSessionTenants.set(locals, context.tenantId);\n }\n }\n resolveEntered = true;\n try {\n return await resolve(event);\n } catch (error) {\n downstream = { error };\n throw error;\n }\n },\n );\n } catch (error) {\n if (downstream) throw downstream.error;\n if (resolveEntered) throw error;\n clearSessionLocals(locals);\n return new Response('Internal Server Error', { status: 500 });\n }\n };\n\n const sessionCookie: SmrtRuntimeSessionCookie = Object.freeze({\n name: options.session?.cookieName ?? 'sid',\n path: options.session?.cookiePath ?? '/',\n ...(options.session?.cookieDomain\n ? { domain: options.session.cookieDomain }\n : {}),\n sameSite: options.session?.cookieSameSite ?? 'lax',\n ...(options.session?.cookieSecure !== undefined\n ? { secure: options.session.cookieSecure }\n : {}),\n maxAgeSeconds: options.sessionTtlSeconds ?? DEFAULT_SESSION_TTL_SECONDS,\n });\n\n const init: ServerInit = () => ready();\n\n return Object.freeze({\n handle: composeHandles(\n readinessHandle,\n tenantSelectionHandle,\n sessionStep,\n authorizedTenantLocalsHandle,\n ),\n init,\n sessionCookie,\n resolvedRuntime,\n ready,\n applicationId,\n applicationStateRoot: () =>\n resolveApplicationStateRoot({\n appId: applicationId(),\n dataDirectory,\n sourceRoot,\n }),\n configurationFingerprint,\n databaseConfig,\n classOptions,\n getCollection,\n runAsPrincipal,\n verifyLocalMcpToken,\n resolveMcpPrincipal,\n localRuntime,\n deployedRuntime,\n health,\n readDiagnostics,\n });\n}\n\n/**\n * The default local writer lease: the state-root `writer.lease` shared with\n * `smrt app` operations, presenting the managed operation instance\n * (`SMRT_OPERATION_INSTANCE`) that `smrt app start` passes to its writer.\n */\nexport function defaultWriterLease(\n context: WriterLeaseContext,\n env: NodeJS.ProcessEnv = process.env,\n): WriterLease {\n return acquireWriterLease(\n prepareApplicationStateRoot({\n appId: context.appId,\n dataDirectory: context.dataDirectory,\n sourceRoot: context.sourceRoot,\n }),\n { operationInstance: env.SMRT_OPERATION_INSTANCE },\n );\n}\n\n/** Locals whose session tenant passed {@link isSessionTenantAuthorized}. */\nconst verifiedSessionTenants = new WeakMap<object, string>();\n\n/**\n * Session service whose loaded context carries a tenant only when that\n * tenant passed {@link isSessionTenantAuthorized}. A session bound to an\n * unauthorized tenant (or missing a required tenant) loads as no session, so\n * no identity, permissions, or tenant context is established for it.\n */\nclass TenantAuthorizingSessionService extends SessionService {\n private readonly tenantRequired: boolean;\n\n constructor(options: SessionServiceOptions, tenantRequired: boolean) {\n super(options);\n this.tenantRequired = tenantRequired;\n }\n\n override async loadSessionContext(\n sessionId: string,\n ): ReturnType<SessionService['loadSessionContext']> {\n const context = await super.loadSessionContext(sessionId);\n if (!context) return null;\n if (typeof context.tenantId === 'string' && context.tenantId.length > 0) {\n return isSessionTenantAuthorized(context) ? context : null;\n }\n return this.tenantRequired ? null : context;\n }\n}\n\nfunction clearSessionLocals(locals: SmrtRuntimeLocals): void {\n locals.user = null;\n locals.membership = null;\n locals.permissions = [];\n locals.tenantId = null;\n locals.sessionId = null;\n locals.tenantContext = undefined;\n verifiedSessionTenants.delete(locals);\n}\n\n/**\n * Step 3: publish the authorized tenant context only when this runtime's\n * session step verified the session tenant and it matches the active\n * AsyncLocalStorage context. Locals populated by any other session layer are\n * never published.\n */\nexport const authorizedTenantLocalsHandle: Handle = async ({\n event,\n resolve,\n}) => {\n const locals = runtimeLocals(event.locals);\n const activeContext = getCurrentTenant();\n if (\n locals.user &&\n locals.tenantId &&\n verifiedSessionTenants.get(locals) === locals.tenantId &&\n activeContext?.tenantId === locals.tenantId\n ) {\n locals.tenantContext = activeContext;\n }\n return resolve(event);\n};\n\n/**\n * Run handles in order. Unlike `sequence()` from `@sveltejs/kit/hooks`, this\n * needs no SvelteKit request store, so the composed handle is usable (and\n * testable) outside the Kit server runtime. The runtime's own handles never\n * pass resolve options; any options a handle passes are forwarded unchanged.\n */\nexport function composeHandles(...handles: readonly Handle[]): Handle {\n return ({ event, resolve }) => {\n const run: (\n index: number,\n current: Parameters<Handle>[0]['event'],\n resolveOptions?: Parameters<Parameters<Handle>[0]['resolve']>[1],\n ) => ReturnType<Handle> = (index, current, resolveOptions) => {\n const next = handles[index];\n if (!next) return resolve(current, resolveOptions);\n return next({\n event: current,\n resolve: (nextEvent, nextOptions) =>\n run(index + 1, nextEvent, nextOptions ?? resolveOptions),\n });\n };\n return run(0, event);\n };\n}\n\nfunction runtimeLocals(locals: unknown): SmrtRuntimeLocals {\n return locals as SmrtRuntimeLocals;\n}\n\n/**\n * No `runtime` block selects `local`; a present non-block value fails closed.\n * Shared with `smrt app` through smrt-config (#3446).\n */\nasync function loadConfiguredRuntime(): Promise<ResolvedApplicationRuntime> {\n return resolveEffectiveApplicationRuntime(await loadConfig());\n}\n\n/**\n * Deep, frozen, validated copy of a bound principal, taken before any await\n * (see `plain-data.ts` for what is rejected: poison keys, accessors,\n * inherited or non-plain data, sparse arrays, functions, deep nesting).\n * Known fields are type-checked from own data (`id`, `tenantId`, `kind`,\n * `tenantBinding`: strings; `scopes`, `roles`: string arrays;\n * `allowCrossTenant`: boolean). Other own enumerable fields (string or\n * symbol keys) are kept as deep-frozen copies.\n */\nfunction snapshotBoundPrincipal(\n principal: unknown,\n): Record<PropertyKey, unknown> {\n if (!principal || typeof principal !== 'object' || Array.isArray(principal)) {\n throw new Error('A bound principal is required.');\n }\n let snapshot: Record<PropertyKey, unknown>;\n try {\n snapshot = deepFrozenPlainCopy(principal) as Record<PropertyKey, unknown>;\n } catch (error) {\n if (error instanceof PlainDataError) {\n throw new Error(`A bound principal ${error.message}.`);\n }\n throw error;\n }\n for (const key of ['id', 'tenantId', 'kind', 'tenantBinding']) {\n const value = ownValue(snapshot, key);\n if (value !== undefined && typeof value !== 'string') {\n throw new Error(`A bound principal has a malformed ${key}.`);\n }\n }\n for (const key of ['scopes', 'roles']) {\n const value = ownValue(snapshot, key);\n if (\n value !== undefined &&\n (!Array.isArray(value) ||\n !value.every((entry) => typeof entry === 'string'))\n ) {\n throw new Error(`A bound principal has malformed ${key}.`);\n }\n }\n const allowCrossTenant = ownValue(snapshot, 'allowCrossTenant');\n if (allowCrossTenant !== undefined && typeof allowCrossTenant !== 'boolean') {\n throw new Error('A bound principal has a malformed allowCrossTenant.');\n }\n return snapshot;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sveltekit.js","names":["name","resolved","tenantId"],"sources":["../src/sveltekit/handlers.ts","../src/sveltekit/owner-setup.ts","../src/sveltekit/tenancy.ts","../src/sveltekit/runtime.ts","../src/sveltekit/sign-out.ts"],"sourcesContent":["/**\n * Mountable runtime routes: liveness health, authorized diagnostics, and the\n * server-owned session summary for the root layout.\n */\n\nimport {\n getRequestScopedDatabase,\n RoleCollection,\n} from '@happyvertical/smrt-users';\nimport { json, type RequestHandler } from '@sveltejs/kit';\nimport type { RuntimeDiagnostics } from '../runtime-diagnostics.js';\nimport type {\n ApplicationRuntimeDiagnosticsOptions,\n SmrtSvelteKitRuntime,\n} from './runtime.js';\n\n/** Explicit permission that grants diagnostics read without the owner role. */\nexport const RUNTIME_DIAGNOSTICS_READ_PERMISSION = 'runtime_diagnostics.read';\n\n/**\n * Mount as `src/routes/api/_runtime/health/+server.ts`:\n * `export const GET = createRuntimeHealthHandler(runtime);`\n *\n * Local responses add the application ID, managed process instance, and the\n * secret-free configuration fingerprint that process managers compare.\n * Deployed responses expose only schema version, status, and profile.\n */\nexport function createRuntimeHealthHandler(\n runtime: Pick<SmrtSvelteKitRuntime, 'health'>,\n): RequestHandler {\n return async () => json(await runtime.health());\n}\n\n/** The locals the diagnostics route authorizes against. */\nexport interface DiagnosticsPrincipalLocals {\n readonly user?: { readonly id?: unknown } | null;\n readonly membership?: {\n readonly userId?: unknown;\n readonly tenantId?: unknown;\n readonly roleId?: unknown;\n isActive?: () => boolean;\n } | null;\n readonly permissions?: readonly string[];\n readonly tenantId?: unknown;\n readonly sessionId?: unknown;\n}\n\ntype DiagnosticsInputValue<T> = T | (() => T);\n\nexport interface RuntimeDiagnosticsHandlerOptions {\n /** Supplies default diagnostics reading and owner-role lookup. */\n readonly runtime?: Pick<\n SmrtSvelteKitRuntime,\n 'readDiagnostics' | 'classOptions'\n >;\n /** Replace the runtime projection (must already be the public allowlist). */\n readonly readDiagnostics?: () => Promise<RuntimeDiagnostics>;\n /** Replace the owner-role lookup. Thrown errors deny. */\n readonly resolveRoleSlug?: (roleId: string) => Promise<string | null>;\n /** Override where the principal comes from. Defaults to `event.locals`. */\n readonly resolveLocals?: (event: {\n readonly locals: unknown;\n }) => DiagnosticsPrincipalLocals;\n /** Public tool inventory (sorted/capped by the projector). */\n readonly toolNames?: DiagnosticsInputValue<readonly string[]>;\n readonly schemaStatus?: DiagnosticsInputValue<\n ApplicationRuntimeDiagnosticsOptions['schemaStatus']\n >;\n readonly migrationStatus?: DiagnosticsInputValue<\n ApplicationRuntimeDiagnosticsOptions['migrationStatus']\n >;\n readonly workerHeartbeatAt?: () => Date | string | null | undefined;\n readonly recentErrors?: () => ApplicationRuntimeDiagnosticsOptions['recentErrors'];\n /** Testable clock. */\n readonly now?: () => Date;\n}\n\nfunction resolveInputValue<T>(\n value: DiagnosticsInputValue<T> | undefined,\n): T | undefined {\n return typeof value === 'function' ? (value as () => T)() : value;\n}\n\n/**\n * Mount as `src/routes/api/_runtime/diagnostics/+server.ts`:\n * `export const GET = createRuntimeDiagnosticsHandler({ runtime });`\n *\n * Authentication and authorization always complete before the private runtime\n * is read. Authorized callers hold `runtime_diagnostics.read` or the `owner`\n * role of their active, session-matching membership. Every failure is a\n * stable `{ schemaVersion: 1, error: { code } }` body.\n */\nexport function createRuntimeDiagnosticsHandler(\n options: RuntimeDiagnosticsHandlerOptions,\n): RequestHandler {\n const runtime = options.runtime;\n const readDiagnostics =\n options.readDiagnostics ??\n (runtime\n ? () =>\n runtime.readDiagnostics({\n toolNames: resolveInputValue(options.toolNames) ?? [],\n observedAt: options.now?.() ?? new Date(),\n schemaStatus: resolveInputValue(options.schemaStatus) ?? 'unknown',\n migrationStatus:\n resolveInputValue(options.migrationStatus) ?? 'unknown',\n workerHeartbeatAt: options.workerHeartbeatAt?.() ?? null,\n recentErrors: options.recentErrors?.() ?? [],\n })\n : undefined);\n const resolveRoleSlug =\n options.resolveRoleSlug ??\n (runtime\n ? async (roleId: string) => {\n const config = runtime.classOptions('Role');\n const requestDb = getRequestScopedDatabase();\n const roles = await RoleCollection.create(\n requestDb\n ? { ...config, db: requestDb as typeof config.db }\n : config,\n );\n return (await roles.findById(roleId))?.slug ?? null;\n }\n : undefined);\n if (!readDiagnostics || !resolveRoleSlug) {\n throw new TypeError(\n 'createRuntimeDiagnosticsHandler requires `runtime`, or both `readDiagnostics` and `resolveRoleSlug`.',\n );\n }\n const resolveLocals =\n options.resolveLocals ??\n ((event: { readonly locals: unknown }) =>\n event.locals as DiagnosticsPrincipalLocals);\n\n return async (event) => {\n const locals = resolveLocals(event);\n const principal = authenticatedPrincipal(locals);\n if (!principal) return stableError(401, 'authentication_required');\n\n let authorized =\n locals.permissions?.includes(RUNTIME_DIAGNOSTICS_READ_PERMISSION) ===\n true;\n if (!authorized) {\n try {\n authorized = (await resolveRoleSlug(principal.roleId)) === 'owner';\n } catch {\n authorized = false;\n }\n }\n if (!authorized) return stableError(403, 'authorization_denied');\n\n try {\n return json(await readDiagnostics());\n } catch {\n return stableError(503, 'diagnostics_unavailable');\n }\n };\n}\n\nfunction authenticatedPrincipal(\n locals: DiagnosticsPrincipalLocals | null | undefined,\n): { roleId: string } | null {\n const userId = locals?.user?.id;\n const tenantId = locals?.tenantId;\n const sessionId = locals?.sessionId;\n const membership = locals?.membership;\n if (\n typeof userId !== 'string' ||\n !userId ||\n typeof tenantId !== 'string' ||\n !tenantId ||\n typeof sessionId !== 'string' ||\n !sessionId ||\n !membership ||\n membership.userId !== userId ||\n membership.tenantId !== tenantId ||\n typeof membership.roleId !== 'string' ||\n !membership.roleId\n ) {\n return null;\n }\n try {\n if (membership.isActive?.() !== true) return null;\n } catch {\n return null;\n }\n return { roleId: membership.roleId };\n}\n\nfunction stableError(status: number, code: string): Response {\n return json({ schemaVersion: 1, error: { code } }, { status });\n}\n\n/** Server-owned session summary for the root layout. */\nexport interface SessionLayoutData {\n readonly session: {\n readonly authenticated: boolean;\n /** Tenant authorized by the active session. */\n readonly activeTenantId: string | null;\n /** URL-selected tenant candidate; display only, never authorization. */\n readonly selectedTenantSlug: string | null;\n /** Display identity of the signed-in user (id, email, name only). */\n readonly user: SessionLayoutUser | null;\n };\n}\n\n/**\n * The only user fields the layout data ever carries. `name` appears only when\n * `locals.user` carries one (the `User` model itself has no name; it lives on\n * the linked profile), so consumers fall back to `email`.\n */\nexport interface SessionLayoutUser {\n readonly id: string;\n readonly email: string;\n readonly name?: string;\n}\n\nfunction layoutUser(user: unknown): SessionLayoutUser | null {\n if (!user || typeof user !== 'object') return null;\n const { id, email, name } = user as Record<string, unknown>;\n if (typeof id !== 'string' || typeof email !== 'string') return null;\n return {\n id,\n email,\n ...(typeof name === 'string' && name ? { name } : {}),\n };\n}\n\n/**\n * Mount as `src/routes/+layout.server.ts`:\n * `export const load = createSessionLayoutLoad();`\n */\nexport function createSessionLayoutLoad(): (event: {\n readonly locals: unknown;\n}) => SessionLayoutData {\n return ({ locals }) => {\n const value = locals as {\n user?: unknown;\n tenantId?: string | null;\n selectedTenantSlug?: string | null;\n };\n return {\n session: {\n authenticated: Boolean(value.user),\n activeTenantId: value.tenantId ?? null,\n selectedTenantSlug: value.selectedTenantSlug ?? null,\n user: layoutUser(value.user),\n },\n };\n };\n}\n","/**\n * Server half of the local owner-setup page (`src/routes/setup/+page.server.ts`).\n *\n * Owner bootstrap is local-profile only, loopback-only, and single-use. The\n * runtime stores only an HMAC of the short-lived token; this module never\n * stores, logs, or returns claim errors, and it re-checks loopback custody on\n * every request instead of trusting the bind host alone.\n */\n\nimport type { Cookies } from '@sveltejs/kit';\nimport { type ActionFailure, fail, redirect } from '@sveltejs/kit';\nimport type { LocalOwnerClaimResult } from '../index.js';\nimport { removeOnboardingHandoff } from '../state-root.js';\nimport type { SmrtSvelteKitRuntime } from './runtime.js';\n\n/** Data returned by the setup page `load`. */\nexport interface OwnerSetupPageData {\n /** False when setup is claimed, disabled (deployed), or not loopback. */\n readonly available: boolean;\n /** One-time token from `?token=` (empty unless available). */\n readonly token: string;\n /**\n * State of the setup link, so the page can explain a dead link instead of\n * offering a form that cannot succeed. `ready`: a token is present and the\n * invitation has not expired (the token itself is verified on submit);\n * `missing`: no `?token=`; `expired`: the invitation lapsed; `unavailable`:\n * claimed, disabled, or not a loopback request.\n */\n readonly link: OwnerSetupLinkState;\n}\n\nexport type OwnerSetupLinkState =\n | 'ready'\n | 'missing'\n | 'expired'\n | 'unavailable';\n\n/** Stable failure codes returned by the setup action. */\nexport type OwnerSetupErrorCode =\n /** Not the local profile (HTTP 404). */\n | 'setup_disabled'\n /** Request is not from a loopback client on a loopback host (HTTP 403). */\n | 'setup_unavailable'\n /** Missing token, blank name, or email without `@` (HTTP 400). */\n | 'setup_invalid_input'\n /** Token wrong, expired, or already used; owner already claimed (HTTP 400). */\n | 'setup_invalid';\n\n/** `form` data returned with `fail(status, …)`. */\nexport interface OwnerSetupFailure {\n readonly code: OwnerSetupErrorCode;\n /** Fixed, secret-free message; never claim error text. */\n readonly message: string;\n}\n\n/** Form field names posted to the setup action. */\nexport const OWNER_SETUP_FIELDS = Object.freeze({\n token: 'token',\n name: 'name',\n email: 'email',\n tenantName: 'tenantName',\n} as const);\n\nexport const DEFAULT_OWNER_SETUP_MESSAGES: Readonly<\n Record<OwnerSetupErrorCode, string>\n> = Object.freeze({\n setup_disabled: 'Local owner setup is disabled.',\n setup_unavailable: 'Local owner setup is available only from this device.',\n setup_invalid_input: 'Name, email, and a valid setup token are required.',\n setup_invalid:\n 'The setup invitation is invalid, expired, or already used. Run pnpm app:stop, pnpm app:recover, pnpm app:start, then pnpm app:open.',\n});\n\n/** The request fields the setup page reads. */\nexport interface OwnerSetupEvent {\n readonly url: URL;\n readonly locals: unknown;\n getClientAddress(): string;\n}\n\nexport interface OwnerSetupActionEvent extends OwnerSetupEvent {\n readonly request: Request;\n readonly cookies: Pick<Cookies, 'set'>;\n}\n\nexport interface OwnerSetupPageOptions {\n /** Where a signed-in visitor or a successful claim is sent. Default `/`. */\n readonly redirectTo?: string;\n /** Replace fixed failure messages (for example app-specific recovery steps). */\n readonly messages?: Partial<Record<OwnerSetupErrorCode, string>>;\n /** Override where the signed-in user comes from. Defaults to `locals.user`. */\n readonly resolveUser?: (event: OwnerSetupEvent) => unknown;\n /**\n * Set false to keep the `smrt app` onboarding hand-off files after a claim.\n * By default the runtime removes `onboarding.json` and\n * `onboarding-launch.html` from the private state root once the claim has\n * committed, so `pnpm app:open` stops offering the spent invitation.\n */\n readonly removeOnboardingHandoff?: boolean;\n /**\n * Best-effort extra cleanup after a successful claim (the hand-off files are\n * already removed by default). Errors are ignored: the owner and session are\n * already authoritative and a stale token now fails closed.\n */\n readonly onOwnerClaimed?: (\n result: LocalOwnerClaimResult,\n event: OwnerSetupActionEvent,\n ) => void | Promise<void>;\n}\n\nexport interface OwnerSetupPage {\n readonly load: (event: OwnerSetupEvent) => Promise<OwnerSetupPageData>;\n readonly actions: {\n readonly default: (\n event: OwnerSetupActionEvent,\n ) => Promise<ActionFailure<OwnerSetupFailure>>;\n };\n}\n\n/**\n * Mount as `src/routes/setup/+page.server.ts`:\n *\n * ```ts\n * import { createOwnerSetupPage } from '@happyvertical/smrt-app-runtime/sveltekit';\n * import { runtime } from '../../hooks.server';\n *\n * export const { load, actions } = createOwnerSetupPage(runtime);\n * ```\n *\n * The form posts to the `default` action with fields `token` (hidden),\n * `name`, `email`, and optional `tenantName`. Success sets the session cookie\n * and redirects 303; failures return `fail(status, { code, message })`.\n */\nexport function createOwnerSetupPage(\n runtime: Pick<\n SmrtSvelteKitRuntime,\n 'resolvedRuntime' | 'localRuntime' | 'sessionCookie'\n > &\n Partial<Pick<SmrtSvelteKitRuntime, 'applicationStateRoot'>>,\n options: OwnerSetupPageOptions = {},\n): OwnerSetupPage {\n const redirectTo = options.redirectTo ?? '/';\n const messages = { ...DEFAULT_OWNER_SETUP_MESSAGES, ...options.messages };\n const failure = (status: number, code: OwnerSetupErrorCode) =>\n fail(status, { code, message: messages[code] } as OwnerSetupFailure);\n const resolveUser =\n options.resolveUser ??\n ((event: OwnerSetupEvent) => (event.locals as { user?: unknown }).user);\n\n const load = async (event: OwnerSetupEvent): Promise<OwnerSetupPageData> => {\n if (resolveUser(event)) redirect(303, redirectTo);\n const resolved = await runtime.resolvedRuntime();\n if (resolved.profile !== 'local' || !isLoopbackRequest(event)) {\n return { available: false, token: '', link: 'unavailable' };\n }\n const local = await runtime.localRuntime();\n const diagnostics = await local.diagnostics();\n const status = diagnostics.bootstrap.status;\n if (status === 'claimed') {\n return { available: false, token: '', link: 'unavailable' };\n }\n const token = event.url.searchParams.get(OWNER_SETUP_FIELDS.token) || '';\n if (status === 'expired')\n return { available: true, token: '', link: 'expired' };\n if (!token) return { available: true, token: '', link: 'missing' };\n return { available: true, token, link: 'ready' };\n };\n\n const claim = async (\n event: OwnerSetupActionEvent,\n ): Promise<ActionFailure<OwnerSetupFailure>> => {\n const resolved = await runtime.resolvedRuntime();\n if (resolved.profile !== 'local') return failure(404, 'setup_disabled');\n if (!isLoopbackRequest(event)) return failure(403, 'setup_unavailable');\n\n let form: FormData;\n try {\n form = await event.request.formData();\n } catch {\n return failure(400, 'setup_invalid_input');\n }\n const field = (name: string) => {\n const value = form.get(name);\n return typeof value === 'string' ? value : '';\n };\n const token = field(OWNER_SETUP_FIELDS.token);\n const name = field(OWNER_SETUP_FIELDS.name);\n const email = field(OWNER_SETUP_FIELDS.email);\n const tenantName = field(OWNER_SETUP_FIELDS.tenantName).trim();\n if (!token || !name.trim() || !email.includes('@')) {\n return failure(400, 'setup_invalid_input');\n }\n\n const local = await runtime.localRuntime();\n let result: LocalOwnerClaimResult;\n try {\n result = await local.claimOwner({\n token,\n name,\n email,\n ...(tenantName ? { tenantName } : {}),\n userAgent: event.request.headers.get('user-agent') || undefined,\n ipAddress: event.getClientAddress(),\n });\n } catch {\n return failure(400, 'setup_invalid');\n }\n\n // claimOwner resolved, so the claim transaction has committed. Clean up\n // before cookie serialization, which can throw on a malformed config.\n if (\n options.removeOnboardingHandoff !== false &&\n runtime.applicationStateRoot\n ) {\n try {\n removeOnboardingHandoff(runtime.applicationStateRoot());\n } catch {\n // Best effort: the spent token already fails closed.\n }\n }\n const cookie = runtime.sessionCookie;\n event.cookies.set(cookie.name, result.sessionId, {\n path: cookie.path,\n ...(cookie.domain ? { domain: cookie.domain } : {}),\n httpOnly: true,\n secure: cookie.secure ?? event.url.protocol === 'https:',\n sameSite: cookie.sameSite,\n maxAge: cookie.maxAgeSeconds,\n });\n try {\n await options.onOwnerClaimed?.(result, event);\n } catch {\n // Best effort only; see OwnerSetupPageOptions.onOwnerClaimed.\n }\n redirect(303, redirectTo);\n };\n\n return Object.freeze({\n load,\n actions: Object.freeze({ default: claim }),\n });\n}\n\n/**\n * True only when both the peer address and the addressed host are loopback.\n * The host check rejects tunnels and DNS-rebinding hosts that forward to a\n * loopback listener; a throwing `getClientAddress` is treated as remote.\n */\nexport function isLoopbackRequest(event: {\n readonly url: URL;\n getClientAddress(): string;\n}): boolean {\n let address: string;\n try {\n address = event.getClientAddress();\n } catch {\n return false;\n }\n return isLoopbackAddress(address) && isLoopbackHostname(event.url.hostname);\n}\n\n/** Loopback IP literal: `127.0.0.0/8`, `::1`, or IPv4-mapped `127/8`. */\nexport function isLoopbackAddress(value: string | null | undefined): boolean {\n if (typeof value !== 'string') return false;\n let host = value\n .trim()\n .toLowerCase()\n .replace(/^\\[|\\]$/g, '');\n if (host === '::1' || host === '0:0:0:0:0:0:0:1') return true;\n if (host.startsWith('::ffff:')) host = host.slice('::ffff:'.length);\n const octets = host.split('.');\n return (\n octets.length === 4 &&\n octets.every((part) => /^\\d{1,3}$/.test(part) && Number(part) <= 255) &&\n Number(octets[0]) === 127\n );\n}\n\n/** Loopback host name as SvelteKit reports it in `url.hostname`. */\nexport function isLoopbackHostname(hostname: string): boolean {\n const host = hostname.trim().toLowerCase().replace(/\\.$/, '');\n return host === 'localhost' || isLoopbackAddress(host);\n}\n","/**\n * Tenant *selection* for SvelteKit requests.\n *\n * Selection and authorization are deliberately separate:\n *\n * - a selector maps a trusted URL shape (by default the leading subdomain) to\n * an active tenant record and publishes it as `locals.selectedTenant*`;\n * - the signed session (`createSessionHandler({ enterTenantContext: true })`)\n * authorizes the signed-in user's membership and establishes the request\n * tenant context.\n *\n * A selected tenant is useful for login and membership-gated switch flows, but\n * it never becomes ambient query authority by itself. The default selector\n * ignores client-provided tenant headers.\n */\n\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport { TenantCollection, TenantStatus } from '@happyvertical/smrt-users';\n\n/** The request fields a tenant selector may read. */\nexport interface TenantSelectionEvent {\n readonly url: URL;\n readonly request: { readonly headers: Headers };\n}\n\n/** URL-derived tenant candidate. Never authorization. */\nexport interface TenantSelection {\n /** Database ID for an active tenant, or null when none resolves. */\n readonly tenantId: string | null;\n /** URL-derived slug. Informational until membership authorizes a switch. */\n readonly tenantSlug: string | null;\n}\n\n/**\n * Replaceable selection extension point. Implementations must preserve the\n * rule that selection alone never establishes authorization context.\n */\nexport type TenantSelector = (\n event: TenantSelectionEvent,\n) => TenantSelection | Promise<TenantSelection>;\n\nconst ROOT_LIKE_HOSTS = new Set(['localhost', '127.0.0.1', '::1', '[::1]']);\nconst RESERVED_SUBDOMAINS = new Set(['www', 'api', 'app', 'admin']);\nconst NO_SELECTION: TenantSelection = Object.freeze({\n tenantId: null,\n tenantSlug: null,\n});\n\n/**\n * Pure URL parser used by the default selector.\n *\n * Set `TENANT_BASE_DOMAIN` (or pass `baseDomain`) in production so\n * multi-label public suffixes are never guessed. Without it, the fallback is\n * intended only for local domains such as `acme.demo.local`.\n */\nexport function selectTenantSlug(\n url: URL,\n baseDomain: string | undefined = process.env.TENANT_BASE_DOMAIN,\n): string | null {\n const hostname = url.hostname.toLowerCase();\n if (\n ROOT_LIKE_HOSTS.has(hostname) ||\n hostname.startsWith('[') ||\n /^\\d{1,3}(\\.\\d{1,3}){3}$/.test(hostname)\n ) {\n return null;\n }\n\n const normalizedBase = baseDomain\n ?.trim()\n .toLowerCase()\n .replace(/^\\.+|\\.+$/g, '');\n\n let candidate: string | undefined;\n if (normalizedBase) {\n if (hostname === normalizedBase) return null;\n const suffix = `.${normalizedBase}`;\n if (!hostname.endsWith(suffix)) return null;\n candidate = hostname.slice(0, -suffix.length).split('.')[0];\n } else {\n const labels = hostname.split('.');\n if (labels.length < 3) return null;\n candidate = labels[0];\n }\n\n return candidate && !RESERVED_SUBDOMAINS.has(candidate) ? candidate : null;\n}\n\nfunction isMissingTenantTable(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n const code = (error as Error & { code?: string }).code;\n return (\n code === '42P01' ||\n error.message.includes('relation \"tenants\" does not exist') ||\n error.message.includes('no such table: tenants')\n );\n}\n\nexport interface SubdomainTenantSelectorOptions {\n /** Explicit public base domain. Defaults to `TENANT_BASE_DOMAIN`. */\n readonly baseDomain?: string;\n /** Collection options for the `Tenant` lookup. */\n readonly classOptions: () => SmrtClassOptions;\n}\n\n/**\n * Default selector: the leading subdomain names an ACTIVE tenant.\n *\n * A missing `tenants` table (a fresh project before its first migration)\n * yields no selection; the runtime never creates that table.\n */\nexport function createSubdomainTenantSelector(\n options: SubdomainTenantSelectorOptions,\n): TenantSelector {\n return async (event) => {\n const tenantSlug = selectTenantSlug(event.url, options.baseDomain);\n if (!tenantSlug) return NO_SELECTION;\n let tenantId: string | null;\n try {\n const tenants = await TenantCollection.create(options.classOptions());\n const tenant = await tenants.findBySlug(tenantSlug);\n tenantId =\n tenant?.status === TenantStatus.ACTIVE ? (tenant.id ?? null) : null;\n } catch (error) {\n if (!isMissingTenantTable(error)) throw error;\n tenantId = null;\n }\n return { tenantId, tenantSlug };\n };\n}\n\n/** Coerce an application selector's result into the published shape. */\nexport function normalizeTenantSelection(\n selection: TenantSelection | null | undefined,\n): TenantSelection {\n const tenantId =\n typeof selection?.tenantId === 'string' && selection.tenantId\n ? selection.tenantId\n : null;\n const tenantSlug =\n typeof selection?.tenantSlug === 'string' && selection.tenantSlug\n ? selection.tenantSlug\n : null;\n return { tenantId, tenantSlug };\n}\n","/**\n * SvelteKit server composition for the validated runtime profiles.\n *\n * Request foundation, in order:\n *\n * 0. Wait for the fail-closed runtime startup gate (`init`).\n * 1. Resolve a URL tenant candidate into `locals.selectedTenant*`. This is\n * selection only and never enters AsyncLocalStorage tenant context.\n * 2. Load the signed session. Its tenant is accepted only for a verified\n * active direct or legitimately inherited membership\n * ({@link isSessionTenantAuthorized}, the deployed runtime's rule);\n * otherwise the request is unauthenticated. An accepted session enters the\n * tenant context and permission set for downstream code, inside the\n * request RLS transaction when the profile selects `database-rls`.\n * 3. Publish the authorized context on `locals.tenantContext` only when it\n * matches the verified session tenant.\n *\n * This ordering prevents a spoofed header or hostname from becoming query\n * authority. Membership-gated tenant switching belongs in an explicit action\n * using `switchSessionTenant()` from `@happyvertical/smrt-users/sveltekit`.\n */\n\nimport {\n loadConfig,\n type ResolvedApplicationRuntime,\n resolveEffectiveApplicationRuntime,\n} from '@happyvertical/smrt-config';\nimport {\n ObjectRegistry,\n type SmrtClassOptions,\n type SmrtCollection,\n type SmrtObject,\n} from '@happyvertical/smrt-core';\nimport { OidcIdentityCollection } from '@happyvertical/smrt-profiles';\nimport {\n enableTenancy,\n getCurrentTenant,\n isTenancyEnabled,\n type MinimalTenantContext,\n withSystemContext,\n} from '@happyvertical/smrt-tenancy';\nimport {\n getCurrentSessionPermissionContext,\n getRequestScopedDatabase,\n MembershipCollection,\n PermissionResolver,\n SessionService,\n type SessionServiceOptions,\n UserCollection,\n UserStatus,\n withPrincipalPermissionContext,\n withSessionPermissionContext,\n} from '@happyvertical/smrt-users';\nimport type {\n SessionHandlerOptions,\n SessionLocals,\n} from '@happyvertical/smrt-users/sveltekit';\nimport { type DatabaseInterface, getDatabase } from '@happyvertical/sql';\nimport type { Handle, ServerInit } from '@sveltejs/kit';\nimport {\n resolveApplicationId,\n runtimeConfigurationFingerprint,\n} from '../application-identity.js';\nimport {\n type DeployedApplicationRuntime,\n type DeployedApplicationRuntimeProfile,\n initializeDeployedApplicationRuntime,\n isSessionTenantAuthorized,\n type PublicAuthenticationProvider,\n} from '../deployed-runtime.js';\nimport {\n resolveBoundMembershipPermissions,\n type TenantBindingMode,\n} from '../direct-membership.js';\nimport {\n initializeLocalApplicationRuntime,\n isLocalMcpTokenPrincipal,\n type LocalApplicationRuntime,\n type LocalMcpTokenPrincipal,\n type LocalOwnerBootstrapInvitation,\n resolveLocalRuntimePaths,\n validateApplicationId,\n} from '../index.js';\nimport {\n deepFrozenPlainCopy,\n ownValue,\n PlainDataError,\n} from '../plain-data.js';\nimport {\n projectRuntimeDiagnostics,\n type RuntimeDiagnostics,\n type RuntimeDiagnosticsProjectionInput,\n} from '../runtime-diagnostics.js';\nimport {\n prepareApplicationStateRoot,\n resolveApplicationStateRoot,\n} from '../state-root.js';\nimport { acquireWriterLease, type WriterLease } from '../writer-lease.js';\nimport {\n createSubdomainTenantSelector,\n normalizeTenantSelection,\n type TenantSelector,\n} from './tenancy.js';\n\n/** `App.Locals` fields populated by the SMRT runtime `handle`. */\nexport interface SmrtRuntimeLocals extends SessionLocals {\n /**\n * URL-selected tenant candidate. Never use this as authorization; the\n * session-authorized tenant remains `tenantId` from {@link SessionLocals}.\n */\n selectedTenantId: string | null;\n selectedTenantSlug: string | null;\n /** Full context for the authorized session tenant, when present. */\n tenantContext?: MinimalTenantContext;\n}\n\n/** Provider component whose readiness a deployed web process must prove. */\nexport type ProviderReadinessComponent =\n | 'authentication'\n | 'assets'\n | 'secrets';\n\n/**\n * Build a provider-owned readiness probe. The probe must check the real\n * backing service and must never return or log credentials.\n */\nexport type ProviderReadinessFactory = (\n component: ProviderReadinessComponent,\n context: { readonly profile: string; readonly provider: string },\n) => () => Promise<void>;\n\n/** Context handed to an application-owned writer lease. */\nexport interface WriterLeaseContext {\n readonly appId: string;\n readonly dataDirectory: string | undefined;\n readonly sourceRoot: string;\n}\n\n/**\n * Held for the life of the local web process; released on failed startup.\n * The root entry's `acquireWriterLease()` returns one.\n */\nexport type { WriterLease } from '../writer-lease.js';\n\n/** Inputs to the public diagnostics projection that only the app can supply. */\nexport interface ApplicationRuntimeDiagnosticsOptions {\n readonly toolNames: readonly string[];\n readonly observedAt: Date;\n /** Application-owned schema verification seam; runtime liveness is not proof. */\n readonly schemaStatus?: 'not-ready' | 'ready' | 'unknown';\n /** Application-owned migration tracker seam; database liveness is not proof. */\n readonly migrationStatus?: 'current' | 'failed' | 'pending' | 'unknown';\n /** Bounded worker seam supplied by the deployment's lease/heartbeat adapter. */\n readonly workerHeartbeatAt?: Date | string | null;\n /** Stable code/timestamp pairs only; raw errors never cross this seam. */\n readonly recentErrors?: readonly {\n readonly code?: unknown;\n readonly at?: unknown;\n }[];\n}\n\n/** Liveness body served by the mounted health route. */\nexport interface RuntimeHealthBody {\n readonly schemaVersion: 1;\n readonly status: 'ready';\n readonly profile: ResolvedApplicationRuntime['profile'];\n /** Local profile only: canonical application ID. */\n readonly application?: string;\n /** Local profile only: managed process instance (`SMRT_PROCESS_INSTANCE`). */\n readonly instance?: string | null;\n /** Local profile only: {@link runtimeConfigurationFingerprint}. */\n readonly configuration?: string;\n}\n\n/** Session-handler options the application may tune. */\nexport type SmrtRuntimeSessionOptions = Omit<\n SessionHandlerOptions,\n 'enterTenantContext'\n>;\n\n/** App-specific options for {@link createSmrtSvelteKitRuntime}. */\nexport interface SmrtSvelteKitRuntimeOptions {\n /**\n * Explicit application ID. Defaults to `SMRT_APP_ID` (strictly validated)\n * or the encoded `package.json` name under `sourceRoot`.\n */\n readonly appId?: string;\n /** Application source checkout. Defaults to `process.cwd()`. */\n readonly sourceRoot?: string;\n /** Local data root. Defaults to `SMRT_DATA_DIR`, then the OS data directory. */\n readonly dataDirectory?: string;\n /** Environment to read. Defaults to `process.env`. */\n readonly env?: NodeJS.ProcessEnv;\n /**\n * Resolved runtime profile. Defaults to `smrt.config` `runtime` when\n * present, otherwise the `local` profile; a present value that is not a\n * runtime block (`null`, `false`, `0`, `''`) fails closed.\n */\n readonly runtime?:\n | ResolvedApplicationRuntime\n | (() => ResolvedApplicationRuntime | Promise<ResolvedApplicationRuntime>);\n /**\n * Explicit, idempotent application migration hook, run during `init`.\n * The runtime never synthesizes application tables itself.\n */\n readonly prepareDatabase?: (db: DatabaseInterface) => Promise<void>;\n /**\n * Local HTTP bind host. Defaults to `HOST`, or `127.0.0.1` when\n * `NODE_ENV=development`. Local production startup without one fails.\n */\n readonly bindHost?: string;\n /** Opt in to persisted local background jobs. Default `SMRT_BACKGROUND_JOBS === 'true'`. */\n readonly backgroundJobs?: boolean;\n /** Opt in to application-defined paid capabilities. Default false. */\n readonly paidCapabilities?: boolean;\n /** Owner-bootstrap token lifetime (1..900 seconds). */\n readonly bootstrapTtlSeconds?: number;\n /** Session lifetime for the owner-bootstrap session cookie. Default 7 days. */\n readonly sessionTtlSeconds?: number;\n /**\n * Local single-writer lease held for the web process, released when local\n * startup fails. Defaults to the state-root writer lease shared with\n * `smrt app` operations ({@link defaultWriterLease}: `writer.lease` under\n * `prepareApplicationStateRoot()`, presenting `SMRT_OPERATION_INSTANCE`).\n * Pass a factory to replace it, or `false` to hold no lease. Deployed\n * profiles never take it.\n */\n readonly acquireWriterLease?:\n | ((context: WriterLeaseContext) => WriterLease)\n | false;\n /**\n * Deployed provider readiness probes. Required by `self-hosted` and `cloud`;\n * deployed startup fails closed without them.\n */\n readonly providerReadiness?: ProviderReadinessFactory;\n /** Replace the default subdomain tenant selector. Selection only. */\n readonly selectTenant?: TenantSelector;\n /** Base domain for the default selector. Defaults to `TENANT_BASE_DOMAIN`. */\n readonly tenantBaseDomain?: string;\n /** Session handler tuning. `enterTenantContext` is always `true`. */\n readonly session?: SmrtRuntimeSessionOptions;\n /** Per-class overrides layered over the runtime database config. */\n readonly classOverrides?: Readonly<Record<string, Partial<SmrtClassOptions>>>;\n /** Call `enableTenancy()` if it is not already enabled. Default true. */\n readonly enableTenancy?: boolean;\n /**\n * Receives an owner-bootstrap invitation that this process's startup newly\n * issued (the plaintext is returned exactly once; only its HMAC is stored).\n * Use it to show a loopback `/setup?token=` URL. Errors are ignored so a\n * failing presenter never blocks startup. Default: not presented.\n */\n readonly onBootstrapInvitation?: (\n invitation: LocalOwnerBootstrapInvitation,\n context: { readonly bindHost: string },\n ) => void;\n /** Testable clock for the local runtime. */\n readonly now?: () => Date;\n}\n\n/** Cookie attributes for a session established outside the session handler. */\nexport interface SmrtRuntimeSessionCookie {\n readonly name: string;\n readonly path: string;\n readonly domain?: string;\n readonly sameSite: 'strict' | 'lax' | 'none';\n /** `undefined` means \"secure when the request is HTTPS\". */\n readonly secure?: boolean;\n readonly maxAgeSeconds: number;\n}\n\nexport type { TenantBindingMode } from '../direct-membership.js';\n\n/** The composed runtime; `handle` and `init` are bound and destructurable. */\n/** A principal a route authenticated itself, for {@link SmrtSvelteKitRuntime.runAsPrincipal}. */\nexport interface SmrtRuntimeBoundPrincipal {\n readonly id: string;\n readonly tenantId?: string | null;\n /** Granted scopes that cap the live permission set (e.g. token scopes). */\n readonly scopes?: readonly string[];\n /**\n * How tenant authority may be established, set by the authenticating\n * adapter (never request input). `direct` (local owner tokens, the default\n * hosted resolver) needs an active direct membership in exactly\n * `tenantId`. `direct-or-inherited` (the default; hosted application-owned\n * mappings) applies the session step's rule: that direct row, or with no\n * direct row, authority inherited from an active inheritable ancestor.\n */\n readonly tenantBinding?: TenantBindingMode;\n}\n\n/** A verified hosted access-token identity (see `McpVerifiedIdentity`). */\nexport interface SmrtRuntimeMcpIdentity {\n /** Exact issuer the token was verified against. */\n readonly issuer: string;\n /** Verified `sub` claim. */\n readonly subject: string;\n}\n\n/** The current account and tenant a hosted MCP identity maps to. */\nexport interface SmrtRuntimeMcpPrincipalMapping {\n readonly id: string;\n readonly tenantId: string;\n readonly kind: 'human';\n /** The default resolver maps only direct memberships. */\n readonly tenantBinding: 'direct';\n}\n\n/** `T` with every nested array and object field readonly. */\nexport type SmrtRuntimeDeepReadonly<T> = T extends readonly (infer U)[]\n ? readonly SmrtRuntimeDeepReadonly<U>[]\n : T extends object\n ? { readonly [K in keyof T]: SmrtRuntimeDeepReadonly<T[K]> }\n : T;\n\n/**\n * The frozen principal `runAsPrincipal` hands its callback: a deep, frozen\n * snapshot of the caller's principal taken before any await, with the\n * effective `tenantBinding` and effective (frozen) `scopes`.\n */\nexport type SmrtRuntimeBoundPrincipalSnapshot<P> = SmrtRuntimeDeepReadonly<\n Omit<P, 'scopes' | 'tenantBinding'>\n> & {\n readonly scopes: readonly string[];\n readonly tenantBinding: TenantBindingMode;\n};\n\nexport interface SmrtSvelteKitRuntime {\n /** `hooks.server.ts` `handle`. */\n readonly handle: Handle;\n /** `hooks.server.ts` `init`: the fail-closed startup gate. */\n readonly init: ServerInit;\n /** Session cookie attributes shared with owner setup. */\n readonly sessionCookie: SmrtRuntimeSessionCookie;\n /** Resolve the runtime profile (memoized; retried after failure). */\n resolvedRuntime(): Promise<ResolvedApplicationRuntime>;\n /** Await profile-specific startup (same promise as `init`). */\n ready(): Promise<void>;\n /** Canonical application ID. */\n applicationId(): string;\n /**\n * Private state root shared with the `smrt app` operator commands (writer\n * lease, operation lock, onboarding hand-off files). Resolves the path only;\n * it does not create it.\n */\n applicationStateRoot(): string;\n /** Secret-free configuration fingerprint. Requires a resolved runtime. */\n configurationFingerprint(): string;\n /**\n * Database for SMRT collections. Requires a resolved runtime. Inside a\n * request running in the RLS transaction (`database-rls` isolation, or\n * `session.postgresRls`) this is the transaction-bound request database;\n * otherwise the base configuration. Call it per request and never retain\n * the result (or collections built from it) beyond that request.\n */\n databaseConfig(): SmrtClassOptions['db'];\n /**\n * Collection options for a class: {@link databaseConfig} plus\n * `classOverrides`. A class whose override sets `db` keeps that database.\n * Same per-request rule as {@link databaseConfig}.\n */\n classOptions(className: string): SmrtClassOptions;\n /**\n * The registered collection for a class, built from {@link classOptions}\n * evaluated on this call: inside an RLS request it is bound to the request\n * transaction. Generated API routes resolve collections through it. Call it\n * per request and never retain the result beyond that request.\n */\n getCollection<T extends SmrtObject = SmrtObject>(\n className: string,\n ): Promise<SmrtCollection<T>>;\n /**\n * Run `fn` as a principal the route verified itself (for example a bearer\n * token mapped by an MCP route), replacing the cookie session's permission\n * context for that call. The principal's permissions are resolved live from\n * its membership in `tenantId` and, when `scopes` is given, capped to it;\n * under the RLS transaction rule of {@link databaseConfig} a fresh\n * transaction publishes that user, tenant and permission set, and\n * `databaseConfig()`/`classOptions()` return it inside `fn`. Rejects (before\n * `fn` runs) without a user id, a tenant, or a membership its\n * `tenantBinding` accepts: `direct` needs an active direct membership in\n * exactly that tenant; `direct-or-inherited` (default) also accepts\n * authority inherited from an active inheritable ancestor when no direct\n * row exists. A suspended or pending direct row is always authoritative.\n *\n * `principal` is read once, before any await: `fn` receives a frozen copy\n * of that snapshot (its effective `tenantBinding`, and `scopes` replaced by\n * the effective, frozen permission set: live permissions capped by the\n * given scopes), never the caller's object. Callers\n * that authorize in code (rather than through database policy, as under\n * `application` isolation) must authorize with these effective scopes so a\n * revoked permission takes effect even while a token still carries it.\n */\n runAsPrincipal<\n T,\n P extends SmrtRuntimeBoundPrincipal = SmrtRuntimeBoundPrincipal,\n >(\n principal: P,\n fn: (bound: SmrtRuntimeBoundPrincipalSnapshot<P>) => Promise<T>,\n ): Promise<T>;\n /**\n * Verify an owner-minted local MCP token (`smrt app token`). Resolves the\n * token's owner principal with its scopes capped to the owner's live\n * permissions, or `null` for anything else: a malformed, unknown, revoked\n * or expired token, an inactive owner or membership, or any non-`local`\n * profile. Pass the runtime as `createHostedMcpResourceAuth({ runtime })`\n * so `/mcp` accepts these tokens in the local profile.\n */\n verifyLocalMcpToken(token: string): Promise<LocalMcpTokenPrincipal | null>;\n /**\n * Default membership-backed hosted MCP principal mapping: the verified\n * issuer/subject must be linked (`oidc_identities`) to exactly one active\n * user with exactly one active direct tenant membership. Anything else\n * (unlinked, ambiguous, inactive, several tenants, the `local` profile)\n * resolves `null`, which denies the request. Token claims never select the\n * tenant. Applications with multi-tenant users pass their own\n * `resolvePrincipal`, which takes precedence.\n */\n resolveMcpPrincipal(\n identity: SmrtRuntimeMcpIdentity,\n ): Promise<SmrtRuntimeMcpPrincipalMapping | null>;\n /** The local runtime. Rejects outside the `local` profile. */\n localRuntime(): Promise<LocalApplicationRuntime>;\n /** The deployed runtime. Rejects in the `local` profile. */\n deployedRuntime(): Promise<DeployedApplicationRuntime>;\n /** Liveness body for the health route (awaits startup). */\n health(): Promise<RuntimeHealthBody>;\n /**\n * Read private runtime state and project it onto the public allowlist.\n * Call only after the route has authenticated and authorized its caller.\n */\n readDiagnostics(\n options: ApplicationRuntimeDiagnosticsOptions,\n ): Promise<RuntimeDiagnostics>;\n}\n\n/** @internal Test and composition seams; not part of the public entry. */\nexport interface SmrtSvelteKitRuntimeSeams {\n readonly initializeLocal?: typeof initializeLocalApplicationRuntime;\n readonly initializeDeployed?: typeof initializeDeployedApplicationRuntime;\n readonly loadRuntime?: () => Promise<ResolvedApplicationRuntime>;\n}\n\nconst DEFAULT_SESSION_TTL_SECONDS = 7 * 24 * 60 * 60;\n\n/**\n * Compose runtime startup, tenant selection, the signed session, and\n * authorized-tenant locals into a SvelteKit `{ handle, init }` pair.\n *\n * @example\n * ```ts\n * // src/hooks.server.ts\n * import { createSmrtSvelteKitRuntime } from '@happyvertical/smrt-app-runtime/sveltekit';\n *\n * export const runtime = createSmrtSvelteKitRuntime();\n * export const { handle, init } = runtime;\n * ```\n */\nexport function createSmrtSvelteKitRuntime(\n options: SmrtSvelteKitRuntimeOptions = {},\n): SmrtSvelteKitRuntime {\n return composeSmrtSvelteKitRuntime(options, {});\n}\n\n/** @internal */\nexport function composeSmrtSvelteKitRuntime(\n options: SmrtSvelteKitRuntimeOptions,\n seams: SmrtSvelteKitRuntimeSeams,\n): SmrtSvelteKitRuntime {\n const env = options.env ?? process.env;\n const sourceRoot = options.sourceRoot ?? process.cwd();\n const dataDirectory = options.dataDirectory ?? env.SMRT_DATA_DIR;\n const initializeLocal =\n seams.initializeLocal ?? initializeLocalApplicationRuntime;\n const initializeDeployed =\n seams.initializeDeployed ?? initializeDeployedApplicationRuntime;\n\n if (options.enableTenancy !== false && !isTenancyEnabled()) {\n enableTenancy();\n }\n\n let appId: string | undefined;\n const applicationId = (): string => {\n appId ??= options.appId\n ? validateApplicationId(options.appId)\n : resolveApplicationId({ sourceRoot, explicitId: env.SMRT_APP_ID });\n return appId;\n };\n\n let resolved: ResolvedApplicationRuntime | undefined;\n let resolvedPromise: Promise<ResolvedApplicationRuntime> | undefined;\n const resolvedRuntime = (): Promise<ResolvedApplicationRuntime> => {\n resolvedPromise ??= (async () => {\n const value =\n typeof options.runtime === 'function'\n ? await options.runtime()\n : (options.runtime ??\n (await (seams.loadRuntime ?? loadConfiguredRuntime)()));\n resolved = value;\n return value;\n })().catch((error: unknown) => {\n resolvedPromise = undefined;\n throw error;\n });\n return resolvedPromise;\n };\n\n const requireResolved = (): ResolvedApplicationRuntime => {\n if (!resolved) {\n throw new Error(\n 'The SMRT application runtime is not resolved yet; await init() first.',\n );\n }\n return resolved;\n };\n\n /** Base (connection-level) database config; never request-scoped. */\n const baseDatabaseConfig = (): SmrtClassOptions['db'] => {\n const runtime = requireResolved();\n if (runtime.profile === 'local') {\n const paths = resolveLocalRuntimePaths({\n appId: applicationId(),\n dataDirectory,\n sourceRoot,\n env,\n });\n return {\n type: 'sqlite',\n url: paths.database,\n secureFile: {\n driver: 'node:sqlite',\n custody: 'trusted-parent',\n root: paths.root,\n },\n };\n }\n const databaseUrl = env.DATABASE_URL;\n if (!databaseUrl) {\n throw new Error(`${runtime.profile} requires DATABASE_URL.`);\n }\n return { type: 'postgres', url: databaseUrl };\n };\n\n /** Base collection options; used for anything that outlives a request. */\n const baseClassOptions = (className: string): SmrtClassOptions => {\n const defaults: SmrtClassOptions = { db: baseDatabaseConfig() };\n const override = options.classOverrides?.[className];\n return override ? { ...defaults, ...override } : defaults;\n };\n\n /**\n * Inside a request running in the RLS transaction, the transaction-bound\n * request database (it carries the `smrt.*` session variables); otherwise\n * undefined. Never cache the returned handle beyond the request.\n */\n const rlsRequestDatabase = (): SmrtClassOptions['db'] | undefined => {\n const context = getCurrentSessionPermissionContext();\n if (context?.postgresRls !== true) return undefined;\n return (getRequestScopedDatabase() ?? undefined) as\n | SmrtClassOptions['db']\n | undefined;\n };\n\n const databaseConfig = (): SmrtClassOptions['db'] =>\n rlsRequestDatabase() ?? baseDatabaseConfig();\n\n const classOptions = (className: string): SmrtClassOptions => {\n const base = baseClassOptions(className);\n if (options.classOverrides?.[className]?.db !== undefined) return base;\n const requestDb = rlsRequestDatabase();\n return requestDb ? { ...base, db: requestDb } : base;\n };\n\n const getCollection = <T extends SmrtObject = SmrtObject>(\n className: string,\n ): Promise<SmrtCollection<T>> =>\n ObjectRegistry.getCollection<T>(className, classOptions(className));\n\n const writerLease =\n options.acquireWriterLease === false\n ? undefined\n : (options.acquireWriterLease ??\n ((context: WriterLeaseContext) => defaultWriterLease(context, env)));\n\n let localPromise: Promise<LocalApplicationRuntime> | undefined;\n let localLease: WriterLease | undefined;\n const localRuntime = async (): Promise<LocalApplicationRuntime> => {\n const runtime = await resolvedRuntime();\n if (runtime.profile !== 'local') {\n throw new Error(\n 'Owner bootstrap is available only in the local profile.',\n );\n }\n if (localPromise) return localPromise;\n const bindHost =\n options.bindHost ||\n env.HOST ||\n (env.NODE_ENV === 'development' ? '127.0.0.1' : null);\n if (!bindHost) {\n throw new Error(\n 'Local production startup requires an explicit loopback HOST; use pnpm app:start.',\n );\n }\n const id = applicationId();\n localLease ??= writerLease?.({\n appId: id,\n dataDirectory,\n sourceRoot,\n });\n localPromise = initializeLocal({\n appId: id,\n dataDirectory,\n sourceRoot,\n env,\n bindHost,\n providers: {\n database: runtime.providers.database,\n authentication: runtime.providers.authentication,\n tenancy: runtime.providers.tenancy,\n assets: runtime.providers.assets,\n secrets: runtime.providers.secrets,\n jobs: runtime.providers.jobs,\n network: runtime.providers.network,\n },\n backgroundJobs:\n options.backgroundJobs ?? env.SMRT_BACKGROUND_JOBS === 'true',\n paidCapabilities: options.paidCapabilities,\n bootstrapTtlSeconds: options.bootstrapTtlSeconds,\n sessionTtlSeconds: options.sessionTtlSeconds,\n prepareDatabase: options.prepareDatabase,\n now: options.now,\n })\n .then(({ runtime: initialized, bootstrap }) => {\n if (bootstrap) {\n try {\n options.onBootstrapInvitation?.(bootstrap, { bindHost });\n } catch {\n // Presentation is best effort; the invitation stays claimable.\n }\n }\n return initialized;\n })\n .catch((error: unknown) => {\n localLease?.release();\n localLease = undefined;\n localPromise = undefined;\n throw error;\n });\n return localPromise;\n };\n\n let deployedPromise: Promise<DeployedApplicationRuntime> | undefined;\n const deployedRuntime = async (): Promise<DeployedApplicationRuntime> => {\n const runtime = await resolvedRuntime();\n if (runtime.profile === 'local') {\n throw new Error('deployed_runtime_unavailable');\n }\n const profile = runtime.profile as DeployedApplicationRuntimeProfile;\n const databaseUrl = env.DATABASE_URL;\n if (!databaseUrl) {\n throw new Error(`${profile} requires DATABASE_URL.`);\n }\n const authenticationProvider = runtime.providers.authentication.provider;\n if (authenticationProvider === 'owner-bootstrap') {\n throw new Error('Deployed profiles require public authentication.');\n }\n const readiness = options.providerReadiness;\n if (!readiness) {\n throw new Error(\n `${profile} requires providerReadiness probes for authentication, assets, and secrets.`,\n );\n }\n deployedPromise ??= initializeDeployed({\n profile,\n providers: {\n database: runtime.providers.database,\n authentication: runtime.providers.authentication,\n tenancy: runtime.providers.tenancy,\n assets: runtime.providers.assets,\n secrets: runtime.providers.secrets,\n jobs: runtime.providers.jobs,\n network: runtime.providers.network,\n },\n database: {\n engine: 'postgres',\n connect: () => getDatabase({ type: 'postgres', url: databaseUrl }),\n close: async (db) => {\n await db.close?.();\n },\n },\n authentication: {\n provider: authenticationProvider as PublicAuthenticationProvider,\n readiness: readiness('authentication', {\n profile,\n provider: authenticationProvider,\n }),\n },\n assets: {\n provider: runtime.providers.assets.provider,\n readiness: readiness('assets', {\n profile,\n provider: runtime.providers.assets.provider,\n }),\n },\n secrets: {\n provider: runtime.providers.secrets.provider,\n readiness: readiness('secrets', {\n profile,\n provider: runtime.providers.secrets.provider,\n }),\n },\n prepareDatabase: options.prepareDatabase,\n });\n return deployedPromise;\n };\n\n const ready = async (): Promise<void> => {\n const runtime = await resolvedRuntime();\n if (runtime.profile === 'local') {\n await localRuntime();\n return;\n }\n await deployedRuntime();\n };\n\n const configurationFingerprint = (): string =>\n runtimeConfigurationFingerprint(requireResolved(), env);\n\n const health = async (): Promise<RuntimeHealthBody> => {\n await ready();\n const runtime = requireResolved();\n const publicHealth = {\n schemaVersion: 1 as const,\n status: 'ready' as const,\n profile: runtime.profile,\n };\n return runtime.profile === 'local'\n ? {\n ...publicHealth,\n application: applicationId(),\n instance: env.SMRT_PROCESS_INSTANCE || null,\n configuration: configurationFingerprint(),\n }\n : publicHealth;\n };\n\n const readDiagnostics = async (\n diagnosticsOptions: ApplicationRuntimeDiagnosticsOptions,\n ): Promise<RuntimeDiagnostics> => {\n await ready();\n const runtime = requireResolved();\n const schema = {\n status: diagnosticsOptions.schemaStatus ?? 'unknown',\n migrations: diagnosticsOptions.migrationStatus ?? 'unknown',\n } as const;\n\n if (runtime.profile === 'local') {\n const local = await localRuntime();\n const diagnostics = await local.diagnostics();\n return projectRuntimeDiagnostics({\n profile: 'local',\n health: 'healthy',\n schema,\n capabilities: {\n 'asset-storage': 'available',\n authentication: 'available',\n 'background-jobs': diagnostics.jobs.backgroundEnabled\n ? 'available'\n : 'disabled',\n database: 'available',\n 'paid-capabilities': diagnostics.paidCapabilitiesEnabled\n ? 'available'\n : 'disabled',\n 'secret-storage': 'available',\n },\n toolNames: diagnosticsOptions.toolNames,\n worker: {\n topology: diagnostics.jobs.topology,\n required: diagnostics.jobs.backgroundEnabled,\n heartbeatAt: diagnosticsOptions.workerHeartbeatAt,\n },\n recentErrors: diagnosticsOptions.recentErrors,\n observedAt: diagnosticsOptions.observedAt,\n });\n }\n\n const deployed = await deployedPromise;\n if (!deployed) throw new Error('deployed_runtime_unavailable');\n const readiness = await deployed.readiness();\n const componentStatus = (\n component: keyof typeof readiness.components,\n ): 'available' | 'unavailable' =>\n readiness.components[component].status === 'ready'\n ? 'available'\n : 'unavailable';\n const input: RuntimeDiagnosticsProjectionInput = {\n profile: runtime.profile,\n health:\n deployed.health().status === 'healthy'\n ? readiness.status === 'ready'\n ? 'healthy'\n : 'degraded'\n : 'stopped',\n schema,\n capabilities: {\n 'asset-storage': componentStatus('assets'),\n authentication: componentStatus('authentication'),\n 'background-jobs': 'available',\n database: componentStatus('database'),\n 'paid-capabilities': 'unknown',\n 'secret-storage': componentStatus('secrets'),\n },\n toolNames: diagnosticsOptions.toolNames,\n worker: {\n topology: deployed.resolvedRuntime.providers.jobs.topology,\n required: true,\n heartbeatAt: diagnosticsOptions.workerHeartbeatAt,\n },\n recentErrors: diagnosticsOptions.recentErrors,\n observedAt: diagnosticsOptions.observedAt,\n };\n return projectRuntimeDiagnostics(input);\n };\n\n const selectTenant: TenantSelector =\n options.selectTenant ??\n createSubdomainTenantSelector({\n baseDomain: options.tenantBaseDomain ?? env.TENANT_BASE_DOMAIN,\n classOptions: () => baseClassOptions('Tenant'),\n });\n\n const readinessHandle: Handle = async ({ event, resolve }) => {\n await ready();\n return resolve(event);\n };\n\n const tenantSelectionHandle: Handle = async ({ event, resolve }) => {\n const locals = runtimeLocals(event.locals);\n const selection = normalizeTenantSelection(\n await selectTenant({ url: event.url, request: event.request }),\n );\n locals.selectedTenantId = selection.tenantId;\n locals.selectedTenantSlug = selection.tenantSlug;\n return resolve(event);\n };\n\n const sessionCookieName = options.session?.cookieName ?? 'sid';\n const sessionSkipPaths = options.session?.skipPaths ?? [];\n const sessionServiceOptions = (): SessionServiceOptions => ({\n ...baseClassOptions('Session'),\n ...options.session,\n defaultTTL: options.session?.ttl ?? DEFAULT_SESSION_TTL_SECONDS,\n autoExtend: options.session?.autoExtend ?? false,\n });\n /** `database-rls` isolation always runs the request RLS transaction. */\n const postgresRls = (): boolean | undefined =>\n requireResolved().providers.tenancy.isolation === 'database-rls'\n ? true\n : options.session?.postgresRls;\n let permissionResolverPromise: Promise<PermissionResolver> | undefined;\n const permissionResolver = (): Promise<PermissionResolver> => {\n permissionResolverPromise ??= PermissionResolver.create(\n baseClassOptions('Permission'),\n ).catch((error: unknown) => {\n permissionResolverPromise = undefined;\n throw error;\n });\n return permissionResolverPromise;\n };\n\n const runAsPrincipal = async <\n T,\n P extends SmrtRuntimeBoundPrincipal = SmrtRuntimeBoundPrincipal,\n >(\n principal: P,\n fn: (bound: SmrtRuntimeBoundPrincipalSnapshot<P>) => Promise<T>,\n ): Promise<T> => {\n // One complete snapshot before the first await: authorization, the\n // permission/RLS context and the principal handed to `fn` all come from\n // it, and the caller's object is never read again (a caller mutating it\n // mid-flight cannot change the bound identity, tenant, binding or scopes).\n if (!principal || typeof principal !== 'object') {\n throw new Error('A bound principal is required.');\n }\n const snapshot = snapshotBoundPrincipal(principal) as P;\n // The snapshot has a null prototype: every read below is own data.\n const tokenDerived = isLocalMcpTokenPrincipal(snapshot);\n const userId = ownValue(snapshot, 'id') as P['id'];\n const tenantId = ownValue(snapshot, 'tenantId') as P['tenantId'];\n const snapshotScopes = ownValue(snapshot, 'scopes') as\n | readonly string[]\n | undefined;\n const requestedTenantBinding = ownValue(snapshot, 'tenantBinding') as\n | TenantBindingMode\n | undefined;\n const requestedScopes =\n snapshotScopes === undefined\n ? undefined\n : Object.freeze([...snapshotScopes]);\n if (\n requestedScopes !== undefined &&\n !requestedScopes.every((scope) => typeof scope === 'string')\n ) {\n throw new Error('A bound principal has malformed scopes.');\n }\n if (typeof userId !== 'string' || userId.length === 0) {\n throw new Error('A bound principal requires a user id.');\n }\n if (typeof tenantId !== 'string' || tenantId.length === 0) {\n throw new Error('A bound principal requires a tenant.');\n }\n // Resolution reads the base connection, before any RLS transaction opens.\n // A token-derived principal is direct-only. A copy that tries to widen\n // its binding is refused rather than silently narrowed.\n if (\n tokenDerived &&\n requestedTenantBinding !== undefined &&\n requestedTenantBinding !== 'direct'\n ) {\n throw new Error(\n 'A local MCP token principal binds only through its direct membership.',\n );\n }\n const binding: TenantBindingMode = tokenDerived\n ? 'direct'\n : (requestedTenantBinding ?? 'direct-or-inherited');\n if (binding !== 'direct' && binding !== 'direct-or-inherited') {\n throw new Error('Unknown tenant binding mode for a bound principal.');\n }\n const resolved = await resolveBoundMembershipPermissions({\n memberships: await MembershipCollection.create(\n baseClassOptions('Membership'),\n ),\n resolver: await permissionResolver(),\n userId,\n tenantId,\n binding,\n });\n if (!resolved) {\n throw new Error(\n binding === 'direct'\n ? 'The bound principal has no active direct membership in its tenant.'\n : 'The bound principal has no authorized membership in its tenant.',\n );\n }\n const cap = requestedScopes ? new Set(requestedScopes) : undefined;\n const permissions = [...resolved.permissions].filter(\n (permission) => !cap || cap.has(permission),\n );\n return withPrincipalPermissionContext(\n {\n ...baseClassOptions('Session'),\n userId,\n tenantId,\n permissions,\n enterTenantContext: true,\n postgresRls: postgresRls(),\n },\n () =>\n fn(\n // Null prototype like the snapshot: a field the principal lacks\n // (say `allowCrossTenant`) never resolves to an inherited value.\n Object.freeze(\n Object.assign(Object.create(null), snapshot, {\n id: userId,\n tenantId,\n tenantBinding: binding,\n scopes: Object.freeze([...permissions]),\n }),\n ) as SmrtRuntimeBoundPrincipalSnapshot<P>,\n ),\n );\n };\n\n const verifyLocalMcpToken = async (\n token: string,\n ): Promise<LocalMcpTokenPrincipal | null> => {\n if ((await resolvedRuntime()).profile !== 'local') return null;\n const local = await localRuntime();\n // A runtime without a token store has no tokens: fail closed.\n return (await local.mcpTokens?.verify(token)) ?? null;\n };\n\n const resolveMcpPrincipal = async (\n identity: SmrtRuntimeMcpIdentity,\n ): Promise<SmrtRuntimeMcpPrincipalMapping | null> => {\n if ((await resolvedRuntime()).profile === 'local') return null;\n if (!identity || typeof identity !== 'object') return null;\n const issuer = ownValue(identity, 'issuer');\n const subject = ownValue(identity, 'subject');\n if (\n typeof issuer !== 'string' ||\n issuer.length === 0 ||\n typeof subject !== 'string' ||\n subject.length === 0\n ) {\n return null;\n }\n // Every read is keyed by the verified issuer/subject and the ids derived\n // from it, and only ids leave this block (the PermissionResolver rule).\n return withSystemContext(async () => {\n const identities = await OidcIdentityCollection.create(\n baseClassOptions('OidcIdentity'),\n );\n let linked: Awaited<ReturnType<typeof identities.findBySubject>>;\n try {\n linked = await identities.findBySubject(issuer, subject);\n } catch {\n return null; // Ambiguous identity: deny.\n }\n if (!linked?.profileId) return null;\n const users = await UserCollection.create(baseClassOptions('User'));\n const owners = await users.list({\n where: { profileId: linked.profileId },\n limit: 2,\n });\n const user = owners.length === 1 ? owners[0] : undefined;\n if (\n typeof user?.id !== 'string' ||\n user.id.length === 0 ||\n user.status !== UserStatus.ACTIVE\n ) {\n return null;\n }\n const memberships = await MembershipCollection.create(\n baseClassOptions('Membership'),\n );\n const tenants = new Set(\n (await memberships.findActiveByUser(user.id))\n .map((membership) => membership.tenantId)\n .filter(\n (tenantId): tenantId is string =>\n typeof tenantId === 'string' && tenantId.length > 0,\n ),\n );\n if (tenants.size !== 1) return null;\n const [tenantId] = tenants;\n return Object.freeze({\n id: user.id,\n tenantId,\n kind: 'human' as const,\n tenantBinding: 'direct' as const,\n });\n });\n };\n\n let sessionServicePromise: Promise<SessionService> | undefined;\n const sessionService = (): Promise<SessionService> => {\n sessionServicePromise ??= (async () => {\n const service = new TenantAuthorizingSessionService(\n sessionServiceOptions(),\n requireResolved().providers.tenancy.context === 'required',\n );\n await service.initialize();\n return service;\n })().catch((error: unknown) => {\n sessionServicePromise = undefined;\n throw error;\n });\n return sessionServicePromise;\n };\n\n /**\n * Step 2. Downstream code runs at most once: a downstream throw (or a\n * failure after `resolve` was entered, such as an RLS commit) propagates\n * unchanged and `resolve` is never re-entered. A session-layer failure\n * before `resolve` fails closed with 500 and no authenticated locals, for\n * both RLS and non-RLS isolation.\n */\n const sessionStep: Handle = async ({ event, resolve }) => {\n const locals = runtimeLocals(event.locals);\n clearSessionLocals(locals);\n const rls = postgresRls();\n // A skipped path skips session loading only. Under RLS it still runs in\n // the request transaction, as an anonymous principal.\n const skipped = sessionSkipPaths.some((path) =>\n event.url.pathname.startsWith(path),\n );\n if (skipped && !rls) return resolve(event);\n const sessionId = skipped\n ? undefined\n : event.cookies.get(sessionCookieName);\n if (!sessionId && !rls) return resolve(event);\n\n let resolveEntered = false;\n let downstream: { error: unknown } | undefined;\n try {\n const service = await sessionService();\n return await withSessionPermissionContext(\n {\n ...sessionServiceOptions(),\n enterTenantContext: true,\n postgresRls: rls,\n sessionId: sessionId ?? null,\n sessionService: service,\n },\n async (context) => {\n if (context.session) {\n locals.user = context.user;\n locals.membership = context.membership ?? null;\n locals.permissions = context.permissions;\n locals.tenantId = context.tenantId;\n locals.sessionId = context.sessionId;\n if (context.tenantId) {\n verifiedSessionTenants.set(locals, context.tenantId);\n }\n }\n resolveEntered = true;\n try {\n return await resolve(event);\n } catch (error) {\n downstream = { error };\n throw error;\n }\n },\n );\n } catch (error) {\n if (downstream) throw downstream.error;\n if (resolveEntered) throw error;\n clearSessionLocals(locals);\n return new Response('Internal Server Error', { status: 500 });\n }\n };\n\n const sessionCookie: SmrtRuntimeSessionCookie = Object.freeze({\n name: options.session?.cookieName ?? 'sid',\n path: options.session?.cookiePath ?? '/',\n ...(options.session?.cookieDomain\n ? { domain: options.session.cookieDomain }\n : {}),\n sameSite: options.session?.cookieSameSite ?? 'lax',\n ...(options.session?.cookieSecure !== undefined\n ? { secure: options.session.cookieSecure }\n : {}),\n maxAgeSeconds: options.sessionTtlSeconds ?? DEFAULT_SESSION_TTL_SECONDS,\n });\n\n const init: ServerInit = () => ready();\n\n return Object.freeze({\n handle: composeHandles(\n readinessHandle,\n tenantSelectionHandle,\n sessionStep,\n authorizedTenantLocalsHandle,\n ),\n init,\n sessionCookie,\n resolvedRuntime,\n ready,\n applicationId,\n applicationStateRoot: () =>\n resolveApplicationStateRoot({\n appId: applicationId(),\n dataDirectory,\n sourceRoot,\n }),\n configurationFingerprint,\n databaseConfig,\n classOptions,\n getCollection,\n runAsPrincipal,\n verifyLocalMcpToken,\n resolveMcpPrincipal,\n localRuntime,\n deployedRuntime,\n health,\n readDiagnostics,\n });\n}\n\n/**\n * The default local writer lease: the state-root `writer.lease` shared with\n * `smrt app` operations, presenting the managed operation instance\n * (`SMRT_OPERATION_INSTANCE`) that `smrt app start` passes to its writer.\n */\nexport function defaultWriterLease(\n context: WriterLeaseContext,\n env: NodeJS.ProcessEnv = process.env,\n): WriterLease {\n return acquireWriterLease(\n prepareApplicationStateRoot({\n appId: context.appId,\n dataDirectory: context.dataDirectory,\n sourceRoot: context.sourceRoot,\n }),\n { operationInstance: env.SMRT_OPERATION_INSTANCE },\n );\n}\n\n/** Locals whose session tenant passed {@link isSessionTenantAuthorized}. */\nconst verifiedSessionTenants = new WeakMap<object, string>();\n\n/**\n * Session service whose loaded context carries a tenant only when that\n * tenant passed {@link isSessionTenantAuthorized}. A session bound to an\n * unauthorized tenant (or missing a required tenant) loads as no session, so\n * no identity, permissions, or tenant context is established for it.\n */\nclass TenantAuthorizingSessionService extends SessionService {\n private readonly tenantRequired: boolean;\n\n constructor(options: SessionServiceOptions, tenantRequired: boolean) {\n super(options);\n this.tenantRequired = tenantRequired;\n }\n\n override async loadSessionContext(\n sessionId: string,\n ): ReturnType<SessionService['loadSessionContext']> {\n const context = await super.loadSessionContext(sessionId);\n if (!context) return null;\n if (typeof context.tenantId === 'string' && context.tenantId.length > 0) {\n return isSessionTenantAuthorized(context) ? context : null;\n }\n return this.tenantRequired ? null : context;\n }\n}\n\nfunction clearSessionLocals(locals: SmrtRuntimeLocals): void {\n locals.user = null;\n locals.membership = null;\n locals.permissions = [];\n locals.tenantId = null;\n locals.sessionId = null;\n locals.tenantContext = undefined;\n verifiedSessionTenants.delete(locals);\n}\n\n/**\n * Step 3: publish the authorized tenant context only when this runtime's\n * session step verified the session tenant and it matches the active\n * AsyncLocalStorage context. Locals populated by any other session layer are\n * never published.\n */\nexport const authorizedTenantLocalsHandle: Handle = async ({\n event,\n resolve,\n}) => {\n const locals = runtimeLocals(event.locals);\n const activeContext = getCurrentTenant();\n if (\n locals.user &&\n locals.tenantId &&\n verifiedSessionTenants.get(locals) === locals.tenantId &&\n activeContext?.tenantId === locals.tenantId\n ) {\n locals.tenantContext = activeContext;\n }\n return resolve(event);\n};\n\n/**\n * Run handles in order. Unlike `sequence()` from `@sveltejs/kit/hooks`, this\n * needs no SvelteKit request store, so the composed handle is usable (and\n * testable) outside the Kit server runtime. The runtime's own handles never\n * pass resolve options; any options a handle passes are forwarded unchanged.\n */\nexport function composeHandles(...handles: readonly Handle[]): Handle {\n return ({ event, resolve }) => {\n const run: (\n index: number,\n current: Parameters<Handle>[0]['event'],\n resolveOptions?: Parameters<Parameters<Handle>[0]['resolve']>[1],\n ) => ReturnType<Handle> = (index, current, resolveOptions) => {\n const next = handles[index];\n if (!next) return resolve(current, resolveOptions);\n return next({\n event: current,\n resolve: (nextEvent, nextOptions) =>\n run(index + 1, nextEvent, nextOptions ?? resolveOptions),\n });\n };\n return run(0, event);\n };\n}\n\nfunction runtimeLocals(locals: unknown): SmrtRuntimeLocals {\n return locals as SmrtRuntimeLocals;\n}\n\n/**\n * No `runtime` block selects `local`; a present non-block value fails closed.\n * Shared with `smrt app` through smrt-config (#3446).\n */\nasync function loadConfiguredRuntime(): Promise<ResolvedApplicationRuntime> {\n return resolveEffectiveApplicationRuntime(await loadConfig());\n}\n\n/**\n * Deep, frozen, validated copy of a bound principal, taken before any await\n * (see `plain-data.ts` for what is rejected: poison keys, accessors,\n * inherited or non-plain data, sparse arrays, functions, deep nesting).\n * Known fields are type-checked from own data (`id`, `tenantId`, `kind`,\n * `tenantBinding`: strings; `scopes`, `roles`: string arrays;\n * `allowCrossTenant`: boolean). Other own enumerable fields (string or\n * symbol keys) are kept as deep-frozen copies.\n */\nfunction snapshotBoundPrincipal(\n principal: unknown,\n): Record<PropertyKey, unknown> {\n if (!principal || typeof principal !== 'object' || Array.isArray(principal)) {\n throw new Error('A bound principal is required.');\n }\n let snapshot: Record<PropertyKey, unknown>;\n try {\n snapshot = deepFrozenPlainCopy(principal) as Record<PropertyKey, unknown>;\n } catch (error) {\n if (error instanceof PlainDataError) {\n throw new Error(`A bound principal ${error.message}.`);\n }\n throw error;\n }\n for (const key of ['id', 'tenantId', 'kind', 'tenantBinding']) {\n const value = ownValue(snapshot, key);\n if (value !== undefined && typeof value !== 'string') {\n throw new Error(`A bound principal has a malformed ${key}.`);\n }\n }\n for (const key of ['scopes', 'roles']) {\n const value = ownValue(snapshot, key);\n if (\n value !== undefined &&\n (!Array.isArray(value) ||\n !value.every((entry) => typeof entry === 'string'))\n ) {\n throw new Error(`A bound principal has malformed ${key}.`);\n }\n }\n const allowCrossTenant = ownValue(snapshot, 'allowCrossTenant');\n if (allowCrossTenant !== undefined && typeof allowCrossTenant !== 'boolean') {\n throw new Error('A bound principal has a malformed allowCrossTenant.');\n }\n return snapshot;\n}\n","/**\n * Sign-out route handler: destroys the current session and clears its cookie\n * with the same cookie attributes the owner-setup action used to set it.\n */\n\nimport { destroySessionCookie } from '@happyvertical/smrt-users/sveltekit';\nimport { type RequestHandler, redirect } from '@sveltejs/kit';\nimport type { SmrtSvelteKitRuntime } from './runtime.js';\n\n/** True when the browser says the request came from this origin. */\nfunction isSameOriginRequest(request: Request, url: URL): boolean {\n const site = request.headers.get('sec-fetch-site');\n if (site !== null) return site === 'same-origin';\n const origin = request.headers.get('origin');\n return origin !== null && origin === url.origin;\n}\n\n/**\n * Mount as `src/routes/sign-out/+server.ts`:\n * `export const POST = createSignOutHandler(runtime);`\n *\n * `POST` only (other methods answer 405) and same-origin only (403 otherwise).\n * The session row is destroyed best-effort and the cookie is cleared even when\n * the row is already gone. Answers 204, or a 303 to `/` when the request\n * accepts HTML so a no-JS form post lands on the home page.\n */\nexport function createSignOutHandler(\n runtime: Pick<SmrtSvelteKitRuntime, 'sessionCookie' | 'classOptions'>,\n): RequestHandler {\n return async (event) => {\n if (event.request.method !== 'POST') {\n return new Response('Method not allowed', {\n status: 405,\n headers: { allow: 'POST' },\n });\n }\n if (!isSameOriginRequest(event.request, event.url)) {\n return new Response('Forbidden', { status: 403 });\n }\n const cookie = runtime.sessionCookie;\n await destroySessionCookie(\n event as unknown as Parameters<typeof destroySessionCookie>[0],\n {\n ...runtime.classOptions('Session'),\n cookieName: cookie.name,\n cookiePath: cookie.path,\n ...(cookie.domain ? { cookieDomain: cookie.domain } : {}),\n },\n );\n // `delete` omits the Secure flag the setup action used; re-issue the\n // expiry with the same attributes so browsers match and drop the cookie.\n event.cookies.set(cookie.name, '', {\n path: cookie.path,\n ...(cookie.domain ? { domain: cookie.domain } : {}),\n httpOnly: true,\n secure: cookie.secure ?? event.url.protocol === 'https:',\n sameSite: cookie.sameSite,\n maxAge: 0,\n });\n if (event.request.headers.get('accept')?.includes('text/html')) {\n redirect(303, '/');\n }\n return new Response(null, { status: 204 });\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-app-runtime",
|
|
3
|
-
"version": "0.55.
|
|
3
|
+
"version": "0.55.17",
|
|
4
4
|
"description": "Validated local, self-hosted, and cloud runtime composition for s-m-r-t applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -29,16 +29,16 @@
|
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@happyvertical/smrt-config": "0.55.
|
|
33
|
-
"@happyvertical/smrt-core": "0.55.
|
|
34
|
-
"@happyvertical/smrt-jobs": "0.55.
|
|
35
|
-
"@happyvertical/smrt-profiles": "0.55.
|
|
36
|
-
"@happyvertical/smrt-tenancy": "0.55.
|
|
37
|
-
"@happyvertical/smrt-users": "0.55.
|
|
32
|
+
"@happyvertical/smrt-config": "0.55.17",
|
|
33
|
+
"@happyvertical/smrt-core": "0.55.17",
|
|
34
|
+
"@happyvertical/smrt-jobs": "0.55.17",
|
|
35
|
+
"@happyvertical/smrt-profiles": "0.55.17",
|
|
36
|
+
"@happyvertical/smrt-tenancy": "0.55.17",
|
|
37
|
+
"@happyvertical/smrt-users": "0.55.17",
|
|
38
38
|
"@happyvertical/sql": "^0.103.4"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@happyvertical/smrt-vitest": "0.55.
|
|
41
|
+
"@happyvertical/smrt-vitest": "0.55.17",
|
|
42
42
|
"@sveltejs/kit": "2.70.3",
|
|
43
43
|
"@types/node": "24.13.6",
|
|
44
44
|
"tsx": "^4.23.13",
|