Skip to content

Run it the way a deployment runs it, and fix what that found - #8

Open
AlexisRodriguezCS wants to merge 2 commits into
mainfrom
v1-hardening
Open

AlexisRodriguezCS wants to merge 2 commits into
mainfrom
v1-hardening

Conversation

@AlexisRodriguezCS

Copy link
Copy Markdown
Owner

Everything so far had been proved against the demo stack: H2, seeders, one container each. The configuration a real deployment uses — the prod profile on Postgres, Flyway, ddl-auto=validate, no seed data — had only ever been tested per service by Testcontainers.

Running the whole stack on it for the first time found that nobody could sign in at all: every seeder is @Profile("!prod"), so the user table was empty and there was no way to create an account. The stack came up healthy and completely unusable.

What changed

  • BootstrapAdmin — creates one ADMIN when the user table is empty and credentials are configured. Configured but users present does nothing, so a restart cannot resurrect a deleted account or reset a changed password; empty but unconfigured says how to fix it in the log rather than leaving a bare 401. No default password ships.
  • infra/qa/prod-smoke.cjs — that run, kept. 11 checks against the prod profile, needing no seed data, which is the point.
  • Locations can be corrected and removed — PATCH for label/tag/building/floor; DELETE refused while anything is placed there. asset-service answers that, and refuses to answer at all if unreachable, because "couldn't verify" must not read as "nothing is there".
  • The rate limiter is no longer silent when it fails open — a counter plus two Prometheus alert rules.
  • People and locations paginate — /paged in the shape asset-service already uses, and /stats so the console stops fetching everyone to count them. The desk map keeps the unpaged read on purpose: it groups by building and floor, and a page boundary through the middle of a floor is not a map.

Verification

11/11 prod smoke on Postgres · 57/57 API matrix · 38/38 browser · 109 web unit · all Gradle modules green.

Why this is a PR

Every commit in this session went straight to main with required checks bypassed — 27 of them. CI caught two real regressions along the way, both after the push. This one waits for the checks instead.

🤖 Generated with Claude Code

AlexisRodriguezCS and others added 2 commits September 8, 2026 07:42
Everything so far had been proved against the demo stack: H2, seeders, one
container each. The configuration a real deployment uses - the prod profile
on Postgres, Flyway, ddl-auto=validate, no seed data - had only ever been
tested per service by Testcontainers. Running the whole thing on it for the
first time found that **nobody could sign in at all**: every seeder is
@Profile("!prod"), so the user table was empty and there was no way to make
an account. The stack was healthy and completely unusable.

BootstrapAdmin creates one ADMIN when the table is empty and credentials are
configured. Both halves matter: configured but users present does nothing,
so a restart cannot resurrect a deleted account or reset a changed password;
empty but unconfigured says how to fix it in the log rather than leaving an
operator with a bare 401. Nothing ships with a default password.

infra/qa/prod-smoke.cjs is that run, kept: sign in, create a tenant and its
records, check gear out and back, correct a person, refuse to delete a desk
with gear on it, delete it once empty. 11 checks, and it needs no seed data,
which is the point.

Also in this pass, from the v1 list:

- **Locations can be corrected and removed.** PATCH for the label, tag,
  building and floor; DELETE refused while anything is placed there, which
  asset-service answers - and it refuses to answer at all if unreachable,
  because "couldn't verify" must not read as "nothing is there".
- **The rate limiter is no longer silent when it fails open.** A counter and
  two Prometheus alert rules: one for the store being unreachable, one for
  sustained refusals. The trade is still the right one; it is just visible.
- **People and locations paginate.** Both get /paged endpoints in the shape
  asset-service already uses, and people-service gets /stats so the console
  stops fetching everyone to count them - nothing at five people, a whole
  tenant over the wire at five thousand. The desk map deliberately keeps the
  unpaged read: it groups by building and floor, and a page boundary through
  the middle of a floor is not a map.

Verified: 11/11 prod smoke on Postgres, 57/57 API matrix, 38/38 browser,
109 web unit, all modules green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Opening this as a pull request rather than pushing to main turned out to
weaken the gate, not strengthen it. `images` - and `e2e`, which needs it -
only ran on a push to main or develop, so a pull request was checked by
build, web and secret-scan while a direct push additionally stood the whole
stack up and drove it through a browser.

That is exactly backwards, and this session proves it: the browser suite is
what caught the seeder losing its identity when services began validating
tokens, and the employee page disagreeing with its own subtitle. Merging on
everything except that suite would have let both through.

Images from a pull request are tagged by commit SHA like any other, plus a
pr-N tag instead of a branch name, so they are identifiable and disposable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant