Nenda kwenye maudhui
KAMOUniverse

Mabadiliko

Nini kipya.

Kila kuboresha jukwaa, kama ni meli.

  1. · kamo-marketing

    Polish video hero unit with modern animations and optimizations

    - Add poster image for instant visual feedback during video load - Implement loading skeleton with pulsing animation - Start video muted (browser autoplay policy compliance) - Add preload="metadata" for efficient video loading - Enhance button styling: larger (h-12 w-12), better border radius (rounded-xl) - Improve shadow with dual-layer effect (24px + 8px) - Implement smooth container fade-in animation (0.8s easeOut) - Add AnimatePresence for smooth mute/unmute icon transitions - Enhance hover effects with scale, shadow, and background transitions - Add tap animation for tactile feedback (scale 0.95) - Track video load state for smooth opacity transitions

  2. · KlusterServices

    KAMOTEAM shared folder on every desktop

    desktop-1 hosts /srv/kamoteam and exports it over NFSv4; every VM user gets ~/Desktop/KAMOTEAM as a symlink (via /etc/skel for new accounts and the agent for existing/adopted ones), and the workstation mounts the same directory. The VM holds the data because it is the always-on side — the workstation is a WiFi laptop on DHCP, so hosting there would drop the folder for VM users every time it sleeps and break their mount whenever its address moved. Full read/write needs three things together, and omitting any one gives a share that looks right until the second person writes: the kamoteam group, setgid plus a DEFAULT POSIX ACL (without it a 022 umask makes each user's files read-only to everyone else), and NFS all_squash to a fixed identity so the workstation's uid 1000 is not silently treated as the VM's uid 1000. NFSv4 only: one port, no rpcbind, which is what makes it forwardable through KubeVirt masquerade. Exposed to the LAN as NodePort 32049.

  3. · kamo-marketing

    Replace hero text unit with autoplay promo video

    Swap the homepage hero copy, CTAs, and stats for a looping muted video while keeping the rotating background treatment.

  4. · KlusterServices

    Alert on OOMKills, and on the occupancy that precedes them

    Nothing was watching memory. MediaService was OOMKilled every few hours for most of a day and EmailService six times, and the way anyone found out was a chat window that had quietly stopped loading messages — a container dying and restarting inside a second reads as a broken feature, not as an outage. Three rules. ContainerOOMKilled fires on the event; ContainerOOMKillLoop separates a one-off from a pod being killed repeatedly, which restarts fast enough to still show 1/1 Running. ContainerMemoryNearLimit is the one that would have caught this days early: >85% of the limit held for 30 minutes. For a JVM on MaxRAMPercentage that is not a busy pod, it is a pod sitting at its heap ceiling with non-heap stacked on top, which is the steady state immediately before the kernel intervenes. A fourth, informational, flags containers with no limit at all — they cannot be OOMKilled by their own cgroup but can take a node down. Verified against real data rather than assumed: the expressions were evaluated on this cluster, and querying the last 12 hours shows reason="OOMKilled" series for exactly kamowsemail (18:33-01:28 UTC) and kamowsmedia (21:28-01:03 UTC) — the two pods that were failing. The rules would have fired critical on both. An alert that silently matches nothing is the same failure as no alert, so that check mattered more than the YAML. Applied by hand; monitoring/ is not a CI apply target.

  5. · KlusterServices

    Block outbound SSH except sage, resolve k1m1/k3m1, bundle the agent dir

    Outbound tcp/22 is rejected for every uid except root and sage, keyed on socket owner so future accounts are covered without re-running anything. Reapplied at boot by a systemd unit, since nftables rules do not survive a reboot. It is a guardrail, not a boundary: kamodev has passwordless sudo apt, which is root-equivalent — the two requirements are in tension and that is documented. k1m1/k3m1 hosts entries go into the cloud-init TEMPLATE as well as /etc/hosts, because manage_etc_hosts regenerates the file on every boot and a plain append silently vanishes. Uses LAN IPs; the WireGuard/CGNAT overlays are retired and 10.0.50.1 is only the node-to-node link. cloud-init now carries desktop/agent/ as one deterministic tarball rather than a placeholder per file — the set went from three files to ten, and each addition otherwise meant editing the template and the CI render step too.

  6. · KlusterServices

    Standard toolchain, taskbar pins and passwordless installs for all users

    Adds krita and maven **************** were already present), pins Chrome, VS Code, Kate, Spectacle and Krita to the taskbar for every user, and lets dev users install software without an admin password. Passwordless install needs BOTH halves: sudoers covers the terminal, polkit covers Discover and every other graphical installer. Scoped to package management via the kamodev group rather than blanket root — see the comments in each file for why that is a smaller blast radius but not a security boundary. Taskbar: new accounts inherit the template from /etc/skel at useradd time; existing accounts get only their launchers= line rewritten, so a personalised panel is not thrown away to fix a list of icons.

  7. · kamo-internal

    Gate the Linux desktop icon on a provision, not on god

    The icon now appears exactly when the connection behind it will work — the SSO endpoint enforces the same rule, so the two cannot disagree. Windows and Master still share one credential, so they stay on isGod.

  8. · kamo-internal

    Dev Machine dialog on the administrator card

    Keeps three states apart that one toggle would collapse: never provisioned (needs a password), provisioned but locked (needs only the switch), and failed (needs the error and a retry). The switch cannot invent a password nobody chose.

  9. · kamo-internal

    Client module and proxy routes

    *** is forwarded explicitly — buildForwardHeaders sets X-OTK but not the session token, and the dev-machine endpoints resolve the caller from it. The action segment is checked against a fixed set rather than passed through.

  10. · SecurityService

    Per-member SSO credentials and a preflight that explains failures

    Linux is gated on an enabled provision rather than god, and served with the member's own credentials. The connection is omitted from the token entirely when they have none — offering one that will be refused surfaces as Guacamole's generic error, which is the unactionable failure this removes.

  11. · SecurityService

    Dev machine service and controllers

    Username derives from the member ACCOUNT email, not the TeamMember work email — a work address can sit on another domain and would silently produce a different account name. Reset-password requires GD and godModeActive together.

  12. · SecurityService

    HTTP client for the dev machine provisioning agent

    health() swallows transport failures and answers false so the SSO preflight can return an actionable 503 rather than a 500.

  13. · SecurityService

    AES-GCM secret codec and POSIX username derivation

    Codec is deliberately reversible — RDP auto-login needs a real password — and uses GCM so a tampered or wrong-key row fails loudly rather than decrypting to noise that would be handed to Guacamole.

  14. · kamo-shared-library

    Dev_machine_accounts entity, status enum and repository

    status and enabled are deliberately separate: a provisioned-but-disabled account keeps its home directory, so re-enabling restores access rather than rebuilding.

  15. · KlusterServices

    Agent secrets in CI, guacamole liveness, availability alerts

    Alert expressions verified against the live Prometheus. Uses kube-state-metrics rather than kubevirt_* — the KubeVirt metrics endpoint is not scraped here, so rules written against it would read as coverage while never firing. Selector label is release=kps to match the Prometheus CR's ruleSelector. Secret handling runs with xtrace off so values stay out of the CI log.

  16. · KlusterServices

    Idempotent agent installer, shared by cloud-init and live installs

    Staged under /opt/kamo-desktop-agent so install-agent.sh has a distinct source and destination. Covers netplan-by-driver, cloud-init network disable, xrdp session policy, /etc/skel/.xsession and the agent itself.

  17. · KlusterServices

    Add the provisioning agent and its unit tests

    Python 3 stdlib only. Every mutation runs subprocess with an argv list, never a shell string; passwords go to chpasswd on stdin so they stay out of /proc.

  18. · SecurityService

    Send int64 ids to the browser as strings, without touching sessions

    CockroachDB unique_rowid() ids are 19 digits, past Number.MAX_SAFE_INTEGER, so an id sent as a JSON number arrives in the browser with its low digits rounded away and every lookup made with it misses. 163 handler responses put raw Long org and member ids into ad-hoc Maps, so this was not something DTO-by-DTO work could close. The reason it stayed open is that the obvious fix takes the platform down. The mapper that serves responses is the same bean KSessionService writes the *** session map to Redis with, and ids are read back out of that map by ((Number) ...).longValue() -- 65 times in this service, and again in DocsService, MediaService and ConversionService, which read the same payload. Quote Long on that bean and every one of those throws. So the rule is attached to the MVC message converters instead, on a copy. Responses get it; sessions, NATS publishers and the service-to-service clients keep the numeric mapper they have always had. It is also not a blanket Long -> String. Long is not a synonym for id here: epoch millis on SecurityRoleDTO and the branch-type and security-model DTOs, LeadIntakeEndpointDTO's counters, Page.getTotalElements(), byte sizes and durations are all Longs the browser does arithmetic and new Date() on, and most arrive inside Maps where a mix-in has no key to attach to. The serializer switches on value instead: above the JS safe range it writes a string, below it a number. Every unique_rowid is above, every timestamp, count and size is below, and fields nobody has classified yet are handled by the same rule. Verified against the peers that read these responses: KamoJwtService and DavAuthFilter already accept either form (instanceof Number ? ... : Long.parseLong(...)).

  19. · kamo-login

    Replace the stock alert with an animated status plate

    The inline sign-in messages ("Login successful! Redirecting...", "Invalid username and/or password.") used MUI's default Alert, whose pastel fill plus matching pastel text read as generic against the glass login card. StatusMessage inverts that formula: a frosted near-white plate in the same glass family as the card, with the severity colour concentrated in one icon chip, a wash that fades out before the text, a hairline border and a soft halo. Ink stays near-black with a whisper of the accent mixed in, so it keeps contrast against any org's --color-primary. Neutral/info messages borrow the org's own primary instead of a stock blue. Motion is one orchestrated entrance -- the plate springs in, the icon draws its own stroke, and a single specular highlight sweeps across. `pending` adds a travelling hairline for work still in flight, wired to the existing `redirecting` state so "Redirecting..." shows progress rather than just claiming it. Errors get one short settling impulse. prefers-reduced-motion collapses all of it to a plain fade. Errors announce as role="alert"/assertive, everything else stays polite. Applied to the login, recover, reset-password and OAuth error pages.

  20. · kamo-marketing

    Add the 25-year anniversary seal

    Two placements, both chosen for where the claim is already being made: Footer — 60px, beside "© 2001–<year> ... established 2025". Site-wide, grouped with the made-in mark as another trust mark. About — 168px, opening the mission section. That page is where the 25-year history is the argument rather than a passing note. Not in the home hero: that headline was just stripped back to one deliberate effect, and a gold medallion beside it would undo the restraint. One AnniversaryBadge component behind both, so the asset, its alt text, and the year it commemorates retire from a single file. The source PNG was 2.4 MB at 1254px. Resized to 512px and encoded to WebP it is 95 KB, and since next/image optimization is on, what actually ships is an AVIF derived per placement: 8.3 KB for the footer at 2x, 35 KB for About.

  21. · kamo-marketing

    Show the 2001 copyright range and the incorporation year

    Renders "© 2001–<year> KamoCRM. KamoCRM Inc. established 2025. All rights reserved." The range start is hard-coded next to the dynamic end year rather than living in the translated string, so no locale can damage it.

  22. · kamo-marketing

    Add the meeting and payroll integrations

    Zoom and Microsoft Teams for meetings, and the ten payroll vendors — ADP Workforce Now, BambooHR, Dayforce, Gusto, Paychex Flex, Paycor, Paylocity, Rippling, UKG, Workday. 33 integrations to 45, with a new Payroll & HR category and Video Conferencing promoted next to Voice. Each page reflects what its provider actually implements rather than a uniform template. Paylocity has no pay-period calendar or PTO balances, Dayforce has no pay-period calendar, and neither Paychex nor Rippling pushes labor allocation, so none of those pages claims otherwise; the auth story differs per vendor too (OAuth for Gusto, Paycor, Rippling and BambooHR, a certificate for ADP, a refresh token for Workday, service accounts for UKG and Dayforce) and each setup flow says so. Logos are the vendor assets from kamo-internal's public/logo/{meet,payroll}. The payroll set are WORDMARKS running from 0.72:1 (Dayforce) to 6.2:1 (Paychex): rendered in the square plate the icon logos use, a 6:1 mark comes out a few pixels tall. Integration now carries the asset's intrinsic logoWidth/logoHeight, and where those are set the hub card and detail page render at a fixed height with automatic width on a plate that grows to fit — the same fix kamo-internal made for the same assets. Square logos are untouched. Hub metadata named only the old five categories and hard-coded "31 native integrations"; it now covers meetings and payroll and counts the registry.

  23. · kamo-marketing

    Count apps and integrations separately with real numbers

    "Apps & Integrations" was one tile reading 31+. It is now two, each with a figure that can be re-derived rather than estimated: Apps 64 — Forgejo projects owned by `kamo`, ratestack excluded Integrations 64 — distinct third-party APIs with working client code The integration count comes off the provider registries (AiProviderType, BulkTextProviderType, SocialProviderType, VoipProviderType, IdentityProviderType, RetailProviderType, MlosIntegration) plus the payroll, meeting and MLOS closing adapters. Enum entries with nothing behind them are excluded, so the 18 unimplemented calendar providers, Encompass and Xbox do not count. Both figures are hand-maintained; the comment records how to recheck them. Four tiles need a four-up grid, so the stats row now goes 1/2/4 across the breakpoints with the tile padding and type scaled to match.

  24. · kamolos

    Enable ProxyHmacFilter — step 2, X-Org-Id is now a proven header

    los.proxy-hmac.enabled has been falling through to its false default since this service deployed, so ProxyHmacFilter existed and never ran. X-Org-Id — the header every org-scoped query in this service trusts — was unsigned and client-shaped on a live multi-tenant loan API. Safe to enable now because step 1 landed first: kamo-internal mounts the same los-proxy-hmac secret and its running pod is signing, verified before this was pushed. The reverse order would have rejected every loan request in the platform. Checked before flipping it: - the filter bypasses /actuator/**, so the startup and readiness probes still pass. Had it caught them the pod would never become ready and this would roll itself back. - it bypasses /api/los/webhooks/**, so Stripe keeps working — those are verified by Stripe's own signature instead. - kamo-internal is the only caller of kamowslos-service, so there is no unsigned service-to-service path left behind. This does not replace the org checks in LoanService and LoanScope. It raises what X-Org-Id means from "a header someone sent" to "a header this proxy sent", while those decide whether the named org may see the row. Defence in depth: neither is sufficient alone.

  25. · kamo-internal

    Sign the proxy identity headers — step 1 of enabling ProxyHmacFilter

    KamoLOS trusts X-Org-Id and X-Member-Id from this proxy. ProxyHmacFilter exists to prove those headers really came from here, but it is disabled in production: the deployment sets no LOS_PROXY_HMAC_ENABLED and no shared secret existed until now, so los.proxy-hmac.enabled falls through to false and the headers are unsigned on a live multi-tenant loan API with 15 real loans on it. The LOS route already computes the signature — it just skips when LOS_PROXY_HMAC_SECRET is absent. Mounting the new los-proxy-hmac secret is what switches signing on. This side goes first on purpose. KamoLOS ignores the signature until its own los.proxy-hmac.enabled is true, so this commit changes no behaviour anywhere; it only starts attaching X-Proxy-Signature and X-Proxy-Timestamp. Doing it the other way — validation before signing — would reject every loan request in the platform. Step 2 sets LOS_PROXY_HMAC_ENABLED=true and the same secret on kamowslos-deployment, after this rollout is confirmed.

  26. · SecurityService

    Resolve the KamoMeet logo overlay into config.json

    KamoMeet is a Meet fork with no access to OrgContext, so it learns org branding from the theme config.json this service bakes. The org stores an overlay TOKEN; this resolves it against that org's own palette and writes the finished hex as kamomeetLogoOverlayColor. Resolving here rather than shipping the thirteen-token table into the fork is the point: that repo already carries the org background slideshow as one of nine independent copies, and a second such table would drift the same way. Baking a resolved colour is also not the "frozen hex" this feature avoids in the database — config.json is regenerated on every branding save, colour changes included. The prefix match is explicit rather than "not SECONDARY means PRIMARY". A test caught what the loose version did: "#FF0000" is exactly as long as "PRIMARY", so it left an empty step and resolved to the primary base — a colour nobody chose, arriving on a screen no one on the platform side ever looks at. Unknown tokens and unset shades are omitted from config.json entirely, which is how every client reads "no overlay". The token table must stay in step with kamo-internal's logoOverlay.ts; the tests are what make a divergence a build failure.

  27. · InitializerService

    Add the kamomeet_logo_overlay column

    Third overlay surface: the KamoMeet pre-meeting screen. Same shape as the other two — nullable, no backfill, no DEFAULT. The drift guard needed only its field name adding, because it iterates the entity's overlay fields rather than naming them one at a time. That is the whole point of having written it that way.

  28. · kamo-shared-library

    Carry the KamoMeet logo overlay on Organization

    Third and last overlay surface, same vocabulary as its two siblings, plus its branch in the generic patch path. This one is different in where it ends up. The home page and loading screen read their token straight off OrgContext; KamoMeet is a Meet fork with no access to that, so SecurityService resolves this token against the org's palette and bakes the finished colour into the theme config.json the fork already fetches. The token therefore never leaves the platform, and the thirteen options exist in one place rather than being copied into a fork that has already accumulated nine independent copies of the background slideshow. Null resolves to NONE, so every existing org starts with no overlay. The kamomeet_logo_overlay column is already applied (KamoInitializer).

  29. · kamo-internal

    A logo overlay for the KamoMeets pre-meeting screen

    Third and last surface, offered next to the other two — same thirteen options, its own setting, default No Overlay. Hidden unless the org can actually reach that screen: the Meetings app switched on AND KamoMeets chosen as the provider. An org on Zoom or Teams never renders a KamoMeets pre-meeting screen, and an org without the app never opens one, so the field would be a control wired to nothing. A stored value survives while hidden, so turning Meetings back on restores the choice instead of silently clearing it. The provider is fetched once on mount. Until it answers the field stays hidden, so it appears rather than flickering away; a failed lookup resolves to KAMO_MEET, which is what the backend does for an org with no provider row — better to show a harmless setting than to hide a real one because a request failed. Unlike its two siblings this token never reaches the surface it governs. KamoMeet is a Meet fork with no access to OrgContext, so SecurityService resolves the shade and bakes the finished colour into that org's theme config.json. Only the settings UI here reads the token.

  30. · InitializerService

    Purge the reports and dashboard rows, and stop seeding VIEW_DASHBOARD

    VIEW_DASHBOARD (0), VIEW_REPORTS (1) and EXPORT_REPORTS (2) were deleted from RoleRightType today, along with the two pages they named. This removes what they left behind, across both encodings. **************** listed VIEW_DASHBOARD among the rights granted to every new organization, so it comes out too. That was the only reference outside a definition site, and the compiler is what found it — a reminder that grepping for a right name and reading the enum are not the same search. VIEW_TASKS is deliberately absent from the purge: app/tasks looks identical to the deleted pages in the source but is scheduled to be completed. Id 0 is a real id — the predicate lists it explicitly, and anything in this path that leaned on truthiness would silently skip VIEW_DASHBOARD's rows.

Ulimwengu wako unasubiri.

Kuanza bure katika dakika. Ongeza bidhaa wakati wowote wewe ni tayari - hakuna uhamiaji, hakuna logins mpya.

Bure milele - hakuna kadi ya mkopo inahitajika