Commit Graph
126 Commits
Author SHA1 Message Date
implementer e52b19b1e7 test: lock in the migration ledger with static + real-stack probes (E00-S03-T03)
CI / Frozen lockfile install (pull_request) Successful in 56s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 25s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 28s
CI / Migration ledger (E00-S03-T03) (pull_request) Successful in 42s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 29s
Static assertions on the committed ledger source (idempotent table DDL,
parameterized idempotent record, has/applied queries, driver-boundary
re-export, CI enforcement) with mutation probes proving non-vacuousness;
docker-gated real-stack probe migrates an empty database (isolated compose
project + host port) and confirms the ledger exists, the applied migrations
are recorded, and a re-run records nothing twice. New additive
database-postgres-ledger CI job gates the criterion on every PR.
2026-08-29 23:20:00 +00:00
implementer 7f6450410e feat: add migration ledger to database-postgres (E00-S03-T03)
MigrationLedger over the package-owned pg Pool: ensure() creates the
schema_migrations table (version text PRIMARY KEY, applied_at timestamptz
NOT NULL DEFAULT now()) with idempotent DDL; record() inserts an applied
migration with a parameterized, idempotent statement (ON CONFLICT DO
NOTHING — a rerun never double-applies); has()/applied() read the ledger
back in apply order. Re-exported from the driver boundary (src/index.ts)
so no other package needs the pg driver to touch migration state.
2026-08-29 23:19:56 +00:00
kpcto 33ac04e795 Merge pull request '[E00-S03-T02] pg/Kysely imports isolated to database-postgres' (#391) from feature/177 into main
CI / Frozen lockfile install (push) Successful in 50s
CI / Secrets not embedded (E00-S02-T08) (push) Successful in 29s
CI / Database-postgres import isolation (E00-S03-T02) (push) Successful in 26s
CI / Compose config (E00-S03-T01) (push) Successful in 25s
2026-08-29 23:04:53 +00:00
implementer 09b7a40d00 fix: pin database-postgres driver to the golden tuple (pg 8.22.0, Kysely 0.29.4)
CI / Frozen lockfile install (pull_request) Successful in 46s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 35s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 25s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 31s
Review finding on PR #391: packages/database-postgres pinned pg@8.23.0 and
kysely@0.29.5, but the architecture doc's golden tuple (Technology-Stack
section 5.2 / section 7) pins pg@8.22.0 and Kysely@0.29.4. Reproducibility
requires the exact documented versions.

- packages/database-postgres/package.json: pg 8.23.0 -> 8.22.0,
  kysely 0.29.5 -> 0.29.4, @types/pg 8.23.1 -> 8.21.0 (no 8.22.x of
  @types/pg is published; 8.21.0 is the closest matching release, types
  for the immediately preceding pg minor)
- pnpm-lock.yaml: regenerated with pnpm 11.23.0 (Node 24); the resolved
  pg dependency tree is unchanged apart from the driver version itself
- tests/database-postgres-imports.test.mjs: exact-pin assertions updated
  to the corrected versions, with a comment noting the @types/pg choice
2026-08-29 11:27:29 +00:00
implementer 97c5306768 test: lock in pg/Kysely import isolation to database-postgres (E00-S03-T02)
CI / Frozen lockfile install (pull_request) Successful in 43s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 30s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 31s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 24s
- database-postgres-imports.test.mjs: static scan of every workspace package
  source proves pg/kysely import specifiers resolve only to
  packages/database-postgres; owner manifest pins the driver and no other
  package declares it; mutation probes prove the scan catches a driver import
  injected into apps/server/src/index.ts; comment-stripping and specifier
  matcher unit probes; CI-enforcement assertion
- workspace-layout / workspace-config / strict-tsconfig / typescript-pin:
  package-set fixtures updated to include packages/database-postgres
- docs/development/non-container.md: workspace package table and build
  expectations updated for the new package
