mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-03 00:36:56 +00:00
Two migration files shipped as version 28: 0028_add_device_secret_lookup (08121f3) and 0028_create_stripe_processed_events (12c55ed). runMigrations keys applied migrations by the integer parsed from the filename and skips any version already present in schema_migrations, so the first file to load records 28 and the second is skipped in silence — no error, no log. loadMigrations enumerates via fs.Glob, which sorts lexically, so add_device_secret_lookup won and stripe_processed_events never ran. On an existing database that leaves stripe_processed_events missing, and StripeEventSeen then fails closed: the error wraps into ErrWebhookProcessing, the webhook returns 500, and Stripe redelivers into the same missing table forever. No subscription, tier, or dispute event is ever applied — defeating the exact idempotency12c55edwas written to add. Fresh installs were unaffected, which is why no test caught it: they take the applySchema path where schema.sql already has the table and both version-28 rows are merely recorded as applied. Renumbered to 0029 rather than renumbering the device migration, so a database that already ran this build (28 recorded, devices.secret_lookup present, stripe table missing) picks the migration up on next boot instead of staying broken. Renumbering the other file would have left that database with the table still missing and re-run its ALTER on a column that exists. Adds two guards: one asserting migration versions are distinct, and one exercising the upgrade path where a duplicate manifests as a missing schema_migrations row. Both fail on a planted duplicate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>