Commit Graph
21 Commits
Author SHA1 Message Date
implementer 961fb0975c ci: gate the .env.example template in CI (E00-S04-T05) 2026-08-30 05:12:06 +00:00
implementer ffda249617 docs: document HOST validation and bind control (E00-S04-T04)
CI / Frozen lockfile install (pull_request) Successful in 54s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 34s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 30s
CI / Migration ledger (E00-S03-T03) (pull_request) Successful in 1m16s
CI / Migration advisory lock (E00-S03-T04) (pull_request) Successful in 54s
CI / Migration failure diagnostic (E00-S03-T05) (pull_request) Successful in 46s
CI / App readiness after migrations (E00-S03-T06) (pull_request) Successful in 1m6s
CI / Field-specific startup errors (E00-S04-T02) (pull_request) Successful in 1m2s
CI / Secret redaction from logs (E00-S04-T03) (pull_request) Successful in 1m1s
CI / TypeBox/Ajv config schema (E00-S04-T01) (pull_request) Successful in 50s
CI / Env adapter owns process.env (E00-S04-T04) (pull_request) Successful in 1m5s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 26s
The non-container guide now notes that HOST is validated at the adapter
boundary as a hostname/IP (invalid values fail startup naming the field) and
that the server passes config.host to server.listen, so a configured HOST
binds exactly that interface and the startup log reflects the actual bind.
The config-env-adapter CI job comment is refreshed to describe the extended
suite (HOST validation + loopback-only boot probe).
2026-08-30 04:42:06 +00:00
implementer 20173a8241 test: lock in the env adapter as the single owner of process.env with static scan, mutation and deterministic probes (E00-S04-T04) 2026-08-30 04:17:22 +00:00
implementer 60bd097b70 test: lock in secret redaction from logs with static, mutation and deterministic probes (E00-S04-T03) 2026-08-30 03:52:55 +00:00
implementer 1a9fd592d9 test: lock in the field-specific startup error with static, mutation and deterministic probes (E00-S04-T02)
- tests/config-startup-error.test.mjs: static assertions on the committed
  startup-error module, the package boundary, the server wiring (validation
  before bind), the compose secret and the Dockerfile shipping, each backed
  by mutation probes; the deterministic probes execute the issue's test plan
  ("start with a missing required field and confirm the error names it") —
  the compiled boundary throws MissingRequiredSettingError naming
  sessionSecret, and booting the committed server without EPPP_SESSION_SECRET
  exits non-zero naming the field while a valid secret boots to /health 200
- health-endpoint/app-readiness boot probes: provide a valid
  EPPP_SESSION_SECRET (the required setting is validated at startup)
- ci.yml: new config-startup-error job (builds config + database-postgres,
  runs the suite); app-readiness job now builds the config package too
- .gitignore: transient .config-startup-probe-*.mjs files
2026-08-30 03:00:45 +00:00
implementer bcea489391 feat: add TypeBox/Ajv config schema package to the workspace (E00-S04-T01)
Adds packages/config (@personal-blog/config) — the EPPP configuration
service foundation. The package defines the configuration schema with
TypeBox (configSchema: host, port, databaseUrl, sessionSecret — the
validated config fields, golden-tuple pins @sinclair/typebox@0.34.52 and
ajv@8.20.0) and compiles it with Ajv (validateConfig). The environment
adapter (T04), field-specific startup errors (T02) and secret redaction
(T03) build on this boundary in later tasks; nothing reads process.env yet.

