@happyvertical/smrt-template-sveltekit 0.43.9 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/AGENTS.md +31 -6
  2. package/README.md +26 -10
  3. package/index.js +53 -2
  4. package/package.json +19 -5
  5. package/template/.dockerignore +12 -0
  6. package/template/.env.cloud.example +9 -0
  7. package/template/.env.example +15 -3
  8. package/template/.env.self-hosted.example +11 -0
  9. package/template/AGENTS.md +32 -0
  10. package/template/Dockerfile +23 -0
  11. package/template/INSTALL_PROMPT.md +11 -0
  12. package/template/README.md +74 -32
  13. package/template/compose.yaml +56 -0
  14. package/template/gitignore.template +45 -0
  15. package/template/package.json +30 -15
  16. package/template/scripts/smrt-app.mjs +884 -0
  17. package/template/scripts/smrt-operation-lock.mjs +102 -0
  18. package/template/scripts/smrt-portability-assets.mjs +707 -0
  19. package/template/scripts/smrt-portability.mjs +470 -0
  20. package/template/scripts/smrt-prepare-migration.mjs +93 -0
  21. package/template/scripts/smrt-process.mjs +82 -0
  22. package/template/scripts/smrt-provider-readiness.mjs +33 -0
  23. package/template/scripts/smrt-runtime-identity.mjs +238 -0
  24. package/template/scripts/smrt-vite.mjs +45 -0
  25. package/template/scripts/smrt-web.mjs +10 -0
  26. package/template/scripts/smrt-worker.mjs +86 -0
  27. package/template/scripts/smrt-writer-lease.mjs +199 -0
  28. package/template/smrt.config.ts +34 -2
  29. package/template/src/hooks.server.ts +4 -1
  30. package/template/src/lib/RuntimeDiagnosticsWebMcp.svelte +11 -0
  31. package/template/src/lib/runtime-diagnostics-webmcp.ts +108 -0
  32. package/template/src/lib/server/application-runtime.ts +262 -0
  33. package/template/src/lib/server/runtime-diagnostics.ts +114 -0
  34. package/template/src/lib/server/smrt.ts +4 -4
  35. package/template/src/routes/+layout.svelte +2 -0
  36. package/template/src/routes/api/_runtime/diagnostics/+server.ts +5 -0
  37. package/template/src/routes/api/_runtime/health/+server.ts +30 -0
  38. package/template/src/routes/setup/+page.server.ts +96 -0
  39. package/template/src/routes/setup/+page.svelte +31 -0
  40. package/template/svelte.config.js +1 -1
  41. package/template/tsconfig.json +1 -0
package/AGENTS.md CHANGED
@@ -14,6 +14,25 @@ It is the ground-up alternative to `smrt-saas-starter`.
14
14
  ## Current generated-project contract
15
15
 
16
16
  - Node `>=24.18.0`; pnpm `10.34.4` via `packageManager` and `engines`.
17
+ - `runtime.profile` is the canonical infrastructure selector. Generated apps
18
+ expose deterministic `app:*` operations and keep runtime state outside source.
19
+ - The production baseline uses adapter-node with separate web, task-worker, and
20
+ schedule-worker processes. Its runtime image retains the generated manifest
21
+ and operator CLI needed by `app:doctor`, `app:export`, and `app:import`.
22
+ - Deployed authentication, asset, and secret readiness is delegated to
23
+ installed provider-owned probe modules; never replace it with truthy flags.
24
+ - Every supported local web writer (`app:start` or `pnpm dev`)
25
+ acquires the same external writer lease. Logical imports and filesystem
26
+ backups fail closed while any writer is alive; logical exports take one
27
+ database transaction snapshot and include filesystem assets referenced by
28
+ exported asset rows in a digest-verified, provider-neutral manifest. Imports
29
+ stage and verify those assets before committing database rows.
30
+ Direct production startup must set an explicit loopback `HOST`.
31
+ - Local `app:start` proves readiness against the loopback-only identity fields
32
+ on the runtime health route using the canonical application ID, random
33
+ process instance, and secret-safe runtime configuration identity; a
34
+ different or stale server is never accepted as this app. Deployed health
35
+ responses expose only generic status and profile.
17
36
  - Directly used `@happyvertical/smrt-*` packages share one current release range.
18
37
  - `@happyvertical/smrt-cli` is a direct dev dependency because scripts/docs use
19
38
  its binary. The template includes `@happyvertical/smrt-web` because the root
