Publish row ids as strings — a new book 404'd the moment you selected it
CockroachDB unique_rowid() produces 64-bit ids. A real one from contact_books is 1200081912680906758; JavaScript's JSON.parse rounds anything past Number.MAX_SA...
Record message sizes instead of writing zero
SearchIndexService wrote sizeBytes = 0 for every message it ever indexed, under the comment "Not available from envelope". It was available — the fetch profile ...
Restore the files 2b23346 dropped, and answer an oversized send with 413
2b23346 was committed through a private index that was never seeded from HEAD, so its tree held only the three files staged into it and the commit deleted the o...
Stop capping every send at 1MB, so a pasted image can actually go out
Composing with a pasted image failed with a bare "Failed to send email". The composer inlines the paste into the body HTML as a base64 data URI, which rides in ...
Stop Hibernate mangling the "::" casts in the native SQL
Hibernate's native-query parser treats "::" as an escaped colon and forwards a single ":". CockroachDB received **************** ARRAY[]:text[]), ' ') and r...
Keep retrying the indexer's durable NATS consumer after a deploy
A rolling deploy overlaps pods and the outgoing one still holds the durable consumer, so the incoming pod's bind to "email-metadata-indexer" fails with [SUB-900...
Adapt the metadata indexers to array-typed columns
Follows the kamo-shared-library change that maps conversations/search_index array columns as String[]/UUID[] rather than List<T>, which is what stopped every in...
Bound the bulk folder index and stop it on structural failure
Uncommitted work from a concurrent session, swept in so the tree is clean. The bulk index walked pages with totalPages set to Integer.MAX_VALUE and no ceiling,...
Never match a lead against the company's own addresses
A lead whose e-mail was set to noreply@kamocrm.com matched every message that address had ever sent — one lead record surfaced the company's entire outbound mai...
Page the lead sweep until the window is covered
A single fixed page silently lost mail whenever a mailbox took more than pageSize messages between ticks — a newsletter burst, a busy shared inbox, or just the ...
Address replies at Reply-To, and stop leaking SMTP exceptions to the UI
A quick reply to a KamoCRM notification failed with `{"error":"Invalid Addresses"}` rendered verbatim in the reply box. That string is Jakarta Mail's own wordin...
Stop classifying a multipart/related HTML root as an attachment
Every transactional email we send with an inlined org logo arrived in our own /messages viewer with a correct subject and a completely empty body. isAttachment...
Scan the shared MFA repositories
Huduma ya MfaEnrollment ni @Huduma katika kamo-shared-library, hivyo wildcard @ComponentScan ('com.kamo') katika programu hii huijenga iwe au la Huduma ina choc...
Merge duplicate kamo key that crashlooped EmailService
The new EmailService pod has been in CrashLoopBackOff since 16e8a05 — 22 restarts, never once started. snakeyaml raises DuplicateKeyException on a repeated top-...
Harden the kubectl download against flaky egress [skip ci]
dl.k8s.io over the runner's egress intermittently drops mid-transfer: curl: (56) OpenSSL SSL_read: decryption failed or bad record mac which fails the deploy ...
Ongeza vituo vya mwisho vya CampaignController kwa /api / barua pepe/campaigns
The bulk-email workspace/editor call GET/POST /api/email/campaigns etc., but only CampaignService (the @Service) was ever created — no @RestController exposed i...
Remove ambiguous unsubscribe requestMatcher that crashed startup
EmailService mounts two servlets (MVC DispatcherServlet + Spring-WS MessageDispatcherServlet at /services/*), so a bare string requestMatchers( "/api/email/unsu...
Kuacha serialed xoauth2 getter kwamba kuvunja MailCredentials Redis cache
MailCredentials is cached in Redis (GenericJackson2Json). The added `isXoauth2()` boolean getter was serialized as a "xoauth2" property but has no field to read...
Scope KamoMail mailbox/domain listing to the org's own domains (cross-tenant leak)
SECURITY: **************** ran 'SELECT email FROM virtual_users' (and **************** 'SELECT name FROM virtual_domains') with no org scoping, so mailbox/domai...
Persist selected OAuth provider on callback so it stops reverting to KamoMail
Switching an org to Microsoft 365 (or Google Workspace / Zoho) never stuck: the OAuth callback stored tokens and only flipped status on a pre-existing provider ...
Resolve welcome-email logo from the org theme host, not the apex
Header/footer logos in system emails (welcome/verification) were built as **************** — an apex path that only exists on the Kamo marketing site, so for cu...
Constant-time internal-auth secret compare (timing oracle)
Replace String.equals with MessageDigest.isEqual so response latency cannot leak the X-Internal-Auth shared secret byte-by-byte. Fleet-wide sweep of the copy-pa...
Return 422 (not 500) when member has no mailbox
listFolders and other read endpoints threw IllegalStateException from MailContext.toMailCredentials() when a member had no IMAP credentials provisioned, which t...
Don't save transactional NoReply sends to a Sent folder
NoReply is a send-only address with no mailbox, but every transactional send still attempted an IMAP append to its Sent folder, producing a noisy AUTHENTICATION...
Mailbox list API returns UI-shaped ****************
GET /api/email/mailboxes returned the raw entity (providerStatus, lastSyncedAt, no member count). The settings UI reads **************** so status was always un...
Authorize privilege-based mailbox access for read + send-as
resolveForMailbox only granted access via a direct MemberMailbox assignment, so mailboxes surfaced through **************** rights threw NoSuchElementException ...
Preserve original filename and content type on attachment download
The **************** endpoints (both the personal and shared-mailbox controllers) were emitting Content-Disposition: attachment with no filename parameter and C...
Sync members.email when primary mailbox is assigned, changed, or removed
When a mailbox was assigned to a member in a KAMO_MAIL org, members.email was never updated, causing EmailCredentialService to fail to resolve IMAP credentials ...
Mark-as-read for shared and member mailboxes
EmailController.setFlags now accepts an optional mailboxId query param and routes through resolveProviderWithMailbox so member-mailbox flags are set against the...
Handle IMAPInputStream in getMessage to prevent ClassCastException
JavaMail can return an IMAPInputStream instead of a decoded String from part.getContent() when message content has not been pre-fetched. The direct (String) cas...
Use getHasAttachments() to match Boolean wrapper type
**************** is a Boolean (wrapper), so Lombok generates getHasAttachments() not isHasAttachments(). The latter only exists for primitive boolean. Use Boole...
Reset stale hasAttachments on sync and accumulate correctly
- Reset has_attachments=false at start of bulk sync so stale true values from old false-positive MIME detection are cleared before re-indexing - Accumulate ha...
Support fromAddress override on send
Adds FromAddressRequest to EmailSendRequest so the frontend can specify which alias/shared/mailbox address to send from. SmtpOnlyProvider now uses that address ...
Fix attachment parsing and hasAttachment false positives
Three fixes to EmailImapService: 1. toEnvelope: narrow multipart check to multipart/mixed only — plain HTML+text emails (multipart/alternative) no longer sh...
Add com.kamo.z.shared.commerce to EnableJpaRepositories scan
**************** (required by **************** in the shared library) lives under com.kamo.z.shared.commerce but was not included in the JPA repository scan, ca...
Correct field name and type in getFromAddresses response
Backend was sending 'email' key but frontend expected 'address', and sent type 'shared_mailbox' when frontend expected 'shared'. Also fixes hasMailboxes flag wh...
Fall back to org_domains when no email_provider_domains default set
KamoMailDomainValidator was hard-failing with 403 for orgs that have a DNS-verified domain in org_domains but never added it to the email provider domain regist...
Wire up status filter and auto-seed required templates on list
- EmailTemplateController.list() now accepts ?status= param and filters using the existing findByOrgIdAndStatus repository query - Auto-seeds required templat...
Preview uses same auto-vars as sendToUser (org logo + name)
The preview endpoint now injects logoUrl, logoSimpleUrl, and orgName from the org's domain before applying sample content variables, matching the exact variable...
Update EMAIL_VERIFICATION sample vars to match current template
Use expiryText instead of expiryMinutes (renamed variable), and update verifyLink to point to the register subdomain (correct send path).
Preserve DB domain case in NoReply from address
Removed toLowerCase() — the domain already comes from resolvePrimaryDomain which reads org_domains from the database, so case is canonical from DB.
Resolve root domain for NoReply from-address instead of bare subdomain
resolvePrimaryDomain() was returning the first domain entry (e.g. "www") which produced NoReply@www. Now prefers root domains (parent == null) so transactional ...
Add internal.auth.secret to EmailService configmap
InternalAuthFilter was rejecting all service-to-service calls (send, seed-org) with 503 because internal.auth.secret was unconfigured. Matches the same INTERNAL...
Allow transactional send on port-25 relay and auto-seed missing templates
Port-25 SMTP (postfix) requires no auth — transactionalCredentials() was throwing ProviderUnavailableException when user/pass were unconfigured, blocking all ve...
Add dao.repositories to JPA scan so EmailTemplateRepository is found
Same pattern as KamoInitializerService — explicit basePackages overrides default scanning and must include every package that has repositories.
Rename mailboxId→id and providerStatus→status in mailbox list response
Align field names with frontend expectations for member mailbox listings.
Support mailbox switching by resolving IMAP credentials per mailboxId
Added getMailCredentialsByEmail to look up virtual_users by email directly, resolveForMailbox to MailProviderResolver with access verification, and optional mai...
Like what you see shipping?
All of it arrives in your workspace on its own. Start on the free plan and read this page again in a month.
