Skip to content

fix(config): purchase_delay_hours defaults to 0 in the DB but is documented as 48 #1934

Open

Description

@cristim

Summary

The documented default for purchase_delay_hours is 48, but a freshly migrated database stores 0. A fresh install therefore executes approved purchases immediately, with no free-cancel window.

Current behaviour

  • internal/config/types.go:61-66 documents "Default: 48".
  • internal/database/postgres/migrations/000070_purchase_history_revocation.up.sql:72 says "default 48", but line 77 adds purchase_delay_hours INT NOT NULL DEFAULT 0.
  • 000001_initial_schema.up.sql:273 seeds the global_config row (id=1), so the row always exists and the column default of 0 applies.
  • The 48 fallback (DefaultPurchaseDelayHours, internal/config/store_postgres.go around line 162) only runs on sql.ErrNoRows, which a migrated database never returns.

Steps to verify

  1. Create a new database and run all migrations.
  2. SELECT purchase_delay_hours FROM global_config WHERE id = 1; returns 0.
  3. GET /api/config returns purchase_delay_hours: 0; approving a purchase executes immediately.

Expected behaviour

Either the shipped default is 48 as documented (free-cancel window on by default), or the docs and constants say 0 consistently.

Proposed fix

Needs a product decision:

  • A: new migration ALTER TABLE global_config ALTER COLUMN purchase_delay_hours SET DEFAULT 48 plus an UPDATE for rows still at 0 only if existing installs should change (they may have chosen 0 deliberately; probably leave them).
  • B: keep 0 and fix the comments and constants: types.go:61-66, 000070...up.sql:72, and the DefaultPurchaseDelayHours fallback in store_postgres.go.

Deploy hazard: migrations run under auto-migrate at deploy, so option A changes behaviour for new databases at rollout time; stage it first and note it in the release notes.

References

#1766, #1765, #1811, commit 5c600b0. OpenAPI text corrected in commit 934dbca.

Severity

Medium: fail-open on a money path for new installs (no cancel window), but existing installs and explicit settings are unaffected.

Activity

  1. cristim commented on Nov 10, 2026

    @cristim
    MemberAuthor

    Claiming (claude-cc-libs), with findings that change the proposed fix. Verified on main 51e9242:

    1. Option A as written (ALTER COLUMN ... SET DEFAULT 48) does not fix a fresh install. 000001 seeds the global_config row (id=1) before 000070 adds the column, so ADD COLUMN ... NOT NULL DEFAULT 0 fills that existing row with 0. A later SET DEFAULT only affects rows inserted afterwards. Changing the seeded row needs an UPDATE, and a new migration cannot tell a fresh install from an existing one that never touched the setting, so that UPDATE is exactly the backfill the issue warns about.
    2. The only no-backfill way to give fresh installs 48 is to change 000070's ADD COLUMN default to 48 in place (existing DBs have already applied it and are untouched; fresh DBs get 48 on the seeded row). Old migrations have been edited in place before (000059, 000027), so there is precedent.
    3. New hazard the issue does not mention: on a deployment with FireSweepOff (no fire_scheduled_purchases sweep), a stored delay above 0 makes every approval return 409 (handler_purchases.go:1199-1205, fix(purchase): reject a purchase delay where no fire sweep is scheduled #1588) and PUT /api/config refuses a write above 0 (handler_config.go:221). A fresh install on such a deployment would get 409 on every approval until an admin sets the delay to 0. Fail-closed, but a visible behaviour change.
    4. The OpenAPI text was already corrected (934dbca): "A fresh install stores 0 ... 48 is only the fallback used when the configuration row is missing". Remaining stale text: internal/config/types.go:61-66 ("Default: 48") and the 000070 up.sql comment (line 72 "default 48").

    Two coherent options, owner decision: (A') 000070 in place -> 48, test that a fresh migrate yields 48, plus accept the FireSweepOff 409 on fresh installs; or (B) keep 0 and fix types.go, the 000070 comment and the DefaultPurchaseDelayHours fallback to say 0 consistently. No existing row is changed by either. Recommended default: A'. Holding implementation for the decision.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions