genesis-stack 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -37,10 +37,15 @@ any resolvable package may declare one when selecting that piece should install
37
37
  that skill. Documentation alone does not require a skill declaration or a
38
38
  catalog dependency.
39
39
 
40
- With Genesis 1.4 or newer, `genesis templates list` also lists the JSKIT starters
41
- in `genesis.templates.json`. Both variants come from
40
+ With Genesis 1.4 or newer, `genesis templates list` also lists the starters
41
+ in `genesis.templates.json`. The JSKIT ones come first, from
42
42
  the [JSKIT seed repository](https://github.com/vibe64-dev/seed-jskit):
43
- `official:jskit/public` and `official:jskit/accounts`. Apply one to an empty
43
+ `official:jskit/public`, `official:jskit/database`, `official:jskit/accounts`
44
+ and `official:jskit/accounts-hub`.
45
+ Then each framework component has one Basic app, from the `basic` branch of its
46
+ `vibe64-dev/seed-<technology>` repository: `official:django/basic`,
47
+ `official:rails/basic`, `official:spring-boot/basic`, `official:express/basic`
48
+ and `official:nextjs/basic`. Apply one to an empty
44
49
  Git/Genesis bootstrap project with `genesis templates apply <id>`. Template IDs
45
50
  name complete applications; selecting a Skill never silently chooses a starter.
46
51
 
@@ -24,6 +24,54 @@
24
24
  "description": "Sign-up, login, a private home, MySQL persistence and isolated database/browser tests.",
25
25
  "repository": "https://github.com/vibe64-dev/seed-jskit.git",
26
26
  "branch": "accounts"
27
+ },
28
+ {
29
+ "id": "jskit/accounts-hub",
30
+ "technology": "jskit",
31
+ "name": "JSKIT · Accounts hub",
32
+ "description": "One sign-in for all your apps: your own accounts service with email sign-in, optional Google, and an Apps page where you approve each app.",
33
+ "repository": "https://github.com/vibe64-dev/seed-jskit.git",
34
+ "branch": "accounts-hub"
35
+ },
36
+ {
37
+ "id": "django/basic",
38
+ "technology": "django",
39
+ "name": "Django · Basic app",
40
+ "description": "Notes form persisted to SQLite, admin site, unit and HTTP tests. No accounts.",
41
+ "repository": "https://github.com/vibe64-dev/seed-django.git",
42
+ "branch": "basic"
43
+ },
44
+ {
45
+ "id": "rails/basic",
46
+ "technology": "rails",
47
+ "name": "Rails · Basic app",
48
+ "description": "Notes form saved to SQLite, sign-in with a seeded development account, ready model and HTTP tests.",
49
+ "repository": "https://github.com/vibe64-dev/seed-rails.git",
50
+ "branch": "basic"
51
+ },
52
+ {
53
+ "id": "spring-boot/basic",
54
+ "technology": "spring-boot",
55
+ "name": "Spring Boot · Basic app",
56
+ "description": "Notes page with a CSRF-protected form, MySQL/MariaDB via JPA and Flyway, and ready unit and HTTP tests. No accounts.",
57
+ "repository": "https://github.com/vibe64-dev/seed-spring-boot.git",
58
+ "branch": "basic"
59
+ },
60
+ {
61
+ "id": "express/basic",
62
+ "technology": "express",
63
+ "name": "Express · Basic app",
64
+ "description": "A small Express notes app that saves a form to SQLite, with unit and HTTP tests. No accounts.",
65
+ "repository": "https://github.com/vibe64-dev/seed-express.git",
66
+ "branch": "basic"
67
+ },
68
+ {
69
+ "id": "nextjs/basic",
70
+ "technology": "nextjs",
71
+ "name": "Next.js · Basic app",
72
+ "description": "A Next.js app with one form that saves notes to SQLite through a Server Action. No accounts.",
73
+ "repository": "https://github.com/vibe64-dev/seed-nextjs.git",
74
+ "branch": "basic"
27
75
  }
28
76
  ]
29
77
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesis-stack",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "description": "The optional curated technology Stack catalog for Genesis projects.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -70,10 +70,9 @@ https://docs.djangoproject.com/
70
70
  after `SecurityMiddleware`; `runserver` serves static files only while
71
71
  `DEBUG` is on. Keep `STATIC_ROOT` and media uploads out of Git.
72
72
  - Tests: `manage.py test` creates `test_<NAME>` and needs permission to
73
- create databases. When the environment supplies a separate disposable test
74
- database instead, set `DATABASES["default"]["TEST"]["NAME"]` from it and keep
75
- `--keepdb`, which reuses an existing database rather than creating or
76
- dropping one. Never point tests at the development or production database.
73
+ create databases; the database integration says how a supplied test
74
+ database replaces it. Never point tests at the development or production
75
+ database.
77
76
  Suites that create users spend most of their time hashing passwords: set
78
77
  `PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]` only
79
78
  in a test settings module or under `if sys.argv[1:2] == ["test"]:`; never
@@ -87,9 +86,10 @@ https://docs.djangoproject.com/
87
86
  review `manage.py check --deploy`. It rejects the console email backend that
88
87
  recent `startproject` templates configure (`MAILERS` or `EMAIL_BACKEND`), so
89
88
  select the production email backend and its credentials from the
90
- environment. When `SECURE_SSL_REDIRECT` is on, add the
91
- readiness path to `SECURE_REDIRECT_EXEMPT` so a plain loopback probe is not
92
- redirected.
89
+ environment. When `SECURE_SSL_REDIRECT` is on, exempt the
90
+ readiness path so a plain loopback probe is not redirected; entries are
91
+ regexes matched against the path without its leading slash, so use
92
+ `SECURE_REDIRECT_EXEMPT = [r"^admin/login/$"]`.
93
93
  - The proposed readiness path is the admin login page `/admin/login/`. An
94
94
  application without `django.contrib.admin` adds a small unauthenticated
95
95
  health view and updates its Outputs and Deployment readiness to it.
@@ -49,10 +49,19 @@ documentation: https://expressjs.com/
49
49
  for form posts; an `Origin` check must accept the origin built from the
50
50
  trusted forwarded host as well as the raw `Host`, because a development
51
51
  proxy may present same-origin requests with a loopback `Origin`. A host or
52
- CORS allow-list must also admit loopback for the health probe.
52
+ CORS allow-list must also admit loopback for the health probe. helmet's
53
+ default `Referrer-Policy: no-referrer` makes browsers send `Origin: null` on
54
+ same-origin form posts, so an `Origin` check refuses every form: set
55
+ `referrerPolicy: { policy: 'same-origin' }` when the app checks `Origin`.
56
+ - helmet sends `X-Frame-Options: SAMEORIGIN` and a Content-Security-Policy with
57
+ `frame-ancestors 'self'`, so a development tool that shows the application
58
+ inside a frame on another origin stays blank. Only when `NODE_ENV` is not
59
+ `production`, pass `frameguard: false` and
60
+ `contentSecurityPolicy: { directives: { frameAncestors: null } }`; production
61
+ keeps both.
53
62
  - Sessions use `express-session` with `secret: process.env.SESSION_SECRET`,
54
- generated with `openssl rand -base64 32`, supplied through the environment,
55
- never committed and never written into a generated dotenv file; refuse to
63
+ generated with `openssl rand -base64 32`, supplied through the environment
64
+ and never committed (a dotenv file holding it stays ignored); refuse to
56
65
  start in production without it. Development may fall back to a random
57
66
  per-process secret, which ends sessions on restart. Use
58
67
  `cookie: { secure: 'auto', httpOnly: true, sameSite: 'lax' }`; `'auto'`
@@ -65,8 +74,12 @@ documentation: https://expressjs.com/
65
74
  with another health path changes both.
66
75
  - Serve static files with `express.static(path.join(__dirname, 'public'))`
67
76
  or the ESM equivalent. A bundler or TypeScript build adds a Workspace setup
68
- step and a Deployment Build step and serves the compiled output.
69
- - Express has no ORM. Select the database component and record the app's own
77
+ step and a Deployment Build step and serves the compiled output; build tools
78
+ are usually devDependencies, so Deployment then installs with plain `npm ci`,
79
+ builds, and may prune with `npm prune --omit=dev` before Serve.
80
+ - Express has no ORM. Select the database component, declare its database
81
+ Resource in the project Stack with the environment names the app reads (no
82
+ Express database component declares it for you), and record the app's own
70
83
  migration tool (Prisma, Drizzle, Knex, Sequelize, TypeORM) as Workspace