2026-08-29 11:14:15 +00:00
implementer 5c202ba21e feat: add database-postgres driver boundary package (E00-S03-T02)
- packages/database-postgres (@personal-blog/database-postgres): the single
  workspace package allowed to import the PostgreSQL driver — declares pg and
  kysely as exact dependencies (@types/pg for types) and its src/index.ts
  imports and re-exports the driver pieces (Pool, Kysely, PostgresDialect) so
  the isolation is real, not a placeholder
- pnpm-lock.yaml: importer for packages/database-postgres plus the resolved
  pg/kysely dependency tree (frozen-lockfile install keeps working)
- .gitea/workflows/ci.yml: new database-postgres-imports job runs
  tests/database-postgres-imports.test.mjs on every PR so the isolation
  criterion gates merges
2026-08-29 11:14:14 +00:00
kpcto 797a1b8dc2 Merge pull request '[E00-S03-T01] PostgreSQL 18.6 container' (#390) from feature/176 into main
CI / Frozen lockfile install (push) Successful in 45s
CI / Secrets not embedded (E00-S02-T08) (push) Successful in 26s
CI / Compose config (E00-S03-T01) (push) Successful in 33s
2026-08-29 11:04:31 +00:00
implementer a78ffea4c5 test: lock in the PostgreSQL 18.6 container criteria (E00-S03-T01)
CI / Frozen lockfile install (pull_request) Successful in 43s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 29s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 24s
- compose-config.test.mjs: assertDbService requires the pinned
  postgres:18.6-bookworm image; new docker-gated real-stack probe starts the
  stack and asserts SHOW server_version exposes 18.6; mutation probe proves
  reverting to a floating major tag fails the criterion; parser probe updated
- build-targets.test.mjs: db-service mutation fixture updated to the pinned
  image tag
2026-08-29 10:45:00 +00:00
implementer 4cd68c9193 feat: pin the database container to PostgreSQL 18.6 (E00-S03-T01)
- compose.yaml: db.image pinned to postgres:18.6-bookworm (exact 18.6 minor,
  same bookworm flavor, no Alpine drift) so the database container is
  reproducible and exposes the expected PostgreSQL version; document the
  rollback (revert image to postgres:18-bookworm)
- .gitea/workflows/ci.yml: new compose-config job runs
  tests/compose-config.test.mjs on every PR so the pinned-version criterion
  gates merges (docker-gated real-stack probes skip cleanly without a daemon)
2026-08-29 10:45:00 +00:00
kpcto 38c17f0e7a Merge pull request '[E00-S02-T08] Secrets are not embedded in image' (#389) from feature/175 into main
CI / Frozen lockfile install (push) Successful in 48s
CI / Secrets not embedded (E00-S02-T08) (push) Successful in 28s
2026-08-29 10:40:36 +00:00
implementer 719fb4380b test: plant nested marker files in the layer-scan probe (E00-S02-T08)
CI / Frozen lockfile install (pull_request) Successful in 52s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 38s
The Docker-gated probe only planted a root-level .env.t08-* marker, which no
Dockerfile COPY instruction ever copies — so it could not observe a nested
build-context leak in the image layers. Plant additional marker files at
nested paths the Dockerfile's COPY apps/server apps/server would sweep into
the build-stage image (apps/server/.env.t08-*, apps/server/secrets/t08-*.pem)
so the end-to-end scan actually verifies the 'any depth' exclusion
guarantee, not just the root form.
2026-08-29 01:53:57 +00:00
implementer f00c13d57a fix: make .dockerignore exclusions apply at any depth (E00-S02-T08)
CI / Frozen lockfile install (pull_request) Successful in 52s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 36s
Resolve the security review of #389 (findings 1-4):

- .dockerignore: every env/credential pattern is now **/-prefixed
  (**/.env, **/.env.*, **/node_modules, **/.npmrc, ..., **/secrets,
  **/*.pem, **/*.key, ...) and the redundant 'secrets/' line is dropped.
  Docker's matcher (moby/patternmatcher) anchors slash-less patterns to
  the context root, so the bare forms excluded nothing under apps/server/;
  **/ matches the root AND any nested depth. (finding 1, 3)
- tests/secrets-not-embedded.test.mjs: the dockerignore matcher is now a
  faithful port of moby/patternmatcher (filepath.Clean + anchored full-path
  match + parent-directory propagation), not gitignore basename semantics;
  asserts nested example paths (apps/server/.npmrc, config/server.key,
  apps/server/secrets/...) are excluded; requires no redundant equivalent
  patterns verbatim; adds mutation probes for bare-pattern and
  duplicate-pattern regressions. (finding 2, 3)
- apps/server/Dockerfile + compose.yaml: guarantee restated precisely
  (credential files excluded at the context root AND at any depth).
- .gitea/workflows/ci.yml: new job runs
  'node --test tests/secrets-not-embedded.test.mjs' on every PR; the
  docker-gated layer-scan probe runs where a daemon exists, skips cleanly
  otherwise. (finding 4)
- tests/compose-config.test.mjs: .dockerignore presence list updated to the
  **/-prefixed forms (node_modules, .env).

Tested: secrets suite 16 tests -> 15 pass / 1 docker-gated skip / 0 fail;
full suite 101 pass / 12 fail / 8 skip, failures identical to clean main
(env-dependent pnpm/Node-24 suites); matcher port verified against the
moby/patternmatcher evidence table.
2026-08-29 01:49:07 +00:00
implementer b3ad55efe8 test: lock in no-secrets-in-image criteria (E00-S02-T08)
CI / Frozen lockfile install (pull_request) Successful in 50s
tests/secrets-not-embedded.test.mjs: static assertions that the Dockerfile
embeds no secrets (no secret-bearing ARG/ENV, no secret-path or blanket COPY)
and .dockerignore excludes env/credential files; non-vacuous mutation probes
for every assertion; Docker-gated probe that builds the image with a marker
env file in the context and scans every layer + image config for secret
values.
2026-08-29 01:28:26 +00:00
implementer 0938da8a73 feat: keep secrets out of the app image (E00-S02-T08)
- .dockerignore: exclude env + credential files (.npmrc, .netrc, .aws, .ssh,
  secrets/, *.pem, *.key, *.p12, *.pfx, *.jks, id_rsa, id_ed25519, ...) from
  the build context so a local secret file cannot be embedded in the image
- Dockerfile: document the T08 guarantee (no secret ARG/ENV, fixed non-secret
  COPY paths, runtime credentials via Compose environment)
- compose.yaml: T08 in scope; runtime credentials stay in service environment,
  never in the image
2026-08-29 01:28:26 +00:00
kpcto 25d7dc836b Merge pull request '[E00-S02-T07] amd64 and arm64 are build targets' (#388) from feature/174 into main
CI / Frozen lockfile install (push) Successful in 48s
2026-08-29 01:20:02 +00:00
bot-implementer f663fd23eb test: lock in amd64/arm64 build target criteria (E00-S02-T07)
CI / Frozen lockfile install (pull_request) Successful in 46s
2026-08-29 01:13:30 +00:00
bot-implementer 5bbcfa9b6d feat: make amd64 and arm64 image build targets (E00-S02-T07) 2026-08-29 01:13:30 +00:00
kpcto 8a01b195f3 Merge pull request '[E00-S02-T06] App root filesystem read-only except mounts/tmpfs' (#387) from feature/173 into main
CI / Frozen lockfile install (push) Successful in 46s
2026-08-29 01:00:33 +00:00
implementer 41428b083e test: lock in read-only rootfs criteria (E00-S02-T06)
CI / Frozen lockfile install (pull_request) Successful in 56s
2026-08-29 00:53:40 +00:00
implementer 7ce3ba53cf feat: make the app root filesystem read-only (E00-S02-T06) 2026-08-29 00:53:40 +00:00
kpcto 7cf7994fa8 Merge pull request '[E00-S02-T05] App runs non-root' (#386) from feature/172 into main
CI / Frozen lockfile install (push) Successful in 47s
2026-08-29 00:48:28 +00:00
implementer 6e8ba388a6 test: lock in non-root execution criteria (E00-S02-T05)
CI / Frozen lockfile install (pull_request) Successful in 51s
tests/non-root-user.test.mjs locks in both acceptance criteria: a static
assertion that the Dockerfile runtime stage declares a non-root USER (not
root/uid 0, 'USER node' exactly), non-vacuous mutation probes, and a
Docker-gated real-stack probe that starts the stack and asserts 'id -u' and
'id -un' inside the running app container report a non-root user, with the
health endpoint still answering as a regression guard.
2026-08-29 00:41:16 +00:00
implementer a4cf365098 feat: run the app image as a non-root user (E00-S02-T05)
The runtime stage of apps/server/Dockerfile now drops root privileges with
'USER node' — the non-root user (uid/gid 1000) the official Node image ships
with — so the app container does not run with root privileges. The server
binds port 3000 (>= 1024) and only reads the root-owned files copied above,
so no extra user creation or ownership changes are required. compose.yaml
header updated: T05 is in scope; T06 (read-only rootfs) and T07 (multi-arch)
remain out of scope.
2026-08-29 00:41:10 +00:00
kpcto a634168d9c Merge pull request '[E00-S02-T04] DB volume persists across restart/recreate' (#385) from feature/171 into main
CI / Frozen lockfile install (push) Successful in 59s
2026-08-29 00:35:07 +00:00
implementer b51af02d06 test: lock in DB volume persistence criteria (E00-S02-T04)
CI / Frozen lockfile install (pull_request) Successful in 45s
compose-config: assert the db service mounts the named db-data volume at
the PostgreSQL data directory and the top-level volumes map declares it;
non-vacuous mutation probes (missing mount, missing volume declaration,
wrong mount target all fail); Docker-gated real-stack probe writes a
fixture row and asserts it survives `docker compose restart` (restart)
and `docker compose down` + `up -d` (recreate), cleaned up with
`docker compose down -v`.
2026-08-29 00:32:15 +00:00
implementer 3dd80f91fe feat: persist the database volume across restart/recreate (E00-S02-T04)
Mount the named `db-data` volume at PostgreSQL's data directory
(/var/lib/postgresql/data) on the db service and declare it in the
top-level volumes map, so the database survives `docker compose
restart` and `docker compose down` + `up -d` (recreate). Reset with
`docker compose down -v` per the issue rollback note. Header comments
in compose.yaml and the server Dockerfile updated: T04 is no longer out
of scope; T05/T06 remain.
2026-08-29 00:32:15 +00:00
kpcto 6a295556ed Merge pull request '[E00-S02-T03] App health endpoint succeeds' (#384) from feature/170 into main
CI / Frozen lockfile install (push) Successful in 58s
2026-08-29 00:27:16 +00:00
implementer dfb7b0e952 fix: handle quoted scoped keys in frozen-install probe (E00-S02-T03)
CI / Frozen lockfile install (pull_request) Successful in 50s
2026-08-29 00:23:27 +00:00
implementer f7257f9258 test: lock in app health endpoint criteria (E00-S02-T03) 2026-08-29 00:21:34 +00:00
implementer 9c049ce6b5 feat: app health endpoint succeeds (E00-S02-T03) 2026-08-29 00:21:34 +00:00
kpcto 9937268d64 Merge pull request '[E00-S02-T02] PostgreSQL health gates app start' (#383) from feature/169 into main
CI / Frozen lockfile install (push) Successful in 41s
2026-08-29 00:07:31 +00:00
implementer 59a102bee3 feat: PostgreSQL health gates app start (E00-S02-T02)
CI / Frozen lockfile install (pull_request) Successful in 47s
2026-08-28 23:59:45 +00:00
kpcto 9e2ac55400 Merge pull request '[E00-S02-T01] docker compose up -d starts DB + app' (#382) from feature/168 into main
CI / Frozen lockfile install (push) Successful in 56s
2026-08-28 23:55:18 +00:00
implementer 3bbea68e6c fix: align app/db image bases with documented stack (E00-S02-T01)
CI / Frozen lockfile install (pull_request) Successful in 48s
Switch the app image from node:24-alpine to node:24.19.0-bookworm-slim in
both build and runtime stages (Technology-Stack 5.4: glibc Debian base
required because argon2 is a native dependency; musl/Alpine causes
native-module build surprises), and the db image from postgres:16-alpine
to postgres:18-bookworm (Technology-Stack 5.2/5.4/6.2 + golden tuple 7
pin PostgreSQL 18.6; 18-bookworm is the 18.x line on Debian bookworm).

Update tests/compose-config.test.mjs so the committed assertions lock in
the corrected image bases (db image, Dockerfile build/runtime stages,
parser probe).
2026-08-28 23:45:18 +00:00
implementer 3be575818d feat: docker compose up -d starts DB + app (E00-S02-T01)
CI / Frozen lockfile install (pull_request) Successful in 43s
2026-08-28 23:35:05 +00:00
kpcto 2b293e2927 Merge pull request '[E00-S01-T14] No core package imports a concrete extension' (#381) from feature/167 into main
CI / Frozen lockfile install (push) Successful in 49s
2026-08-28 23:26:41 +00:00
implementer 27851fcaeb test: lock in no core package imports a concrete extension (E00-S01-T14)
CI / Frozen lockfile install (pull_request) Successful in 47s
2026-08-28 23:24:02 +00:00
kpcto 3c287d0a41 Merge pull request '[E00-S01-T13] Clean clone installs with frozen lockfile' (#380) from feature/166 into main
CI / Frozen lockfile install (push) Successful in 1m17s
2026-08-28 23:15:14 +00:00
implementer ca9e0ffaf6 test: lock in clean-clone frozen lockfile install (E00-S01-T13)
CI / Frozen lockfile install (pull_request) Successful in 45s
2026-08-28 23:10:26 +00:00
kpcto c8693fe0b7 Merge pull request '[E00-S01-T12] Root commands exist for build/test/typecheck' (#379) from feature/165 into main
CI / Frozen lockfile install (push) Successful in 1m8s
2026-08-28 23:01:50 +00:00
implementer 5c6ca444d9 test: lock in root build/test/typecheck commands (E00-S01-T12)
CI / Frozen lockfile install (pull_request) Successful in 44s
2026-08-28 22:56:48 +00:00
kpcto 0ad2d4db0b Merge pull request '[E00-S01-T11] apps/packages/extensions separated' (#378) from feature/164 into main
CI / Frozen lockfile install (push) Successful in 52s
2026-08-28 22:50:38 +00:00
implementer 729a67b79d test: lock in apps/packages/extensions workspace layout separation (E00-S01-T11)
CI / Frozen lockfile install (pull_request) Successful in 44s
2026-08-28 22:43:01 +00:00
kpcto 24d1c1c9fc Merge pull request '[E00-S01-T10] Strict base tsconfig committed' (#377) from feature/163 into main
CI / Frozen lockfile install (push) Successful in 42s
2026-08-28 22:36:29 +00:00
implementer 4b5519465f test: lock in strict base tsconfig for all packages (E00-S01-T10)
CI / Frozen lockfile install (pull_request) Successful in 45s
2026-08-28 21:46:57 +00:00
kpcto 9c76a64bee Merge pull request '[E00-S01-T09] TypeScript 6.0.3 exact dependency' (#376) from feature/162 into main
CI / Frozen lockfile install (push) Successful in 48s
2026-08-28 21:41:38 +00:00
implementer 628885ae0f test: lock in TypeScript 6.0.3 exact dependency (E00-S01-T09)
CI / Frozen lockfile install (pull_request) Successful in 44s
2026-08-28 21:33:52 +00:00
kpcto 12a3829039 Merge pull request '[E00-S01-T08] Node engine restricted to 24.x' (#375) from feature/161 into main
CI / Frozen lockfile install (push) Successful in 51s
2026-08-28 09:18:23 +00:00
implementer 6323e486bc docs: document enforced Node 24.x engine restriction (E00-S01-T08)
CI / Frozen lockfile install (pull_request) Successful in 43s
2026-08-28 09:15:09 +00:00
implementer 7c548ac43b test: lock in Node 24.x engine restriction (E00-S01-T08) 2026-08-28 09:15:09 +00:00