Quote and borrower-portal links only name a live domain
QuoteEmailClient settled for any active domain, verified or not, so an org still setting up its white-label domain had quote links mailed to sign.<that domain>....
An organization's .svg names show its own art, not the platform's
A new organization's folder is seeded from the base theme, so it starts out with KamoCRM's art under img/logo-full.svg and favicon/favicon.svg. An organization ...
An org without the app must not accrue achievements either
The award gate asked for VIEW_ACHIEVEMENTS and stopped there. Rights are not entitlement-filtered, so that right stays granted β and keeps answering true β in a...
Refuse to award a member whose security type excludes it
AchievementController checks VIEW_ACHIEVEMENTS on all four of its endpoints, and that read like the rule being enforced. It was not: it is the rule enforced on ...
The same-commit-rebuild guard could never fire, so every dispatch failed
Rebuilding this service without a commit of its own β a workflow_dispatch, a re-run, or a kamo-shared-library change it must pick up β has been failing at deplo...
A pane lock survives the editor alt-tabbing away
The client heartbeats its pane lock every 15s and the sweeper released any lock unheard from for 45s β three missed beats, which reads like a generous margin an...
The baseline award was two transactions, not one
`ensureBaselineFor` calls `ensureBaseline` on `this`, and Spring's transaction advice is a PROXY β a self-invocation does not pass through it, so the inner REQU...
A callback can carry the link to the call
The lead page composes a callback in the calendar's own dialog, and every field that dialog collects is meant to survive the trip through here. meetingUrl did n...
Wire the god guard into the identity-rights test's policy
UserAccountEditPolicy took a third constructor argument and this test still built it with two, so test-compile failed β and CI runs `mvn package -DskipTests`, w...
Keep the dispatch's status write in step with the denial reason
Two services move a system bug's status, and only one of them knew about the new denial_reason column. A dispatch applying IN_PROGRESS to a report that had been...
Log a referral code that arrives with no visitor guid
A signup carrying a marketing code but no visitor guid had its referral dropped in complete silence β the branch that publishes the attribution event is gated o...
Publishing an application opens its chat key to the page it is on
The support widget on a published application loaded with Kamo's default config instead of the org's: it auto-opened on desktop with auto-open turned off, wore ...
Close the last two catalog-bump gaps, and stop echoing exceptions
Two reads and nine response bodies, both halves of the same report. SessionRefreshController loaded the member one statement before the rights read that was fi...
Stop handing the database's own error text to whoever is signing in
The catch-all at the end of login() put e.getMessage() in the response body, and that body is rendered verbatim on the sign-in screen. On 2026-09-05 the excepti...
Send the application receipt after the commit, not inside it
The submit handler is @Transactional, and it was calling EmailService inline β blocking HTTP into blocking SMTP with a 30-second read timeout, while holding a d...
Stop stranding phones on a rotation they never received
KamoMobile was signing itself out at random, most visibly across app updates, and the exchange endpoint was doing it. Three separate ways: The secret rotated F...
Merge the digest output instead of declaring a second outputs block
The previous commit added an `outputs:` key to a job that already had one. Forgejo's parser rejects a duplicate mapping key and skips the workflow entirely, so ...
A rebuild of the same commit deployed nothing and reported success
The image is tagged with the commit SHA, so rebuilding the same commit produces an identical image reference. `kubectl set image` then changes nothing, the Depl...
The lockout warning promised the wrong rung, and the wrong count
The strike meter on the sign-in screen was telling people something the server then did not do. tierFor starts the five-second delay at the SIXTH failure, but a...
A catalog-version bump must not read as "this host has no organization"
Org resolution funnels every lookup through one query helper that swallows the exception and answers Optional.empty(). For an organization that genuinely is not...
Restore SpfAdvisoryService, which main no longer compiles without
45829a6 deleted this file. Nothing in that commit wanted it gone β it was a staged deletion of mine that a concurrent `git add -A` picked up, and the same commi...
Name the constructor Spring should use, or the context will not start
My own regression, and the second context-startup break on this service today: Error creating bean with name 'terminalTicketRegistry': Failed to inst...
Let Spring pick the terminal registry's constructor
SecurityService has been crash-looping on every new pod since the shared handoff store landed: "No default constructor found" for TerminalTicketRegistry, which ...
Resend the verification e-mail under the organization the member is actually on
Nobody at a white-labelled organization has been able to ask for a second verification e-mail. The screen said the letter was on its way every time, and no lett...
Share terminal tickets across pods, or half of them are refused
Opening a terminal fails about half the time with "The server refused the terminal connection. Your ticket may have expired", and the log says: Terminal ha...
Answer "who owns this account" about the member, not the reader
The member payload now carries accountOwnedByThisOrg. The console used to work this out for itself by comparing the READER's securityOrgId to the current organi...
Restore the @Lazy that stops the context depending on itself
Every SecurityService pod built since 4bd25e7 fails to start: The dependencies of some of the beans in the application context form a cycle: βββ->βββ ...
The operator write must answer in the same shape it was asked in
The console adopts this response as its new state. It returned the address and `canChange` but not `mode` or `isSelf`, so after a successful operator write `mod...
A god may fix their own address from any workspace
The tenancy rule refused a god-eligible caller on their OWN record whenever their session was in an organization other than the one that owns the account. That ...
Make a System User session terminal, so entry cannot chain
The System User is god by construction, so every session minted for it carries GD and offers break glass. That is fine only because the identity does not compos...
Say whether an org HAS a logo instead of guessing a URL for one
Every "which networks can I reach" row carried logoUrl and logoSimpleUrl derived from the org's alias alone, for any org whose alias was path-safe β which is al...
Read the account inside a transaction before sending a reset
sendPasswordReset had no transaction, so the User came back detached and `securityProvider` stayed an unresolvable lazy proxy. Working out which organization's ...
Let a rollout finish what the old pod was doing
Deploys replaced the only pod of each service with nothing to catch the requests in flight. Three settings, applied across the fleet: - preStop sleeps 10s befo...
A member's work address must name them without a mail provider
Forgot-password resolved the address typed on the form against two sources: the account e-mail, and the org mailbox flagged Primary on the member's settings. Th...
Only the member or god may set a member's password
MANAGE_MEMBER_SECURITY passed the change-password gate, which made "can administer members" and "can take over any member's account" the same right. They are no...
Every terminal handshake was 403 behind the edge proxy
The reported symptom was a terminal that opened and immediately said "Disconnected", with nothing to act on. Reproduced end to end: the ticket mints fine (200) ...
Assert the mask obfuscates, and skip the gate for callers with no leads
LeadFieldMaskTest still expected a null e-mail. The mask now obfuscates rather than blanks β a blank field reads as "this lead has no e-mail", which is a differ...
A refused terminal handshake answered 200
Found by probing the deployed endpoint, not by reading it. Returning false from a HandshakeInterceptor aborts the handshake but sets nothing on the response, so...
Do not mail a tenant its own domain until that domain is verified
A domain typed into the create form is a CLAIM, not a host. Ownership is proven by a DNS TXT record afterwards and a certificate is issued after that, so betwee...
Stop a failed welcome email from destroying the member
MemberCreationService documented steps 4-6 as best effort, and for the emails it was not true. sendVerificationEmail is @Transactional, so it joined the member-...
Open a workspace on its OWN domain, not the host the sign-in was served from
A member who signed in on login.kamocrm.com, picked a workspace from the chooser, and landed inside it β on internal.kamocrm.com, wearing the platform's hostnam...
Stop dropping setupFee on save, and unbreak the build
withTermsUnchanged rebuilds UpdateQuoteRequest positionally for a member who may not rewrite the terms. setupFee was added to that record and not added here, so...
A share link's email was dropped twice before it could be sent
"I created a link and got no email." Two independent silent drops, both of which return a swallowed false that the panel rendered as plain success. 1. kamo.ema...
Accept a blank status label as the removal it is
The PUT is the org's complete set of custom labels, so it has to be able to carry an emptied box. Two things stopped it: an empty list was rejected as a malform...
The agent health preflight must fail in seconds, not twenty
health() inherited the client's 20-second default. It is a liveness check in front of every KamoDesktop launch, and when the dev machine ran out of memory on 20...
Stop refusing a domain because somebody else typed it first
Creating an organization 409'd on any existing row for the domain, which meant the first organization to enter "acme.com" locked out every other one β including...
Give the generated favicons the same revalidation requirement as every other theme asset
Replacing an org's simple logo updated every surface that reads img/logo.svg and left the browser tab on the old mark. Generation was never the problem: provisi...
A transient database fault must not 500 the request
The lead Communications tab reported "We couldn't load this lead's communications" for org 1168485648209608710 on 2026-08-28. The endpoint returned 500 five tim...
Require an open god-mode window, not eligibility alone, for CONFIGURE_SYSTEM
hasRight was granting the bypass on the raw GD session flag, which SessionPlatformFacts's own javadoc names as the exact anti-pattern it exists to prevent: GD i...
Make seeded and uploaded branding revalidate, so an org stops being served the base theme it was seeded with
A new organization is seeded with the base theme β the platform's own logo and backgrounds copied to the new org's keys β and those keys never change when the o...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.