Stop the logo pass starving backgrounds, and read <picture>
Diagnosed against a real site that returned six logos, brand colours and zero background images. Three separate causes, all ours. The logo pass could eat the e...
Seed MANAGE_WEBINAR_SCHEDULES from MANAGE_SUPPORT_SETTINGS
Until availability moved onto the member, MANAGE_SUPPORT_SETTINGS holders were the one group who set it — they authored the dated slots on each webinar type, wh...
Keep room for the compact mark among a pile of lockups
A straight "best six by score" is the wrong cap for this, and the reason is the shape of real sites: a company's wide lockup appears in the header, in JSON-LD a...
The clinical API — patients, schedule, chart, and the gate in front
The access rule is the part worth reading. Being logged in is not a reason to see a chart. The overwhelming majority of real HIPAA enforcement is not outside at...
Baseline the desktop probe, which is guarded where the scan cannot see
**************** has been failing on main since ba3f85c. DesktopProbeController#probe is not unguarded — it checks X-Internal-Auth through authorized(auth), con...
An unfinished domain picks a different host, not a refusal
Entering an org was refused outright unless its own custom domain had finished DNS and SSL. That was right while entering meant navigating to https://internal.<...
Expose the scan on /api/security/org/brand-scan
Its own controller rather than a method on OrganizationController, which already takes 25 constructor dependencies. The path still lands under the same prefix, ...
Orchestrate the scan behind one cached, budgeted call
One response rather than three endpoints because it is one scan: the logo must be downloaded before its pixels can contribute a colour, and the colours are want...
Take hero imagery, falling back to carousel slides
The fallback is conditional by design. A site with a hero has already chosen one picture to represent itself; sweeping its carousel too would bury that choice i...
Reconcile brand colours from five independent sources
Five because each is authoritative on a different kind of site and silent on the rest: a design system states everything in CSS variables, a PWA in its manifest...
Find a site's logo from twelve independent signals
Twelve rather than one because there is no single place a logo lives. A Shopify store declares it in JSON-LD; WordPress puts it in a header img; a React app ren...
Index a site's CSS and parse its page once
A declaration scanner rather than a CSS grammar: the scan has three questions (custom properties, background images, colours) and none needs the cascade resolve...
Start the 3-day trial on first entry into the org, not at creation
Creating an org and opening it are different events. An org can be created and left sitting, and one nobody has walked into must not have spent its evaluation —...
Rank colours perceptually rather than by RGB frequency
Counting RGB values across a stylesheet elects the site's off-white page background every time -- it is the most-used colour on almost every site ever built. Wh...
Read real image dimensions from the bytes
Nothing here trusts the page. An HTML width attribute is a layout instruction, not a fact about the file -- a 2400px logo is routinely declared width="180" and ...
Ask for different work for a bug and an enhancement
A bug says 'find out why and fix it' and points at the logs; an enhancement says 'this already works, build the better version' and points at the current behavi...
Guarded outbound fetching for the website brand scan
The member types the URL, so every request this issues is an SSRF primitive, and there is no egress NetworkPolicy and no forward proxy in this cluster -- Redis ...
Remind the dispatched session to push its work
A session that fixes a bug and leaves the change sitting on the dev machine has not fixed anything anybody else can see. The sentence is split out of the entit...
Start the 3-day trial when the org is created
The trial was seeded pending and started on the owner's first sign-in, because an org was unreachable until its DNS verified — starting the clock at creation wo...
Internal probe endpoint so the access canary tests the real path
The KamoDesktop canary needs a link minted the way the icon mints one. It holds the same signing key, so it could build its own — and would then be testing itse...
Rebuild now the shared library has AiDispatchRequest
The previous build failed on a class that had not been pushed to kamo-shared-library (fa243ae). Nothing to change here; this just re-runs the build against a li...
Answer whether a web alias is still free
The org-creation wizard is eleven steps long and the alias was only checked when the form was submitted, so someone who picked a name another organization alrea...
Stop the SSO token expiry from emptying a live desktop session
"The requested connection does not exist. Please check the connection name and try again." on a KamoDesktop reconnect was not a missing connection. It was the S...
Send To AI, gated on MANAGE_SYSTEM_BUGS
The class carries @RequirePlatformRight, so the interceptor resolves the caller and refuses before any handler runs — a per-method check would have been one for...
Turn the workspace picker on
A member of several organizations now chooses which one to open after their password, instead of it being chosen for them by the hostname. This is the switch th...
Send the member to the workspace host, not a relative path
SignInCompletionService returned a relative /validate?otk=. That resolves against the host that served the SIGN-IN — login.<apex> — and kamo-login has a /valida...
Turn the workspace picker back off
The redirect is wrong. SignInCompletionService returns a relative /validate?otk=, which resolves against the host that served the sign-in -- login.<apex> -- and...
Turn the workspace picker on
A member of several organizations now chooses which one to open after their password, instead of having it chosen for them by the hostname. This is the switch t...
Accept an explicit org, not only the host
Registration bound the account to whatever organization the host resolved to. On register.kamocrm.com — the signup page for every organization with no register ...
Delete the "first organization in the database" fallbacks
Seven blocks across five controllers ended their org-resolution with, in effect: if the server name is localhost or a bare IP, return whichever organization the...
Keep an alias unique platform-wide, on both write paths
Creation checked the alias only within one security provider, and PUT /org/{id} checked nothing at all — it applied whatever it was given. Both were reasonable ...
Refuse the AI training corpus exports for PHI tenants
ANALYTICS_EXPORT is blocked because exports leave the boundary with no de-identification. Both training exports already had a fail-closed audit — there IS a rec...
Send the tree on the app-catalog endpoint too
Branch Types → Allowed Apps builds its editor from /features/app-catalog, not from the tenant features listing, so it was the one surface that could not see whi...
Rebuild against the Affiliate Sales Scoreboard rights
No source change here. SecurityService is what enumerates RoleRightType.values() to serve the rights editor (SecurityRoleController) and to populate a grant-all...
Serve an org's branding to a screen with no session
Login, registration and the meeting lobby paint an organization's colours before any session exists. They knew which one from the hostname, which is exactly wha...
Refuse an alias that names more than one organization
byAlias took LIMIT 1 over orgs.alias, which has no uniqueness constraint: the create path checks it per security provider and PUT /org/{id} does not check it at...
Serve the app tree, and fix the unguarded auto-enable
FeatureController now sends parent/children/isParent on every serialized app so the client can render children nested under their parent. Served rather than mir...
Resolve the domain on /org/current server-side
internal.kamocrm.com failed to render with "Invalid organization data: missing 'id' or 'domain'". Organization has no `domain` field -- it has a `domains` colle...
Entitlement-check every vertical, not just the gated two
Every CommerceType now names its own child app under POS, so the isGated() short-circuit that waved retail, rentals, service, subscriptions and procurement stra...
Give the workspace path the same post-login work as the host path
Two gaps found by re-reading the original login tail against the new one, before enabling the picker. The form-encoded /login endpoint built its LoginRequest w...
Let the shell fetch its org from the session, not the host
kamo-internal's root layout reads the Host header, strips "internal." and fetches /org/domain/<that>. On a host serving one tenant that is the same question as ...
Let a member sign in without a host naming their org
Adds the second half of the split: a sign-in that names no organization authenticates the user, then asks which workspace. POST /api/security/session/select tur...
Separate the credential check from org authorization
Signing in was two questions fused into one query. "Are these credentials good" is answered by the users row -- the password lives there, so it was never org-sc...
Stop requiring DNS verification to sign in
OrgHostResolver.resolveByDomain filtered on od.is_dns_verified = TRUE, so a domain row that existed but had not finished verifying resolved to nothing and login...
Attach stack traces to their log line, and fix levels I got wrong
printStackTrace() writes to stderr directly, so 186 of them survived the println sweep untouched — still unconditional, still unattributed, and now detached fro...
Finish taking SecurityService off System.out/err
The remaining 853 println calls across 43 files, none of which could be turned off: println has no level, so every one of them printed on every code path that r...
Take CNAME verification off System.out
With the session and Hibernate noise gone this was the largest recurring source left in the pod log: every CNAME lookup drew a seven-line box on stdout. That wa...
Take session lookup off System.out, and its *** id with it
After the previous commit removed the Hibernate session-metrics blocks and MemberController's per-request tracing, this was 88% of what remained: every session ...
Stop the pod log rotating faster than it can be read
Roughly 4,600 lines in four minutes, so the retention window was a couple of minutes: chasing a certificate problem on 2026-08-22 the probe output had already r...
Verify-dns answers for the present, not for a stored flag
The endpoint skipped probing entirely when ssl_confirmed was already true, on the reasoning that under the all-hosts rule it could only have been set when every...
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.
