Matthew Meszaros
5a967cc3ce
feat: let an IMAP mailbox exclude folders from sync (email_accounts.sync_skip_folders, migration 000196) so a folder another tool fills never reaches the unified inbox: the worker drops skipped folders and their subfolders before the walk, retires an already-synced one with its stored mail, and removes mail that later moves into one only when its Message-ID is found there; PUT /emails/:id/sync and the drawer's Sync card set the list, GET reports it with the server's folder list, warmbly mailbox skip-folders mirrors it, with docs, OpenAPI and error-code invalid_sync_folder
2026-09-22 05:15:49 -07:00
Matthew Meszaros
cc898cc040
Merge pull request #641 from warmbly/fix/password-change-session-revocation
...
feat: end every session on a password change and answer with a fresh token pair for the calling device
v0.5.11
2026-09-21 12:03:31 +00:00
Matthew Meszaros
e1989f9116
Merge remote-tracking branch 'origin/main' into fix/password-change-session-revocation
...
# Conflicts:
# internal/app/auth/reset_password.go
2026-09-21 04:56:50 -07:00
Matthew Meszaros
711e89f9fe
Merge pull request #643 from warmbly/fix/sso-link-existing-password-account
...
feat: attach a provider identity to an existing password account only after its password is presented
2026-09-21 11:33:46 +00:00
Matthew Meszaros
eac3f6d615
Merge remote-tracking branch 'origin/main' into fix/sso-link-existing-password-account
...
# Conflicts:
# docs/content/docs/guides/security.mdx
2026-09-21 04:28:20 -07:00
Matthew Meszaros
db0f0c6132
feat: carry the caller's session into ReissueSession from the request instead of looking it up, evict every revoked session from the cache and write a revoked tombstone where the delete is refused, and answer a password change whose reissue failed with the distinct 409 password_changed_sign_in_again that the dashboard turns into a sign-out, documented in error-codes, the endpoint reference and OpenAPI
2026-09-21 04:23:46 -07:00
Matthew Meszaros
ff2204f8ef
feat: route the sso_link completion through the one login path (completeLogin) so the ban check, the 2FA gate, login recording and device memory apply before a parked identity is attached, carry the session provider into every 2FA challenge so a Google or Apple sign-in on a 2FA account is recorded as such, make IdentityRepository.Link report a pair another account holds as ErrIdentityTaken instead of silently updating nothing, refuse a spent address budget before charging a link try, drop the dead expiry check and the duplicate link fields on the web Session model
2026-09-21 03:57:29 -07:00
Matthew Meszaros
e0db7d3c72
Merge pull request #642 from warmbly/fix/reset-token-invalidation-after-password-change
...
feat: refuse a password reset link issued before the password was last changed
2026-09-21 10:50:49 +00:00
Matthew Meszaros
ae143caf3f
Merge pull request #644 from warmbly/fix/account-name-validation
...
feat: validate every person, workspace and company name through internal/pkg/displayname on each write path and render stored names safely in platform email
2026-09-21 10:47:13 +00:00
Matthew Meszaros
4578980b34
feat: fall back to My Organization when the first name is blank in displayname.DefaultWorkspace, and only treat a scheme as a link when a non-space follows its colon so names like Big Data: EU pass in both the Go and web validators
2026-09-21 03:42:17 -07:00
Matthew Meszaros
a1b7a8ad9b
feat: attach a parked federated identity only after the ban check and, on a 2FA account, only once the second factor passes by carrying it through the 2FA pending record into twofa.VerifyLogin, charge each sso_link password attempt atomically before the check, treat an identity a parallel challenge already linked as a re-login instead of a refusal, ask for no password when the identity cannot be linked, end an exhausted or expired challenge with sso_link_expired so the dashboard returns to the email step, and document the code in error-codes, the API reference and OpenAPI
2026-09-21 03:35:17 -07:00
Matthew Meszaros
9426c0da51
feat: validate every person, workspace and company name through internal/pkg/displayname on each write path (profile, onboarding, setup, IdP sign-in, org create and rename, org import, enterprise inquiry, admin testers, warmblyctl) with a 400 invalid_name code, render stored names in platform email through the same rules, mirror them in the web forms, check the org slug format, and document the rules in error-codes, security and AGENTS.md
2026-09-21 03:34:03 -07:00
Matthew Meszaros
0f60fd9b84
feat: attach a Google, Apple or OIDC identity to an existing password account only after that account's password is presented: resolveFederatedUser parks the sign-in as link_required with a single-use sso_link pending token, POST /auth/sso/link checks the password against the provider-asserted address on the sign-in failure budget and links then issues the session through finishLoginAs, the dashboard collects it on a new login step, and the API reference, endpoints list, security guide and OpenAPI spec describe the third login result
2026-09-21 03:25:29 -07:00
Matthew Meszaros
36eb5e72e9
feat: stamp users.password_changed_at on every password write and refuse a password reset link issued at or before it, so a link requested earlier dies when the password is changed from settings, by another reset link or by warmblyctl, with the rule documented on the reset endpoint and the security guide
2026-09-21 03:23:14 -07:00
Matthew Meszaros
7626aaefe4
feat: end every session on a password change, the calling one included, and answer POST /auth/me/password with the token pair of a fresh session for that device; the dashboard stores the new pair, and the endpoint reference, security guide and OpenAPI document the response
2026-09-21 03:18:27 -07:00
Matthew Meszaros
f11df9268f
Merge pull request #640 from warmbly/fix/admin-list-pagination
...
feat: make every admin panel list page past the first and filter by id, and report failed admin requests
2026-09-21 09:29:48 +00:00
Matthew Meszaros
4dde9708c9
feat: honour an ascending created_at sort on the admin users list with a matching id tiebreak, and record an admin API failure whose call omitted the method as GET, the method axios sent
2026-09-21 02:24:36 -07:00
Matthew Meszaros
cc244e5159
feat: make every admin panel list page past the first and filter by id: bind query-string ids through models.ParamUUID since gin cannot set a uuid.UUID, page the explorers and secondary lists by an opaque offset cursor with an id tiebreak instead of an id keyset that disagreed with the sort, page the audit log on (created_at, id) with an inclusive YYYY-MM-DD end day and read next_cursor on its page, cast every before-date bound to timestamptz, coalesce nullable audit ip and user agent, and report failed admin queries and 5xx mutations to PostHog or Sentry with method, path, status, code and request id
2026-09-21 02:11:38 -07:00
Matthew Meszaros
c19e431d36
Merge pull request #638 from warmbly/feat/warmup-mail-retention
...
feat: platform-owned retention of warmup mail in mailboxes, per-message record pruning, and a deletion strike limited to the first day
2026-09-21 08:59:26 +00:00
Matthew Meszaros
3ea6118a27
feat: redeliver a warmup retention delete when the mailbox is not loaded on the worker, drop the stored body when the IMAP message is already gone on a redelivery while returning a search failure rather than treating it as absence, and encode the accepted warmup_retention_days range (0, or 3 to 3650) in both OpenAPI schemas
2026-09-21 01:21:28 -07:00
Matthew Meszaros
0a5e7947b4
feat: return a failed warmup retention delete from the worker so the bus redelivers it up to five times, re-key a Graph message in the map on every move so the sender copy's body can be dropped, never expunge a whole IMAP folder for one message (UID EXPUNGE, else MOVE to Trash, else refuse), build the two retention indexes concurrently in their own migrations 000193 and 000194, keep the dashboard stepper off 1 and 2 days, and describe retention as applying wherever the placement files warmup mail
2026-09-21 01:11:22 -07:00
Matthew Meszaros
1513419a2a
feat: delete warmup mail from each mailbox once past a per-mailbox retention window (email_accounts.warmup_retention_days, else retention.warmup_mail_days, default 30) via a consumer sweep that retires the receipt and sender copy and a worker delete action that trashes on Gmail, deletes on Graph, expunges on IMAP and drops the stored body, prune per-message warmup records after retention.warmup_event_days, and count a warmup deletion as tampering only within 24 hours of arrival and never for a retired message, judging Gmail's Trash label on the same rule
2026-09-21 00:52:02 -07:00
Matthew Meszaros
79850ec9cb
Merge pull request #636 from warmbly/fix/upgrade-dialog-provider-boundary
...
feat: keep the global modals inside UpgradeDialogProvider so the mailbox allowance dialog can offer an upgrade
v0.5.10
2026-09-20 18:11:48 +00:00
Matthew Meszaros
eaa7cfb129
feat: mount the global modals inside UpgradeDialogProvider so the mailbox-allowance dialog can offer the plan that lifts the cap instead of throwing UpgradeDialogProvider not found, and assert the provider boundary so a sibling cannot drift back outside it
2026-09-20 20:08:24 +02:00
Matthew Meszaros
e919d415b0
Merge pull request #634 from warmbly/fix/warmup-pool-reciprocity
...
feat: reciprocal warmup pool: free mailboxes write back to the paying mailboxes that wrote to them, draws favour the inbox owed the most, and inbound volume is capped per day (#633 )
v0.5.9
2026-09-20 17:58:42 +00:00
Matthew Meszaros
bb96cfb030
feat: assert the quarantine term in the tampering-versus-rates band tests and require the expiry to be nil or set on both sides before comparing
2026-09-20 10:53:51 -07:00
Matthew Meszaros
0ea00926c9
feat: apply the warmup inbound cap inside the candidate query before the tier is sized or sampled and count mail dispatched today alongside verified arrivals, judge the tampering band apart from the rate bands and keep the more severe finding, and bound received analytics by UTC instants instead of a session-timezone date cast
2026-09-20 10:15:33 -07:00
Matthew Meszaros
26594391c8
feat: judge tampering with received warmup mail on a ladder inside the health bands, one deletion warns, two pause for seven days and four or two spam flags block for thirty, instead of a review-required block on the first deletion ( #635 )
2026-09-20 09:50:42 -07:00
Matthew Meszaros
d6384d3c0e
feat: make cross-tier warmup an exchange so a proven free mailbox writes back to the paying mailboxes that wrote to it, favour the inbox owed the most on every draw, cap what any inbox receives per day inside WarmupPartnerCandidates so a thin tier is neither starved nor flooded, and surface received counts in the mailbox drawer, warmup analytics and the API ( #633 )
2026-09-20 09:42:53 -07:00
Matthew Meszaros
93a0545955
Merge pull request #632 from warmbly/feat/geoip-mirror-and-resilient-fetch
...
feat: mirror the GeoIP databases and stop a single failed fetch costing a container its geo data
2026-09-20 16:08:00 +00:00
Matthew Meszaros
6026168334
feat: stop blocking boot on the GeoIP download and swap the database in when it lands, retry a refused mirror with backoff honouring Retry-After, revalidate a database older than a week with If-Modified-Since instead of keeping the first copy forever, and add a twice-weekly job mirroring both MaxMind editions to a private bucket so the fleet stops spending a 30-a-day allowance per boot
2026-09-20 17:55:38 +02:00
Matthew Meszaros
a5136f5bf6
Merge pull request #631 from warmbly/fix/inbox-awaiting-reply-actions
...
feat: make Archive leave every working unibox view, fix Awaiting reply's address match, and add row and multi-select triage actions
v0.5.8
2026-09-20 14:24:42 +00:00
Matthew Meszaros
36b7bbfdfb
Merge pull request #630 from warmbly/fix/widen-unibox-email-mailbox
...
feat: widen unibox_emails.mailbox to bigint so high UIDVALIDITY folders can sync
2026-09-20 14:22:59 +00:00
Matthew Meszaros
78fe43b8d2
feat: widen unibox_emails.mailbox to bigint so a folder whose UIDVALIDITY is at or above 2^31 can have its mail filed and listed instead of failing to bind against int4, finishing the widening 000179 started
2026-09-20 16:18:14 +02:00
Matthew Meszaros
7b93e48bd4
feat: make Archive mean something everywhere by keeping filed conversations out of every working unibox view except All mail and the Archive folder, read a mailbox address out of the raw From header so Awaiting reply stops missing every thread sent as "Name <addr>", file and mark read by thread id rather than by message id, and add per-row triage actions plus a multi-select selection bar to the conversation list
2026-09-20 07:16:35 -07:00
Matthew Meszaros
1e11d90cae
Merge pull request #628 from warmbly/fix/unibox-seen-follows-provider
...
Follow the provider's read state in the unibox
2026-09-20 14:01:05 +00:00
Matthew Meszaros
bf8d4b2aa4
Merge pull request #629 from warmbly/ci/serialize-release-runs
...
feat: serialize release workflow runs so two tags cannot race the buildx cache
v0.5.7
2026-09-20 13:57:48 +00:00
Matthew Meszaros
f615c3f4d5
feat: serialize release workflow runs on one concurrency group so two tags pushed close together queue instead of racing the shared buildx cache scope and failing the second build on a missing layer blob
2026-09-20 15:57:02 +02:00
Matthew Meszaros
48ecca2400
feat: follow the provider's read state in the unibox by storing seen from the \Seen flag on every IMAP, Gmail and Graph arrival, carrying read-state changes onto unibox_emails.seen in the FLAGS_ADD/FLAGS_REMOVE and UPDATE_EMAIL handlers, and backfilling existing rows from their flags in 000190
2026-09-20 06:54:29 -07:00
Matthew Meszaros
ab22cb5c6b
Merge pull request #627 from warmbly/fix/public-object-acl-and-passkey-challenge-budget
...
feat: store public objects on buckets that refuse ACLs, and give the passkey login challenge its own per-IP budget
2026-09-20 13:45:59 +00:00
Matthew Meszaros
5b16a09b02
feat: write public objects without a canned ACL so a bucket whose ownership is owner-enforced still stores avatars, form assets, OAuth logos and email-body images, give the passkey login challenge its own per-IP budget separate from the one password sign-in draws on, and surface the API's own message at upload and passkey call sites instead of a generic sentence
2026-09-20 15:40:52 +02:00
Matthew Meszaros
63e26f35f1
Merge pull request #626 from warmbly/brand/email-signature-steel
...
Add steel Warmbly wordmark for theme-agnostic email signatures
2026-09-20 12:59:13 +00:00
Matthew Meszaros
742a173b51
Add steel Warmbly wordmark for theme-agnostic email signatures
2026-09-20 14:58:07 +02:00
Matthew Meszaros
a6630fd9b8
Merge pull request #625 from warmbly/brand/email-signature-dark
...
Add white Warmbly wordmark for dark-mode email signatures
v0.5.6
2026-09-20 11:41:10 +00:00
Matthew Meszaros
876076942a
Add white Warmbly wordmark for dark-mode email signatures
2026-09-20 13:40:13 +02:00
Matthew Meszaros
6703e0570a
Merge pull request #624 from warmbly/brand/email-signature-wordmark
...
Add Warmbly wordmark for email signatures
v0.5.5
2026-09-20 11:32:39 +00:00
Matthew Meszaros
e04fe778b5
Merge pull request #623 from warmbly/fix/gmail-app-password-connect
...
feat: fall back to 587 STARTTLS when a mailbox's port 465 never answers, store the port that signed in, and make mailbox connect errors readable
2026-09-20 11:29:39 +00:00
Matthew Meszaros
56286f94e8
Add Warmbly wordmark for email signatures
2026-09-20 13:26:33 +02:00
Matthew Meszaros
4e37b968a2
feat: say in the mailboxes guide and the submission dialer comment that a refusal or an unresolvable name arriving before 587 is dialled is returned as is while a later one lets a connecting 587 be used, instead of claiming 587 would fail the same way
2026-09-20 13:25:46 +02:00
Matthew Meszaros
c20d1c99f2
feat: race a 587 STARTTLS dial against a mailbox's silent port 465 on every send and connect check so the fleet keeps sending where outbound 465 is blocked, store a connect that passed that way with 587, name the port actually used in a refusal, make the Google app-password hint say Google itself refused the pair and name the alias and wrong-account causes, and render long error toasts wide, dismissable and longer-lived instead of a narrow four-second column
2026-09-20 13:16:52 +02:00