@@ -23,17 +42,17 @@ It is the ground-up alternative to `smrt-saas-starter`.
23
42
  `@modelcontextprotocol/server` (plus its `/stdio` subpath),
24
43
  `@happyvertical/smrt-core`, and `@happyvertical/smrt-config` are always
25
44
  emitted and are always dependencies. `@happyvertical/smrt-jobs` (task actions)
26
- and `@happyvertical/smrt-tenancy` (tenant-scoped objects) are conditional;
27
- tenancy is already a dependency for other reasons, and jobs is documented in
28
- the template README as an add-when-needed. Do not assume pnpm exposes the
45
+ and `@happyvertical/smrt-tenancy` (tenant-scoped objects) are also declared
46
+ for the default worker and tenant surfaces. Do not assume pnpm exposes the
29
47
  CLI's or core's transitive dependencies to the app root — its strict layout
30
48
  does not (#2297).
31
49
  - `smrtConsumer()` explicitly consumes profiles, tenancy, and users manifests;
32
50
  `smrtPlugin()` scans `src/lib/objects`, generates Vite virtual definitions,
33
51
  SvelteKit routes, runtime registration, and knowledge artifacts.
34
- - `pnpm db:migrate` builds first to refresh generated artifacts, then runs the
35
- current manifest-driven migration command. Do not restore deprecated
36
- `smrt db:setup` documentation.
52
+ - `pnpm db:migrate` builds first to refresh generated artifacts, then the
53
+ migration wrapper holds the shared operation/writer exclusion for the full
54
+ manifest-driven migration command. Do not restore deprecated `smrt db:setup`
55
+ documentation or split the safety check from the migration process.
37
56
 
38
57
  ## Application patterns
39
58
 
@@ -55,6 +74,12 @@ It is the ground-up alternative to `smrt-saas-starter`.
55
74
  page session. Write/destructive effects require an explicit page-owned policy.
56
75
  Live browser collections remain opt-in per page and must seed from SSR
57
76
  `initialData` to avoid a duplicate first request.
77
+ - `RuntimeDiagnosticsWebMcp` owns exactly one additional read-only tool,
78
+ `smrt.runtime.diagnostics.read`. It uses same-origin page-session fetch to the
79
+ authored `/api/_runtime/diagnostics` route and aborts its registration on
80
+ unmount. The route requires a direct active tenant membership plus either the
81
+ owner role or the explicit `runtime_diagnostics.read` permission before any
82
+ runtime projection/probe; it never calls principal-bound server tools.
58
83
 
59
84
  ## Tests
60
85
 
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @happyvertical/smrt-template-sveltekit
2
2
 
3
3
  The minimal “learn and build from the ground up” SvelteKit template for
4
- s-m-r-t 0.38.25. The package exports `copyTemplate()`, `getTemplatePath()`, and
4
+ s-m-r-t 0.43.10. The package exports `copyTemplate()`, `getTemplatePath()`, and
5
5
  `templateInfo` and is the `sveltekit` template used by `smrt gnode create`.
6
6
 
7
7
  ## 1. Install and run
@@ -13,18 +13,15 @@ node --input-type=module -e "import { copyTemplate } from './packages/template-s
13
13
  cd my-app
14
14
  pnpm install
15
15
  cp .env.example .env
16
- pnpm db:migrate
17
- pnpm check
18
- pnpm build
19
- pnpm dev
16
+ pnpm app:install
20
17
  ```
21
18
 
22
- Run the copy command from the monorepo root. The public 0.38.25 CLI advertises
19
+ Run the copy command from the monorepo root. The public 0.43.10 CLI advertises
23
20
  `gnode create`, but its command dispatcher currently rejects `gnode`; this
24
21
  package's tested `copyTemplate()` export is the working scaffold path.
25
22
 
26
23
  Generated projects require Node.js 24.18.0 or newer and pnpm 10.34.4. They pin
27
- all directly used `@happyvertical/smrt-*` packages to 0.38.25.
24
+ all directly used `@happyvertical/smrt-*` packages to 0.43.10.
28
25
 
29
26
  For programmatic scaffolding:
30
27
 
@@ -37,8 +34,10 @@ copyTemplate('/absolute/path/to/my-app', {
37
34
  });
38
35
  ```
39
36
 
40
- `copyTemplate()` substitutes the package name and excludes package-internal
41
- `.svelte-kit` and test fixtures.
37
+ `copyTemplate()` turns an unscoped project name such as `my-app` into the
38
+ runtime-safe package identity `@smrt-app/my-app`; explicitly scoped package
39
+ names are preserved. It also excludes package-internal `.svelte-kit` and test
40
+ fixtures.
42
41
 
43
42
  ## 2. Understand the generated files
44
43
 
@@ -67,7 +66,8 @@ Add objects beside Item and export them from `src/lib/objects/index.ts`.
67
66
 
68
67
  ## 4. Initialize or migrate the database
69
68
 
70
- Generated projects use SQLite at `./app.db` by default:
69
+ Generated local projects use SQLite in the current user's operating-system
70
+ application-data directory by default:
71
71
 
72
72
  ```bash
73
73
  pnpm db:migrate
@@ -149,3 +149,19 @@ Use this template for learning and focused, ground-up applications. Choose
149
149
  onboarding, billing/subscriptions, workers, provider configuration, deployment
150
150
  infrastructure, and mobile applications. This package intentionally avoids
151
151
  becoming a second SaaS starter.
152
+
153
+ The generated project has one canonical `runtime.profile` and deterministic
154
+ `app:*` operations. Local state stays outside source. The production baseline
155
+ uses adapter-node and includes a container plus separate task/schedule workers;
156
+ Compose requires operator-supplied database secrets, and cloud examples
157
+ describe provider composition without pretending to provision it.
158
+ Install/start/stop/recover are local-profile commands; deployed web processes
159
+ run the production Node build or container directly.
160
+
161
+ Every supported local web entry point (`app:start` or `pnpm dev`) shares one
162
+ writer lease outside the source checkout. Direct production startup requires
163
+ an explicit loopback `HOST`; prefer `app:start`. Filesystem backup and logical
164
+ import fail closed while a writer is alive; logical export reads every model
165
+ table from one transaction snapshot and reports that uploaded assets are
166
+ excluded. The production image retains the generated manifest and operator CLI
167
+ required by doctor/export/import.
package/index.js CHANGED
@@ -7,8 +7,11 @@
7
7
  import {
8
8
  cpSync,
9
9
  existsSync,
10
+ lstatSync,
10
11
  mkdirSync,
11
12
  readFileSync,
13
+ renameSync,
14
+ rmSync,
12
15
  writeFileSync,
13
16
  } from 'node:fs';
14
17
  import { dirname, join } from 'node:path';
@@ -24,6 +27,16 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
24
27
  */
25
28
  const COPY_SKIP = new Set(['.svelte-kit', '__tests__', 'test', 'tests']);
26
29
 
30
+ function pathEntryExists(path) {
31
+ try {
32
+ lstatSync(path);
33
+ return true;
34
+ } catch (error) {
35
+ if (error?.code === 'ENOENT') return false;
36
+ throw error;
37
+ }
38
+ }
39
+
27
40
  /**
28
41
  * Get the path to the template directory
29
42
  */
@@ -31,6 +44,23 @@ export function getTemplatePath() {
31
44
  return join(__dirname, 'template');
32
45
  }
33
46
 
47
+ function normalizePackageSegment(value) {
48
+ return value
49
+ .toLowerCase()
50
+ .replace(/[^a-z0-9._-]+/g, '-')
51
+ .replace(/^-+|-+$/g, '');
52
+ }
53
+
54
+ function packageNameForProject(name) {
55
+ if (name.startsWith('@')) {
56
+ const slash = name.indexOf('/');
57
+ const scope = normalizePackageSegment(name.slice(1, slash));
58
+ const packageName = normalizePackageSegment(name.slice(slash + 1));
59
+ if (slash > 1 && scope && packageName) return `@${scope}/${packageName}`;
60
+ }
61
+ return `@smrt-app/${normalizePackageSegment(name) || 'app'}`;
62
+ }
63
+
34
64
  /**
35
65
  * Copy the template to a destination directory
36
66
  *
@@ -63,12 +93,30 @@ export function copyTemplate(destination, options = {}) {
63
93
  },
64
94
  });
65
95
 
96
+ // npm pack omits files literally named `.gitignore`. Ship the canonical
97
+ // contents under a neutral name and materialize the ignore file only in the
98
+ // generated project so installed-package scaffolds retain secret hygiene.
99
+ const packagedGitignore = join(destination, 'gitignore.template');
100
+ if (!existsSync(packagedGitignore)) {
101
+ throw new Error('Template package is missing gitignore.template');
102
+ }
103
+ const destinationGitignore = join(destination, '.gitignore');
104
+ if (pathEntryExists(destinationGitignore) && !options.overwrite) {
105
+ rmSync(packagedGitignore, { force: true });
106
+ } else {
107
+ rmSync(destinationGitignore, { force: true });
108
+ renameSync(packagedGitignore, destinationGitignore);
109
+ }
110
+
66
111
  // Update package.json with project name if provided
67
112
  if (options.name) {
68
113
  const packageJsonPath = join(destination, 'package.json');
69
114
  if (existsSync(packageJsonPath)) {
70
115
  const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
71
- packageJson.name = options.name;
116
+ // s-m-r-t runtime identities use scoped package-qualified class names.
117
+ // Keep a friendly directory/project name while making the generated
118
+ // package identity valid for isolated runtime registration.
119
+ packageJson.name = packageNameForProject(options.name);
72
120
  writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
73
121
  }
74
122
  }
@@ -81,13 +129,16 @@ export function copyTemplate(destination, options = {}) {
81
129
  */
82
130
  export const templateInfo = {
83
131
  name: 'sveltekit',
84
- description: 'Minimal SvelteKit project with s-m-r-t framework integration',
132
+ description: 'Profile-aware installable SvelteKit project with s-m-r-t integration',
85
133
  features: [
86
134
  'SvelteKit 2.x with Svelte 5',
87
135
  'Auto-generated REST API routes',
88
136
  's-m-r-t CLI integration',
89
137
  'TypeScript support',
90
138
  'SQLite database (configurable)',
139
+ 'Local, self-hosted, and cloud runtime profiles',
140
+ 'Deterministic install, diagnostics, and portability commands',
141
+ 'Production Node container and separate worker entry points',
91
142
  'Session-authorized tenancy with separate subdomain selection',
92
143
  ],
93
144
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-template-sveltekit",
3
- "version": "0.43.9",
3
+ "version": "0.44.0",
4
4
  "description": "Minimal SvelteKit project template with s-m-r-t framework integration",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -29,19 +29,33 @@
29
29
  "directory": "packages/template-sveltekit"
30
30
  },
31
31
  "peerDependencies": {
32
- "@happyvertical/smrt-core": "0.43.9"
32
+ "@happyvertical/smrt-core": "0.44.0"
33
33
  },
34
34
  "devDependencies": {
35
+ "@happyvertical/sql": "^0.89.4",
36
+ "@types/node": "24.13.2",
37
+ "typescript": "5.9.3",
35
38
  "vitest": "4.1.10",
36
- "@happyvertical/smrt-tenancy": "0.43.9",
37
- "@happyvertical/smrt-users": "0.43.9"
39
+ "@happyvertical/smrt-assets": "0.44.0",
40
+ "@happyvertical/smrt-playbooks": "0.44.0",
41
+ "@happyvertical/smrt-jobs": "0.44.0",
42
+ "@happyvertical/smrt-profiles": "0.44.0",
43
+ "@happyvertical/smrt-config": "0.44.0",
44
+ "@happyvertical/smrt-app-runtime": "0.44.0",
45
+ "@happyvertical/smrt-scanner": "0.44.0",
46
+ "@happyvertical/smrt-users": "0.44.0",
47
+ "@happyvertical/smrt-web": "0.44.0",
48
+ "@happyvertical/smrt-tenancy": "0.44.0"
38
49
  },
39
50
  "publishConfig": {
40
51
  "registry": "https://registry.npmjs.org",
41
52
  "access": "public"
42
53
  },
43
54
  "scripts": {
55
+ "build": "pnpm run typecheck && node --check index.js",
44
56
  "test": "vitest run",
45
- "test:watch": "vitest"
57
+ "test:postgres": "node ../../scripts/run-with-ci-postgres.mjs -- pnpm exec vitest run __tests__/runtimeProfileReference.postgres.optional.test.ts",
58
+ "test:watch": "vitest",
59
+ "typecheck": "tsc --noEmit -p tsconfig.fixture.json"
46
60
  }
47
61
  }
@@ -0,0 +1,12 @@
1
+ node_modules
2
+ .git
3
+ .svelte-kit
4
+ build
5
+ dist
6
+ .smrt
7
+ .env
8
+ .env.*
9
+ data
10
+ backups
11
+ exports
12
+ *.log
@@ -0,0 +1,9 @@
1
+ SMRT_RUNTIME_PROFILE=cloud
2
+ DATABASE_URL=postgresql://managed-connection-from-secret-provider
3
+ HOST=0.0.0.0
4
+ PORT=3000
5
+ # Configure installed provider-owned modules that probe the real services.
6
+ # SMRT_AUTH_READINESS_MODULE=@your-org/auth-readiness
7
+ # SMRT_ASSETS_READINESS_MODULE=@your-org/assets-readiness
8
+ # SMRT_SECRETS_READINESS_MODULE=@your-org/secrets-readiness
9
+ SMRT_WORKER_CONCURRENCY=8
@@ -1,6 +1,18 @@
1
- # Database
2
- DATABASE_URL=./app.db
3
- DATABASE_TYPE=sqlite
1
+ # One canonical infrastructure profile. Local is the safe default when omitted.
2
+ SMRT_RUNTIME_PROFILE=local
3
+
4
+ # Optional stable application id and user-owned data path. The default is the
5
+ # operating system's application-data directory, never this source checkout.
6
+ # SMRT_APP_ID=my-app
7
+ # SMRT_DATA_DIR=/absolute/user-owned/path
8
+
9
+ # app:*, db:migrate, and the Vite dev/build launchers load this ignored file.
10
+ # Keep SMRT_* runtime identity here; Vite's mode-specific env files are not
11
+ # runtime identity sources.
12
+ # Direct `node build` does not; export HOST explicitly or prefer app:start.
13
+ # Local owner bootstrap is loopback-only.
14
+ HOST=127.0.0.1
15
+ PORT=5173
4
16
 
5
17
  # Optional apex used for subdomain tenant selection.
6
18
  # The value selects a tenant candidate; session membership still authorizes it.
@@ -0,0 +1,11 @@
1
+ SMRT_RUNTIME_PROFILE=self-hosted
2
+ HOST=0.0.0.0
3
+ PORT=3000
4
+ # Put DATABASE_URL and POSTGRES_PASSWORD in the ignored `.env` file. Compose
5
+ # refuses to start if either secret is absent; never commit their values.
6
+ # Provider adapters keep real credentials private. Each setting must name an
7
+ # installed module whose checkReadiness() probes the real backing service.
8
+ # SMRT_AUTH_READINESS_MODULE=@your-org/auth-readiness
9
+ # SMRT_ASSETS_READINESS_MODULE=@your-org/assets-readiness
10
+ # SMRT_SECRETS_READINESS_MODULE=@your-org/secrets-readiness
11
+ SMRT_WORKER_CONCURRENCY=4
@@ -11,6 +11,21 @@ tools, WebMCP definitions, and agent/developer knowledge artifacts.
11
11
  - Regenerate them with `pnpm build` or `pnpm dev`; run `pnpm db:migrate` after
12
12
  object schema changes.
13
13
  - Keep `CLAUDE.md` as the one-line `@AGENTS.md` shim.
14
+ - The root-owned `smrt.runtime.diagnostics.read` WebMCP tool calls only the
15
+ authenticated `/api/_runtime/diagnostics` route as the page user. Keep its
16
+ registration singular/read-only and preserve owner disposal. The route must
17
+ authorize a direct active tenant principal before projection or probing and
18
+ return only the schema-versioned allowlist; never expose private runtime
19
+ objects, identity fields, paths, URLs, secrets, PII, raw errors, or logs.
20
+ - Never improvise setup internals. Run `pnpm app:install`; use individual
21
+ `app:*` commands only for recovery or explicit operator control.
22
+ - Recover an interrupted unclaimed owner invitation with `pnpm app:stop`, then
23
+ `pnpm app:recover`, `pnpm app:start`, and `pnpm app:open`; never print or copy
24
+ its token.
25
+ - Treat `smrt.config.ts` `runtime.profile` as the only infrastructure selector.
26
+ - Treat the local health route's configuration digest as a loopback-only
27
+ identity; it must never contain or hash provider credentials. Deployed
28
+ health responses expose only generic status and profile.
14
29
 
15
30
  ## Data and authorization
16
31
 
@@ -24,6 +39,9 @@ tools, WebMCP definitions, and agent/developer knowledge artifacts.
24
39
  membership-gated browser tenant changes.
25
40
  - Use `assertOperationPermission()` for hand-written server mutations and pass
26
41
  the exact session permission snapshot when available.
42
+ - Keep data, assets, secrets, PID state, backups, and exports outside source.
43
+ Never copy secrets into diagnostics, logs, exports, or agent prompts.
44
+ - Local onboarding creates real authorization records and stays loopback-only.
27
45
 
28
46
  ## Extension rules
29
47
 
@@ -35,3 +53,17 @@ tools, WebMCP definitions, and agent/developer knowledge artifacts.
35
53
  Keep the guard in place for SSR and seed live collections with SSR
36
54
  `initialData`.
37
55
  - Do not enable knowledge HTTP routes in production without explicit admin auth.
56
+ - Run task and schedule workers as separate deployed processes. Extend the
57
+ portability adapter instead of converting database files. Deployed provider
58
+ readiness must come from installed modules that probe the real backing
59
+ services; environment booleans are not readiness evidence.
60
+ - Every supported local web entry point (`app:start` or `pnpm dev`) holds the
61
+ same writer lease. Direct production startup requires an explicit loopback
62
+ `HOST`; prefer `app:start`. Stop the app before backup/import; never bypass
63
+ or delete a live lease. Deployed imports require stopped web/workers plus
64
+ explicit `SMRT_MAINTENANCE_MODE=true`.
65
+ - Logical export reads every model table in one transaction and includes
66
+ filesystem assets referenced by exported asset rows in a bounded,
67
+ digest-verified manifest. Keep imports transactional, stage and verify assets
68
+ before commit, and preserve the relationship-aware ordering/deferred-cycle
69
+ contract.
@@ -0,0 +1,23 @@
1
+ FROM node:24-alpine AS build
2
+ WORKDIR /app
3
+ RUN corepack enable
4
+ COPY package.json pnpm-lock.yaml* ./
5
+ RUN pnpm install --frozen-lockfile
6
+ COPY . .
7
+ RUN pnpm build
8
+
9
+ FROM node:24-alpine AS runtime
10
+ WORKDIR /app
11
+ RUN corepack enable
12
+ COPY --from=build /app/package.json /app/pnpm-lock.yaml* ./
13
+ RUN pnpm install --frozen-lockfile --prod=false
14
+ ENV NODE_ENV=production
15
+ ENV SMRT_RUNTIME_PROFILE=self-hosted
16
+ COPY --from=build /app/build ./build
17
+ COPY --from=build /app/.smrt ./.smrt
18
+ COPY --from=build /app/scripts ./scripts
19
+ COPY --from=build /app/smrt.config.ts ./smrt.config.ts
20
+ RUN chown -R node:node /app
21
+ USER node
22
+ EXPOSE 3000
23
+ CMD ["node", "build"]
@@ -0,0 +1,11 @@
1
+ # Agent install prompt
2
+
3
+ > Install this application using its documented deterministic path. Verify Node
4
+ > and pnpm requirements, run `pnpm install --frozen-lockfile` when a lockfile
5
+ > exists (otherwise `pnpm install`), then run `pnpm app:install`. Do not edit
6
+ > source or invent database/authentication steps. If installation fails, run
7
+ > `pnpm app:doctor`, show me its secret-free recovery findings, and ask before
8
+ > changing provider configuration.
9
+
10
+ The prompt is intentionally thin. `scripts/smrt-app.mjs` owns operations and
11
+ recovery.
@@ -1,9 +1,8 @@
1
1
  # s-m-r-t SvelteKit starter
2
2
 
3
- This is the small, ground-up starting point for s-m-r-t 0.38.25. It contains
4
- one object and the current SvelteKit application foundation. It intentionally
5
- does not include billing, onboarding, workers, deployment infrastructure, or
6
- provider-specific production configuration.
3
+ This is the small, ground-up starting point for s-m-r-t 0.43.10. It contains
4
+ one object and a profile-aware operational surface for local, self-hosted, and
5
+ managed-cloud environments. It does not provision external providers.
7
6
 
8
7
  ## 1. Install and run
9
8
 
@@ -13,20 +12,33 @@ is declared in `packageManager`.
13
12
  ```bash
14
13
  pnpm install
15
14
  cp .env.example .env
16
- pnpm db:migrate
17
- pnpm check
18
- pnpm build
19
- pnpm dev
15
+ pnpm app:install
20
16
  ```
21
17
 
22
- Open `http://localhost:5173`. The app loads, but tenant data remains closed
23
- until your application adds a sign-in flow, an active membership, and role
24
- permissions. That is intentional: the starter demonstrates safe boundaries
25
- without inventing an authentication provider.
18
+ `app:install` validates the canonical profile, builds, migrates, initializes
19
+ the secure local owner invitation, starts the production Node build on
20
+ loopback, and opens that single-use invitation. Stop a running local app before
21
+ re-running install, setup, or recovery; each operation is repeatable from that
22
+ stopped state. `pnpm app:doctor`
23
+ prints secret-free JSON diagnostics and recovery steps. Individual
24
+ setup/start/doctor/open/stop/backup/export/import operations are available as
25
+ `pnpm app:<operation>`.
26
+
27
+ `app:install`, `app:start`, `app:stop`, and `app:recover` are local-profile
28
+ operations. Self-hosted and cloud web processes run the production Node build
29
+ or container directly; workers use the separate worker commands below.
30
+
31
+ If installation is interrupted after the invitation is created, rerun
32
+ `pnpm app:start` and `pnpm app:open`; the private state directory retains the
33
+ loopback handoff without printing its token. If that invitation expired or was
34
+ lost, run `pnpm app:stop`, then `pnpm app:recover`, start, and open again.
35
+ Recovery rotates only an
36
+ unclaimed invitation and cannot replace an existing owner.
26
37
 
27
38
  `pnpm db:migrate` first runs the Vite build so the manifest, runtime
28
39
  registration, generated routes, and types match the current objects; it then
29
- applies the manifest-derived schema to SQLite. Re-run it after object changes.
40
+ holds the shared application-operation lock while it applies the
41
+ manifest-derived schema to SQLite. Re-run it after object changes.
30
42
 
31
43
  ## 2. Understand the generated files
32
44
 
@@ -95,15 +107,17 @@ construct the collection outside a SvelteKit request.
95
107
 
96
108
  ## 4. Initialize or migrate the database
97
109
 
98
- SQLite defaults to `./app.db`; override it with `DATABASE_URL` and
99
- `DATABASE_TYPE`.
110
+ The canonical `runtime.profile` in `smrt.config.ts` defaults to `local`. Local
111
+ SQLite, assets, secrets, backups, exports, and PID state live in the current
112
+ user's operating-system data/state directories, outside this checkout.
113
+ `SMRT_DATA_DIR` is rejected when it overlaps source.
100
114
 
101
115
  ```bash
102
116
  pnpm db:migrate
103
117
  ```
104
118
 
105
- The current command is `smrt db:migrate`. `smrt db:setup` is deprecated in
106
- 0.38.25 and is intentionally not used. Migrations are manifest-driven: change
119
+ The current command is `smrt db:migrate`; deprecated `smrt db:setup` is
120
+ intentionally not used. Migrations are manifest-driven: change
107
121
  the TypeScript object, regenerate the manifest, and run the migration again.
108
122
  There are no hand-written migration files in this workflow.
109
123
 
@@ -216,8 +230,8 @@ pnpm smrt objects
216
230
  pnpm smrt schema Item
217
231
  ```
218
232
 
219
- The 0.38.25 CLI's manifest-only `objects` and `schema` commands work directly
220
- in this source-first template. Executing local-object CRUD through the generic
233
+ The CLI's manifest-only `objects` and `schema` commands work directly in this
234
+ source-first template. Executing local-object CRUD through the generic
221
235
  CLI additionally requires a compiled JavaScript project entry point; REST and
222
236
  the page action are the runnable CRUD examples here.
223
237
 
@@ -234,20 +248,15 @@ with:
234
248
  node .smrt/mcp-server/index.js
235
249
  ```
236
250
 
237
- The generated entry resolves its imports from this project, not from the CLI, so
238
- every module it imports must be a dependency here. `@modelcontextprotocol/server`,
239
- `@happyvertical/smrt-core`, and `@happyvertical/smrt-config` are always imported
240
- and are already declared. Two more are emitted only when your objects need them,
241
- and you must add them yourself:
242
-
243
- | Emitted when | Dependency | Already declared? |
244
- | --- | --- | --- |
245
- | An exposed object declares task actions (`@smrt({ mcp: { tasks: true } })`) | `@happyvertical/smrt-jobs` | No — add it |
246
- | An exposed object is tenant-scoped | `@happyvertical/smrt-tenancy` | Yes |
251
+ The generated entry resolves its imports from this project, not from the CLI,
252
+ so every module it imports must be a dependency here.
253
+ `@modelcontextprotocol/server`, `@happyvertical/smrt-core`,
254
+ `@happyvertical/smrt-config`, `@happyvertical/smrt-jobs`, and
255
+ `@happyvertical/smrt-tenancy` are already declared for the default runtime and
256
+ worker surfaces.
247
257
 
248
- Add the jobs package on the same release line as the other
249
- `@happyvertical/smrt-*` pins in `package.json`, then reinstall. Missing one of
250
- these fails at startup with `ERR_MODULE_NOT_FOUND` naming the package to add.
258
+ Missing a dependency added by a custom MCP extension fails at startup with
259
+ `ERR_MODULE_NOT_FOUND` naming the package to declare.
251
260
 
252
261
  WebMCP is wired at the root Provider as a read-only, authenticated browser
253
262
  surface. The template includes `@happyvertical/smrt-web` on the synchronized
@@ -321,3 +330,36 @@ from first principles. Move to `smrt-saas-starter` when you want a
321
330
  production-shaped SaaS baseline with onboarding, billing/subscriptions,
322
331
  background workers, provider configuration, deployment conventions, and
323
332
  mobile surfaces. Those concerns are intentionally absent here.
333
+
334
+ ## Runtime profiles and deployment
335
+
336
+ - `local`: SQLite, loopback single-use owner bootstrap, user-owned files, and
337
+ embedded/on-demand jobs.
338
+ - `self-hosted`: PostgreSQL, public authentication, operator providers, and
339
+ separate workers. Copy `.env.self-hosted.example` to `.env.self-hosted` for
340
+ the service configuration. Separately create the ignored plain `.env` file
341
+ with `DATABASE_URL` and `POSTGRES_PASSWORD` for Compose interpolation, then
342
+ use Compose. Compose refuses to start when either secret is absent. Configure
343
+ installed authentication, asset, and secret readiness modules; each module
344
+ must probe its real provider before web or workers can start.
345
+ - `cloud`: hosted identity, managed providers, required tenant context, and
346
+ scalable workers. `.env.cloud.example` records the composition boundary.
347
+
348
+ The adapter-node `Dockerfile` and `compose.yaml` provide the production path.
349
+ Compose gates the web and both workers on the one-shot, idempotent migration
350
+ service. The runtime image retains the generated manifest and operator CLI for
351
+ doctor/export/import. `pnpm worker` and `pnpm worker:schedule` are separate from
352
+ the web process.
353
+ Logical export/import is manifest-driven JSON and refuses a non-empty target;
354
+ it orders parent tables first and defers nullable cycle edges until every row
355
+ exists. Export reads all model tables from one transaction snapshot. Every
356
+ schema-v2 export includes filesystem assets referenced by exported asset rows
357
+ through a bounded, provider-neutral manifest with byte lengths and SHA-256
358
+ digests. Import stages and verifies those assets before committing database
359
+ rows; legacy database-only schema-v1 bundles are rejected with a version error.
360
+ Every supported local web
361
+ entry point (`app:start` or `pnpm dev`) holds a shared writer lease. Direct
362
+ production startup must set an explicit loopback `HOST`, and `app:start` is the
363
+ recommended entry point. Stop the app before backup/import. For deployed import, stop
364
+ web/workers and set `SMRT_MAINTENANCE_MODE=true`. Extend
365
+ `scripts/smrt-portability.mjs` for domain-specific transformations.
@@ -0,0 +1,56 @@
1
+ x-runtime-environment: &runtime-environment
2
+ DATABASE_URL: ${DATABASE_URL:?Set DATABASE_URL in the ignored .env file}
3
+
4
+ services:
5
+ web:
6
+ build: .
7
+ env_file: .env.self-hosted
8
+ environment: *runtime-environment
9
+ command: node build
10
+ ports:
11
+ - "3000:3000"
12
+ depends_on:
13
+ migrate:
14
+ condition: service_completed_successfully
15
+ worker:
16
+ build: .
17
+ env_file: .env.self-hosted
18
+ environment: *runtime-environment
19
+ command: node scripts/smrt-worker.mjs task
20
+ depends_on:
21
+ migrate:
22
+ condition: service_completed_successfully
23
+ schedule-worker:
24
+ build: .
25
+ env_file: .env.self-hosted
26
+ environment: *runtime-environment
27
+ command: node scripts/smrt-worker.mjs schedule
28
+ depends_on:
29
+ migrate:
30
+ condition: service_completed_successfully
31
+ migrate:
32
+ build:
33
+ context: .
34
+ target: build
35
+ env_file: .env.self-hosted
36
+ environment: *runtime-environment
37
+ command: pnpm app:setup
38
+ depends_on:
39
+ database:
40
+ condition: service_healthy
41
+ database:
42
+ image: postgres:18-alpine
43
+ environment:
44
+ POSTGRES_DB: smrt_app
45
+ POSTGRES_USER: smrt_app
46
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in the ignored .env file}
47
+ healthcheck:
48
+ test: ["CMD-SHELL", "pg_isready -U smrt_app"]
49
+ interval: 5s
50
+ timeout: 3s
51
+ retries: 20
52
+ volumes:
53
+ - postgres-data:/var/lib/postgresql/data
54
+
55
+ volumes:
56
+ postgres-data: