genesis-stack 1.3.0 → 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
 
@@ -54,6 +59,17 @@ consistent backups. `laravel-sqlite` adds Laravel's `DB_DATABASE` filename
54
59
  binding and `DB_CONNECTION=sqlite` default. Hosts own provisioning; applications
55
60
  own schema changes and preservation of existing data during adoption.
56
61
 
62
+ The `django`, `rails`, `spring-boot`, `express` and `nextjs` pieces require
63
+ their language piece, link the framework's official documentation, and propose
64
+ its conventional Workspace setup, Outputs, Verification and Deployment for
65
+ review during adoption. Their database integrations (`django-*` and `rails-*`
66
+ for PostgreSQL, MySQL and SQLite; `spring-boot-*` for PostgreSQL and MySQL)
67
+ bind the framework's own configuration names without a password default.
68
+ Express and Next.js have no framework ORM, so their applications declare
69
+ database Resources in the project Stack. `express` and `nextjs` conflict because
70
+ both propose how to run the same Node.js process. No starter templates exist for
71
+ these frameworks yet.
72
+
57
73
  ## Resource-estimate proposals
58
74
 
59
75
  JSKIT proposes a host's optional `Resource estimates` section for its concrete
@@ -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.0",
3
+ "version": "1.4.0",
4
4
  "description": "The optional curated technology Stack catalog for Genesis projects.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -0,0 +1,79 @@
1
+ # Stack piece: django-mysql
2
+
3
+ ## Description
4
+
5
+ Django MySQL database engine, driver, connection settings, migration, and test
6
+ database conventions. Database documentation:
7
+ https://docs.djangoproject.com/en/stable/ref/databases/
8
+
9
+ ## Requires
10
+
11
+ - `django`
12
+ - `mysql`
13
+
14
+ ## Conflicts
15
+
16
+ - `django-postgresql`
17
+ - `django-sqlite`
18
+
19
+ ## Environment defaults
20
+
21
+ - Default `DB_HOST`: `127.0.0.1`
22
+ - Default `DB_PORT`: `3306`
23
+ - Default `DB_NAME`: `django`
24
+ - Default `DB_USER`: `root`
25
+
26
+ ## Resources
27
+
28
+ ```json genesis-resource
29
+ {
30
+ "id": "database",
31
+ "kind": "mysql",
32
+ "environmentAlternatives": [
33
+ {
34
+ "bindings": {
35
+ "database": "DB_NAME",
36
+ "host": "DB_HOST",
37
+ "password": "DB_PASSWORD",
38
+ "port": "DB_PORT",
39
+ "username": "DB_USER"
40
+ },
41
+ "allowEmpty": ["password"],
42
+ "preferred": true
43
+ },
44
+ {
45
+ "bindings": {
46
+ "url": "DATABASE_URL"
47
+ }
48
+ }
49
+ ],
50
+ "optionalBindings": {
51
+ "testDatabase": "TEST_DB_NAME"
52
+ }
53
+ }
54
+ ```
55
+
56
+ ## Guidance
57
+
58
+ - Use `ENGINE: "django.db.backends.mysql"` with the `mysqlclient` driver.
59
+ It compiles against the MySQL or MariaDB client library and needs its
60
+ headers, `pkg-config` and a C compiler when no binary wheel matches; the
61
+ database server itself is not enough.
62
+ - Build `DATABASES["default"]` from `DB_NAME`, `DB_USER`, `DB_PASSWORD`,
63
+ `DB_HOST` and `DB_PORT`, or parse `DATABASE_URL` with the app's existing
64
+ parser (for example `dj-database-url`) when it is set. Keep one source of
65
+ truth; do not merge partial values from both.
66
+ - Set `OPTIONS: {"charset": "utf8mb4", "init_command": "SET sql_mode='STRICT_TRANS_TABLES'"}`
67
+ so text is stored losslessly and invalid values fail instead of being
68
+ truncated.
69
+ - When `TEST_DB_NAME` is supplied, set `DATABASES["default"]["TEST"]["NAME"]`
70
+ to it and run tests with `--keepdb`; Django reuses the existing database.
71
+ Without it, the user needs permission to create `test_<NAME>`.
72
+
73
+ ## Adoption
74
+
75
+ - Confirm the driver, the settings source of each connection value, any
76
+ `DATABASE_URL` parser, `OPTIONS` and the test database name from the
77
+ settings module before declaring readiness or connecting. Keep the project
78
+ Resource contract aligned with names the application deliberately uses
79
+ instead.
@@ -0,0 +1,78 @@
1
+ # Stack piece: django-postgresql
2
+
3
+ ## Description
4
+
5
+ Django PostgreSQL database engine, driver, connection settings, migration, and test
6
+ database conventions. Database documentation:
7
+ https://docs.djangoproject.com/en/stable/ref/databases/
8
+
9
+ ## Requires
10
+
11
+ - `django`
12
+ - `postgresql`
13
+
14
+ ## Conflicts
15
+
16
+ - `django-mysql`
17
+ - `django-sqlite`
18
+
19
+ ## Environment defaults
20
+
21
+ - Default `DB_HOST`: `127.0.0.1`
22
+ - Default `DB_PORT`: `5432`
23
+ - Default `DB_NAME`: `django`
24
+ - Default `DB_USER`: `postgres`
25
+
26
+ ## Resources
27
+
28
+ ```json genesis-resource
29
+ {
30
+ "id": "database",
31
+ "kind": "postgresql",
32
+ "environmentAlternatives": [
33
+ {
34
+ "bindings": {
35
+ "database": "DB_NAME",
36
+ "host": "DB_HOST",
37
+ "password": "DB_PASSWORD",
38
+ "port": "DB_PORT",
39
+ "username": "DB_USER"
40
+ },
41
+ "allowEmpty": ["password"],
42
+ "preferred": true
43
+ },
44
+ {
45
+ "bindings": {
46
+ "url": "DATABASE_URL"
47
+ }
48
+ }
49
+ ],
50
+ "optionalBindings": {
51
+ "testDatabase": "TEST_DB_NAME"
52
+ }
53
+ }
54
+ ```
55
+
56
+ ## Guidance
57
+
58
+ - Use `ENGINE: "django.db.backends.postgresql"` with the `psycopg` (version 3)
59
+ driver; `psycopg[binary]` needs no build toolchain. Older apps on
60
+ `psycopg2` keep it until a deliberate upgrade.
61
+ - Build `DATABASES["default"]` from `DB_NAME`, `DB_USER`, `DB_PASSWORD`,
62
+ `DB_HOST` and `DB_PORT`, or parse `DATABASE_URL` with the app's existing
63
+ parser (for example `dj-database-url`) when it is set. Keep one source of
64
+ truth; do not merge partial values from both.
65
+ - When `TEST_DB_NAME` is supplied, set `DATABASES["default"]["TEST"]["NAME"]`
66
+ to it and run tests with `--keepdb`: Django then reuses the existing
67
+ database instead of needing permission to create one. Without it, the test
68
+ role needs `CREATEDB`.
69
+ - Keep schema changes in Django migrations; avoid raw SQL schema edits
70
+ outside `RunSQL` operations committed with the app.
71
+
72
+ ## Adoption
73
+
74
+ - Confirm the driver, the settings source of each connection value, any
75
+ `DATABASE_URL` parser, `CONN_MAX_AGE`/pooling options and the test database
76
+ name from the settings module before declaring readiness or connecting.
77
+ Keep the project Resource contract aligned with names the application
78
+ deliberately uses instead.
@@ -0,0 +1,55 @@
1
+ # Stack piece: django-sqlite
2
+
3
+ ## Description
4
+
5
+ Django SQLite database file, migration, and test database conventions.
6
+ Database documentation:
7
+ https://docs.djangoproject.com/en/stable/ref/databases/#sqlite-notes
8
+
9
+ ## Requires
10
+
11
+ - `django`
12
+ - `sqlite`
13
+
14
+ ## Conflicts
15
+
16
+ - `django-postgresql`
17
+ - `django-mysql`
18
+
19
+ ## Resources
20
+
21
+ ```json genesis-resource
22
+ {
23
+ "id": "database",
24
+ "kind": "sqlite",
25
+ "environmentAlternatives": [
26
+ {
27
+ "bindings": {
28
+ "database": "DB_NAME"
29
+ },
30
+ "preferred": true
31
+ }
32
+ ]
33
+ }
34
+ ```
35
+
36
+ ## Guidance
37
+
38
+ - Use `ENGINE: "django.db.backends.sqlite3"` and read `NAME` from `DB_NAME`.
39
+ Preserve an absolute filename supplied by the environment. Only a
40
+ standalone checkout without `DB_NAME` falls back to a path such as
41
+ `BASE_DIR / "db.sqlite3"`, which stays out of Git.
42
+ - Repeated setup must preserve the file, its records and the migration
43
+ ledger: run `migrate --noinput`, never delete or recreate the file as
44
+ routine preparation.
45
+ - Tests use an in-memory database unless `DATABASES["default"]["TEST"]["NAME"]`
46
+ names a file; never point it at the development file.
47
+ - SQLite allows one writer at a time. Keep Gunicorn to a small worker count
48
+ and short transactions, and consider `"OPTIONS": {"transaction_mode": "IMMEDIATE"}`
49
+ with a `timeout` when concurrent writes report "database is locked".
50
+
51
+ ## Adoption
52
+
53
+ - Read the settings module's `DATABASES` before declaring readiness and keep
54
+ any existing database file and its data when adopting supplied storage.
55
+ Verify migrations against the configured file and keep tests separate.
@@ -0,0 +1,166 @@
1
+ # Stack piece: django
2
+
3
+ ## Description
4
+
5
+ Django web application, settings, migrations, management commands, static
6
+ files, test runner, and production serving conventions. Identified by
7
+ `manage.py` beside a settings module, with `django` in `requirements.txt` or
8
+ `pyproject.toml`. Authoritative framework documentation:
9
+ https://docs.djangoproject.com/
10
+
11
+ ## Requires
12
+
13
+ - `python`
14
+
15
+ ## Guidance
16
+
17
+ - Use the Django version the application pins and follow the matching
18
+ official documentation. Preserve Django's apps, models, forms, class-based
19
+ or function views, middleware, signals, admin and management commands
20
+ instead of adding parallel framework plumbing.
21
+ - The development server binds the supplied loopback host and port:
22
+ `manage.py runserver {host}:{port}`. Never bind `0.0.0.0` to make a proxy
23
+ work.
24
+ - Behind a loopback development reverse proxy that rewrites `Host` and sends
25
+ `X-Forwarded-Host`, `X-Forwarded-Proto` and `X-Forwarded-Port`, set
26
+ `USE_X_FORWARDED_HOST = True`, `USE_X_FORWARDED_PORT = True` and
27
+ `SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")`. This is
28
+ safe only because the server is reachable solely through that proxy; keep
29
+ the bind on loopback. Absolute URLs, redirects and `request.is_secure()`
30
+ then use the browser-facing origin. Without `SECURE_PROXY_SSL_HEADER`, an
31
+ HTTPS browser origin makes every form POST fail Django's CSRF origin check.
32
+ - Read `ALLOWED_HOSTS` from a comma-separated `DJANGO_ALLOWED_HOSTS`. When it
33
+ is unset, allow `["*"]` only while `DEBUG` is on, because the browser-facing
34
+ development host is not known in advance; otherwise default to
35
+ `["127.0.0.1", "localhost"]`. Production lists its public host names and
36
+ keeps `127.0.0.1` so a loopback readiness probe is admitted.
37
+ - Read `CSRF_TRUSTED_ORIGINS` from a comma-separated
38
+ `DJANGO_CSRF_TRUSTED_ORIGINS`. It is usually unnecessary once the
39
+ forwarded host and protocol are trusted, because Django then compares the
40
+ `Origin` header with the browser-facing origin. Never trust loopback or
41
+ wildcard origins to make a failing form POST pass; fix the proxy trust
42
+ instead.
43
+ - Read `DEBUG` from `DJANGO_DEBUG` (`1` enables it) and default it off. Set
44
+ it in the development environment only.
45
+ - `XFrameOptionsMiddleware` answers `X-Frame-Options: DENY`, so a development
46
+ tool that shows the application inside a frame stays blank. When that is
47
+ needed, remove the middleware only while `DEBUG` is on; production keeps it.
48
+ - Read `SECRET_KEY` from `DJANGO_SECRET_KEY` and fail when it is missing.
49
+ Replace the insecure key `startproject` writes into settings. Generate each
50
+ environment's value with
51
+ `python -c 'from django.core.management.utils import get_random_secret_key as k; print(k())'`
52
+ and supply it through that environment's secret facility; never commit it
53
+ or write it into a generated dotenv file. Development and production use
54
+ different keys. Every `manage.py` command imports settings, so supply the
55
+ development key before workspace setup runs `migrate`.
56
+ - Setup and deployment run `manage.py migrate --noinput`. `makemigrations` is
57
+ authoring: run it after model changes and commit the generated files. Never
58
+ use `flush`, `migrate --fake`, `reset_db`, or delete or rewrite applied
59
+ migrations as routine preparation.
60
+ - Seed reference data and development users through idempotent management
61
+ commands (`get_or_create`/`update_or_create`, `set_password`) or data
62
+ migrations. `createsuperuser --noinput` fails once the user exists, so it
63
+ is a one-off command, never a repeated setup step: seed the development
64
+ administrator and a non-administrator in one app-owned command that reads
65
+ their passwords from the environment (for example
66
+ `DJANGO_SUPERUSER_PASSWORD`) and run it after `migrate` in the project's
67
+ setup. Never hard-code production credentials.
68
+ - Static files: set `STATIC_ROOT`, run `collectstatic --noinput` in
69
+ deployment, and add `whitenoise.middleware.WhiteNoiseMiddleware` directly
70
+ after `SecurityMiddleware`; `runserver` serves static files only while
71
+ `DEBUG` is on. Keep `STATIC_ROOT` and media uploads out of Git.
72
+ - Tests: `manage.py test` creates `test_<NAME>` and needs permission to
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.
76
+ Suites that create users spend most of their time hashing passwords: set
77
+ `PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]` only
78
+ in a test settings module or under `if sys.argv[1:2] == ["test"]:`; never
79
+ outside tests.
80
+ - Production serves with Gunicorn through an app-owned `gunicorn.conf.py`
81
+ (read from the working directory by default) containing
82
+ `wsgi_app = "<project>.wsgi"` and
83
+ `bind = f"{os.environ.get('HOST', '127.0.0.1')}:{os.environ.get('PORT', '8000')}"`.
84
+ Add `gunicorn` and `whitenoise` to the requirements. Keep `DEBUG` off,
85
+ enable `SESSION_COOKIE_SECURE` and `CSRF_COOKIE_SECURE` behind HTTPS, and
86
+ review `manage.py check --deploy`. It rejects the console email backend that
87
+ recent `startproject` templates configure (`MAILERS` or `EMAIL_BACKEND`), so
88
+ select the production email backend and its credentials from the
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
+ - The proposed readiness path is the admin login page `/admin/login/`. An
94
+ application without `django.contrib.admin` adds a small unauthenticated
95
+ health view and updates its Outputs and Deployment readiness to it.
96
+ - The proposals install from `requirements.txt` into `.venv`. Poetry, uv or
97
+ other `pyproject.toml` workflows replace the install lines in the project's
98
+ sections with that tool's equivalent.
99
+ - Select `django-postgresql`, `django-mysql` or `django-sqlite` for the
100
+ database settings; it owns the engine and environment names.
101
+
102
+ ## Adoption
103
+
104
+ - Inspect `manage.py`, the settings module (single file or split
105
+ base/development/production modules), `DJANGO_SETTINGS_MODULE`,
106
+ `requirements*.txt` or `pyproject.toml` and its lock file, `wsgi.py`/`asgi.py`,
107
+ `INSTALLED_APPS`, `MIDDLEWARE`, `DATABASES`, and existing environment names
108
+ before changing project contracts. Preserve the pinned Django major.
109
+ - Record the real install, migrate, seed, test, static and serving commands.
110
+ Replace any proposed line that does not match (ASGI servers such as
111
+ Uvicorn or Daphne, a different settings module, a non-admin readiness path)
112
+ before claiming readiness.
113
+ - Confirm how settings read the secret key, debug flag, allowed hosts, CSRF
114
+ origins and proxy headers; move committed values into the environment
115
+ without changing their meaning.
116
+ - Select the matching Django database piece when the app uses PostgreSQL,
117
+ MySQL or SQLite.
118
+
119
+ ## Workspace setup
120
+
121
+ - Prepare `Create virtual environment` with `python` when `manage.py` exists: `python3` `-m` `venv` `.venv`
122
+ - Prepare `Install Python dependencies` with `python` if `requirements.txt` exists: `.venv/bin/python` `-m` `pip` `install` `-r` `requirements.txt`
123
+ - Prepare `Apply migrations` with `python` when `manage.py` exists: `.venv/bin/python` `manage.py` `migrate` `--noinput`
124
+
125
+ ## Outputs
126
+
127
+ ### Target `app`: Run Django app
128
+
129
+ - Default.
130
+ - Mode: `interactive`
131
+ - Runtimes: `python`
132
+ - Run `Develop`: `.venv/bin/python` `manage.py` `runserver` `{host}:{port}`
133
+
134
+ #### Presentation
135
+
136
+ - Kind: `web`
137
+ - Preferred port: `8000`
138
+ - URL path: `/`
139
+ - Ready when: `GET` `/admin/login/` returns `200`
140
+
141
+ ## Verification
142
+
143
+ - Verify `application`: `.venv/bin/python` `manage.py` `test` `--noinput` `--keepdb`
144
+
145
+ ## Deployment
146
+
147
+ - Runtimes: `python`
148
+ - Recreate on restore: `.venv`
149
+ - Ready when: `GET` `/admin/login/` returns `200`
150
+ - Prepare `Create virtual environment`: `python3` `-m` `venv` `.venv`
151
+ - Prepare `Install dependencies`: `.venv/bin/python` `-m` `pip` `install` `-r` `requirements.txt`
152
+ - Build `Collect static files`: `.venv/bin/python` `manage.py` `collectstatic` `--noinput`
153
+ - Migrate `Apply migrations`: `.venv/bin/python` `manage.py` `migrate` `--noinput`
154
+ - Serve `Start`: `.venv/bin/gunicorn`
155
+
156
+ ## Deslop
157
+
158
+ - Preserve Django's apps, models, managers, querysets, forms, views,
159
+ middleware, signals, admin registrations and management commands as the
160
+ owners of their behavior.
161
+ - Keep validation in forms, serializers or model `clean` methods and
162
+ authorization in views, permissions or decorators. Remove service layers,
163
+ repositories or helper modules only when they merely rename ORM or framework
164
+ operations.
165
+ - Preserve migration history; cleanup is not permission to squash, rewrite or
166
+ delete applied migrations or change model behavior.
@@ -0,0 +1,161 @@
1
+ # Stack piece: express
2
+
3
+ ## Description
4
+
5
+ Express HTTP server, routing, middleware, session, proxy trust, and production
6
+ serving conventions. An Express application declares `express` in
7
+ `package.json` and starts from an entry such as `app.js`, `server.js`,
8
+ `src/index.*` or express-generator's `bin/www`. Authoritative framework
9
+ documentation: https://expressjs.com/
10
+
11
+ ## Requires
12
+
13
+ - `nodejs`
14
+
15
+ ## Conflicts
16
+
17
+ - `nextjs`
18
+
19
+ ## Guidance
20
+
21
+ - Read the authoritative documentation at `https://expressjs.com/` and match
22
+ the installed Express major. Express 5 changes route path syntax, rejected
23
+ promise handling and `req.host`; follow
24
+ `https://expressjs.com/en/guide/migrating-5.html` only for an intended
25
+ upgrade. Preserve the existing module system, router layout, template
26
+ engine and lockfile manager; a pnpm or Yarn lockfile replaces the proposed
27
+ npm lines with that manager's equivalents.
28
+ - Listen on the host and port supplied through the environment:
29
+ `server.listen(Number(process.env.PORT ?? 3000), process.env.HOST ?? '127.0.0.1')`.
30
+ express-generator's `bin/www` reads `PORT` but listens on every interface;
31
+ pass the host. Let a taken port fail instead of choosing another one. The
32
+ proposed development Run is `npm start`; when the project develops through
33
+ a watch script (`node --watch`, nodemon, tsx), point Run at that script.
34
+ - Behind a loopback development reverse proxy that rewrites `Host` to the
35
+ loopback address and sends `X-Forwarded-Host`, `X-Forwarded-Proto` and
36
+ `X-Forwarded-Port`, set `app.set('trust proxy', 'loopback')` before any
37
+ middleware so `req.protocol`, `req.secure`, `req.hostname`, Express 5
38
+ `req.host` and `req.ip` describe the browser-facing request. Never use
39
+ `trust proxy` `true` for a server reachable without that proxy. Express
40
+ ignores `X-Forwarded-Port`, and Express 4 `req.hostname` drops the port.
41
+ Prefer relative redirects (`res.redirect('/login')`) and root-relative asset
42
+ URLs; build an absolute URL from `req.protocol` and Express 5 `req.host`, or
43
+ from a configured public origin, never from the raw `Host` header. On
44
+ Express 4 take the host from `X-Forwarded-Host` only when
45
+ `req.app.get('trust proxy fn')(req.socket.remoteAddress, 0)` accepts the
46
+ peer, and from `Host` otherwise.
47
+ - Express has no host allow-list or CSRF protection of its own. When the app
48
+ adds them, keep a session-bound CSRF token (or a maintained token library)
49
+ for form posts; an `Origin` check must accept the origin built from the
50
+ trusted forwarded host as well as the raw `Host`, because a development
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. 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.
62
+ - Sessions use `express-session` with `secret: process.env.SESSION_SECRET`,
63
+ generated with `openssl rand -base64 32`, supplied through the environment
64
+ and never committed (a dotenv file holding it stays ignored); refuse to
65
+ start in production without it. Development may fall back to a random
66
+ per-process secret, which ends sessions on restart. Use
67
+ `cookie: { secure: 'auto', httpOnly: true, sameSite: 'lax' }`; `'auto'`
68
+ depends on the proxy trust above. The default MemoryStore leaks and loses
69
+ sessions on restart: production uses a persistent store backed by the app's
70
+ database or cache.
71
+ - Register an unauthenticated `GET /health` returning `200` before session,
72
+ authentication, CSRF and HTTPS-redirect middleware. Keep it cheap and free
73
+ of writes. The proposed Outputs and Deployment readiness use it; an app
74
+ with another health path changes both.
75
+ - Serve static files with `express.static(path.join(__dirname, 'public'))`
76
+ or the ESM equivalent. A bundler or TypeScript build adds a Workspace setup
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
83
+ migration tool (Prisma, Drizzle, Knex, Sequelize, TypeORM) as Workspace
84
+ setup and Deployment Migrate steps, for example `prisma migrate deploy` or
85
+ `knex migrate:latest`. Never run `prisma migrate dev`, `prisma db push` or
86
+ `sync({ force: true })` against supplied data. Seeds are idempotent
87
+ upserts. Tests use a separate disposable database, never the development
88
+ one.
89
+ - Deployment installs with `npm ci --omit=dev` (a lockfile is required) and
90
+ serves `npm start`, which receives `HOST` and `PORT` only through the
91
+ environment. Set `NODE_ENV=production` in the production environment, not
92
+ as a project-wide default: it enables view caching and stops the error
93
+ handler exposing stack traces. Exclude `/health` from any HTTPS redirect or
94
+ host check, and close the server on `SIGTERM`. `npm start` runs the server
95
+ under npm and a shell that do not pass `SIGTERM` on, so the supervisor must
96
+ signal the whole process group or serve `node <entry>` directly.
97
+ - When the app has login, hash passwords with bcrypt or argon2 and store a
98
+ role. Seed one development administrator and one ordinary user through an
99
+ idempotent script that reads their passwords from the environment; never
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.
106
+
107
+ ## Adoption
108
+
109
+ - Inventory `package.json` (Express major, `type`, `main`, `start`/`dev`/`test`
110
+ scripts), the lockfile, the entry file and its `listen` call, middleware
111
+ order, session store, template engine, authentication library, database
112
+ and migration tool, and any TypeScript or bundler build.
113
+ - Replace every proposal line that does not match before claiming readiness.
114
+ Many Express apps (express-generator included) have no `test` script: record
115
+ the real check instead of inventing `npm test`.
116
+ - Make the server honour `HOST`/`PORT`, trust only the loopback proxy, and
117
+ expose `/health`. Select the matching database component.
118
+ - An Express server that hosts Next.js through a custom server is a Next.js
119
+ project; select `nextjs` instead and keep the custom server project-owned.
120
+
121
+ ## Workspace setup
122
+
123
+ - Prepare `Install dependencies` with `nodejs` when `package.json` exists: `npm` `install`
124
+
125
+ ## Outputs
126
+
127
+ ### Target `app`: Run Express app
128
+
129
+ - Default.
130
+ - Mode: `interactive`
131
+ - Runtimes: `nodejs`
132
+ - Run `Develop`: `npm` `start`
133
+
134
+ #### Presentation
135
+
136
+ - Kind: `web`
137
+ - Preferred port: `3000`
138
+ - URL path: `/`
139
+ - Ready when: `GET` `/health` returns `200`
140
+
141
+ ## Verification
142
+
143
+ - Verify `application`: `npm` `test`
144
+
145
+ ## Deployment
146
+
147
+ - Runtimes: `nodejs`
148
+ - Recreate on restore: `node_modules`
149
+ - Ready when: `GET` `/health` returns `200`
150
+ - Prepare `Install dependencies`: `npm` `ci` `--omit=dev`
151
+ - Serve `Start`: `npm` `start`
152
+
153
+ ## Deslop
154
+
155
+ - Keep middleware order meaningful: proxy trust, health, static files, body
156
+ parsing, session, authentication, routers, 404, then one error handler.
157
+ - Use `express.Router` modules over wrapper frameworks that rename Express
158
+ routing. On Express 5 let rejected promises reach the error handler instead
159
+ of repeating try/catch blocks; on Express 4 keep the app's existing pattern.
160
+ - Preserve routes, response shapes, cookies, the module system and the
161
+ lockfile manager.
@@ -46,8 +46,7 @@ Java package, public type, dependency, lifecycle, and test conventions.
46
46
  connected source.
47
47
  - Running a web process: bind the supplied listen host and port, trust forwarded
48
48
  headers only from a loopback proxy, and read secrets from the environment.
49
- Spring Boot: set `server.address`/`server.port` from the supplied values and
50
- `server.forward-headers-strategy=framework`.
49
+ For Spring Boot select `spring-boot`; its piece owns the framework traps.
51
50
 
52
51
  ## Connected development
53
52