@happyvertical/smrt-template-sveltekit 0.43.10 → 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.
- package/AGENTS.md +31 -6
- package/README.md +26 -10
- package/index.js +53 -2
- package/package.json +19 -5
- package/template/.dockerignore +12 -0
- package/template/.env.cloud.example +9 -0
- package/template/.env.example +15 -3
- package/template/.env.self-hosted.example +11 -0
- package/template/AGENTS.md +32 -0
- package/template/Dockerfile +23 -0
- package/template/INSTALL_PROMPT.md +11 -0
- package/template/README.md +74 -32
- package/template/compose.yaml +56 -0
- package/template/gitignore.template +45 -0
- package/template/package.json +30 -15
- package/template/scripts/smrt-app.mjs +884 -0
- package/template/scripts/smrt-operation-lock.mjs +102 -0
- package/template/scripts/smrt-portability-assets.mjs +707 -0
- package/template/scripts/smrt-portability.mjs +470 -0
- package/template/scripts/smrt-prepare-migration.mjs +93 -0
- package/template/scripts/smrt-process.mjs +82 -0
- package/template/scripts/smrt-provider-readiness.mjs +33 -0
- package/template/scripts/smrt-runtime-identity.mjs +238 -0
- package/template/scripts/smrt-vite.mjs +45 -0
- package/template/scripts/smrt-web.mjs +10 -0
- package/template/scripts/smrt-worker.mjs +86 -0
- package/template/scripts/smrt-writer-lease.mjs +199 -0
- package/template/smrt.config.ts +34 -2
- package/template/src/hooks.server.ts +4 -1
- package/template/src/lib/RuntimeDiagnosticsWebMcp.svelte +11 -0
- package/template/src/lib/runtime-diagnostics-webmcp.ts +108 -0
- package/template/src/lib/server/application-runtime.ts +262 -0
- package/template/src/lib/server/runtime-diagnostics.ts +114 -0
- package/template/src/lib/server/smrt.ts +4 -4
- package/template/src/routes/+layout.svelte +2 -0
- package/template/src/routes/api/_runtime/diagnostics/+server.ts +5 -0
- package/template/src/routes/api/_runtime/health/+server.ts +30 -0
- package/template/src/routes/setup/+page.server.ts +96 -0
- package/template/src/routes/setup/+page.svelte +31 -0
- package/template/svelte.config.js +1 -1
- 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
|
|
27
|
-
|
|
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
|
|
35
|
-
|
|
36
|
-
`smrt db:setup`
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
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()`
|
|
41
|
-
|
|
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
|
|
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
|
-
|
|
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: '
|
|
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.
|
|
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.
|
|
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-
|
|
37
|
-
"@happyvertical/smrt-
|
|
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:
|
|
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,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
|
package/template/.env.example
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
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
|
package/template/AGENTS.md
CHANGED
|
@@ -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.
|
package/template/README.md
CHANGED
|
@@ -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.
|
|
4
|
-
one object and
|
|
5
|
-
|
|
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
|
|
17
|
-
pnpm check
|
|
18
|
-
pnpm build
|
|
19
|
-
pnpm dev
|
|
15
|
+
pnpm app:install
|
|
20
16
|
```
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
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
|
-
|
|
99
|
-
|
|
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
|
|
106
|
-
|
|
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
|
|
220
|
-
|
|
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,
|
|
238
|
-
every module it imports must be a dependency here.
|
|
239
|
-
`@
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
|
|
249
|
-
|
|
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:
|