Skip to content
KAMOUniverse

Changelog

What's new.

Every improvement to the platform, as it ships.

  1. · kamo-internal

    Gate My-Profile backgrounds by the new rights + member/org slideshow

    - Gate the My Profile "Background & Theme" section by MANAGE_OWN_BACKGROUND_IMAGES (self) / **************** (others); let a background-admin open another member's profile. - Persist the org-vs-own choice via each background's isInSlideshow flag (org => none active, own => all active) — no schema change; the fetch heuristic now derives the toggle from active images to match what the slideshow serves. - OrgSlideshowLayer prefers the member's own active backgrounds (GET **************** fail-soft) and falls back to the org backgrounds. New thin /active proxy route.

  2. · kamo-shared-library

    Add Manage Own / Others Background Images rights (General group)

    Adds MANAGE_OWN_BACKGROUND_IMAGES (165) and **************** (166) to RoleRightType, serviceType null so they land in the General settings group (mirrors the STATE_LICENSING_*_OWN/ALL precedent). Gate the per-member background-images feature on My Profile. Append-only; version unchanged.

  3. · SecurityService

    Notify the assigned loan officer when a loan's borrower docs are all received

    Fires LOAN_DOCS_READY_LO at the satisfy points alongside the borrower DOC_COMPLETE. Resolves the LO via loanFile -> **************** -> Member; self-guarded to the loan being fully collected, an assigned LO, and a resolvable email.

  4. · kamo-shared-library

    LOAN_DOCS_READY_LO canonical template — notify assigned LO when a loan's docs are all in

  5. · kamo-internal

    Assign a loan officer to a deal from the originator section

    The read-only Loan Originator section now has a loan-officer picker (org members via /members/names) + Assign, calling PUT **************** at the transaction level (inherited by all child loans). On assign it re-fetches the app so the live projection immediately reflects the LO's name/NMLS/state-license. Hidden once frozen (as-originated). This makes the whole system-managed originator operable — before this there was no way to set the assignment, so the LO fields always projected empty. (New UI strings inline pending an i18n pass, matching the section's existing notice.) Verified: tsc --noEmit clean.

  6. · kamo-internal

    Provider config fields for cloud mail access

    Add the config inputs the EmailService resolver reads to authenticate cloud mailboxes: - Google Workspace: service-account key (JSON) for domain-wide-delegation IMAP. - IceWarp: mail host, service mailbox password, transport security. - Exchange (on-prem BASIC/NTLM): mail host, service mailbox password, transport security.

  7. · EmailService

    Route all cloud providers' mail through IMAP/SMTP with OAuth2 (XOAUTH2)

    Cloud providers (Microsoft 365, Exchange Online, Google Workspace, Zoho, IceWarp) inherited read/send from **************** but the resolver never populated IMAP/SMTP credentials for them, so every folder/message read threw MailboxNotConfiguredException -> the "email not configured" screen (e.g. org optionone on M365). KamoMail worked only because it alone received credentials (internal Dovecot). Now all providers flow through the one IMAP/SMTP path; KamoMail is unchanged. - MailCredentials: full connection spec **************** - ImapStoreFactory + EmailSmtpService: SASL XOAUTH2 (native com.sun.mail) or basic auth per spec; STARTTLS required (fail-closed) so tokens never transit cleartext. - MailProviderResolver: per-provider credential wiring + member default mailbox + OAuth token refresh. M365/Exchange-Online use app-only client-credentials tokens (outlook.office365.com; **************** tenant derived from the mailbox domain; Google uses service-account domain-wide delegation; Zoho uses the stored OAuth token; IceWarp/on-prem Exchange use basic auth from provider config. - EmailImapService: resolve Sent/Drafts via RFC 6154 SPECIAL-USE so sent copies land in the provider's native folder (Gmail [Gmail]/Sent Mail, Exchange Sent Items). - ProviderController: persist config-form fields into the encrypted configJson (previously dropped); list capabilities without minting a token. - Removed the throwing RealtimeNotifier stubs from cloud providers (polling covers realtime; no push-webhook infra). M365/Exchange-Online require Application IMAP.AccessAsApp + SMTP.SendAsApp with admin consent and New-ServicePrincipal in Exchange Online (scopable per mailbox).

  8. · kamo-internal

    Deal-scope a vault upload from the account documents tab

    The upload dialog now offers an optional "Deal" selector (the account's transactions); choosing one stamps loanTransactionId so a time-sensitive doc (credit report, bank statements) is bound to that deal and only satisfies its loans — while "Account-wide" keeps the timeless reuse for identity docs. Completes the transaction-scoped document model end-to-end. Verified: tsc --noEmit clean.

  9. · SecurityService

    Vault upload can deal-scope a document (loanTransactionId)

    The vault upload now accepts an optional loanTransactionId, stamped onto the MemberDocument so a time-sensitive doc (credit report / bank statement) can be bound to a specific transaction — the satisfaction guard then applies it only to that deal's loans. Omitted = account-timeless reuse (unchanged). Surfaced on MemberDocumentDTO. Verified: mvn compile clean.

  10. · kamo-internal

    Per-device background switch — plain gradient or org photo slideshow

    Adds a two-icon switch top-right (under the trial pill; it rises into the pill's place once the trial ends) to choose the console background: - Plain gradient (default, best performance) — the existing oklab-blob backdrop. - Org photo slideshow — reuses the same themed assets the login/register sites use: <themePath>/json/config.json's backgroundImageCount → <themePath>/img/bg/{n}.webp, preloaded with 404s dropped and crossfaded (respects prefers-reduced-motion; falls back to the plain field if no images resolve). The choice is saved PER DEVICE in localStorage (useBackgroundMode), not server-side, because it's a rendering/perf choice that should stay local. The shared Backdrop honours it (home + settings); the toggle lives on home. Each icon has a tooltip. New home.background.* strings shipped to the dictionary.

  11. · SecurityService

    Freeze the originator snapshot at initial Loan Estimate (Reg Z/TRID)

    Completes the originator lifecycle: a shared resolveProjection() now feeds BOTH the live read projection and the freeze (no drift). On issuing an INITIAL_LE disclosure set, MortgageDisclosureController freezes the as-originated LoanOriginator — captures the projected LO + org + state-license onto the WORM row and stamps frozenAt/ frozenState (idempotent; the @PreUpdate guard blocks any later change). After this an org/LO rename never alters who the borrower originated with. Verified: mvn compile clean.

  12. · kamo-internal

    Transaction-first pipeline — PrimeReact expandable rows

    Replaces the flat loan grid with a PrimeReact DataTable of TRANSACTIONS that start collapsed and expand IN PLACE to their member loans (no two-click drawer), so a steward opens a specific loan directly. Parent columns: Transaction #, Deal Status, Borrower(s), Loans, Target Close; child rows: Loan #, Borrower, Property, Amount, Status, Open. Orphan loans render as single-loan rows. Keeps search, status filter, CSV export, New, and the Board/HMDA entry points. PrimeReact 10 (MIT). Also commits the refactor design spec. Verified: tsc --noEmit + next build both clean.

  13. · kamo-internal

    Re-add the trial pill top-right (color-swapped NavPri pill)

    Keeps the trial line in the masthead status strip and re-adds a prominent pill in the top-right so an org's trial mode stays obvious. It's the NavPri expanded trial pill with its fill/text colours swapped (primary background, white text/icon) using the same **************** label; shown only for staff (TEAM_MEMBER) on a TRIAL entitlement.

  14. · kamo-internal

    Live "insert between" while dragging, not just drop-to-swap

    Previously the order only changed on drop, so it read as swapping into a slot. Now the list re-flows live as you drag: dnd-kit onDragOver moves the dragged option to the hovered option's slot (arrayMove), so the member sees exactly where it will land between the others. useHomeLayout.move advances its order ref synchronously so consecutive drag-over events compose correctly, and takes a save flag so the live preview skips writes; the final order persists once on drop via a new commit().

  15. · kamo-internal

    Sibling-loan switcher on the application view

    A transaction can group multiple loans; the app header now shows a switcher to jump between the deal's sibling loans (hidden for single-loan deals) instead of only being reachable via the pipeline. Verified: tsc --noEmit clean.

  16. · kamo-shared-library

    LoanFileDTO.loanTransactionId (app view knows its deal)

    Surfaces the loan's transaction so the /applications view can show/switch sibling loans. Verified: mvn install clean.

  17. · kamo-internal

    **************** (FE type)

    Mirrors the backend field for the pipeline Transaction # column. Verified: tsc --noEmit clean.

  18. · SecurityService

    Surface transactionNumber on TransactionSummaryDTO (pipeline Transaction #)

    The per-transaction pipeline row now carries the human transaction number. Verified: mvn compile clean.

  19. · SecurityService

    Originator projection resolves the per-state license

    Completes the live originator projection: the assigned LO's StewardLicense (NMLS_MLO) for the SUBJECT PROPERTY's state (from property.address.subdivision, normalized to ISO-3166-2), a currently-valid one, populates originatorStateLicenseId. Verified: mvn compile clean.

  20. · kamo-internal

    Drag-and-drop to rearrange launchpad options, saved per member

    Members can now reorder their home options by dragging, and the arrangement is persisted server-side per member (survives reloads and follows them across devices) via the same generic ui-preferences store the nav layout uses — a new `homeLayout` key holding the ordered option ids. - useHomeLayout(availableIds) loads the stored order, merges it with the currently-gated options (new options append, removed ones drop), and persists reorders with an optimistic, debounced PUT to **************** - dnd-kit DndContext over the masonry: MouseSensor (5px) + TouchSensor (250ms) so a quick click still opens a card / its sub-item while a press-drag rearranges (pointer-only, like the live navs). Reorder commits on drop the drop target rings. FeatureCard gets draggable={false} so native link-drag doesn't fight dnd-kit.

  21. · kamo-internal

    Make Materials the first tab + default landing; remove Overview

    Overview tab removed; Materials is now first. The /marketing index redirects to the first accessible tab (Materials by default), so anyone opening Marketing lands on Materials.

  22. · SecurityService

    Live originator projection — org + assigned LO, on read

    GET /mortgage-apps/{uid} now overlays a LIVE, system-managed originator projection (computed on read, never stored) unless the row is frozen: org name/NMLS/address from the Organization, and loan-officer name/NMLS/teamMemberId from the member ASSIGNED to the loan's transaction (legal name via User.EmbNameFull.formatFull(), NMLS via Member.nmlsId). Stale hand-entered identity is cleared — the section reflects only the source of truth. A frozen row (initial LE issued) is the WORM snapshot, left untouched. Remaining for the full projection: the per-state StewardLicense (needs the subject state on the app graph) and the freeze trigger at initial LE issuance.

  23. · kamo-internal

    Loan Originator section is read-only / system-managed

    The Loan Originator card no longer hand-edits URLA §9 data (the duplication of **************** it was re-capturing). It renders read-only: a LIVE projection of the assigned loan officer + org before the initial Loan Estimate, then the frozen as-originated snapshot after (frozenAt/frozenState/source now on LoanOriginatorDTO). Removes the editable inputs + debounced patchOriginator autosave. Backend projection/freeze wiring lands next (SecurityService).

  24. · SecurityService

    Transaction-level loan-officer assignment + transaction number

    ensureTransactionForApp now stamps an org-unique transactionNumber (TXN-<uid>). New **************** + PUT /mortgage-apps/transactions/ {uid}/loan-officer sets the deal's assigned loan officer (a Member.id, inherited by all child loans) — the single source that will drive the system-managed LoanOriginator projection/snapshot (originator is never hand-entered). Operator- gated, org-scoped.

  25. · InitializerService

    Originator/transaction-first columns (@Order 90)

    Additive nullable columns + org-unique transaction-number index for the MLOS originator/transaction refactor: mlos_loan_transaction (transaction_number + assigned_lo_*), loan_originators (frozen_at/frozen_state WORM markers), mlos_member_document (loan_transaction_id deal-binding). Idempotent.

  26. · kamo-shared-library

    Originator/transaction-first schema — LO assignment, WORM freeze, deal-scoped docs

    Backbone for the system-managed-originator + transaction-first refactor (spec 2026-07-08): - LoanTransaction: transactionNumber (org-unique) + assignedLoanOfficerMemberId (+ audit) — the transaction-level LO assignment that drives the originator projection, inherited by all child loans. - LoanOriginator: frozenAt/frozenState + @PreUpdate WORM guard — the originator is a LIVE projection of the assigned member/org until the initial Loan Estimate issues (Reg Z/TRID), then an immutable as-originated snapshot. LoanOriginatorDTO exposes frozenAt/frozenState/source (LIVE|SNAPSHOT). - MemberDocument: loanTransactionId deal-binding — a time-sensitive vault doc (credit report, bank statement) is transaction-scoped; NULL = account-timeless. Time-validity stays governed by the existing FreshnessPolicy (NOT duplicated). Projection/freeze/assignment logic lives in SecurityService (which depends on both donotmodify and mlos), not here — the locked core stays mlos-decoupled.

  27. · kamo-internal

    Add Product filter dependent on Vendor

    Adds a Product filter to the /leads filter bar, immediately right of the Vendor filter. It behaves like the Status/SubStatus pair: disabled until a vendor is selected, its options are the products belonging to the selected vendor, and it clears when the vendor changes. The client-side filter predicate and filter state already existed; this wires up the dropdown.

  28. · kamo-shared-library

    Shared AusEngineClassifier — one source of truth for the §8.2 agency mapping

    The program→engine, run→engine, and decision→gate mappings were local to **************** The capital-markets lock-stage AUS hard-stop needs the identical classification, and a fair-lending gate must not have two copies that can drift. Extract the three pure mappings into **************** (static, exhaustive switches = compile-forced completeness / §8.2 drift guard). Pricing's resolver now delegates to it; capital-markets' LockStageAusGate uses the same. Additive, no schema.

  29. · kamo-internal

    AI auto-tag + caption UI; provider cost-tier/vision settings (P3)

    Settings → AI → Providers: each provider gains a Cost tier (Light/Medium/Heavy) select and a Supports vision toggle (+ list chips). Materials drawer: 'Suggest tags with AI' (accept chips to apply) and 'Generate description with AI' — both call the org's own configured providers via MediaService→AIService; fail-closed with a clear message when no vision provider is set up.

  30. · AIService

    Provider cost-tier/vision passthrough + internal one-shot vision endpoint

    AiProviderController reads/echoes costTier + supportsVision. ProviderRoutingService gains selectVisionProvider(orgId, tier). **************** runs a one-shot vision completion reusing the org's provider/adapter (no session/RAG/tools). New AiInternalController POST /api/ai/internal/vision (X-Internal-Auth, OTK-exempt) for service-to-service callers.

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