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...
Deploy FreePBX 17 + Jigasi SIP bridge on k1m1 and k2m1
FreePBX (tiredofit/freepbx:17-latest) runs with hostNetwork so SIP/RTP preserves real client IPs, pinned one-per-node via nodeSelector. SIP on 5060 UDP/TCP (506...
Parse formData before forwarding to prevent binary corruption
request.text() on multipart/form-data corrupts binary file bytes; pass parsed FormData as init.body so forwardToApi skips request.text() and lets fetch regenera...
Remove minHeight:100vh causing double scrollbars
The outer box's minHeight:100vh exceeded the layout's calc(100vh - 56px) container, which caused both the browser-level scroll and the inner overflow:auto to be...
Drop editor page header, rename General sub-tab
- Removes the back-arrow IconButton + shield icon + title + subtitle from the top of the security-model / master-model editor. The Cancel button in the stic...
Drop stale Pay column header from security-models table
When the Employee Self-Pay Kamo Fees field was removed, the matching TableCell in the row body was deleted but the header row still had the '<TeamMember> Pay' c...
Surface real reason behind GoDaddy 400 error
GoDaddy now returns HTTP 400 with code=UNABLE_TO_AUTHENTICATE and body "Bad Request : Bad Request" when an account does not meet their current production Domain...
Censorship sub-tab now renders ATF first per spec
Reorders the Censorship sub-tab cards to match the requested order: 1. Alcohol, tobacco & firearms 2. Allowed Member Age Ranges 3. Allowed Employee (Team ...
Platform-admin view, pagination, live search, filters, org logos
Platform admins (top-level org owner) now see every org in the system via the new /api/security/org/all-networks endpoint instead of just their own memberships....
Add GET /api/security/org/all-networks for platform admins
Top-level org owners can now fetch all organizations in the system with membership info relative to the caller. Orgs where the caller has a member record get is...
Sub-tabs in security-model editor + cleanup
- Security-model editor on **************** is now split into four sub-tabs: General (Basic Info, Visibility, Who Can Create, Platform Settings, Domain Conf...
Drop teamMemberPayFeesSetting from DataLoader
Master-model provisioner no longer passes the removed tri-state pay-fees setting when creating the master security model — the field is gone from ChildOrgSecuri...
Drop teamMemberPayFeesSetting parameter
Mirrors the shared-library change — SecurityModelController, MasterModelController, and AppliedModelController no longer read/write teamMemberPayFeesSetting. Th...
Drop teamMemberPayFeesSetting
Employee self-pay fee policy is gone — the BillingGroup refactor handles billing per-group, not via the security model. Removes the teamMemberPayFeesSetting col...
Eager-load domains via findByIdWithDomains, drop @Transactional
@Transactional on a controller method keeps a DB connection open across the entire MinIO upload, and also risks LazyInitializationException if the inner getOrga...
Remove duplicate ?type= from apiPath
forwardToApi already appends incomingSearch (?type=...) from the original request URL. Embedding ?type= a second time in the apiPath produced ?type=full?type=fu...
Restore mailbox switcher for int64 member IDs
Use getMemberIdString for mailboxes/member fetch, normalize id/mailboxId from EmailService, and drive the switcher from accessible personal mailboxes when avail...
Alias visibility UI + Subdomain Branches button on branch-types page
SubdomainBranchesDialog: - Alias editor replaced chip list with a table-style row list showing Members and Team Members checkboxes per alias — admins control ...
Alias visibility filtering + auto-verify subdomain ownership
OrganizationController: - Web-alias org creation now auto-marks ownershipVerified=true and isDnsVerified=true on the created OrgDomain — no TXT record needed ...
Add per-alias creator-type visibility flags
Add visibleToMembers / visibleToTeamMembers boolean columns to ChildOrgSecurityModelAlias so admins can restrict which parent aliases appear to Members vs Team ...
Cache-bust logo URLs using themeRevision from config.json
DynamicMetaLoader now stores themeRevision in sessionStorage and fires KAMO_THEME_CONFIG_LOADED after fetching config.json. ThemeLogo listens for that event and...
Cache-bust logo URLs using themeRevision from config.json
DynamicMetaLoader now stores themeRevision in sessionStorage and fires KAMO_THEME_CONFIG_LOADED after fetching config.json. ThemeLogo listens for that event and...
Upload directly to public/{domain-folder}/img/ instead of staging bucket
The uploadLogo endpoint was writing to a hardcoded 'sumtingwong' placeholder (unfilled setting) instead of the org's actual public theme path. Logos now go dire...
Remove downed k2m1 node from edge-nodes default
k2m1 (10.8.2.1) is permanently down. Leaving it in the failover list caused upload retries to hang until the new OkHttp timeout fires. Default is now k1m1-only;...
Apply connect/write/read timeouts to MinioClient via OkHttpClient
Without timeouts, a downed site-replication peer (k2m1) caused makeBucket and putObject calls to block indefinitely, surfacing as an infinite "Saving..." on the...
Settings dialog, web-alias wizard path, and DNS provisioning panel
Adds the Subdomain Branches UI across three surfaces: - **************** new "Subdomain Branches" button opens a dialog for the org-level enable toggle + ...
Org-level config, GoDaddy client, and web-alias org create path
Wires the new Subdomain Branches feature end-to-end on the backend: - SubdomainBranchesController for org-level enable + GoDaddy creds (live-verified) - GoD...
Add entities, cipher, and alias/DNS log scaffolding
Adds per-org GoDaddy creds (AES-GCM encrypted), an allowRootSubdomains flag and child-org alias collection on the security model, a DNS provisioning audit log, ...
Mailbox checkbox reads org_domains, not provider domains
The previous wiring read /api/email/domains (email_provider_domains table). That table is only populated when a third-party email provider integration is set up...
Drop My Profile tip banner + unify section spacing
All four My Profile sections now share the same vertical rhythm — 8px between each section's header and its field(s), 16px between sections. The Alert banner ex...
Tidy My Profile layout + drop phone verification
Reorganize the My Profile step so all four sections share the same layout and title format. Order is now Legal Name, alias, e-mail, phone. - FormName: remove t...
Drop SMS verification for the phone field on signup
The phone number collected during self-service registration is now a plain optional text field — no /register/phone/initiate, no SMS code, no /register/phone/ve...
Drop RegistrationPhoneCode entity + repository
Self-service registration no longer uses SMS-code verification for the phone field — the register form collects the phone number as plain text and stores it as-...
Prefix/suffix dropdowns, mailbox domain gate, explicit status
- Name prefix and suffix are now dropdowns populated from the NamePrefixType / NameSuffixType catalogs delivered by the org-config endpoint. Displays abbrev...
Explicit team status + name prefix/suffix catalogs
- GET **************** now returns namePrefixes and nameSuffixes, sourced from NamePrefixType and NameSuffixType so the Add Member / Add Team Member forms d...
Unwrap {markets:[...]} envelope in Attach dialog
GET /api/security/commerce-markets returns { markets: [...], message: "..." }, not a bare array. The Attach Commerce Market dialog was treating the whole respon...
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.