71
84
  setup and Deployment Migrate steps, for example `prisma migrate deploy` or
72
85
  `knex migrate:latest`. Never run `prisma migrate dev`, `prisma db push` or
@@ -85,6 +98,11 @@ documentation: https://expressjs.com/
85
98
  role. Seed one development administrator and one ordinary user through an
86
99
  idempotent script that reads their passwords from the environment; never
87
100
  hard-code production credentials.
101
+ - Commands that run outside the server (migrations, seeds, administrative or
102
+ development sign-in helpers) use the server's database and secrets from the
103
+ same environment variables. A command started without that environment loads
104
+ the ignored dotenv the host generates with `process.loadEnvFile()` rather than
105
+ falling back to a different database file or connection.
88
106
 
89
107
  ## Adoption
90
108
 
@@ -32,6 +32,15 @@ https://mobily-enterprises.github.io/jskit-ai/
32
32
  `AUTH_LOCAL_SESSION_SECRET` as a required environment name in the project's
33
33
  established environment contract and committed environment example. Never
34
34
  commit or invent its value.
35
+ - An application with accounts always has the host's browser-preview login,
36
+ without waiting to be asked: when adding authentication, add the
37
+ `auth/auth-surface` pattern's executable `tools/preview-identity` and copy its
38
+ exact `#### Preview identity` block (Command `tools/preview-identity`,
39
+ Protocol `vibe64.preview-identity.command.v1`, Identity types `email`
40
+ `user-id`, Enabled environment `AUTH_DEV_BYPASS_ENABLED`, Secret environment
41
+ `AUTH_DEV_BYPASS_SECRET`, Runtimes `nodejs`) under the web `Outputs` target.
42
+ Which existing accounts the preview uses stays the person's choice; never
43
+ create users for it.
35
44
  - Before creating or modifying JSKIT Vue/Vuetify UI, read the Material 3
36
45
  contract at
37
46
  `https://mobily-enterprises.github.io/jskit-ai/guide/framework/material-3`
@@ -77,6 +86,25 @@ https://mobily-enterprises.github.io/jskit-ai/
77
86
  input fields never grant administrator access. Verify the requested provider
78
87
  outcomes and denied access with controlled fixtures. Genesis transports
79
88
  guidance and opaque declarations; it does not execute integrations or payments.
89
+ - Shipping the app to phones ("install on my phone"): the host-owned
90
+ `## Packaging` section is written only when the person adds a mobile target.
91
+ It declares a separate `web-bundle` package whose Build produces the bundled
92
+ client (Output its build folder) with the absolute HTTPS API base the client
93
+ calls, then a `capacitor` package with `Content: package <that id>`,
94
+ `Platforms: android`, a permanent reverse-DNS `Id`, `Name`, a committed
95
+ 1024x1024 PNG `Icon` without transparency, `Version from: package.json` and
96
+ one `Permission` line, with a plain reason, per native capability. The host
97
+ generates and discards the native project on every build: never commit
98
+ `android/`, `ios/`, `capacitor.config.*`, Gradle files, build numbers or
99
+ signing keys. Native plugins are ordinary client dependencies at the exact
100
+ versions in `package-lock.json`, and the host's shell is Capacitor 8; it
101
+ blocks a lockfile mismatch or another major. `@jskit-ai/mobile-capacitor` is
102
+ still on Capacitor 7, so propose mobile packaging for a JSKIT client only
103
+ once its Capacitor dependencies are 8.x. A deep-link scheme and app-link
104
+ domains live once in `## Environment defaults` (for example `APP_URL_SCHEME`,
105
+ `APP_LINK_DOMAINS`) and Packaging names them with `Deep link scheme from` and
106
+ `App link domains from`; the server's CORS and OAuth callbacks must accept
107
+ the app's origin and scheme.
80
108
 
81
109
  ## Adoption
82
110
 
@@ -96,7 +124,9 @@ https://mobily-enterprises.github.io/jskit-ai/
96
124
  component when the script or preparation file does not exist.
97
125
  - When the project declares an application-owned identity exchange for a host's
98
126
  browser preview, preserve its command, selectors, protocol, environment names,
