Skip to content

Bind unclaimed paid Cloud licences within minutes, not hourly - #1542

Merged
helpcodeai merged 1 commit into
mainfrom
fix/bind-paid-licences-fast
Nov 5, 2026
Merged

helpcodeai merged 1 commit into
mainfrom
fix/bind-paid-licences-fast

Conversation

@helpcodeai

Copy link
Copy Markdown
Contributor

Summary

A Cloud customer paid at 18:29 UTC on 5 Nov 2026 from the "blocked calls" email. That checkout goes through the public pricing page, so it does not name the workspace. His browser did not come back to /settings/license/activate, so the licence stayed unbound. The fallback (bindUnclaimedPaidLicences, from #1451) runs only from the hourly onboarding cron. The licence site also holds every licence back for 15 minutes. Together that means a paying customer can stay locked out for up to 75 minutes. In this case it was about 50 minutes.

Changes

  • LicenseService runs the binder every minute on the Cloud (UNCLAIMED_SWEEP_MS). The timer is unref'd and cleared on shutdown. Self-hosted is unchanged: no timer.
  • A licence is bound once it is 2 minutes old (UNCLAIMED_SETTLE_MS, read from the createdAt the site already sends). The browser's own activation still goes first: Stripe sends it back within seconds, and it knows the workspace for certain. The site applies the same delay (HelpCode-ai/anythingmcp-website PR, 15 → 2 minutes). The Cloud keeps the delay too, in case the site does not.
  • One run at a time. A call while another is in progress returns at once with nothing done. So the minute sweep and the hourly cron (licensesBound= in the drip line) never bind the same key twice. The files worker does not load LicenseModule, so only the backend process runs the timer.
  • Quiet minute runs.
    • The summary line is logged only when a licence is bound or fails.
    • The per-licence already_paid / bound_elsewhere warnings and "site unreachable" warnings stay at debug.
    • The hourly cron run still logs everything as before, so stale no_account licences do not add 1,440 lines a day.
  • Binding rules are unchanged: the checkout's workspace, else the verified ADMIN with the same address. Workspaces that already pay and keys bound elsewhere are still skipped.

No schema change, no migration, no new endpoint.

Cloud: paid licences without a returning browser are bound about 2–3 minutes after the payment instead of up to 75. Self-hosted: no change (the binder and the timer are Cloud only).

Type

  • Bug fix

Testing

  • Added/updated unit tests

In packages/backend:

  • npx jest src/license/license-unclaimed.spec.ts: 18 passed. New tests cover:
    • the settle delay, including a licence sold 1 minute ago (left) vs 3 minutes ago (bound);
    • an unreadable sale date;
    • quiet logging;
    • no overlap between two runs;
    • the timer starting on Cloud only and stopping on shutdown.
  • npx jest src/license src/ee/cloud: 21 suites, 392 tests passed.
  • npx eslint on the 3 changed files: clean.
  • npx tsc --noEmit: no errors in the changed files. The errors left are the known local baseline: picomatch, @aws-sdk/client-s3, basic-ftp, ssh2 and @smithy types missing from this machine's node_modules. CI runs the full suite.

Checklist

  • My code follows the existing code style
  • I have tested my changes locally

A buyer who paid from an email link and whose browser did not come back
to the activation page waited up to 75 minutes for the plan: the binder
ran only from the hourly onboarding cron, and the licence site held each
licence back for 15 minutes.

LicenseService now runs the binder every minute on the Cloud, binds a
licence two minutes after the sale, never runs two bindings at once, and
logs a minute run only when it binds or fails a licence.
@helpcodeai
helpcodeai merged commit 2c87b7e into main Nov 5, 2026
17 checks passed
@helpcodeai
helpcodeai deleted the fix/bind-paid-licences-fast branch November 5, 2026 19:33
@github-actions github-actions Bot locked and limited conversation to collaborators Nov 5, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant