Schema for object-store-only storage measurement
object_storage_days records what a sweep reads back from MinIO for the two things that carry no size in the database: branding assets and the docs workspace. Bu...
Count branding and Docstive document storage
Almost everything an organization stores records its own size somewhere — an imgs row, a kb_media row, a mailbox measurement. Two things do not. Branding assets...
Show text message attachment storage
MMS attachments live inline in the database as data: URIs rather than as files, so they had no size record and were never counted. They now have their own line.
Count text message attachments
MMS attachments are stored, and were not counted. They are also not what they look like: VoipMessageService encodes the attachment as a data: URI and writes it ...
Show every storage category separately
Call recordings, voicemails, chat attachments, HR resources and marketing materials each get their own line instead of sharing one "Documents and images" row wi...
Count each kind of stored file as its own category
Call recordings, voicemails, chat attachments, HR resources, marketing materials, meeting recordings and every kind of document are all rows in imgs, distinguis...
Measure each org's own KamoMail mailboxes
Walks every mailbox Kamo hosts and records, per arrival day, the RFC822 bytes it holds — headers, body and encoded attachments, across every folder including Tr...
Schema for per-mailbox storage measurement
**************** holds, per mailbox and per arrival day, the RFC822 bytes that mailbox is holding. Storage accounting had no honest source for mailbox bytes: th...
Measure the mail Kamo actually hosts, per mailbox
EMAIL was sourced from email_metadata.search_index, which was wrong in both directions. It missed KamoMail entirely — an org on KAMO_MAIL has its messages and a...
Say why mailbox storage can read zero
Mailbox bytes count only the mail Kamo actually hosts. An organization on Microsoft 365 or Google Workspace has its messages on that provider's disks, so its Ma...
Let a platform admin run the snapshot sweep on demand
The sweep is what makes period removals and lifetime totals for the hard-deleting domains answerable at all, so days lost to an outage are lost permanently unle...
Show storage usage on the account stats tab
The tab said "No usage data available for this period" for every organization. billingApi.getCurrentUsage() is a stub returning { meters: [] }, and UsageMeters ...
Report what an organization is storing
The account stats tab has always said "No usage data available for this period", for every organization, because getCurrentUsage() in kamo-internal is a stub re...
Schema runner for org_storage_snapshots
Mirrors OrgStorageSnapshot as explicit DDL, per the house style — ddl-auto creates the table first, so the CREATE is a fallback and the SET DEFAULTs are what ac...
Per-organization storage usage model
Nothing has ever summed storage per organization. Six entities carry a byte count per file, and no code aggregated any of them, which is why the account stats t...
Declare the **************** app split
The backfill only ever filled a plan row that was missing — deliberately, so it could not silently move a tier — which left apps that had never been priced wher...
Split Apps & Features into Available / Not available / Deprecated
One flat list mixed apps the platform offers with ones it does not and ones it has retired, so the operator had to read every chip to tell them apart. The buck...
A tenant-readable app catalog for Branch Types
Branch Types -> Allowed Apps started loading its app list from /platform-config/app-catalog, which requires a top-level org owner. The host page is gated on CON...
Add an Apps & Features tab and fix the Allowed Apps list
The new tab, to the right of Organizations, controls what every organization is offered: availability, development stage, display name, description, an operator...
Serve the app catalog and read features through it
FeatureController derived "is this app offered" from the enum's stage in four separate places, so the answer could only be changed by redeploying the platform. ...
Make app availability and stage editable at runtime
Every attribute the apps settings page renders — display name, description, stage, deprecation — was a compile-time constant in the ServiceType enum, and "Avail...
One properly categorised catalog row per app, on every entitlement root
Nine apps — POS, Marketing Tools, Calculator, Club, Inventory, Legal, Games and the two origination systems — had no catalog representation at all: no pricing r...
Server-paginated organizations console
The tab loaded every organization in one request and rendered a card each, with the backend fanning out per org, then per account, then per subscription. Search...
Fail closed on apps a plan never mentions
Turns kamo.entitlement.fail-open off now that AppEntitlementCatalogMigration has run against prod and every active plan on both entitlement roots (KamoCRM, Sign...
Serve the org directory as a filtered, sorted, paginated page
GET /orgs takes page/size/q/sort/dir plus seven filters and returns the house page envelope with estate-wide aggregates. GET /orgs/{orgId} serves full detail fo...
Batched page hydration at a constant query count
Parent titles, primary domain, the plan/market behind the entitlement and the subscription-catalog flag are each one batched IN (:ids) over the whole page, so a...
Directory row/page DTOs and the native query repository
PlatformOrgRow carries the two facets separately: entitlement (inbound subs targeting the org -- what governs it) and billing held (accounts the org owns -- non...
Org directory SQL with per-facet CTEs
Held accounts, inbound subscriptions, members, licensees and children each aggregate in their own CTE before being joined to orgs. Joining them in one pass woul...
Validated, clamped org directory request
Size clamps to [1,100] and an unrecognised sort falls back to title, so a hand-edited URL can neither request the whole estate nor reach the SQL as free text. T...
Derive entitlement mode through one shared, deterministic resolver
TrialAdminService picked its trial subscription with findFirst() over an unordered list, so an org holding both a pending and an active trial could report eithe...
Backfill app entitlement rows into the platform plan catalog
Entitlement now resolves on a typed service_type column, but no catalog row carried one, so every plan still resolved to 'everything included' via the fail-open...
Gate mortgage and personal-loan verticals on app entitlement
A MORTGAGE market appeared on /commerce whenever POS was on, regardless of the MLOS app, and /features/enable had no billing check at all — the padlock in the s...
Rename LOS to MLOS, add Personal Loan Origination System, gate commerce verticals
Apps tab now shows 'Commerce: Mortgage Loan Origination System' (the title is served from the backend enum, not the TS mirror) and a new 'Commerce: Personal Loa...
Resolve app entitlement by ServiceType; rename LOS to MLOS
Entitlement resolved ServiceType.name() against **************** The catalog's codes are marketing codes (USERS, VIDEO_CONF, HR_SYSTEM, ...) and never matched, ...
Email a member from their row
Adds a mail action beside chat, call and profile on a member row, opening the Messages tool with the recipient already filled in. It is hidden unless the org ha...
Accept 3 GiB attachments by streaming them to storage
file.getBytes() cannot represent an upload this large — a Java array stops just above 2 GiB — so every attachment past that died on an OutOfMemoryError regardle...
Stream content-addressed storage so an object can exceed heap
Chat attachments are moving to a 3 GiB ceiling. storeContentAddressed took a byte[], which cannot express that at all — a Java array stops at Integer.MAX_VALUE ...
Title the mortgage form panes Borrower(s)
The Loan/Borrower/Subject Property form serves every borrower on the file, so the tab and pane titles now come from translations and read Borrower(s). Loan and ...
Drive the email template off the From address, behind an on/off switch
The template picker in the composer footer is gone. Which template a message wears is not a separate decision from who it is sent as: the member's own primary a...
Switch mailboxes from the same searchable picker the composer uses
The switcher was the weaker half of the pair: a select holding every mailbox the member can open, which for org-wide access is every mailbox in the org. It also...
Let /sendable drop the alias group for callers that open mailboxes
The /messages mailbox switcher offers the same set as the composer's From field, minus one thing: an alias cannot be opened. Mail addressed to it lands in the m...
Download the executed document, and choose a real signing order
The sender had no route to the finished document at all. EnvelopeSummary advertised a signedDocumentUrl pointing inside the kamo-signer app's own BFF, authentic...
Preview variables for the e-signature templates
The admin template editor renders every placeholder blank for a key with no sampleVariables arm, so the seven esign templates previewed as a page of empty gaps ...
The five envelope-lifecycle email templates that were never written
An e-signature envelope had exactly two templates: the invite and the step-up code. Nothing existed for anything that happens AFTER a signer signs — which is wh...
Rebuild the mortgage market settings into seven coherent tabs
The MORTGAGE market page carried fifteen tabs, twelve of them lender config, and several answered the same question in two places: hedging was switched on under...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.