Wiring for the new workspace package: lockfile importer + resolved
typebox/ajv tree, apps/server/Dockerfile manifest copy (frozen in-image
install must match the lockfile importers), config-schema CI job, package
set fixtures (workspace-layout, workspace-config, strict-tsconfig,
typescript-pin), probe-file gitignore entry.
2026-08-30 02:29:52 +00:00
implementer c20110be15 test: lock in the app readiness gate with static, deterministic and real-stack probes (E00-S03-T06) 2026-08-30 01:56:22 +00:00
implementer 52190d082c test: lock in the migration failure diagnostic (E00-S03-T05)
CI / Frozen lockfile install (pull_request) Successful in 44s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 28s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 26s
CI / Migration ledger (E00-S03-T03) (pull_request) Successful in 44s
CI / Migration advisory lock (E00-S03-T04) (pull_request) Successful in 51s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 25s
CI / Migration failure diagnostic (E00-S03-T05) (pull_request) Failing after 51s
Static assertions + mutation probes on the committed runner source, a
deterministic stub-pool behavioral probe (intentionally failing migration
fixture -> structured diagnostic naming the failing migration, apply and
record phases), a docker-gated real-stack probe against a real database
(the issue's test plan), and CI enforcement via the additive
database-postgres-diagnostic job.
2026-08-30 01:17:23 +00:00
implementer dac3679e33 fix: memoize in-flight acquire so concurrent acquire() is re-entrant-safe (E00-S03-T04)
CI / Frozen lockfile install (pull_request) Successful in 49s
CI / Secrets not embedded (E00-S02-T08) (pull_request) Successful in 26s
CI / Database-postgres import isolation (E00-S03-T02) (pull_request) Successful in 26s
CI / Migration ledger (E00-S03-T03) (pull_request) Successful in 45s
CI / Migration advisory lock (E00-S03-T04) (pull_request) Successful in 50s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 25s
Security-review finding F2: two concurrent acquire() calls on the same
MigrationLock instance could each check out a connection; the second
pg_advisory_lock would overwrite this.client, leaking the first locked
connection until session end.

acquire() now memoizes the in-flight acquire in acquireInFlight and
returns it on re-entry, so exactly one connection is checked out and no
locked connection leaks. The memo is cleared once the acquire settles.
tryAcquire()/release() paths unchanged.

Locked in by:
- static criterion test: acquireInFlight field, re-entry guard returns
  the in-flight acquire, memo cleared on settle
- mutation probe: removing the re-entry guard fails the criterion
- real-stack probe: two concurrent acquire() calls on one instance leave
  pool.totalCount at 1 (exactly one connection), the lock granted once,
  nothing left after release; probe fails (hangs) on the pre-fix code
- CI job comment updated to reflect the re-entrancy criterion
2026-08-30 00:29:20 +00:00
implementer a2b98439e9 test: lock in the migration advisory lock with static + real-stack probes (E00-S03-T04)
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 24s
CI / Migration ledger (E00-S03-T03) (pull_request) Successful in 49s
CI / Migration advisory lock (E00-S03-T04) (pull_request) Successful in 53s
CI / Compose config (E00-S03-T01) (pull_request) Successful in 25s
2026-08-30 00:13:46 +00:00
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 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
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
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 a2fa1dee32 chore: bootstrap pnpm workspace and package manifests
CI / Frozen lockfile install (pull_request) Successful in 48s
E00-S01-T01: create workspace/package manifests
- root package.json pins packageManager pnpm@11.23.0
- pnpm-workspace.yaml separates apps/*, packages/*, extensions/* entries
- skeleton manifests for apps/server, packages/core, extensions/example
- committed frozen lockfile generated by pnpm 11.23.0
- minimal CI: pnpm install --frozen-lockfile + workspace verification
2026-08-27 23:07:58 +00:00
kpcto c955a30a50 Remove .gitea/workflows/ci.yml 2026-08-26 22:34:50 +00:00
kpcto 94c1019a6f Update .gitea/workflows/ci.yml
CI / Run tests (pull_request) Successful in 21s
CI / Secret scan (gitleaks) (pull_request) Successful in 57s
2026-08-26 18:17:41 +00:00
kpcto 39587532c6 Update .gitea/workflows/ci.yml
CI / Run tests (pull_request) Successful in 28s
CI / Secret scan (gitleaks) (pull_request) Failing after 13s
2026-08-26 18:02:07 +00:00
kpcto d7740e3819 Update .gitea/workflows/ci.yml
CI / Run tests (pull_request) Failing after 21s
CI / Secret scan (gitleaks) (pull_request) Failing after 13s
2026-08-26 17:56:07 +00:00
implementer 86aa3afbbf refactor: newsletter signup posts no credential (SEC-14-R1 option a)
CI / Secret scan (gitleaks) (pull_request) Failing after 12s
CI / Run tests (pull_request) Successful in 15s
Rework PR #15 per security review SEC-14-R1: the client no longer carries
an API token. js/newsletter-config.js ships only the non-secret endpoint,
enforced https-only at config load time via validateEndpoint() (mirroring
the protocol allowlist in js/reading-list.js); js/newsletter.js POSTs
email-only with no Authorization header. Failure paths keep the single
user-safe message that never leaks token, endpoint, status, or raw body;
success still shows the confirmation. CI gains a gitleaks step that fails
on any secret hit; README documents the server-side token, the residual
signup-abuse risk, and the authoritative server-side validation follow-up.
2026-08-26 11:27:11 +00:00
bot-implementer 6aabecbe76 chore: add CI workflow running the test suite 2026-08-25 10:44:14 +00:00