Skip to content
KAMOUniverse

Changelog

What's new.

Every improvement to the platform, as it ships.

  1. · kamo-internal

    Task types, the session and lead clients, and due times in the member's zone

  2. · kamo-shared-library

    Task columns, links, comments, Inbox kind, note links and authorship

  3. · kamo-shared-library

    AI signature disclosure on the signature field config

  4. · KlusterServices

    Pinned, hash-verified model fetch and a warm-up that holds the pod until every model is resident

    The warm-up also drops a model whose load failed: Speaches 0.9.0-rc.3 keeps the entry of a failed load, so the next startupProbe would get 409 and /api/ps would list a model that never loaded.

  5. · kamo-shared-library

    Never-grantable AI rights, ARTIFICIAL template type, AI access-log events

  6. · kamo-internal

    AI member client library and BFF proxies

    The client for an AI member's state, pause/resume/retire, hiring checklist and retry, seats, the organization's workforce controls and the platform kill switch, plus four relays to SecurityService. A relay refuses a . or .. segment with 404 before forwarding: encodeURIComponent leaves dots intact and the URL fetch builds would resolve them above the relayed prefix.

  7. · kamo-shared-library

    Recurrence bound, reminder coder and EXDATE entity; UTC zone default

  8. · TranslateService

    Present Kamo's upstream token to bergamot and libretranslate

  9. · SecurityService

    The Tasks tab is tasks — served by KBService, gated here like the lead itself

  10. · RAGService

    Present the TEI API key to the embedding and reranking models

  11. · kamo-shared-library

    STRING registry enums, 24 provider types, digit-tolerant converters

  12. · APIService

    Route the notification centre for the phone

  13. · DaemonService

    Kamoai-hire-steps job drives SecurityService's hire-step sweep every minute

  14. · KBService

    The SP06 schema — tasks, links, comments, Inbox, note links and authorship

  15. · kamo-shared-library

    AI member state, org pause and kill-switch columns (read-only in JPA)

  16. · KlusterServices

    Svc_ai publishes ai.registry.changed.> (KamoAI SP01 registry events)

  17. · kamo-shared-library

    Task status, priority, record-link kinds, Inbox and master-note sections

  18. · KlusterServices

    Svc_security publishes kamoai.platform.state, svc_kamoai subscribes to it (KamoAI SP02)

    SecurityService announces the platform AI kill switch on kamoai.platform.state and KamoAI Service listens for it. rt.org.<org>.ai-workforce needs no new grant: svc_security already publishes rt.org.>.

  19. · KlusterServices

    Retire the shared kamo_svc login

  20. · kamo-internal

    Image bubbles draw a 480px preview; the lightbox keeps the original

    A chat image is drawn at most 240x240 CSS px and the bubble downloaded the whole original to do it — 3-8 MB per phone photo, per viewer. AttachmentChip now draws **************** MediaService's 480px WebP (tens of KB), made the first time anyone asks and answered with the original whenever an image has none (an animation, a vector). The lightbox, downloads, video and audio keep reading the original. Fallback: if the preview fails the bubble tries the original at the same URL generation, and only the ORIGINAL's failure enters useAttachmentSource's recovery (cookie re-plant, then the HEAD verdict) — a broken preview is never reported as a missing file. The chip remembers which url the preview failed at, so a recovery that changes the url (the re-plant, Retry) tries the preview again instead of leaving the whole original in the bubble. /api/media/* is not a proxied prefix, so the preview has its own route file. Both routes now share app/lib/imagingProxyRelay.ts (same OTK forward, same pass-through of MediaService's CSP/nosniff/disposition, plus x-attachment-rendition), and refuse an id that is not a UUID before building the upstream URL. Deploy AFTER MediaService (the /preview endpoint). If this lands first the preview request 404s upstream and every bubble falls back to the original — today's behaviour, one extra small request.

  21. · MediaService

    Image attachments are drawn from a 480px preview, not the original

    A chat bubble draws an image at most 240x240 CSS px and every viewer downloaded the original to do it — 3-8 MB per phone photo. GET **************** now answers a WebP bounded to 480px on its long edge; the original endpoint, the lightbox and downloads are unchanged. One preview per stored file (ImgDat), not per attachment: <imgDatId>_thumb_480.webp in the imaging bucket, beside the original. The _thumb infix is what SecurityService's OrgObjectStorageSweep bills as a derivative of that dat. It is deliberately not the imaging library's _thumb_p0.png (a 640px PNG DocsService serves under document rights and flags on the shared dat). Made at upload and on first request. Both attach paths (upload, and the dedup link) queue it in the background; a request that finds none makes it on the spot via ConversionService /api/conversion/image/preview — so every existing attachment gets one the first time anyone looks, with no backfill. Concurrent requests for the same content join one generation; at most two run per pod; the background queue drops rather than grows. A refusal (animation, undecodable, not smaller) is remembered as a zero-byte object and the original is served from then on; a failure (busy, down, a 404 from a ConversionService without the endpoint) is not remembered. Whenever there is no preview the endpoint serves the original exactly as /imaging/proxy/{id} does, so an <img> pointed at it always gets a picture. X-Attachment-Rendition says which. Access: both endpoints go through one authorize() — authentication, existence, revocation, message removal, missing content, session membership, same order, same answers — before the preview store is touched. A revoked attachment's preview is refused at the same instant as its original. ImagingProxyPreviewAccessTest pins each refusal to the original's (status and body, GET and HEAD); a negative control (membership check skipped for the preview) fails it. A preview GET records the member's first download, as the whole-file GET of the original did when bubbles drew the original. Deploy AFTER ConversionService f7729d9 (the endpoint). Before it lands every preview request fails soft to the original; nothing is marked.

  22. · ConversionService

    /image/preview cuts a chat photo into one 480px WebP

    A chat image attachment is drawn at most 240x240 CSS px and every viewer downloaded the original to do it — a phone photo is 3-8 MB. MediaService will store one preview per stored file (ImgDat) and serve it to chat bubbles; this is the cutter it calls. ImagePreviewService fits the long edge to 480 (2x the box), never upscales, and writes lossy WebP with no metadata. EXIF orientation is baked in (a portrait photo's preview would otherwise lie on its side next to its upright original), with ffmpeg's own autorotate off so no build can rotate twice. Vectors, animations (GIF, APNG, animated WebP), undecodable bytes, oversized sources and previews no smaller than the original are REFUSED — 422 with X-Preview-Refused, which the caller may remember. Busy/timeout/ffmpeg failure is 503 and says nothing about the image. Memory, after the logo ladder OOM-killed both pods: ffmpeg runs one at a time, single-threaded **************** 1), scales before rotating, and decodes a JPEG at 1/2^k in the IDCT (-lowres, keeping 2x the preview edge). Peak RSS measured with /usr/bin/time -f %M on the exact command: 12 MP JPEG 68 MB, 48 MP JPEG 123 MB, progressive 12 MP 92 MB, 16 MP PNG 115 MB (184 MB for pure noise). Ceilings: JPEG 52 MP, PNG/WebP/GIF 16.8 MP, source 40 MiB. Internal-only on the public-chat secret MediaService already presents to /transcribe; fails closed.

  23. · kamo-internal

    Draw logos from the server's pre-sized variants, never the multi-MB original

    A member saw the org header logo small in the composer but a >3 MB image in the sent mail: every logo surface linked `img/logo-full.svg` / `img/logo.svg`, which for a raster-logo org is an SVG wrapping the whole upload (crm: 2.95 MB), drawn 24-120 px tall - and in email, in a format Outlook does not show. **************** now write **************** beside the originals (LOGO_VARIANTS_CONTRACT). This routes every logo consumer through one helper and keeps the original as the fallback, because variants will be missing on the live bucket for a while: - **************** LOGO_RUNGS, pickLogoRung (smallest rung >= cssPx x 2, else 1024), logoVariantUrl, logoDisplayCap, the probe (probeImage, cached per tab), cachedRemoteImageExists (HEAD, 10 min) and the sent-email settle/verify of PNG rungs. useLogoVariant.ts: useLogoVariantSrc (<img>: variant, then original on error, then the caller's own fallback), useProbedLogoUrl (CSS/mask/canvas: '' until probed, never both), useEmailLogoLadders, useRemoteImageVerdicts. - ThemeLogo picks the rung from its own box (NavPri 64, home 128, settings 128, loading 256, validate 192, watermarks/pane 1024) and falls back on error; the overlay mask follows. Backdrop/error/not-found watermarks, the page-transition cover (chooseCoverLogo + probe), esign watermark (1024) and picker (64), chess mark (192), Hosted Computer launch page (128, data-fallback onerror), Meet branding previews (128), email template editor previews, /network chips (via the image proxy, variant -> original -> initial) and Hold'em chip faces (128). - Email: ResolvedImages carries each logo's PNG ladder (probed); the header, signature and badge renderers print the rung for the size they draw. Every send path confirms each printed rung with a HEAD and puts the original back where it is missing (composer + its meeting invite, campaign/drip wrap, the meet-invite route). Uploaded footer badges ask EmailService for `?h=<2x>` (<= 128). Stored placeholder HTML never carries a rung. The meet-invite route now reads the theme from the org alias instead of the pre-alias FQDN folder. - Left on originals by design: LabelManager, setup-wizard LogosStep, favicons. Tests: rung picking, URL shape, probe/HEAD caches, both fallbacks, ThemeLogo, cover choice, OrgLogo chain, launch-page fallback, badge ?h=, and a wiring guard that renders every header/signature layout with a laddered logo and fails on any raw /img/logo URL or unverified send path.

  24. · kamo-register

    Register logos load a pre-sized copy, and the original when it is missing

    Each theme folder now carries img/sized/<stem>/h<rung>.webp beside every logo. A PNG org's logo.svg / logo-full.svg is its whole upload wrapped in base64 (several MB), drawn 30-72px tall here; the rung is the smallest height covering the drawn size at 2x. - logoVariants.ts: the rung ladder, pickLogoRung, variant URLs and a per-tab record of variants that failed. - ThemeLogo tries the variant first, rung read off its numeric height: the landing logo (72) h192, the flow header (30/44) h64/h96, the completion screen (52), /org (60) and /sorry (64) h128. It had no error handling at all; now a failed variant shows the original URL exactly as it was built before. The hidden <img> is the probe the overlay mask follows.

  25. · kamo-login

    Sign-in logos load a pre-sized copy, and the original when it is missing

    Each theme folder now carries img/sized/<stem>/h<rung>.webp beside every logo. A PNG org's logo.svg / logo-full.svg is its whole upload wrapped in base64 (several MB), drawn 30-140px tall here; the rung is the smallest height covering the drawn size at 2x. - logoVariants.ts: the rung ladder, pickLogoRung, variant URLs, a per-tab record of variants that failed, and useLogoVariantSrc for images that are not a ThemeLogo. - ThemeLogo tries the variant first (rung read off height / maxHeight / width): the sign-in and recover logo (maxHeight 140) h384, the dialog logo (96) h192, reset-password and verify-email-change (52) h128, logout (120) h256. On an error it shows the original URL exactly as it was built before, then walks the extension fallbacks as it always did. The hidden <img> is the probe the overlay mask follows. - The 48px animated mark on sign-in and recover: h96, then the original. Its preload waits for mount, so the hydrating render no longer fetches the whole original. - WorkspacePicker rows (~30px): h64, then the org's logoUrl, then the monogram.

  26. · EmailService

    Inline pre-sized PNG logos; footer badges served at the drawn height

    Transactional emails only inlined a logo when the original was an SVG under 512 KB. A PNG logo org's email linked the whole upload instead — crm's 2.2 MB logo-full.png for a header drawn at most 48px tall. - EmailTemplateService asks EmailLogoService for the pre-sized PNGs SecurityService now cuts beside every logo (img/sized/logo-full/h96.png for the 48px header, img/sized/logo/h64.png for the 28px footer mark), inlined as cid: parts and cached 10 min. Per logo, the old path (rasterize the original SVG, else link it) remains the fallback while an org's copies do not exist yet. - **************** takes ?h=: a PNG/JPEG badge taller than the requested height (snapped up to 32/48/64/96/128) is scaled down with area averaging and cached in-process; GIF/WebP/SVG, short badges and any failure get the stored bytes, so every URL in mail already sent is unchanged.

  27. · SecurityService

    Pre-sized copies of every org logo beside the original

    Every surface drew org logos from the original upload: 2.2 MB for crm's full logo at a 30-56px nav/header, 2.95 MB (its .svg stand-in) in the email composer, 4.3 MB for its traced lifestyle SVG. ThemeLogoVariantService now keeps **************** + manifest.json for all eight logo slots (stem = the original's file name), cut by ConversionService /image/logo-variants. Clients pick the smallest rung >= drawn height x 2 and fall back to the original. Originals are untouched. - upload-logo: the old logo's copies are deleted first (nothing stale is ever served), then the new ones are cut; the request waits at most 20s, the rest finishes in the background. After the ext is saved. - provisionFull/provisionUpdate: ensureAll re-cuts only stems whose source bytes changed (SHA-256 in the manifest), drops copies of a logo that is gone. Before writeConfigFiles, like the favicons. - DataLoader: a background backfill for every live org (not on the boot path). - img/sized/ is never seeded from the base theme.

  28. · kamo-signer-monorepo

    Draw the org's logo from its pre-sized variant, the original only if it is missing

    A signing page drew the org's uploaded logo — crm's logo-full.svg is 2.95 MB — at 28-44px tall. The theme service now cuts img/sized/<stem>/h<rung>.webp beside every logo; ask for the smallest rung that is sharp at dpr 2 and fall back to the original URL, exactly as it was built before, on any error (folders not yet backfilled, a failed generation). - packages/theme logoVariant.ts: LOGO_RUNGS, pickLogoRung (dpr 2), logoVariantUrl, logoVariantFromUrl; exported from the theme package. - ThemeLogo: the variant is the first attempt, sized from the caller's numeric height, else maxHeight, else the width bound (no bound: the original, as before). An error on it goes to the original, then the existing extension walk and fallbackText. The overlay mask follows the hidden <img>, which is the probe. TopBar h64, **************** h96. - Quote letterhead (max-height 56 -> h128): QuoteLogo derives the variant from branding.logoUrl at render time, so quoteThemeAlias still reads the folder from the unchanged URL. The page is server-rendered, so the mount also catches a variant that 404ed before hydration.

  29. · kamo-apps

    Logos ask for a pre-sized variant, and fall back to the original

    An organization's logo is drawn 30-180px tall here but downloaded whole: crm's logo-full.png is 2.2 MB, its .svg stand-in 2.95 MB. ThemeLogo now asks for img/sized/<stem>/h<rung>.webp first, the rung sized for twice the tallest it can be drawn (read off its own height/maxHeight/width props: the rail 128, the top bar 64, the received header 96, the portal sign-in 384, the portal shell 64). A missing variant falls back to the original exactly as it was built before, and only then walks the extension list. The overlay mask paints with a variant only after the hidden image has loaded it. The loan officer's photo asks for the 150px _md cut instead of the full upload; its error falls back to the original, then to initials.

  30. · ConversionService

    /image/logo-variants cuts a logo into pre-sized PNG + WebP rungs

    Org logos are uploaded once and drawn everywhere at 24-120px, and every surface downloaded the original: crm's logo-full.png is 2.2 MB (its .svg stand-in 2.95 MB, which is what the email composer links at 40-48px), its traced lifestyle SVG 4.3 MB. LogoVariantService renders any logo (SVG via Batik, raster via ffmpeg with a pinned demuxer) to a height ladder 32..1024 in both PNG (email) and lossy WebP with alpha (web, mobile), never upscaling a raster and capping width at 4096. One ffmpeg process per logo. The endpoint answers a zip of h<rung>.png/.webp + manifest.json. SVG: embedded data: images are allowed (Figma exports are one), every other resource and all scripts are refused; a plain SVG 1.1 DOCTYPE (Illustrator) is dropped, one with an internal subset is refused. Measured on real logos: crm logo-full h128 = 67 KB PNG / 19 KB WebP.

Your universe is waiting.

Start free in minutes. Add products whenever you are ready — no migration, no new logins.

Free forever — no credit card required