Confidential thread markers and EMAIL_THREAD labels — PUT …/thread-confidential on member and shared mailboxes, one audited writer, threadKey and threadConfidential on both message GETs, POST **************** the org graph (KamoCollab CP05 Task 20)
EmailThreadKeys is the one thread-naming rule **************** now calls it): root Message-ID, UUIDv5 thread id, and the EMAIL_THREAD key without angle brackets...
Declare the disclosure ledger recorder, paired with its repository scan (KamoCollab CP08 Task 12)
DisclosureLedgerConfig declares DisclosureRecorder; EmailServiceApplication's @EnableJpaRepositories gains **************** in the same commit. DisclosureLedger...
The two contact notification kinds, and which members an internal address reaches (KamoCollab CP04 Task 11)
CONTACT_REQUESTED and CONTACT_DECIDED are gated on ACCESS_CHAT and listed after MENTIONED. POST **************** ({orgId, addresses[]}, at most 100, KAMOAI_INTE...
The CP05 DDL for per-thread Confidential markers and the thread_id indexes
KamoCollab CP05 Task 4 (spec 5.1): public.email_thread_labels keyed by (org_id, thread_key), ix_thread_messages_thread and the partial ix_search_index_thread. H...
Labels page in one date order with an exact total; Microsoft 365 folders are counted
Labels - A label was paged through the join table by UID and each page sorted by date afterwards. UIDs only order mail within one folder and a label spans f...
Message listings say how many messages they hold (X-Total-Count)
The message list in kamo-internal can now be browsed by pages, and its pager needs the listing's size for "51-100 of 1,240" and a Last button. Every listing ans...
Lower-case NATS mailbox subjects for live updates
Mixed-case addresses like Sales@kamocrm.com published on different NATS subjects than subscribers used, so watched mailboxes never refreshed.
A composed message carries its meeting, and calendar edits ask before telling guests
- /api/email/send takes meetingInvite: the event is filed on the sender's calendar in the same transaction, its ics rides on the composed message with an X-...
An AI hire no longer waits forever on an email provider that was never connected
An org that chose Google Workspace (or another third-party provider) but left it at PENDING_SETUP, or switched it off, can never produce the AI's mailbox, so th...
A provisioning race Yugabyte reports as a serialization conflict no longer 500s the calendar widget (SP98 T26)
listCalendars provisions the shared and the personal calendar find-then-create, each in its own transaction, and treated a lost race as done only when it surfac...
MailSearchWiringTest registers the recipient-domain validator EmailController now takes (SP98 T26)
ec2c638 gave EmailController a RecipientDomainValidator; the wiring test's context did not have one, so it has failed on main since 2026-09-28 **************** ...
Calendar chat lines say they are system lines (KamoCollab CP00 T5)
MediaChatClient.sendDirect now posts "source": "SYSTEM" beside fromMemberId, toMemberId and message: a calendar invitation or cancellation is a notice written i...
A member's new name reaches their mailbox's display name (SP95 T35)
POST **************** {previous, current} -> {updated}, behind the KamoAI internal secret like provision-mailbox. The mailboxes the member holds as primary whos...
The AI signature disclosure names the company by its spoken name
An AI member's email disclosure filled {organization} with the organization's title (its letterhead), so Luna's mail said "an AI member of the KamoCRM Inc. team...
Key the cross-root retry on alias-domain membership, not root-or-not
Round 3 review (email-fix-review-3.md) confirmed round 2's BLOCKER fixed (walked sage@kamouniverse.com through both the app code and Postfix's own platform-alia...
Retry every owned domain unconditionally, and drop the +tag fold
Round 2 review (email-fix-review-2.md) found the round 1 fix for MAJOR #3 (multiple root domains) still reopened the original bug: - BLOCKER: kamouniverse.com ...
Close three false-refusal paths the review found in RecipientDomainValidator
Independent review of D-luna-1 (email-fix-review-1.md) found two BLOCKERs and a MAJOR, each a real false refusal of legitimate mail, demonstrated from code alre...
Refuse a guessed recipient at the org's own domain before it reaches the mail server
Luna (AI member) asked to email the owner and guessed **************** instead of looking up the real address (sage@kamocrm.com). kamouniverse.com is one of the...
The KamoMail domain check reads once a minute, and rows off an org's domains leave every list
SP99-T1-f, finished (SP98 Task 8). SP99's final review already refuses a KamoMail binding whose address is not on a domain the organization holds (95c58af); thi...
An AI organiser's invitations carry the AI disclosure its email does
Event invitations wear no compose template, so an invitation, update or cancellation organised by an AI member carried none of the disclosure its email carries ...
A late delivery still raises NEW_EMAIL and buzzes the phone; only IDLE is judged by age
NewMailAnnouncer's 30-minute Date-header gate, there for IMAP IDLE reconnect replays, also dropped Dovecot push arrivals, which report a delivery (MessageNew) a...
A template icon is drawn through Batik once, not on every send
InlineImageTransport memoised a drawing only within one message, so every send that wore the compose template decoded and transcoded the same handful of social ...
A From's mailbox id picks a signature only when the caller may send as it
The compose identity took fromAddress.id at its word: E1's send and E4's render check the From's address and the sending mailbox, never the id, so a member coul...
Stopping the repeat from an edited instance leaves that instance as it showed
SP98-F-2. The count-exhausted way into a split that leaves one event was pinned by fdd44fc; this pins the other way in, an explicit stop from an edited instance...
The groupware provider finds a series by its UID as its master
SP98-F-3. CalendarService.findEventByUid read "the one row with this UID" in a calendar, and an edited instance of a series is a row of its own under the series...
An inbound invitation answers with its summary, as the account that received it
SP98-F-5. **************** returned the CalendarEvent entity, whose attendees point back at their event, so writing it recursed until the stack overflowed; it n...
Address books are found, and reached only by their owner with the web's rights
SP98-F-4. CardDAV wrote resourcetype as empty text for the home, every book and every card, so no client ever found an address book; a book is now *************...
A calendar edit changes only what it sends; a new one without a zone takes the member's
The calendar create and edit bound the UserCalendar entity, whose defaults (zone UTC, visible, sort order 0) stood in for every field a body left out. KamoMobil...
Personal-mailbox connects send the api.kamouniverse.com redirect
The owner has added **************** to the platform Google and Microsoft consoles, keeping the api.kamocrm.com one. Both hosts serve the path.
The OAuth-default guard reads the platform API host from the shared constant
Follows 8f68912. The guard asserted that the YAML defaults use the paths the settings screen advertises, but it hard-coded the api.kamocrm.com host. The default...
The platform's Gmail, Microsoft 365 and Zoho connects send api.kamouniverse.com redirects
The owner added **************** (Google) and **************** (Microsoft, Zoho) to the platform apps' consoles and kept the api.kamocrm.com ones, so both are v...
The platform organization's letters are signed KamoUniverse, not its legal name
The platform org's title is the company's legal name, "KamoCRM Inc.", and it stays in legal text. Its letters now carry the brand, its short title "KamoUniverse...
An alias goes only to a member of the caller's own organization
POST /api/email/aliases took any memberId: an administrator could create an alias in their organization that forwards to another tenant's member, and read that ...
Only an open god-mode window bypasses a tenant right, never GD alone
Every EmailService rights check let a session with GD through ("GD passes everything"), but GD is god-mode ELIGIBILITY, permanently true for the operators who h...
EmailService sends, links and names the platform as kamouniverse.com
The platform's primary apex is kamouniverse.com; kamocrm.com stays served in full. What EmailService writes today now names the primary apex, and what it recogn...
KamoMail binds only an address on a domain the organization holds; the provider needs its right
The cross-tenant read path parked as SP99-T1-f. A self-registered organization could point its provider at its own IceWarp or Exchange (PUT /api/email/provider ...
Indexing a listed page can no longer fail the listing
recordSyncedPage was @Transactional and caught every exception of its own. The index repositories join the caller's transaction, so a write that met a concurren...
Basic sign-in asks SecurityService's real credential check
DavAuthFilter posted Basic credentials to **************** which never existed (405), so every username-and-password sign-in from iOS or DAVx5 was answered 401 ...
A mail domain, and every account on it, belongs to the organization that proved it
The KamoMail server's virtual_domains and virtual_users are shared by every tenant, and nothing stopped one tenant from writing to another's part of them: - PO...
OrgPrimaryDomain may read an organization's domains
It is the one rule the no-reply sending domain is chosen by, for the three files already allowed here; it builds no link. Completes e43179d.
An organization's NoReply domain is chosen by name, not by row order
The campaign From, the transactional Reply-To and the provider default domain each took "the first root org_domains row". Organization.getDomains() is unordered...
A meeting invite answers with a summary, not the entity graph
Sending or updating a meeting invite answered with the CalendarEvent entity. Its attendees point back at their event, so Jackson recursed until the stack overfl...
A composer's meeting invite goes into the organizer's own calendar
MeetingInviteController handed MeetingInviteService.sendInvite the session's member id in the user-id slot, and sendInvite looked the calendar up with findByUse...
A signature's icons do not put a paperclip on the message
"kamo Marketing teaser" listed with a paperclip and opened with nothing to download. It has no file: the two parts it carries are the 14px phone and email icons...
A calendar message's plain-text part says what its HTML says
The text/plain alternative of every calendar message was "You've been invited to: " + subject, so an instance cancellation read "You've been invited to: Cancell...
Let the WebDAV verbs through Spring Security's firewall
StrictHttpFirewall admits only GET, HEAD, POST, PUT, PATCH, DELETE and OPTIONS, and answered every PROPFIND, PROPPATCH, REPORT, MKCOL and MKCALENDAR to /dav/** ...
A moved series keeps its days, its end and its exceptions; a lead callback never repeats
A whole-series move (ALL, or THIS_AND_FOLLOWING) now carries the rule with it (spec D-9): UNTIL moves by the same local step, so the last instance stays; a week...
Dovecot push authoritative for KamoMail; NEW_EMAIL no longer self-raisable
POST /api/notifications/self is removed with SELF_RAISABLE: NEW_EMAIL is raised by NewMailAnnouncer and CHAT_MESSAGE by MediaService, so a browser has nothing l...
CalDAV reads are authorised, bodies are generated, and a phone's PUT is parsed
Calendars are listed as calendar collections of events, resources are named by the UID their calendar-data states (encoded in hrefs), writes pass the read gate,...
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.
