Move one-time access tokens and signup tokens onto the Francis actor
framework, so their state is coordinated cluster-wide and expired entries
are purged without the periodic db_cleanup job.
One-time access tokens:
- Each token is its own actor, keyed by the token value, storing the user
ID and optional device token in actor state with a TTL matching the
token's expiration. Expired state is purged automatically.
- Exchange consumes the token by invoking the actor (atomic validate +
delete) outside of any DB transaction, then loads the user and issues an
access token. On a later failure it compensates by restoring the token
(best-effort, using a non-cancelable context).
- The one_time_access_tokens table is dropped; the CLI writes token state
through a minimal, non-running actor host.
Signup tokens:
- A singleton actor holds every signup token in its state and keeps a
single cleanup alarm scheduled for the earliest expiration; when it
fires it purges expired tokens and reschedules. Listing uses Peek.
- Sign-up consumes a token by invoking the actor to atomically increment
its usage count, performs user creation in a transaction, and, on
failure, compensates by releasing the token (best-effort).
- Existing tokens are migrated from the database into the actor state on
first startup; the tables are retained only for that migration.
Also removes ClearOneTimeAccessTokens and ClearSignupTokens, and lets the
importer skip tables no longer present in the schema.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UBMz9v9s8S8RPYNWzshkBu