Scroll wide email content inside shadow DOM, not outside
The previous attempts at adding overflow-x-auto to light DOM ancestors could not catch wide email content because the shadow tree's visual paint overflow does n...
Use getHasAttachments() to match Boolean wrapper type
**************** is a Boolean (wrapper), so Lombok generates getHasAttachments() not isHasAttachments(). The latter only exists for primitive boolean. Use Boole...
Expand JPA repository scan to include commerce package
The new kamo-shared-library brings in **************** which requires **************** (in com.kamo.z.shared.commerce). MediaService only scanned donotmodify.da...
Restore HashMap import in ChatWebSocketController
The previous refactor removed the HashMap import but the handleTyping method still constructs a HashMap for the typing event payload, causing the Forgejo build ...
Add min-w-0 to email content div so overflow-x-auto fires
Without min-w-0, the flex-col child's default min-width:auto resolves to min-content, letting wide email tables expand the div past the column width. The outer ...
Remove duplicate connectStomp call in sendMessage
When the first message creates a new session, onSessionCreated updates props which triggers useEffect to call connectStomp. The inline call in sendMessage was r...
Proactively create NATS consumer at session creation to prevent message loss
Previously the NATS JetStream push consumer was only created when a STOMP client sent /app/chat/subscribe. With DeliverPolicy.New, any messages published before...
Close reconnect gap causing dropped real-time messages
Remove `connected` from subscription useEffect deps in TicketChat and ChatBox. Previously, every WebSocket reconnect triggered the effect cleanup which called u...
Allow system user to manage org branding and fix logo auth
- Add system user bypass as first guard in canManageOrgBranding so platform system user has full branding control over all non-parent orgs - Fix uploadLogo en...
Contain horizontal overflow in email view pane to 3rd column
- Add overflow-x-hidden to MessageBrowser outer container as hard boundary - Add overflow-x-hidden to column 3 wrapper and overflow-x-auto to email content area...
Production fail2ban with PJSIP jails and persistent state
- Add fail2ban-configmap.yaml with [asterisk] and [freepbx-web] jails, incremental ban escalation (1h→24h→30d ceiling), LAN ignoreip, and a custom asterisk-...
Replace Coqui TTS with Piper TTS (en_US-norman-medium)
Coqui VCTK model has no named speakers — replace with Piper TTS which has the exact norman[medium] voice. Piper binary (~3 MB) and model (~60 MB) are downloaded...
Reset stale hasAttachments on sync and accumulate correctly
- Reset has_attachments=false at start of bulk sync so stale true values from old false-positive MIME detection are cleared before re-indexing - Accumulate ha...
Add resetHasAttachmentsForFolder query to ConversationRepository
Allows sync to reset stale has_attachments flags before re-indexing so corrected MIME detection can rebuild the field from actual attachment data.
Integrate Coqui TTS with norman voice (VCTK p267)
- Deploy coqui-tts server (VCTK VITS model, CPU-only) as ClusterIP service on port 5002 with model cache at /var/lib/coqui-tts on k1m1 - Inject coqui_tts.agi ...
Pre-populate FROM on reply/forward and fix send fromAddress
- DraftMessage now carries fromAddress so it flows from openNewMessage through to the composer - MessageBrowser stores the active mailbox emailAddress (includ...
Support fromAddress override on send
Adds FromAddressRequest to EmailSendRequest so the frontend can specify which alias/shared/mailbox address to send from. SmtpOnlyProvider now uses that address ...
Add LAN DNS pod for split-DNS on k1m1
Deploys a dnsmasq pod with hostNetwork on k1m1 that listens on 192.168.4.22:53. Overrides pbx.k1.kluster.kamocrm.com -> 192.168.4.22 so LAN phones reach the PBX...
Add local_net for split-DNS / LAN client RTP fix
Phones on the same LAN as k1m1 connect via split DNS (domain resolves to 192.168.4.22 internally). Without local_net, Asterisk still advertises the external dom...
Persist sorcery contact=memory fix and block SIP scanner in startup.sh
- Inject [res_pjsip] contact=memory into sorcery.conf before Asterisk starts; the default sorcery-astdb SQLite backend silently drops all REGISTER contacts - ...
Sub-tab content fills full content-area width
Drop the centered max-width constraint (was 920 / 1200 / 680 / 720 depending on tab) on SubTabShell. The page already lives inside SettingsView's content column...
Fix attachment parsing and hasAttachment false positives
Three fixes to EmailImapService: 1. toEnvelope: narrow multipart check to multipart/mixed only — plain HTML+text emails (multipart/alternative) no longer sh...
Add download button to classic view attachment section
The classic view (MessageBrowser) had its own attachment list that was missing the download button — only the conversation view was fixed previously. Also align...
Add com.kamo.z.shared.commerce to EnableJpaRepositories scan
**************** (required by **************** in the shared library) lives under com.kamo.z.shared.commerce but was not included in the JPA repository scan, ca...
Proper layout, padding, and provider logos
Layout / spacing fixes: - All six tabs (Providers, Extensions, Phone Users, Devices, Voicemail, Health) now wrap their content in a TabShell with px:{xs:2,md:...
Correct field name and type in getFromAddresses response
Backend was sending 'email' key but frontend expected 'address', and sent type 'shared_mailbox' when frontend expected 'shared'. Also fixes hasMailboxes flag wh...
Load FROM addresses from accessible mailboxes and aliases
Replaces the broken getFromAddresses() endpoint (which sent 'email' instead of 'address') with getAccessibleMailboxes() + listMemberAliases(), matching the same...
Use CockroachDB-compatible DROP INDEX CASCADE syntax
CockroachDB rejects ALTER TABLE ... DROP CONSTRAINT for UNIQUE constraints with: unimplemented: cannot drop UNIQUE constraint "uk_org_oauth_provider" using ...
Drop stray VOIPService spec md from repo
Per project convention (.md files don't ship with services).
Include com.kamo.z.shared.commerce in repository scan
Latest kamo-shared-library propagates **************** (donotmodify.dao.services) which has a constructor dependency on **************** **************** The se...
Platform configuration tabs (General / VOIP & SMS / System User / Advanced)
Wraps **************** in SettingsView with four tabs: - General: read-only platform overview — total orgs, active phone-server instance count, active SMS-pr...
Add attachment download button and decouple mark-as-read
- Add a Download icon button to each attachment row so users can force a file download instead of only opening in a new tab - Decouple markConversationRead fr...
Global VOIP sync job (admin-tunable interval)
VoipGlobalSyncJob ticks every minute and, on each tick, reads the SystemConfiguration singleton to decide whether a global sync is due: - If voipSyncEnabled is ...
System-config + platform-overview endpoints
Platform-wide configuration surface, gated by the existing top-level-owner guard (matches SystemUserConfigController's requireTopLevelOwner pattern). Endpoints...
Per-instance resource controllers + org aggregate + internal sync
Per-instance resource CRUD (extensions / users / devices / voicemail): - VoipExtensionsController at **************** - VoipUsersController at /api/voip/instanc...
Multi-instance provider refactor + full FreePBX + Flowroute SMS
Core model change: one org → many VoipProviderInstances (was: one per org). Every provider API call is now instance-scoped. PhoneProvider interface: - New meth...
Multi-instance phone/SMS provider migration
Runs after Hibernate ddl-auto:update creates new tables (VOIP_PROVIDER_INSTANCE, BULK_TEXT_PROVIDER_INSTANCE, PHONE_EXTENSION, PHONE_END_USER, PHONE_DEVICE, PHO...
Multi-instance phone/SMS provider schema foundation
- Rename VoipProviderConfig → VoipProviderInstance; drop UNIQUE(org_id), add instance_name, config_json, last_sync_at, last_sync_error. - Rename BulkTextProvide...
Move hostPath from MinIO (rclone FUSE) to local /var/lib/pbx
MinIO via rclone FUSE does not support POSIX chmod or symlink() — both required by FreePBX's module system. Changed all FreePBX and MariaDB hostPath volumes fro...
Wrap fwconsole/amportal via php for MinIO FUSE hostPath
/var/lib/asterisk is backed by MinIO via rclone FUSE which doesn't support POSIX chmod, so fwconsole scripts can't be made executable on that volume. startup.sh...
Add preStop hook and terminationGracePeriodSeconds
Containers were getting stuck in Terminating because background Asterisk processes kept running after apache2ctl (PID 1) exited. preStop hook sends SIGTERM to A...
Add bootstrap.php require to regenerated /etc/freepbx.conf
The installer writes freepbx.conf with datasource='' and a require_once for bootstrap.php, which config.php depends on to load the FreePBX class. Our regenerate...
Regenerate /etc/freepbx.conf on restart
/etc is ephemeral (not a hostPath), so /etc/freepbx.conf written by the installer is lost on every pod restart. Added else branch to regenerate it from env vars...
Bypass start_asterisk, wait for control socket instead
start_asterisk uses `pidof asterisk` which returns a false positive on pod restarts, causing the wait-for-Asterisk loop to hang. Now startup.sh starts Asterisk ...
Start Asterisk in background, wait for it, then run installer
The entrypoint.sh does not run the FreePBX installer — it only starts services. startup.sh now replicates the entrypoint setup steps, starts Asterisk in backgro...
Let entrypoint handle install, not startup.sh
FreePBX installer (php install -n) requires Asterisk to already be running. Our startup.sh was calling it before Asterisk started, causing "Error communicating ...
Mount /etc/asterisk only, not /etc, to fix PHP simplexml error
Mounting the full /etc as a hostPath volume was corrupting PHP extension loading even after seeding from the image — simplexml_load_file undefined at install ti...
Reduce CPU limits to 2:1 ratio to eliminate overcommitment
Docs 4→1 core, jibri 4→2 cores, libretranslate 2→1 core, bergamot 2→1 core, coturn 1→0.5 core. Saves ~7.5 cores off node limits.
Switch to escomputers/freepbx:17 with correct architecture
tiredofit/freepbx has no FreePBX 17 image (abandoned since 2022). escomputers/freepbx:17 (updated Apr 2026, Asterisk 21 + FreePBX 17) requires a separate MariaD...
Drop public admin ingress; access via http://k[12]m1:8088
Admin UI is now internal-only over LAN/WireGuard on port 8088 (FreePBX's nginx bound directly on hostNetwork). No Traefik route, no Let's Encrypt cert, no publi...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.