99
- runtime and timeout in the project's existing section.
127
+ runtime and timeout in the project's existing section. When an application
128
+ with JSKIT accounts lacks it, add it from the `auth/auth-surface` pattern as
129
+ in Guidance.
100
130
  A host-saved identity name/value is not a substitute for this application
101
131
  contract.
102
132
  - Treat generator descriptors, receipts, sync commands, legacy service
@@ -42,12 +42,19 @@ framework documentation: https://nextjs.org/docs
42
42
  WebSocket host for the proxy.
43
43
  - Server Actions reject a POST whose `Origin` host differs from
44
44
  `X-Forwarded-Host` (or `Host` without it) with "Invalid Server Actions
45
- request". When a development proxy rewrites `Origin` to the loopback address
46
- while forwarding the browser host, admit exactly that loopback host in
47
- `experimental.serverActions.allowedOrigins` in development only, for example
48
- `` process.env.NODE_ENV === "development" && process.env.PORT ? [`127.0.0.1:${process.env.PORT}`] : [] ``
49
- (ports cannot be wildcarded). The list also applies in production; never
50
- admit arbitrary origins there.
45
+ request". A proxy that forwards both unchanged, or presents a same-origin
46
+ request entirely on the loopback origin (loopback `Origin` and `Host`, no
47
+ forwarded host), needs no setting. Add an exact host to
48
+ `experimental.serverActions.allowedOrigins` only for a real reported
49
+ mismatch, in development only (ports cannot be wildcarded); the list also
50
+ applies in production, so never admit arbitrary origins.
51
+ - Redirect with relative paths. A Server Action's redirect travels in a
52
+ framework response header, not `Location`, so a proxy does not rewrite it,
53
+ and Auth.js `signIn`/`signOut` with `redirectTo` build an absolute URL from
54
+ the request's host headers, which a proxy may present as the loopback
55
+ address. Call them with `redirect: false`, then `redirect("/path")`.
56
+ `request.nextUrl` in `proxy`/middleware names the server's own host, so an
57
+ Auth.js `callbackUrl` may be a loopback URL: use only its path.
51
58
  - `NEXT_PUBLIC_*` variables are inlined into client bundles at build time:
52
59
  never put secrets in them, and their production values must exist when
53
60
  `next build` runs, not only at start. Server-only values are read from
@@ -58,6 +65,13 @@ framework documentation: https://nextjs.org/docs
58
65
  file instead) and set `AUTH_TRUST_HOST=true` when the app is served behind a
59
66
  trusted reverse proxy, so callback and redirect URLs use the forwarded
60
67
  origin. Other auth libraries need their equivalent trusted-host setting.
68
+ - With `cacheComponents` enabled (the recent `create-next-app` default),
69
+ pages that read the session must render at request time: call
70
+ `await connection()` before `auth()`, wrap the read in `<Suspense>`, or
71
+ export `instant = false`; otherwise `next build` fails. Gate protected
72
+ routes in `proxy`/middleware (Auth.js `authorized` callback), not only with
73
+ `redirect()` during render: the client router can replay a cached
74
+ render-time redirect to the login page after sign-in.
61
75
  - Readiness and health probes must reach a public route: middleware (or
62
76
  `proxy`) that redirects `/` to a login page makes a `GET /` probe fail.
63
77
  Exclude the probe path from the auth matcher, or add a small public route
@@ -86,6 +100,20 @@ framework documentation: https://nextjs.org/docs
86
100
  for development through an idempotent seed script with credentials from the
87
101
  environment, and keep role checks on the server (Server Components, route
88
102
  handlers, Server Actions), not only in client components.
103
+ - Shipping the app to phones ("install on my phone") needs a static client:
104
+ `output: "export"` (no Server Actions, route handlers, middleware or
105
+ request-time rendering in that build), with API calls going to an absolute
106
+ HTTPS base read from a `NEXT_PUBLIC_*` variable. The host-owned
107
+ `## Packaging` section, written only when the person adds a mobile target,
108
+ declares a `web-bundle` package (Build `npm` `run` `build`, Output `out`)
109
+ and a `capacitor` package with `Content: package <that id>`,
110
+ `Platforms: android`, a permanent reverse-DNS `Id`, `Name`, a committed
111
+ 1024x1024 PNG `Icon` without transparency, `Version from: package.json` and
112
+ one `Permission` line per native capability. Native plugins are ordinary
113
+ dependencies at exact `package-lock.json` versions on Capacitor 8; the host
114
+ generates and discards the native project, so never commit `android/`,
115
+ `capacitor.config.*` or signing keys. A server-rendered app is not yet
116
+ supported as a phone app: it needs a separate static client first.
89
117
 
