Stop the pay-group entity sitting on a legacy table
BillingGroup was mapped to billing_groups. That table already existed — one of a seven-table subsystem (billing_group_members, _subscriptions, _licenses, _invoi...
Never let the quota service stop a service from starting
StorageQuotaService took its five repositories as required constructor arguments. Services in this estate component-scan com.kamo with a wildcard but declare an...
Pin how the pool divides into per-mailbox limits
Dovecot needs a byte figure per mailbox, but storage is one organization-wide pool across eighteen domains. The division has to hold one property above all — th...
Make a failed payment mean something
PAST_DUE counted as fully live in both entitlement resolvers, with no timestamp and no cap, so a customer whose card failed kept every feature for as long as St...
Pin the delegation-mode semantics
These predicates are what the surfaces hide themselves on, so getting one wrong either offers a button that cannot work or silently permits a charge the owner m...
Let members and groups pay for themselves, under the owner's rules
Three things were missing and one was actively harmful. account_members carried a global unique on member_id — a member could administer exactly ONE billing ac...
Show what turning off fail-open would cost
kamo.entitlement.fail-open grants any app the plan matrix never mentions. It is documented as a temporary valve for the catalog backfill — turn it off once the ...
Opt-in auto-expand with a ceiling
Two columns on the subscription that holds the blocks: whether to buy another one automatically when the organization runs out of room, and the most it may ever...
Record what each Stripe price was created with
The mapping stored only which price id to use, so a catalog price change could not even be noticed. ensurePlanPrices short-circuited on the row's existence and ...
Give the storage allowance an effect
Storage was measured across nineteen domains, nightly, with a build-failing coverage test — and nothing anywhere computed an allowance or refused a byte. The pl...
Give add-ons a unit size and a purchasable quantity
An add-on's unit only ever existed as prose inside its description ("Each unit adds 25 GB") and as a hardcoded constant in the marketing site, so nothing could ...
Durable record of a conversation the client raised by itself
MediaSessionMember gains AUTO_OPENED_AT, beside msgIdLastViewed because it is the same grain — a fact about one member's relationship to one session. That one a...
Restore the CRLF endings on the attendee repository
The previous commit rewrote the file through a script that normalised its line endings, so a twelve-line addition arrived as a fifty-line rewrite. That buries t...
Batched guest lookup, and delete-by-event
The event dialog shows who an event is with, and the list endpoint returns a month of events at a time. Asking for guests per event is a query per row; findByEv...
AUTO_ASSIGNMENT strategy and a durable record of an unseen handover
AUTO_ASSIGNMENT(3) is appended to SupportAssignmentStrategyType — assign immediately to an agent who is genuinely online, preferring whoever holds the least wor...
Record when an account was created
Account carried no creation timestamp at all — not a null column, no column. The account list has a Date Created column and orders by it, so that column has bee...
Geolite range index must be ASC, not YugabyteDB's default HASH
The IP lookup is a range scan: WHERE network_start <= ? ORDER BY network_start DESC LIMIT 1 YugabyteDB partitions the LEADING index column by HASH unless tol...
Scope calendars to the member, and add the org-wide publishing right
UserCalendar and CalendarEvent hung off USER_ID alone and carried no organisation. A user is global and a member is that user inside one organisation, so an acc...
PublicChatApiKey.scopes jsonb binding
A field with an initializer was skipped by the earlier jsonb sweep, so this one still bound as varchar: 'column scopes is of type jsonb but expression is of typ...
CockroachDB-only SQL that PostgreSQL rejects
CockroachDB spells the text type STRING; PostgreSQL and YugabyteDB use TEXT. 25 casts across native queries were still emitting CAST(x AS STRING) and ::STRING, ...
@Lob String must pin LONGVARCHAR or PostgreSQL reads it as an OID
Hibernate's PostgreSQL dialect maps @Lob String to an OID (large-object reference) and reads it with getLong(). CockroachDialect mapped the same annotation to p...
Count each file once per organization, not once per upload
Billing was per imgs record, so an organization that uploaded the same file five times was charged five times. The owner's rule is one charge per distinct file ...
Match stored content on BLAKE3 alone, so a browser can afford to ask
Proving possession required both hashes, and a browser has no fast SHA3-256 — js-sha3 is pure JavaScript at ~25 MB/s. Measured on a 358 MB video that was ~20 se...
Bill generated thumbnails to the organization
Thumbnails and per-channel tracks are storage an organization's uploads cause, so on the owner's instruction they now count toward that organization's total rat...
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...
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 ...
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 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...
Read the latest snapshot date without loading the history
A summary needs one date. It was reading it by loading a two-year range of snapshots and taking the max — five domains times every day of history, on every page...
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...
A platform product is not one of its own tenants
isOrgEntitledToApp exempted only the top-level org, so an org marked isPlatformProduct was billed as if it were a customer. sign.pink sits on a FREE_DEFAULT sub...
Stop a page load from persisting the entitlement projection
computeEffectiveFeatures added the caller's Hibernate-managed OrgFeature rows straight to its output and then flipped isActive on them. Its own javadoc calls th...
Pin the deprecated-alias exclusion
The catalog key is ServiceType's numeric id, and several retired constants deliberately share one with the app that replaced them — DOC_MANAGER with DOCS on 8, ...
Stop mortgage endpoints enforcing a personal-loan right
The LOS_* -> MLOS_* rename was applied to the enum declaration and nowhere else. Because the freed names were immediately re-declared as the personal-loan right...
Answer the plan-matrix question for every app in one pass
The Apps & Features tab asks whether each app appears in the plan matrix, and the per-app form scans the plan-feature and add-on tables once per app — two dozen...
Resolve deprecation through the app catalog
An app the operator retires has to disappear everywhere, not just from the apps page — otherwise it keeps showing up in the security-model editor and in entitle...
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...
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, ...
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 ...
Script the half of the scan invariant this repo cannot see
SharedLibBeanSafetyTest can only police the library: it knows a bean requires a repository from some package, and nothing about whether the ~40 services scan it...
Detect the unsafe repository dependencies instead of listing them
The guard carried 29 baselined offenders and a location exemption, and caught none of the four outages it exists to prevent. Both were guesses standing in for a...
Stop the mailbox pointer from failing startup platform-wide
DepartmentService is a @Service, and every service carries @ComponentScan("com.kamo"), so all ~40 of them construct it. Giving it a MailboxRepository therefore ...
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...
Let a department and an org point at one of their mailboxes
A department's contact email was free text, so it could name an address the org does not own -- two of the three departments with one set today do exactly that....
A manager, and a directory search that survives a big org
TeamMember gains an optional self-reference for who it reports to. columnDefinition is pinned to INT8 so Hibernate cannot copy Member.ID's DEFAULT unique_rowid(...
Let a department sit under a parent department
Departments were a flat list, so an org chart with Operations > Customer Success > Onboarding had no way to say so. Department gains an optional self-referencin...
Stamp AccountAddress dates so an address can actually be saved
DATE_CREATED is NOT NULL with a DEFAULT now(), but nothing ever set the field. Hibernate names every insertable column in its INSERT, so it sent an explicit NUL...
Make the atomic claims actually atomic, and stop starving the oldest payout
An adversarial review of the money path found the concurrency design was not running at all. @Modifying without @Transactional. Spring Data opens no transactio...
Find a member's commission splits for their own commissions view
Org-scoped rather than keyed on the member alone: ORG_ID is duplicated onto this table so a row can never be read cross-tenant by id, and a finder that ignored ...
Tume ya wanachama wa timu ya kuongoza na Stripe malipo ya uwanja
Adds com.kamo.z.shared.commission: an append-only signed ledger, a per-member balance row that exists purely to serialize concurrent withdrawals, the Commission...
Je, unaona nini kuhusu usafiri?
Kila moja ya hizi updates ardhi katika nafasi yako ya kazi moja kwa moja. Kuanza bure na kuangalia kukua wiki baada ya wiki.