The FreePBX helper's one key rings a phone
kamo-asterisk-support holds a single API key, documented and issued as a VOIP_RECORDING_UPLOADS key, and sends its ring events with it. The ring endpoint accept...
The public subscription catalog takes ?universe=
The KamoUniverse site shows the Family, Community and Personal plan ladders, and the public catalog proxy only forwarded locale, so it could only ever return th...
Give a public chat's own turns the message rate limit, not the session one
Every public-chat path lives under /sessions/, so the limiter's contains("sessions") test put the conversation itself on the session-creation budget: 3 requests...
PBX policy pull for kamo-asterisk-support; ring VOIPService with the cluster secret
KamoAI internal endpoints never leave the gateway
Carves the seven KamoAI internal subtrees (master spec §7 item 13) out of the wildcard forwards, and with them the server-to-server endpoints that predate the p...
The public address Zapier posts Arive events to
POST **************** trigger> forwards to SecurityService's AriveInboundController, which owns the token check. The query string (the event) crosses the hop, a...
Give the gateway a memory request and limit
The deployment had no resources: block at all. No request, so the scheduler could not reason about this pod's footprint; no limit, so nothing capped the JVM's -...
Forward commission Stripe Connect webhooks, which reached nothing at all
CommissionService registers **************** directly with Stripe (OrgStripeSetupService), but no /api/commissions/** forward existed anywhere in this gateway -...
Carve out VOIPService's internal SMS-template API, and never forward a client's own X-Internal-Auth
Two related gaps in the same trust boundary. /api/voip/sms/templates/** was forwarded wholesale by the /api/voip/** wildcard. VOIPService's InternalAuthFilter ...
Derive X-Real-IP from the right-most hop, not the client's own
forward() set the outgoing X-Real-IP to X-Forwarded-For.split(",")[0] -- the LEFT-most hop, which is the one part of that header a client controls outright: a r...
Bound upstream calls with a connect and a read timeout
Both RestTemplate beans were built from a bare JdkClientHttpRequestFactory with no timeout at all. One upstream that accepts the connection and then never answe...
Stop logging session tokens, OTKs and auth headers on every request
forward() printed the entire copied-header map on every single call -- System.out.println("APIService: copied headers: " + outHeaders) -- which serializes X-***...
Scrub copies of live credentials from config and dumps
A sweep of every repo for the values of the cluster's live Secrets (2026-09-23) found copies here: the JWT signing secret as a `${JWT_SECRET:<literal>}` default...
No live credentials in application.yml defaults
The local-development defaults carried the database OWNER's password (${DB_PASSWORD:<literal>}), and apiservice/securityservice also the live changelog webhook ...
Forward /api/checklists to KBService
Checklists live in the Notes app and are served by KBService beside the notes themselves; without this route the whole feature answers 404 at the gateway.
Forward the Hosted Computer agent path, and only that path
/api/hosted-computers/agent/** -> ComputeService. A Hosted Computer's network policy denies the cluster entirely — no database, no service, not even cluster DN...
Liveness probe on /actuator/health/liveness, not the DB-aware aggregate
The aggregate /actuator/health includes the DataSource indicator, so restarting the database failed liveness on every pod at once and restarted the whole platfo...
Release idle Hikari connections now that YSQL pooling is shared
The connection manager no longer pins sessions **************** so idle app connections no longer each hold a database backend. Keep the pool maximum, but stop ...
Accept OCI image indexes when resolving the built digest [skip ci]
The images are pushed as OCI image indexes, so asking the registry for a single image manifest only answered 404, the digest came back empty and the check faile...
Restart when a same-commit rebuild leaves pods on the old digest [skip ci]
The rollout step tried to detect a same-commit rebuild by comparing the Deployment's image reference before and after `set image`. "Apply manifests" has already...
Never forward client-supplied identity headers upstream
forward(), forwardWebhook() and forwardCallback() copied every inbound header except Host (and, for the sessionless two, the credential headers) onto the upstre...
Forward carrier SMS webhooks, which reached nothing at all
Traefik sends every path on api.kamocrm.com to this gateway with no path split, and this class forwarded /api/voip/** and nothing else. So /api/bulktext/inbound...
Forward carrier SMS webhooks, which reached nothing at all
Traefik sends every path on api.kamocrm.com to this gateway with no path split, and this class forwarded /api/voip/** and nothing else. So /api/bulktext/inbound...
Prove the deploy by digest, not by tag
The preceding commit stops `set image` being a silent no-op. This asserts the outcome: after the rollout, the tag is resolved to a digest at the registry and th...
A rebuild of the same commit deployed nothing and reported success
The image is tagged with the commit SHA, so rebuilding the same commit produces an identical image reference. `kubectl set image` then changes nothing, the Depl...
Drop the /api/settings forward, which pointed at nothing
EmailService serves the sync-integration controller at **************** not /api/settings/integrations — so that forward reached a path the service does not map...
Route /api/contacts, /api/calendar and /api/settings to EmailService
KamoMobile's contacts and calendar screens 404'd on every request. Neither path was routed at the api host, so nothing reached EmailService and the service logg...
Run two pods
replicas 1 -> 2. This service runs no @Scheduled work and binds no exclusive NATS durable, so a second pod duplicates nothing — it is stateless request serving,...
Give the rollout room for the 15s minReadySeconds now costs
progressDeadlineSeconds was 60. That is the window a rollout has to show progress before Kubernetes gives up and marks it failed, and the previous commit added ...
Restore this manifest's CRLF line endings
The previous commit rewrote the file with a script that normalised it to LF, which is a whole-file diff for a change that touched a dozen lines. No content chan...
APIService never shut down gracefully at all
Deploys replaced the only pod of each service with nothing to catch the requests in flight. Three settings, applied across the fleet: - preStop sleeps 10s befo...
API-key surface for live call events from a customer PBX
Companion to VoipRecordingUploadController, on a deliberately separate VOIP_CALL_EVENTS scope: uploading a recording after the fact and making somebody's phone ...
Forward /api/email/oauth/callback to EmailService
Google's OAuth redirect target. Same shape as the email and meet provider callbacks beside it: public and sessionless, because the popup lands on this gateway o...
Forward the tracking beacon to MediaService
Clones forwardPublicWebinar rather than the generic forward(): that method prints every URI, header map and body length to stdout with no level, and a visitor b...
Rebuild against shared-library @Lob LONGVARCHAR fix
Hibernate's PostgreSQL dialect read @Lob String columns as OIDs via getLong(). Affects notes, contacts, calendar, OAuth tokens, IMAP passwords, email campaign b...
Point at YugabyteDB and use PostgreSQLDialect
CockroachDB has been replaced by YugabyteDB. Connection strings move from cockroachdb-public:26257 to yb-tserver-service:5433, and the Hibernate dialect from **...
Mirror Maven Central through the Google GCS copy
Every Docker stage starts from a cold ~/.m2 and refetches the whole dependency tree, so Central sees the full weight of every concurrent service build. It answe...
Encode the decoded token exactly once on the way upstream
21670e5 routed SocialWebhookController through UpstreamUri's FULLY-PRE-ENCODED entry point, but its URL is a hybrid: `token` is an @PathVariable, so Spring hand...
Forward the caller's query bytes on the public-facing proxies
The gateway stopped double-encoding forwarded query strings in 3c24d32, but four other proxies in this service still concatenated already-percent-encoded bytes ...
Forward the caller's query bytes instead of encoding them twice
Every URL this gateway builds is assembled from getRequestURI() and getQueryString() — both already percent-encoded — and was then handed to RestTemplate as a S...
Enforce tenant access rules at the gateway
This filter has been inert since it was written. resolveOrgId read org:domain:<host> from Redis, nothing ever wrote that key, and the null return fell through t...
Route the meeting provider OAuth callback
Zoom and Microsoft redirect the browser to this gateway after an org authorises Kamo's app. The popup lands here with no tenant session — the org travels in ?st...
Harden the kubectl download against flaky egress [skip ci]
dl.k8s.io over the runner's egress intermittently drops mid-transfer: curl: (56) OpenSSL SSL_read: decryption failed or bad record mac which fails the deploy ...
Proxy the public AI->human handoff route
Adds POST **************** proxied to MediaService alongside its createSupportSession sibling. Without this the handoff feature was dead on arrival: MediaServi...
Route /api/email + /api/support through the api host
(EmailService) and Support (MediaService, same upstream as /api/media) were unrouted → 404. Add both forwards and fix email.service.url from a localhost default...
GET /api/public-chat/bootstrap gateway route
Add bootstrap endpoint that reuses the existing proxyWithAuth pipeline (key validation + opt-in origin + PUBLIC_CHAT scope + rate-limit + X-Public-Chat-Key-Hash...
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.