90
118
  ## Adoption
91
119
 
@@ -138,7 +166,7 @@ framework documentation: https://nextjs.org/docs
138
166
  - Ready when: `GET` `/` returns `200`
139
167
  - Prepare `Install dependencies`: `npm` `ci`
140
168
  - Build `Build`: `npm` `run` `build`
141
- - Serve `Start`: `npm` `start` `--` `--hostname` `127.0.0.1`
169
+ - Serve `Start`: `sh` `-c` `exec npm start -- --hostname "$HOST"`
142
170
 
143
171
  ## Deslop
144
172
 
@@ -41,6 +41,17 @@ Node.js package, command, module, dependency, and test conventions.
41
41
  `package.json` and `package-lock.json` stay unchanged; Verify resolves each
42
42
  package from the application root and fails unless it resolves to its
43
43
  connected source root. An ordinary `npm ci` restores the published packages.
44
+ - Publishing a library to npm: the host-owned `## Packaging` section declares
45
+ the package (`npm` packer, its Workdir, `Version from: package.json`) only
46
+ when a publishing target is added; the host builds with `npm pack` from the
47
+ saved commit and owns tags, credentials and uploads. Keep `name`, `version`,
48
+ `license`, `files` (or `.npmignore`) and an `exports`/`main` entry correct in
49
+ `package.json`; never commit tarballs, tokens or `.npmrc` credentials, and
50
+ remove `"private": true` only for a package that is really published. A
51
+ build step before packing (TypeScript, bundling) is a declared Build command
52
+ such as `npm run build`, not a `prepublishOnly` script. A project using
53
+ Changesets or release-please keeps that tool and declares
54
+ `Version from: external`.
44
55
  - Long-running web processes accept their listen host and port from arguments
45
56
  or the environment rather than hard-coding them, and fail if the port is
46
57
  taken instead of silently choosing another. For Express or Next.js select
@@ -42,6 +42,21 @@ Python package, module, callable, dependency, and test conventions.
42
42
  PEP 610 `direct_url.json` is editable with the source root's `file:` URL and
43
43
  that its modules import from there. Do not use `uv sync`, which needs tracked
44
44
  `[tool.uv.sources]` and lockfile changes.
45
+ - Publishing a library to PyPI: the host-owned `## Packaging` section declares
46
+ the package (`python-dist` packer, `Runtimes: python`, its `dist` Output,
47
+ `Version from: pyproject.toml`) only when a publishing target is added; the
48
+ host builds the sdist and wheel from the saved commit and owns tags,
49
+ credentials and uploads. The build runs in a clean copy where `build` is
50
+ not installed, and managed Pythons refuse a bare `pip install` (PEP 668), so
51
+ a plain `python3 -m build` fails. The package installs its own throwaway
52
+ tools: Prepare `python3 -m venv .build-tools`, Prepare
53
+ `.build-tools/bin/python -m pip install --quiet build`, Build
54
+ `.build-tools/bin/python -m build`; add `.build-tools/` to `.gitignore`. Keep a PEP 621
55
+ `[project]` table with a static `version`, `readme`, `license` and
56
+ `requires-python` so `twine check --strict` passes; versions use PEP 440
57
+ (`1.5.0rc1`). Never commit `dist/`, tokens or `.pypirc`. A project with a
58
+ dynamic version (setuptools-scm, hatch-vcs) declares
59
+ `Version from: external`.
45
60
  - Running a web process: bind the supplied listen host and port, trust forwarded
46
61
  headers only from a loopback proxy, and read secrets from the environment.
47
62
  For Django select `django`; its piece owns the framework traps. Flask uses
@@ -39,7 +39,10 @@ Rails MySQL connection, Active Record migration, and database environment conven
39
39
  "allowEmpty": ["password"],
40
40
  "preferred": true
41
41
  }
