Skip to content

feat: store Stalwart numeric account id on provider link#64

Open
jzunigax2 wants to merge 2 commits into
masterfrom
feat/store-stalwart-account-id
Open

feat: store Stalwart numeric account id on provider link#64
jzunigax2 wants to merge 2 commits into
masterfrom
feat/store-stalwart-account-id

Conversation

@jzunigax2

@jzunigax2 jzunigax2 commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Why

Groundwork for per-message quota metadata in Bridge (follow-up to the discussion on internxt/bridge#220): Stalwart's message-ingest.* webhook events identify mailboxes by Stalwart's internal numeric account id, which we never persisted — provisioning only stores the address string as external_id. Without this mapping, a webhook consumer cannot resolve events to a user/bucket.

What

  • StalwartAccountProvider.createAccount now uses the id already returned by Account/set (previously discarded): decodes it from Stalwart's custom base32 JMAP encoding and returns it through the AccountProvider port.
  • Provider-agnostic port: CreateAccountResult carries { provider, externalId, internalId } — the adapter self-describes how it identifies the account. AccountService no longer hardcodes 'stalwart' or assumes the external id equals the address, and the internal id is an opaque string so a future provider can use any id format. All Stalwart specifics (base32 codec, principal-name semantics) stay inside infrastructure/stalwart/.
  • stalwart-id.codec.ts: decoder for Stalwart's base32 alphabet (abcdefghijklmnopqrstuvwxyz792013, matching crates/utils/src/codec/base32_custom.rs in the Stalwart source).
  • mail_provider_accounts.provider_internal_id: new VARCHAR(255) NOT NULL column, unique per (provider, provider_internal_id) among non-deleted rows. NOT NULL with no default is safe — no production data exists yet.
  • Provisioning reorder: the provider link is now created after the provider account (the id isn't known earlier), with undo on failure (delete provider account + local rows), following the existing undo pattern. addAddress gets the same undo coverage.

Tests

  • stalwart-id.codec.spec.ts: alphabet vectors, positional decoding, error cases (empty, invalid char, overflow).
  • Provider spec: returns provider name, external id, and decoded internal id.
  • Account service spec: link created from the provider result, undo cases when link creation fails after the provider account exists (both provisioning and addAddress).

319 tests passing.

🤖 Generated with Claude Code

Stalwart telemetry webhooks identify mailboxes by the server's internal
numeric account id, which provisioning never persisted. Capture the id
returned by Account/set, decode it from Stalwart's custom base32 JMAP
encoding, and store it on mail_provider_accounts so webhook events can
be resolved to a user and network bucket with a single indexed lookup.

- AccountProvider.createAccount now returns the decoded internal id
- provider link creation moved after Stalwart account creation during
  provisioning (with undo on failure), so the id is known at insert time
- new provider_internal_id column, NOT NULL, unique per provider among
  non-deleted rows

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jzunigax2 jzunigax2 marked this pull request as draft June 11, 2026 17:57
The provider now self-describes the identifiers of the account it
created: CreateAccountResult carries provider name, external id, and an
opaque string internal id. AccountService no longer hardcodes
'stalwart' or assumes the external id equals the address, and the
provider_internal_id column is a string so a future provider can use
any id format.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@jzunigax2 jzunigax2 requested a review from xabg2 June 12, 2026 04:27
@jzunigax2 jzunigax2 self-assigned this Jun 12, 2026
@jzunigax2 jzunigax2 added the enhancement New feature or request label Jun 12, 2026
@jzunigax2 jzunigax2 marked this pull request as ready for review June 12, 2026 04:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants