Skip to content

fix(license): stop losing trials, and repair the ones already lost - #635

Merged
keysersoft merged 2 commits into
mainfrom
keysersoft/trial-reliability
Sep 17, 2026
Merged

keysersoft merged 2 commits into
mainfrom
keysersoft/trial-reliability

Conversation

@keysersoft

@keysersoft keysersoft commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

What was happening

131 verified cloud users have no licence at all. Not an expired trial, none: they verified their email and landed on the licence wall.

/api/license/trial on anythingmcp.com rate-limits 3 registrations per hour per IP. That is a sane number for a human starting trials by hand. It is not a sane number for this backend, which calls that route server to server for every verified email, always from the same droplet. The whole cloud shared one bucket of three an hour, and since activation is best-effort by design (verification must succeed even when the licence API is down), every loss was a warning line nobody read.

Measured on 2026-09-16: 11 trials created, 9 rejected with 429, and romanpravnyk1992@gmail.com signed up at 14:51 and still had no licence 19 hours later.

What changes

Service token LICENSE_SERVICE_TOKEN goes out as x-amcp-service-token so the licence API can tell its own cloud from the public. Unset self-hosted, which keeps the public limit. Needs the matching website change.
Retry 3 attempts, exponential backoff with jitter, for 429 / 5xx / no answer. A 4xx on the merits is not retried.
Idempotent activate-trial The client calls it right after verification already activated the trial, so it answered 400 on every single signup. An org that already holds a licence now gets it back with a success.
Repair pass The onboarding cron hands a trial to any verified user whose workspace has none, bounded at 25 per run and paced. The 131 drain themselves, and any future miss heals on the next run.

Any one of the four would have prevented the bug. Together they also make the retry safe, because the endpoint becomes idempotent on the website side.

Also here

The MOTIS healthcheck, which had failed 2300 times in a row on a container that was serving traffic perfectly: it probed localhost (→ ::1, where MOTIS does not bind) and / (which MOTIS answers 404). Now 127.0.0.1 and /metrics. Verified against the live container before and after.

Verified

  • npx jest whole backend: 4204 passed, 0 failed
  • 19 tests across the trial paths, including "gives up after the third attempt", "does not retry a 400", "never sends the organization id" (one email must not farm a trial per workspace) and the repair pass on self-hosted being a no-op
  • lint and typecheck clean

Deploy order

Website PR HelpCode-ai/anythingmcp-website#198 goes first (it teaches the licence API about the token and makes the route idempotent), then this, with LICENSE_SERVICE_TOKEN set on both sides.

131 verified users on cloud have no licence at all. The licence API rate-limits
anonymous callers to three trial registrations per hour per IP, and this backend
calls it server to server for every user who verifies an email, from one IP. So
the whole cloud shared a bucket of three an hour: on any busy hour, everyone
past the third silently ended up on the licence wall instead of onboarding.
Activation is best-effort by design, so nothing ever surfaced.

Four changes, each of which alone would have prevented it:

- Present LICENSE_SERVICE_TOKEN as x-amcp-service-token, so the licence API can
  tell its own cloud apart from the public and give it a bucket that fits.
  Unset in self-hosted installs, which keep the public limit.
- Retry the trial request three times with exponential backoff and jitter, for
  throttling, upstream faults and no answer at all. A 4xx on the merits is not
  retried.
- activate-trial is idempotent. The client calls it right after verification
  already activated the trial, so it used to answer 400 on every single signup:
  an error shown to a user whose trial was fine, and a wasted call against the
  rate limit. An org that already holds a licence gets it back, with a success.
- A repair pass in the onboarding cron hands a trial to any verified user whose
  workspace has none, bounded and paced. Whatever the reason a trial goes
  missing, the next run picks it up and the backlog drains itself.

Also fixes the MOTIS healthcheck, which had reported unhealthy 2300 times in a
row while serving traffic perfectly: it probed localhost, which resolves to ::1
inside the container where MOTIS binds IPv4 only, and / which MOTIS answers 404.

Trial paths now covered by 19 tests.
@keysersoft
keysersoft merged commit c78358c into main Sep 17, 2026
11 checks passed
@keysersoft
keysersoft deleted the keysersoft/trial-reliability branch September 17, 2026 07:50
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 17, 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