Stop handing an alias row over as the org's domain
**************** walked org.getDomains() filtering only on isActive. That collection holds the org's root domain AND its alias rows -- api, login, theme and the...
Correct the sign.pink finding
The inventory called SignLinkBuilder's sign.sign.pink fallback a "foreign apex" and I recorded it as one. It is not: sign.pink is a Kamo-owned organization (id ...
Design the EHR/EMR vertical and the parent/child app catalog
Two specs from the pre-build analysis of ~30 repos and the 2026 ambulatory EHR market. The program design records the six decisions that bind every sub-project...
Make an org without a domain enterable
Entering an org meant navigating to **************** With no domain that URL rendered as the literal **************** so Enter was disabled and owners were rout...
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 ...
Write a scheduled-support description as rich text
The description on **************** was a three-row textarea. It is the /kb/new writing surface now, minus pictures and the media library — those upload into KB...
Add the workspace picker
A sign-in that names no organization now comes back with a list instead of a session. This is the client half: /api/login passes that payload through (before th...
Carry the type description in both of its shapes
The description is a Lexical document now, so the service stores the plain-text mirror the client sends alongside it and hands readers whichever shape they can ...
Rich-text description columns on webinar_type
Mirrors the shared-lib entity change. Two things the table needs before MediaService maps them. The description column has to be unbounded: it holds a Lexical ...
Store the type description as rich text
The description on **************** is a Lexical editor state now rather than a line of prose, so the column has to be an unbounded TEXT bound as a long varchar...
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...
Take the chat-core that stops a sent message showing twice
@kamo/chat-core 0.1.6. The conversation reducer now adopts the pending optimistic row when the member's own message comes back over STOMP, instead of appending ...
Remember the last workspace a member signed in to
users.last_org_id backs the workspace picker. An organization is no longer named by the hostname, so a member who belongs to several chooses one after signing i...
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...
A file may be dropped anywhere on a chat window, not only on the input strip
The drop target was the composer surface alone — a few dozen pixels at the bottom of a window that is otherwise all messages. A screenshot let go over the conve...
Plan the host-independent tenancy rollout
Six phases, each independently shippable, because pushing deploys: the host path keeps working until Phase 6 removes it. Phase 1 is the crux and is additive --...
Design host-independent tenancy
Org identity comes from the hostname today: OrgHostResolver.resolveByFqdn turns <alias>.<domain> into (orgId, providerId), and its SQL requires od.is_dns_verifi...
Stop RingCentral OAuth signing white-label tenants out
Two faults, both on the same hop, and each one hides the other. RingCentral rejects every scope value this app can ask for — "invalid_request / Parameter [scop...
Show cents on an add-on rate that has them
Additional Storage is $2.50 per 10 GB block, and the catalog stores that as a number — so 2.50 arrives as 2.5 and rendered straight into the card it read "+$2.5...
Settle Additional Storage at $2.50 per 10 GB block
Confirms the block size and rate as $2.50 per 10 GB rather than the $4 per 25 GB the production row had drifted to. The seeder, the catalog row, the Stripe pric...
Price Additional Storage as $4 per 25 GB unit, FLAT not metered
Additional Storage is bought by the unit, belongs to no plan on any tier, and is not seat-based. The seeder still described it as $2.50 per 10 GB block, which i...
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...
Correct what the add-on binding gap actually was in production
The previous commit's reasoning was inferred from the loader, not measured. It claimed every KamoCRM add-on row had service_type NULL and that hosted mail was t...
Stop offering Business the add-ons it already includes
Choosing Business still presented video conferencing, the AI assistant, the HR system and e-signatures as things to buy, even though the plan bundles all four. ...
Bind add-ons to the app they sell, and stop Business granting mail free
**************** is the column **************** matches on. KamoCRMPricingLoader never wrote it, so every KamoCRM add-on row has it NULL and that comparison can...
Stop selling add-ons the chosen plan already includes
The catalog has always recorded which add-ons a plan bundles, in **************** — Business and Enterprise carry video conferencing, the AI assistant, the HR s...
Let the SSL check lower the flag, not only raise it
checkDns only ever latched sslConfirmed to true. A domain whose stored flag was set under the old any-one-host rule therefore kept a green SSL card and an unloc...
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...
Re-verify domains already marked SSL-confirmed, and heal them
findAwaitingVerification filters confirmed domains out by design, so once ssl_confirmed was set nothing ever looked at that domain again. That was fine while th...
Open the swatch's own colour window, not the first on the page
The swatch reached its hidden <input type="color"> through document.getElementById on an id built from the picker's translated label. A label is only unique ins...
Stop the colour group fighting the colour you are choosing
ModernColorGroup was controlled and uncontrolled at the same time. It kept local state for every slot, mirrored the `value` prop into that state in one effect, ...
auto-cert: let the junk-cert cleanup actually see the junk it was written for
cleanup_orphan_certificates() listed with '-l auto-cert.kamo/fqdn'. That label is newer than the scanner certs the function exists to remove, so the selector ex...
Read the new org's id when it arrives quoted
The last step of the wizard appeared to do nothing, and clicking Create a second time answered that the organization already exists — because the first click ha...
Make post-commit org setup actually write, and heal the orgs it missed
A @Transactional method called from **************** does not open a transaction of its own. Spring fires those callbacks from processCommit BEFORE cleanupAfter...
auto-cert: issue an org's certificates as a batch, not one every 90 seconds
A new organization needs a certificate for all eleven of its hosts, and each one is a separate cert-manager Certificate. The loop waited for each host to become...
Confirm SSL only when every org host has its own certificate
Certificates are minted one per FQDN -- a single dnsName each, so the leaf CN matches the hostname the browser connected to -- and auto-cert works through an or...
Show per-host certificate progress, not a single spinner
Certificates are issued one per subdomain, and they do not all land at once, so the SSL step now reports how many of the org's hosts are live and names the ones...
Lift the wallpaper and widget shortcuts above the media pod
The profile header band laid "My Wallpapers", "My Widgets" and the media chrome out as one wrapping row, so the two shortcuts read as a fourth switch sitting al...
Point frame media at an endpoint that actually serves it
Every photo and video in a picture frame rendered as a broken image. The URL pointed at **************** I took that for a general imaging proxy — it speaks HT...
Serve frame media ourselves, with Range
Every photo rendered as a broken image. The URL pointed at MediaService's **************** which I took for a general imaging proxy because it spoke HTTP Range....
Drop the greeting block and put the masthead on one row
The launchpad's masthead carried a kicker, a "good afternoon" heading and a status line of apps/title/trial/date beside the logo, and the member's own controls ...
Move Binders and Uploads to the right of the strip
The strip now separates what a document manager IS from where it takes you. Left: Originals, Modified, Common — three views of the same documents. Right: the fo...
Make the Spirit Animal visible, and stop the palette vanishing
Two reports, two different mistakes of mine. THE PATTERN WAS INVISIBLE. It painted behind the pane, and every pane surface — FeatureCard, LauncherParentCard, W...
Rebuild 533bbeb4 now the Expression strings are in the dictionary
The image build clones kamo-translation-dictionary and copies it over messages/, then fails if a component names a key those messages do not define. 533bbeb4 wa...
Set the headline in quotation marks
Quotes around the banner line, as asked — but rendered from CSS (`open-quote`/`close-quote` against `quotes: auto`) rather than typed into the string, for two r...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.