42
- ]
42
+ ],
43
+ "optionalBindings": {
44
+ "testDatabase": "TEST_DB_NAME"
45
+ }
43
46
  }
44
47
  ```
45
48
 
@@ -51,13 +54,15 @@ Rails MySQL connection, Active Record migration, and database environment conven
51
54
  `port: <%= ENV.fetch("DB_PORT", 3306) %>`,
52
55
  `username: <%= ENV["DB_USERNAME"] %>`, `password: <%= ENV["DB_PASSWORD"] %>`;
53
56
  and `database: <%= ENV["DB_DATABASE"] %>` for development and the
54
- production primary. Keep `test` on its own disposable database name.
57
+ production primary. Point `test` at
58
+ `<%= ENV.fetch("TEST_DB_NAME", "<app>_test") %>`, never at the development
59
+ or production database.
55
60
  - Keep `encoding: utf8mb4`. `mysql2` compiles against the MySQL client
56
61
  library; `trilogy` (Rails 7.1+) is pure Ruby and avoids it. Do not switch an
57
62
  existing app's adapter without testing it.
58
- - `bin/rails test` purges and recreates the test database, which needs
59
- `CREATE`/`DROP` privileges unless the test database is supplied and
60
- disposable.
63
+ - `bin/rails test` purges the test database whenever its schema is out of
64
+ date by dropping and recreating it, so the user needs `CREATE` and `DROP`
65
+ on that database name even when `TEST_DB_NAME` names it.
61
66
  - Rails 8 production declares separate `cache`, `queue` and `cable`
62
67
  databases. When only one database is supplied, follow the Solid Cache, Solid
63
68
  Queue and Solid Cable single-database instructions (their schemas become
@@ -39,7 +39,10 @@ Rails PostgreSQL connection, Active Record migration, and database environment c
39
39
  "allowEmpty": ["password"],
40
40
  "preferred": true
41
41
  }
42
- ]
42
+ ],
43
+ "optionalBindings": {
44
+ "testDatabase": "TEST_DB_NAME"
45
+ }
43
46
  }
44
47
  ```
45
48
 
@@ -51,9 +54,18 @@ Rails PostgreSQL connection, Active Record migration, and database environment c
51
54
  `port: <%= ENV.fetch("DB_PORT", 5432) %>`,
52
55
  `username: <%= ENV["DB_USERNAME"] %>`, `password: <%= ENV["DB_PASSWORD"] %>`;
53
56
  and `database: <%= ENV["DB_DATABASE"] %>` for development and the
54
- production primary. Keep `test` on its own disposable database name.
55
- - `bin/rails test` purges and recreates the test database, which needs the
56
- `CREATEDB` privilege unless the test database is supplied and disposable.
57
+ production primary. Point `test` at
58
+ `<%= ENV.fetch("TEST_DB_NAME", "<app>_test") %>`, never at the development
59
+ or production database.
60
+ - `bin/rails test` purges the test database whenever its schema is out of
61
+ date by dropping and recreating it, so the role needs `CREATEDB` (and must
62
+ own the database) even when `TEST_DB_NAME` names it.
63
+ - After loading fixtures, Rails checks their foreign keys
64
+ (`verify_foreign_keys_for_fixtures`, on by default) by updating
65
+ `pg_catalog.pg_constraint`, which only a superuser may do. With an ordinary
66
+ `CREATEDB` role every fixture test fails with `permission denied for table
67
+ pg_constraint`; set `config.active_record.verify_foreign_keys_for_fixtures =
68
+ false` in `config/environments/test.rb` rather than granting superuser.
57
69
  - Rails 8 production declares separate `cache`, `queue` and `cable`
58
70
  databases. When only one database is supplied, follow the Solid Cache, Solid
59
71
  Queue and Solid Cable single-database instructions (their schemas become
@@ -44,6 +44,11 @@ Authoritative framework documentation: https://guides.rubyonrails.org/
44
44
  same way: allow the observed development origin in
45
45
  `config.action_cable.allowed_request_origins` rather than disabling its
46
46
  forgery protection.
47
+ - Rails sends `X-Frame-Options: SAMEORIGIN`, so a host that shows development
48
+ pages inside a frame on another origin gets a blank frame. Only in
49
+ `development.rb`, drop it with
50
+ `config.action_dispatch.default_headers.delete("X-Frame-Options")`;
51
+ production keeps the header.
47
52
  - Secrets: development uses `tmp/local_secret.txt`. Production needs
48
53
  `SECRET_KEY_BASE` (generate with `bin/rails secret`) or `RAILS_MASTER_KEY`
49
54
  for `config/credentials.yml.enc`, supplied through the deployment
@@ -55,13 +60,19 @@ Authoritative framework documentation: https://guides.rubyonrails.org/
55
60
  Ask for the key or move the needed values to environment variables.
56
61
  - `bin/rails db:prepare` creates the database, loads the schema and runs
57
62
  `db/seeds.rb` when it is new, and migrates it otherwise, so repeated setup is
58
- safe. Seeds must be idempotent (`find_or_create_by!`). Commit generated
63
+ safe. It seeds only a database without a `schema_migrations` table: one that
64
+ already has it (a development server's pending-migration check creates it on
65
+ the first request) is only migrated, so setup also runs `bin/rails db:seed`.
66
+ Seeds must therefore be idempotent (`find_or_create_by!`) and guard
67
+ development-only records with `Rails.env.development?`. In development every
68
+ request, including `/up`, fails with a pending-migration error until
69
+ `db:prepare` has run against the supplied database. Commit generated
59
70
  migrations and `db/schema.rb`; never run `db:reset`, `db:setup`,
60
71
  `db:schema:load`, `db:drop` or edit an applied migration as routine setup.
61
72
  - `DATABASE_URL` overrides the connection of every environment, including
62
73
  `test`. Keep it unset where tests run, and point `test` in
63
- `config/database.yml` at its own disposable database (a supplied disposable
64
- test database name when the environment declares one), never at the
74
+ `config/database.yml` at its own disposable database (the database
75
+ integration's test database binding when it is supplied), never at the
65
76
  development or production database. `bin/rails test` recreates the test
66
77
  schema.
67
78
  - Mode is set per environment: `RAILS_ENV=production` belongs in the
@@ -78,9 +89,18 @@ Authoritative framework documentation: https://guides.rubyonrails.org/
78
89
  a precompiled gem. Dependency installation therefore also uses `cpp`.
79
90
  `trilogy` avoids the MySQL client library. A `Gemfile.lock` resolved only on
80
91
  another platform needs `bundle lock --add-platform x86_64-linux` (or the
81
- deployment platform) before frozen deployment installs.
82
- - Production: `bin/rails server -e production` reads `PORT`; bind loopback
83
- with `-b`. Rails 8 enables `assume_ssl`/`force_ssl`; with `force_ssl` but no
92
+ deployment platform) before frozen deployment installs. `rails new` also
93
+ adds `image_processing`, which needs the libvips library for Active Storage
94
+ variants: provide libvips where variants are used, or remove the gem.
95
+ - Install gems into the project's `vendor/bundle` (the proposed Prepare sets
96
+ `bundle config set --local path vendor/bundle`), as deployment does: the
97
+ Ruby installation's own gem directory is often read-only (a system or
98
+ packaged Ruby), and `bundle install` then fails with
99
+ `Bundler::PermissionError`. Keep `/vendor/bundle` and `/.bundle` in
100
+ `.gitignore`.
101
+ - Production: `bin/rails server -e production` reads `PORT` but not `HOST`;
102
+ pass the supplied host with `-b`, as the proposed Serve does. Rails 7.1+
103
+ generates `force_ssl` and Rails 8 also `assume_ssl`; with `force_ssl` but no
84
104
  `assume_ssl`, exclude `/up` from the redirect:
85
105
  `config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } }`.
86
106
  Rails 8 runs Solid Queue jobs inside Puma only with `SOLID_QUEUE_IN_PUMA=1`
@@ -89,12 +109,17 @@ Authoritative framework documentation: https://guides.rubyonrails.org/
89
109
  Older apps add an equivalent route and update the Outputs and Deployment
90
110
  readiness.
91
111
  - Login: Rails 8 `bin/rails generate authentication`, Devise or the app's own
92
- `has_secure_password`. Seed one administrator (an `admin` boolean or role)
112
+ `has_secure_password`. Controllers generated after authentication require a
113
+ signed-in user, so their tests sign in first (`sign_in_as(users(:one))` from
114
+ the generated session test helper). Seed one administrator (an `admin` boolean or role)
93
115
  and one ordinary user idempotently in `db/seeds.rb` for development only,
