Skip to content

fix(action-token): make link reservations wallet-scoped, atomic and self-healing - #581

Merged
mahdi2ba merged 1 commit into
Greenstand:keycloakfrom
mahdi2ba:fix-action-token-reservation-followups
Sep 21, 2026
Merged

mahdi2ba merged 1 commit into
Greenstand:keycloakfrom
mahdi2ba:fix-action-token-reservation-followups

Conversation

@mahdi2ba

Copy link
Copy Markdown
Collaborator

Follow-ups to #579 from its review, verified one by one against the merged code.

  1. generate() flags the tokens and inserts the link row in one transaction. A short reservation (concurrent link or send) or a failed insert rolls the flags back, so no token can stay flagged for a link that does not exist. The UPDATE is scoped to the sender wallet.
  2. Explicit token ids must belong to the sender wallet. A token of a managed wallet named without sender_wallet gave a link that claim time refused forever. Now 409 at generate time, to the sender.
  3. Housekeeping releases every token flagged for a non-active or missing link in one statement, and cancel() updates state and releases in one transaction. This heals whatever a crash between two statements left behind.
  4. New migration makes token_action_token_id_idx a partial index (action_token_id IS NOT NULL). Nearly every row is NULL, so it stays tiny and off the write path of ordinary token updates. Prod note: the migration job must finish before the pods roll out.
  5. GET /action-tokens?state=expired is accepted.

Three integration specs added to actiontoken-reservation.spec.js.

Refs #574

…elf-healing

Follow-ups to Greenstand#579 (Greenstand#574), from its review.

- generate() flags the tokens and inserts the link row in one transaction.
  A short reservation (a concurrent link or send got there first) or a
  failed insert rolls the flags back, so no token can stay flagged for a
  link that does not exist. The UPDATE is also scoped to the sender wallet.
- Explicit token ids must belong to the sender wallet. walletB could name a
  token of walletC, a wallet it manages, without sender_wallet; the link
  then spoke for walletB and claim time refused it forever. Refuse at
  generate time, to the sender, with a clear message.
- Housekeeping releases every token flagged for a link that is not active,
  in one statement (NOT EXISTS active row), instead of a per-id loop after
  expiry. That also heals whatever a crash between two statements left
  behind. cancel() updates state and releases in one transaction.
- New migration turns token_action_token_id_idx into a partial index on
  action_token_id IS NOT NULL: nearly every row is NULL, so the index stays
  tiny and off the write path of ordinary token updates.
- GET /action-tokens?state=expired is accepted; rows can be expired since
  Greenstand#579.

Three integration specs: an explicit token of a managed wallet without
sender_wallet is refused and flags nothing; a token flagged for a link with
no row is released on the next link activity; expired links list by state.

Refs Greenstand#574

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@mahdi2ba
mahdi2ba merged commit 046dc40 into Greenstand:keycloak Sep 21, 2026
1 check passed
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.44.0-keycloak.32 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@dadiorchen

Copy link
Copy Markdown
Collaborator

@mahdi2ba because we won't change the action token system, so this is not needed, right?

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

Development

Successfully merging this pull request may close these issues.

2 participants