Saving a calendar/contacts integration never blanks or drops its stored credentials
A contact integration's credentialsJson was replaced by whatever a save sent. The org CalDAV/CardDAV tab sends all four server fields (empty — they never show w...
No secret has a literal fallback in code or config; the Janus API secret never goes to a browser
The Janus API, token and admin secrets and the TURN password were committed four times over: as @Value defaults in WebRTCController and JanusService, as default...
Securityservice never stores a password as typed
Two writers put plaintext into USERS.PASSWORD. Both called User.encodePassword(), which needs a static encoder that only initializerservice ever sets **********...
Retail store connections are reachable only by their own org's commerce settings managers
The retail provider-config handlers on CommerceMarketController resolved the session's org and then used whatever market and config uid the path named. Update, ...
A Canva connect only returns the browser to the org's own console
POST /api/media/canva/connect-start stored whatever returnUrl the body named, and the OAuth callback on the API host redirects to it after Canva's consent scree...
The System User acts only inside the organization it was entered into
There is one System User for the whole platform. Platform operators, and support staff holding a grant scoped to a single ticket's organization, enter a tenant ...
Send the typed address with the reset code, and forward the caller's address
The reset page sent the 8-character code from the letter on its own, and SecurityService checked it against every account's outstanding reset at once, with no c...
A shared mailbox's access list is only readable inside its own organization
GET **************** called **************** which read the access rows for any mailbox id. Every other access operation (update, delete, grant, revoke) loads t...
Only an org's owner, its CONFIGURE_SYSTEM members or active god mode can change its settings
PUT /api/security/org/{id} took no request at all. ResourceServerConfig permits every request and APIService relays /api/security/**, so one anonymous request c...
An OAuth app save keeps its client secret unless a new one is typed; owners' credentials stay out of org JSON
PUT /api/oauth-config/{id} stored whatever arrived under clientSecret. Responses mask the secret, and the edit form sends a blank back while promising "Leave bl...
The provider form never holds the stored API key
AIService no longer returns provider API keys (only hasApiKey and a last-four mask). The edit button used to copy the returned key into the form, so it was in t...
Provider API keys never leave AIService, and only MANAGE_AI_SETTINGS can change a provider
GET /api/ai/providers and GET /api/ai/providers/{id} answered with each provider's DECRYPTED API key. The list is what every member's AI chat model picker loads...
Keep every credential field out of JSON, and guard the rule library-wide
Follow-up to 034378d2, which covered the columns NAMED as ciphertext. Read-only audits of origin/main in every repository found 61 more fields holding credentia...
Overlays clear the tool windows, scrollbars stack with their layer, and the rest of the shell geometry
What was left after pages stopped painting over the shell. Overlays opened BEHIND tool windows. Hand-rolled modals, menus and toasts named levels inside the sh...
Start a Facebook / Instagram connection through the authenticated connect-start
The Connect button navigated to **************** MediaService trusted both values with no session, so anyone who knew a connection id could bind their own page ...
Only an authorized member can connect a Facebook or Instagram page, and only back to their own console
The Meta connect flow trusted its URL. GET **************** took connectionId and returnUrl from the query string with no session, and state was unsigned base64...
Refuse webhook posts for Discord connections, which only ever receive DMs over the Gateway
Discord DMs reach MediaService only through DiscordGatewayManager's WebSocket, which publishes straight to social.inbound.raw. But every social connection has a...
Pages stay under the nav, the Interaction Center and the tool windows
The same two mistakes as /calendar's, everywhere else they were made. AuthedChrome's content box names no z-index, so a z-index in page code is a level in the R...
A person taken out of a drip stays out
The engine moves on up to 200 due people per drip from copies it read at the start of a pass, up to 25 seconds old by the time each is used. Meanwhile a member ...
The drip enroll dialog finds leads by email
The lead search behind it now matches a lead's email (kamo-shared-library 992569a6, live with SecurityService 5c7319a), so the dialog says so: name, email, comp...
A lead search matches the lead's email too
The /leads grid's boxes and the drip enroll dialog send what was typed as search terms, OR-ed across the lead's names, company, vendor lead id and number. The e...
The calendar stays under the nav and the tool windows
/calendar positioned itself over the viewport — fixed at 56/80 with z-index 1000 — instead of filling the content box AuthedChrome gives every page. That box na...
Keep every ciphertext column out of JSON, and guard the rule library-wide
Follow-up to 096feaa1 (Organization's GoDaddy credentials were being served by an endpoint that needs no session). A scan of every **************** found fourte...
Stop GET /org/{id} shipping the GoDaddy credentials, and lock it to this service's mapper
The fix is in kamo-shared-library 096feaa1: Organization.godaddyApiKey and godaddyApiSecret are now @JsonIgnore. That change reaches production only when a serv...
Never serialize the GoDaddy key and secret ciphertext
GET /api/security/org/{id} (and /org/domain/{host}) returns the Organization entity itself and answers without a session, and that body carried godaddyApiKey an...
An unconfigured build stops asking SecurityService for org ""
getHuntOffer fetched the promotion status even with no hunt configuration, so that the attempt would tag every prerendered page site-hunt. With no HUNT_ORG_ID t...
People in a paused drip still leave it when their lead moves on
A paused drip's exit rules (status change, reassignment, goal click) are now listened for, so resuming it never sends to someone who should have left while it w...
A drip's People counts follow the search, so All is always the sum of its chips
Chained drips hear when someone finishes a drip
A drip that starts when someone finishes another drip listens for the engine's own DRIP_COMPLETED events, but the recorder's trigger map left that kind out, so ...
A reply to a sending domain is refused as the recipient, in words that fit NoReply
Campaigns now send Reply-To: NoReply@<their From's domain> by default, and those replies reach postfix-k3m1-inbound. The refusal said "Sender address rejected: ...
An entry page rides out a service blip, and says so when it cannot
Entry pages failed in bursts while services rolled out: SecurityService answered 500 for a database schema-version bump or dropped the connection while restarti...
Retry public reads the database aborted with 40001
Every DDL bumps the YSQL catalog version, and a transaction that began on the old one fails with "catalog snapshot ... invalidated: MISMATCHED_SCHEMA" (SQLSTATE...
Stop refreshing a mailbox grant no app can refresh, and say it needs reconnecting
An org connected Microsoft 365 through Kamo's platform app; the platform app was later switched off and the org has no registration of its own. The resolver the...
Skip enabled connections that are not filled in yet
An enabled MERIDIAN_LINK connection with no instance URL, appCode or secret collapsed to a null|null poll-unit and failed token exchange every tick, logging ERR...
Read the public-chat secret by its own key
This pod also mounts mlos-internal-auth, whose INTERNAL_AUTH_SECRET env var relaxed-binds onto internal.auth.secret and outranks the ConfigMap. Every caller rea...
Rebuild so queue consumers self-heal their stale durables
social-inbound-consumer and marketing-conversion-consumer predate their deliver groups and live on SOCIAL_MESSAGES / MARKETING_EVENTS, which the shared NATS rec...
Reconcile durable consumers on the stream that owns the subject
The filter and deliver-group reconciles looked consumers up on the service's configured stream only. Subscriptions to subjects captured by another stream (socia...
Email's Deliverability tab has a settings-menu entry
The tab renders for whoever may manage the opt-out list (MANAGE_EMAIL_OPT_OUTS or the organization owner), but the settings menu never listed it, so settingsMen...
Every refused request says why, not just campaign calls
2a72597 gave the campaign controllers the member's sentence instead of "Bad Request". The same loss happened everywhere else in EmailService that refuses with a...
The scrollbar guard and the recipient-status tests pass again
Both were red on main, and vitest does not run in CI, so nothing noticed. - oneScrollbar.test.ts ("is the only thing in the app that styles a scrollbar") fai...
Take kamo-meet and kamo-analytics off the public changelog
Both names leave changelog.public-projects. Every public read goes through that one list, so the marketing site's /changelog stops showing them everywhere at on...
Prune unused images on k3m1 too, and stop bulk removals timing out
k3m1 had no prune job, and kubelet image GC waits for 85% disk, so every deploy's SHA-tagged image piled up: 3,368 images (539G), 86 in use. Add a daily k3m1-im...
A refused campaign call says why, instead of "Bad Request"
CampaignService refuses with a ResponseStatusException carrying a sentence written for the member ("Pause the campaign before deleting it.", "Only paused campai...
A checkbox, button or link inside a grid row no longer opens the row too
The ⋮ bug from /marketing/email, in the six other grids that have it. AG Grid fires rowClicked from its own native listener on the row container, which runs bef...
Coalesce pool-availability recounts instead of recounting per lead
countAssignablePool scans every lead in the org+market and ran synchronously after every createLead commit. At ~14k leads it already cost ~165 ms, more than the...
The subscriptions article is not announced as a public service announcement
The chip above the headline and the line on the share card both called it one, which the banner strip has already stopped doing. The byline row now opens with t...
The featured-article strip drops the public service announcement badge
The badge belonged to the seat-fee article. The strip points at the subscriptions article now, which is not a public service announcement, so the link stands on...
Stop printing a message key as the legal line, and calibrate the guard
The footer's terms-and-privacy sentence was the only rich-text message on the site. The translation service rewrote its tags as prose in 12 of 21 locales, so t....
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...
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...
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.