94
116
  with passwords from the environment; never seed production credentials.
95
117
  - The JavaScript dependency step assumes npm. A `yarn.lock` or `bun.lockb`
96
118
  means that line uses the matching tool; importmap apps have no
97
- `package.json` and skip it.
119
+ `package.json` and skip it. The proposed Deployment assumes importmap: with
120
+ `jsbundling-rails`, `cssbundling-rails` or another `package.json` build, add
121
+ `nodejs` to its Runtimes and a Prepare `npm ci` step before
122
+ `assets:precompile`.
98
123
 
99
124
  ## Adoption
100
125
 
@@ -114,9 +139,11 @@ Authoritative framework documentation: https://guides.rubyonrails.org/
114
139
 
115
140
  ## Workspace setup
116
141
 
142
+ - Prepare `Keep gems inside the project` with `ruby` when `Gemfile` exists: `bundle` `config` `set` `--local` `path` `vendor/bundle`
117
143
  - Prepare `Install Ruby dependencies` with `ruby` `cpp` when `Gemfile` exists: `bundle` `install`
118
144
  - Prepare `Install JavaScript dependencies` with `nodejs` if `package.json` exists: `npm` `install`
119
145
  - Prepare `Prepare database` with `ruby` when `bin/rails` exists: `bin/rails` `db:prepare`
146
+ - Prepare `Seed development data` with `ruby` when `bin/rails` exists: `bin/rails` `db:seed`
120
147
 
121
148
  ## Outputs
122
149
 
@@ -148,7 +175,7 @@ Authoritative framework documentation: https://guides.rubyonrails.org/
148
175
  - Prepare `Install Ruby dependencies`: `bundle` `install`
149
176
  - Build `Precompile assets`: `bin/rails` `assets:precompile` `RAILS_ENV=production`
150
177
  - Migrate `Prepare database`: `bin/rails` `db:prepare` `RAILS_ENV=production`
151
- - Serve `Start`: `bin/rails` `server` `-e` `production` `-b` `127.0.0.1`
178
+ - Serve `Start`: `sh` `-c` `exec bin/rails server -e production -b "$HOST" -p "$PORT"`
152
179
 
153
180
  ## Deslop
154
181
 
@@ -48,6 +48,12 @@ https://docs.spring.io/spring-boot/
48
48
  such a proxy. A proxy may present same-origin requests with an `Origin`
49
49
  rewritten to the loopback address; keep CORS configuration for genuine
50
50
  cross-origin clients rather than comparing `Origin` with the request host.
51
+ - Spring Security answers `X-Frame-Options: DENY`, so a development tool that
52
+ shows the application inside a frame stays blank. When that is needed,
53
+ disable frame options only through a property set in the `dev` profile
54
+ (for example `app.allow-framing=true`, read by the security configuration
55
+ before `http.headers(h -> h.frameOptions(f -> f.disable()))`); production
56
+ keeps `DENY`.
51
57
  - Add `spring-boot-starter-actuator`, expose
52
58
  `management.endpoints.web.exposure.include=health`, and let Spring Security
53
59
  `permitAll` `/actuator/health`; the proposed readiness checks use it. Its
@@ -21,6 +21,12 @@ SQLite file-backed persistence, migrations, and backup conventions.
21
21
  persistent database filename through the `database` resource semantic and
22
22
  the environment name the application actually reads. A driver selector such
23
23
  as `DB_CONNECTION=sqlite` is configuration, not a database filename.
24
+ - When no framework database component declares that resource (an Express,
25
+ Next.js or plain Node.js app, for example), the project Stack declares it
26
+ under `## Resources` as one fenced `json genesis-resource` block, such as
27
+ `{"id": "database", "kind": "sqlite", "environmentAlternatives": [{"bindings": {"database": "DB_DATABASE"}, "preferred": true}]}`.
28
+ The application opens the filename in that variable; a gitignored
29
+ repository-relative file is only a fallback for a checkout without it.
24
30
  - Keep durable database files outside disposable build and release directories.
25
31
  A host may supply a persistent filename; do not replace that value with a
26
32
  repository-relative default or recreate an existing database during setup.