feat(006): deploy-chosen container port + FLEET_IMAGE_PULL_SECRET wiring#65
Conversation
Two gaps blocked any real in-pod fleet placement of a chassis image, surfaced while preparing the in-pod fleet E2E: - The addon's DeploySpec.port (default 4000) was unreachable from the HTTP API, while enrahitu chassis images are fixed on 8080, so a placement would hang at the rollout wait on a probe aimed at a port nothing listens on. DeployRequest gains an optional validated port, persisted on FleetApp and forwarded on update so an image change cannot silently revert probes to 4000. Exposed on FleetAppView and the deploy attestation payload. Live DB needs: ALTER TABLE "fleet_app" ADD COLUMN "port" BIGINT NOT NULL DEFAULT 4000 (CoreLedger schema init is CREATE-only). - Spec 009 section 4.4 always listed FLEET_IMAGE_PULL_SECRET as deploy-set, but the Deployment never set it, so placed pods carried no imagePullSecrets and private images could not pull. The deploy now sets it to ghcr-pull. The spec 010 catalog entry claimed it was a base64 dockerconfigjson derived from GHCR_PAT; no code ever read it that way (fleet/config.ts consumes a Secret NAME), so the entry is corrected (user-supplied, non-secret) and .env.example regenerated; secrets:validate and secrets:check stay green. Specs 006/009/010 amended accordingly; derived shards recompiled.
AI Code ReviewGood — the ledger maps Bugs and Logic Errors
await record("update", app, auth.userID, { tenantId: app.tenantId, app: app.name, image: img }, gated.configHash);Port is now a meaningful deployment parameter — an update that changes from a 4000-default deployment to one explicitly using 8080 is not captured in the governance audit trail. Existing rows silently inherit wrong port after migration SecurityNo issues. Spec CouplingClean. All three specs (006, 009, 010) carry amendments; PerformanceNo concerns. Summary: Two issues worth addressing before merge — the Automated review by Claude. Diff size: 163 lines. |
…vidence The update record() now includes the persisted port, matching deploy. The spec 006 amendment states the verified reason DEFAULT 4000 cannot mislabel an existing 8080 placement: the live fleet_app table holds exactly one row, terminal removed, whose placement died Forbidden before any cluster object existed.
AI review acknowledgmentBoth findings taken; fixes in 91dca32.
Migration DEFAULT data risk: assumption verified against the live database and now stated in the spec. |
AI Code ReviewBugsColumn type mismatch: entity declares
|
AI review acknowledgment (second pass)Column type mismatch: refuted with production evidence. CoreLedger's Postgres dialect maps Port immutability undocumented: taken. The Old base64 blob possibly injected in production: refuted with pod evidence. The running container's environment contains zero No migration artifact / crash window: accepted, sequencing controlled. The ALTER is a one-time additive step and will be applied to the live database immediately after this merge and before the new image digest is pinned, in the same operational sequence (precedent: the spec 011 |
AI Code ReviewBugs and Logic ErrorsPrivileged port range accepted without guard (
Deployment ordering risk (acknowledged in spec, flagging for completeness): if the image rolls before the manual Security
No injection surface introduced.
Spec CouplingAll three affected specs (006, 009, 010) carry amendment sections matching the code changes. PerformanceNothing of concern. Automated review by Claude. Diff size: 172 lines. |
isValidPort joins the pure helpers in ops.ts: placed pods run as a non-root UID with no NET_BIND_SERVICE, so a port below 1024 could only reproduce the permanent rollout hang this change exists to prevent; reject it up front with the reason in the error. Boundary tests cover 1023/1024/65535/65536, negatives, floats, NaN, Infinity. The fleet_app port ALTER is applied to the live database ahead of the merge, so no deploy ordering window exists; the spec records both.
AI review acknowledgment (third pass)All three items taken or closed with evidence; changes in b545299. Privileged ports: taken, rejected up front. Boundary tests: taken. Migration ordering window: closed, not just acknowledged. The |
AI Code ReviewBugs and Logic ErrorsBIGINT/INTEGER column type mismatch ( The entity declares Port immutability enforced by omission, not by assertion The spec calls port immutable after deploy ("changing it means remove + redeploy"), but Security
Port value reaches the native addon without an additional clamp. No authz gaps introduced; port is validated after the tenant write-auth check, in the correct order. Spec Coupling
Minor: the spec 006 amendment documents the PerformanceNo concerns — integer column, no new indexes required, O(1) validation. Automated review by Claude. Diff size: 208 lines. |
AI review acknowledgment (fourth pass): merging
All substantive findings across the four passes are fixed or closed with evidence; merging. |
What
Two gaps blocked any real in-pod fleet placement of an enrahitu chassis image, surfaced while preparing the in-pod fleet E2E (the backlog item's own parenthetical: "pull secret, port 8080"):
Deploy-chosen container port. The addon's
DeploySpec.port(default 4000) was never reachable from the HTTP API, while chassis images are fixed on 8080 (EXPOSE 8080, no$PORTin the entrypoint), so placing one would create every object and then hang at the 180s rollout wait on a probe aimed at a port nothing listens on.DeployRequestgains an optionalport(integer 1-65535, default 4000 unchanged), persisted onFleetAppand forwarded onupdate, which rebuilds the Deployment spec and would otherwise silently revert a port-8080 app's probes to 4000 on its first image change. Exposed onFleetAppViewand in the deploy attestation payload.FLEET_IMAGE_PULL_SECRETwiring. Spec 009 §4.4 has always listed it as deploy-set, but the Deployment never set it, sofleetImagePullSecret()resolved empty in production and placed pods carried noimagePullSecretsat all: private images could not pull. The Deployment now sets it toghcr-pull. The spec 010 catalog entry claimed a base64 dockerconfigjson derived fromGHCR_PAT; no code ever read it that way (fleet/config.tsconsumes a Secret NAME, and fleet's RBAC deliberately cannot create Secrets), so the entry is corrected (user-supplied, non-secret, still required) and.env.exampleregenerated.Operational notes
ALTER TABLE "fleet_app" ADD COLUMN "port" BIGINT NOT NULL DEFAULT 4000;(precedent: the spec 011user_accountALTER, 2026-07-22).secrets); nothing automates that yet..envvalue forFLEET_IMAGE_PULL_SECRETpredates the catalog correction and should be updated to the Secret name (ghcr-pull).portyet; follow-up under spec 007 territory if wanted.Verification
npm run typecheckclean; full vitest run 25 files / 144 passed (18 skipped Postgres arms).secrets:validate(37 keys) andsecrets:checkgreen.spec-spine compile/index/lintclean;coupleOK, 10 paths, no drift. Specs 006/009/010 amended in the same change.