Commit Graph

2 Commits

Author SHA1 Message Date
Matthew Meszaros 44da2f464f feat: add the control plane for mailbox sync fair use, so a mailbox syncs under an operator-editable policy and its progress survives worker replacement: a sync section on the instance settings document (backfill window in days, backfill cap per mailbox, daily new-mail budget per mailbox and per organization, each clamped on read and write with compiled defaults in constants.go), models.SyncPolicy and models.SyncState with a provider-shaped jsonb SyncCursor, a new email_sync_state table plus an index on tasks.message_id that the reply lookup was scanning sequentially without, an EmailSyncStateRepository whose Put also stamps email_accounts.last_synced_at which nothing had written since the baseline so every admin and dashboard Last synced surface read NULL, an OrganizationID and Sync block on the ADD_EMAIL payload resolved by the loader from instance settings and the saved state and, for IMAP, the saved unibox_mailboxes folder cursors that the loader had never populated so every worker restart re-walked every folder from scratch, a SYNC_STATE consumer handler that persists the relay and publishes ACCOUNT_SYNC_STATE plus a warning when the import completes or fair use flips, an internal own-conversation endpoint the worker's priority lane asks whether a new message replies to a campaign task, a mapped message or a stored thread, GET /emails/:id/sync for the dashboard, and SYNC_FLOOD and SYNC_FAIR_USE mail error codes with user copy for the two patterns that deactivate a mailbox 2026-08-18 08:43:20 -07:00
Matthew Meszaros 734cb5fe08 feat: make self-hosted onboarding survivable by fixing invite_only, which could not onboard anyone (the accept route is JWT-only, so redeeming the invitation that would create your account required already having one, making the self-host default silently identical to fully closed), threading the invitation token through registration so an invited person lands in the inviting organization instead of a stray workspace, gating SSO just-in-time provisioning behind DISABLE_REGISTRATION (it bypassed the gate entirely, so an instance set to true was still open to anyone the IdP would assert) with SSO_AUTO_PROVISION as the opt-out, correcting the OIDC redirect URL that pointed at /api/v1 against a route at /v1 and 404'd every SSO login, scoping the first-launch exemption so it no longer overrides an explicit lockdown, preserving the remaining TTL when restoring a losing setup token so a public endpoint cannot hold the claim window open forever, replacing a generic 403 with typed registration_invite_only, registration_closed, invitation_invalid, setup_token_invalid and setup_already_complete codes that name the next step, logging why no claim link was issued on an already-claimed instance instead of staying silent, adding a warmblyctl operator CLI (status with health checks and a non-zero exit, reissuable setup-link, user create/list/reset-password/grant-admin/revoke-admin/disable-2fa, hash-password) so a locked-out operator no longer needs hand-written psql, adding read-only instance configuration over 104 environment variables with structural secret redaction and fingerprints, 35 health checks, a database-backed settings tier for the three keys no environment variable owns, hiding the signup form when the config already says invite_only rather than failing the whole form with a toast, and documenting first run, accounts and access, configuration, instance health and troubleshooting alongside the root .env.example the README told operators to write but never shipped (#114) 2026-08-16 05:58:11 +02:00