Lock Tailwind dark mode variant to class-based, never OS media query
Tailwind v4 defaults dark: utilities to respond to prefers-color-scheme (OS preference). The @variant override ensures dark: classes only activate when a .dark ...
Use inline filter style on logo link for reliable white rendering
Tailwind filter classes on img elements are inconsistent across mobile browsers. Inline style on the anchor element guarantees brightness(0) invert(1) applies e...
Force dark color-scheme so browser never applies light mode rendering
Changing from 'light only' to 'dark' in globals.css, generateViewport, and the html element style — this stops the browser from rendering SVGs and native UI in ...
Prevent logo from rendering behind nav row on mobile
The div wrapper with CSS filter created a new stacking context that conflicted with the header's backdrop-filter on mobile browsers, causing the logo to paint b...
Resolve scrollbar and mobile clipping on JumpToBar and PreviewBanner
- Add overflowY: hidden to JumpToBar inner container to prevent vertical scrollbar - Allow PreviewBanner content to wrap on mobile so text isn't clipped on port...
Replace space-y-4 with inline flex gap on footer link lists
Tailwind space-y-4 uses :where() (specificity 0) which races with CSS loading on hard refresh causing inconsistent vertical spacing. Inline styles are embedded ...
Set Traefik externalTrafficPolicy=Local to preserve client IP
With the default "Cluster" policy, kube-proxy SNATs external traffic to the node IP before forwarding to Traefik. That caused Traefik to see the node IP as the ...
Add missing requestIntegration i18n key across all locales
The "Submit a request" button under the "Need a custom integration?" CTA on /integrations was falling back to the raw key **************** because the key wasn'...
Backlink target to www.kamocrm.com (canonical host)
The PoweredByKamo component shipped pointing at apex https://kamocrm.com/ but the canonical kamo-marketing host is https://www.kamocrm.com/. Updating all three ...
Clean up legacy analytics deployment on each CI run
Delete old analytics deployment, service, and ingressroute that conflict with the kamo-analytics deployment managed by its own CI/CD pipeline.
Strip X-Frame-Options header in iframe-compatible middleware
Set X-Frame-Options to empty string to remove the SAMEORIGIN value that was being inherited, allowing CSP frame-ancestors to control iframe embedding instead.
Reset document.title in rebrand() when React overwrites it
React re-applies RSC payload after hydration, resetting the title to "analytics". Add a title check inside the MutationObserver callback so it gets corrected ev...
Replace "analytics" text nodes with "Kamo Analytics" instead of clearing
Use TreeWalker to remove all "analytics" text nodes from rendered DOM
The workspace nav title "analytics" comes from JS bundles loaded after auth, rendered as a text node next to the logo SVG. Uses TreeWalker to find and clear all...
Add guarded MutationObserver for client-side rendered logo/text
Login page and dashboard content is rendered by Next.js JS bundles, not in the HTML body, so sub_filter can't reach it. Adds a script with a busy-flag-guarded M...
Redirect favicons at nginx level, fix RSC payload escaping
- Serve favicon via nginx 302 redirect instead of trying to rewrite HTML/RSC payload href strings (cleaner, always works) - Fix RSC title replacement with pro...
Use pure sub_filter replacements instead of MutationObserver
MutationObserver caused infinite loop (DOM change -> observer fires -> DOM change). Switch to direct nginx sub_filter string replacements targeting both static ...
Load analytics by default and include www subdomain in allowed domains
- Track all visitors by default since analytics is cookieless/GDPR-friendly - Only disable if user explicitly denies analytics in cookie preferences - Add www.k...
Comprehensive analytics rebranding for login page and dashboard
- Replace title dynamically (handles "Login | analytics", "Dashboard | analytics" etc.) - Replace "analytics" h2 text on login page with Kamo logo image - Repla...
Use MutationObserver to rebrand analytics client-side rendered UI
analytics is a Next.js app that sets title/logo via JavaScript after initial HTML load. Replaces static sub_filter approach with injected script that uses Mutat...
Fall through to next provider when translation equals source text
Bergamot was returning the source text unchanged for single-word nav items, acronym phrases (API, CRM), and compound words (Change Log) while reporting success ...
Populate DNS statuses when already confirmed; show org name prominently throughout
Delta translation engine + wire all hardcoded UI strings to i18n
Translation system: - translate.ts now operates in delta mode — only keys that are missing or still identical to English are sent to the API; existing transla...
Suppress Chrome auto-translate popup on locale-switched pages
Add translate="no" to <html> and <meta name="google" content="notranslate"> so Chrome does not offer to translate pages the user has already intentionally switc...
Bound translation concurrency to 3 threads to prevent DB pool exhaustion
Unbounded @Async (SimpleAsyncTaskExecutor) created 50 threads simultaneously when the retry scheduler fired, each opening a DB transaction and draining HikariCP...
Three bugs preventing changelog translations from being saved
1. **************** was discarding events because handleWebhook() had no @Transactional — save + publishEvent() ran outside any transaction. Add @Transact...
Query CommitLogs with LEFT JOIN so zero-translation records are caught by retry
The old query selected from CommitLogTranslation — commit logs with no translations at all were invisible to it. Use LEFT JOIN from CommitLog so the HAVING COUN...
Apply word replacement at translation time, not ingestion
Original text is preserved in the DB so that adding a new rule and re-triggering translation is all that is needed to update existing records. Changes: - Chang...
Handle empty API responses and graceful degradation on billing tab
- billingApi get/put helpers now safely handle empty response bodies instead of crashing on res.json() with empty input - PlansBillingTab loadData wraps every...
Check TeamMember.isOrganizationOwner flag for owner detection in my-networks endpoint
The previous check only looked at org.getOwner() (user_id_owner column) which can be null for existing orgs. The authoritative owner flag lives on the TeamMembe...
Resolve org/member context from *** session for billing proxy
BillingService controllers require X-Org-Id and X-Member-Id headers. When calling directly (bypassing APIService), these weren't being set. Now the Next.js prox...
Use string type for org/member IDs in network page to handle CockroachDB unique_rowid() precision
Serialize org and member IDs as strings in my-networks endpoint to prevent JS precision loss
Add Next.js API proxy route for /api/billing/*
The billing API client was calling /api/billing/* as a relative URL, which hit the Next.js server instead of the APIService gateway. Added a catch-all proxy rou...
Include parent org in /org/domain response
The getOrganizationByDomain endpoint manually builds a response map but never included the parent organization reference. This prevented the frontend from knowi...
Add parentId and parent to OrgProviderProps type
The Organization props interface was missing parentId and parent fields, causing them to be dropped during TypeScript compilation even though the backend sends ...
Purge incomplete model directories in init container
The service crashes on startup if it finds a directory in /models that has missing or partial model files. Add a sweep at init start that removes any directory ...
Remove GitHub LFS downloads, use only data.statmt.org models
GitHub raw URLs return LFS pointer files, not actual binaries. Drop enru/enar downloads entirely — LibreTranslate handles those locales. Only download the 4 pai...
Narrow bergamot to es/fr/de/pl, restore bergamot-first order
GitHub LFS files can't be downloaded with plain curl (raw URL returns the pointer not the content). Removing enru/enar from bergamot scope; those locales fall t...
Narrow BergamotProvider targets to 6 pairs with available models
pt, it, uk, tr, ro, id, vi have no bergamot model source — LibreTranslate handles them. bergamot now claims: es, fr, de, pl, ru, ar.
Scale bergamot to 0, update image to mozilla/translation-service
The Google Cloud Storage model download bucket (bergamot-models-sandbox) returns 404 for all files and the firefox-translations-models repo was archived Dec 202...
Route all translations through LibreTranslate while bergamot is offline
BergamotProvider is moved to fallback position. LibreTranslate supports all 13 locales bergamot was handling **************** so this eliminates the flood of "C...
Proxy *** challenge through login server for multi-domain support
Replaces direct cross-origin fetch to capcha.{domain}/api/challenge with a same-origin proxy at /api/capcha/challenge. The previous approach required a valid TL...
Scale bergamot back to 1 replica — translation failures caused by missing pod
Bergamot was at 0 replicas with its service still registered. The ProviderRouter in TranslateService had no circuit breaker, so every request for es/fr/de/pt/it...
Don't let null translated description hide the English original in changelog
The translation lookup for non-en locales was unconditionally overwriting description with **************** which is null when: - the translation was saved befo...
Resolve 23 compilation errors from controller-service signature mismatches
Controllers were built by a separate agent from services, causing method name and parameter type mismatches. Fixed all controllers to match actual service signa...
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.
