Changelog
What's new.
Every improvement to the platform, as it ships.
· kamo-login
OAuth mode — the standard page untouched, tenant chrome off (IoT SP09)
/?oauth_request=<64 hex> is the OAuth sign-in page by a beforeFiles rewrite in next.config.mjs to /oauth/sign-in (the browser keeps the URL), not by a switch inside app/page.tsx: a switch on the search params makes / either a dynamic render (searchParams) or a client bail-out to an empty Suspense fallback (useSearchParams) — both change the standard page's first render. With the rewrite app/page.tsx is not edited at all, / stays a static prerender, and its HTML is identical to origin/main's but for the random field-name suffix it draws at every build. The has-pattern is mode.mjs's OAUTH_REQUEST_VALUE; Next anchors it and reads the LAST value of a repeated parameter, so isOAuthMode reads the last value too (pinned against Next's own matchHas in tests/oauth-mode). analyticsAnalytics renders nothing in the OAuth context (/oauth/... or the OAuth-mode URL) — checked in the server render too, because an afterInteractive Script still emits a preload <link> for analytics.kamocrm.com there; DynamicMetaLoader and the ribbon's org wording stand down in the same context (effects only). Nothing changes for any other route. The error card is a client component (OAuthErrorView): MUI from a server component hydrated with a mismatch (#418) in the real build.
· SecurityService
Token endpoint with PKCE, rotation with a 24-hour grace, reuse detection and RFC 7009 revocation (IoT SP09)
· kamo-shared-library
OPERATE_DEVICE and OPERATE_SECURITY_DEVICE charter classes (SP11-D2)
IoT SP11 (master §6.14, §10 SP11-D2): physical actions through the Tool Plane's iot_ pack are charter-approved per class. AiActionClass appends OPERATE_DEVICE (COMFORT/EQUIPMENT functions, automation toggles) and OPERATE_SECURITY_DEVICE (SECURITY functions, automations with a SECURITY action, CRITICAL/MAJOR alarm acknowledgements) after JOIN_MEETING, both APPROVE_BEFORE: an unnamed class resolves to its default, so every charter asks a person until it says otherwise. CharterSection.APPROVAL holds 24 (one per class). Every resolved charter gains the two classes, so every charter stamp moves once (CharterStampTest's legacy golden moves, as JOIN_MEETING's did). AiAutonomyModeTest's draft-kind pin fails on origin/main as well, untouched here.
· SecurityService
Consent view and single-use decision bound to the sign-in session, pending grants and codes (IoT SP09)
· SecurityService
O3 binds a fresh sign-in to the authorization request with an atomically spent one-time key (IoT SP09)
· SecurityService
Authorization requests O1/O2 with RFC 6749 redirect rules, PKCE S256 and rate limits (IoT SP09)
· SecurityService
Principal-provider SPI and the IoT voice-link provider (IoT SP09)
· SecurityService
Atomic one-time-key spend, request store, rate limits and redirect URLs (IoT SP09)
· kamo-login
The OAuth sign-in with the missing MFA step, and the consent screen (IoT SP09)
/api/login, /api/session/select and /api/login/mfa accept an optional oauthRequestId (64 hex). Absent, nothing changes (tests/***-cookie pins each route's answer and Set-Cookie). Present but malformed: 400 before anything is asked of SecurityService. Valid, once SecurityService answered with a one-time key and the *** id was read from OTK<otk> as today: O3 attach spends the key, the browser gets {success, oauthNext} and never the key or the workspace redirect, and the *** cookie is planted exactly as an ordinary sign-in plants it — only when consent is next (an expired request or a fault leaves no cookie). An unspendable key is told as a fault, never as a credential error. MFA and workspace answers are relayed as today; the page keeps the request id and sends it with the next call. OAuthSignIn (own markup in OAuthShell): username/password with the same proof-of-work capcha, StrikeMeter/LockoutPanel unchanged, MfaStep (new: six-digit code or recovery code, its own lockout), WorkspacePicker unchanged with logos left out (they live on theme.<apex>). MfaStep is used only here; the standard page is not touched. /oauth/consent: client and scopes, the iot.voice explanation (spec 4.24.3), NEW link (name 1-120, default Alexa) or REPLACE per listed link, eligibility reasons in words (SI, not AI), Link Alexa / Cancel; follows redirectTo only when it is https: (O4 lists no redirect URIs to compare with). **************** relay O2/O4/O5 with the *** cookie as X-***-Token, forward only consentToken/decision/link, and clear the cookie when a decision is done. handoff is handoff.mjs (not .ts) so node --test can import it.
· InitializerService
Scan the OAuth server repositories (IoT SP09)
· kamo-shared-library
Alexa link, virtual sensor and endpoint entities; IotCommand.voiceLinkId; ALEXA_* activity kinds (IoT SP09)
· kamo-login
/oauth/authorize, /token and /revoke, one shared session-cookie writer (IoT SP09)
The Kamo OAuth server's public endpoints, relayed to SecurityService (O1, O6, O7) over cluster DNS at SECURITY_SERVICE_URL (default kamowssecurity-service, as app/api/recover does): - GET /oauth/authorize forwards only the seven OAuth parameters and answers a relative 303: /?oauth_request=<id> for a valid request, the client's registered https: redirect for a redirect error, /oauth/error otherwise (an unknown client or unregistered redirect URI is shown, never redirected to). - POST /oauth/token and /oauth/revoke forward the body byte for byte with Authorization and Content-Type only (plus the caller's address for SecurityService's limit), relay WWW-Authenticate, always no-store; a fault, or any status that is not the endpoint's own (200/400/401/429/503), is 503 temporarily_unavailable — never invalid_grant. - **************** in OAuthShell (bundled Kamo logo, Privacy and Terms on www.kamocrm.com, nothing from another origin). app/lib/***Cookie.ts is the one copy of the clear-then-set *** block that /api/login, /api/session/select and /api/login/mfa each carried. The three copies were attribute-for-attribute identical, so nothing is parameterised; the helper takes the Host header the routes read. Finding, preserved on purpose: Next's cookie jar is keyed by name, so only the last set() reaches the wire — the three clears never did. tests/***-cookie pins the exact Set-Cookie headers against the old inline block, for each route end to end. npm test = node --experimental-strip-types --test tests/*.test.mjs (Node 22 takes no directory argument, and planCheckout.test.mjs imports a .ts).
· kamo-shared-library
OAuth server entities, repositories, read-only token verifier and access-log event types (IoT SP09)
· kamo-shared-library
Token generation, PKCE S256 and scope parsing for the Kamo OAuth server (IoT SP09)
· kamo-shared-library
**************** and **************** (SP04 Tasks 21-22)
The column exists in production (SP04 DDL, default TRUE).
· KlusterServices
Google Trust Services issues the platform login host's certificate (IoT SP09)
Amazon's account linking refuses Let's Encrypt, so login.kamouniverse.com alone moves to a gts-prod ClusterIssuer (ACME with External Account Binding); every other host keeps letsencrypt-prod. The EAB key id is a placeholder until operator step B1: this commit is not pushed to main before then.
· KlusterServices
Google Trust Services issues the platform login host's certificate (IoT SP09)
Amazon's account linking refuses Let's Encrypt, so login.kamouniverse.com alone moves to a gts-prod ClusterIssuer (ACME with External Account Binding); every other host keeps letsencrypt-prod. The EAB key id is a placeholder until operator step B1: this commit is not pushed to main before then.
· kamo-internal
IOT in the NoReply audit subsystems
IoT Devices SP06: EmailService audits alarm letters (iot.alarm_notice) under NoReplySubsystem.IOT; the audit filter knows it.
· kamo-internal
The OAuth audit events in the access log filters (IoT SP09)
· EmailService
Preview sample variables for the iot share letters
Rebuilt on the new shared library BEFORE IoTService sends (a missing template key 404s forever).
· SecurityService
Reserve the iot alias; pin iot.<domain> resolution
The rebuild carries DomainAliasType.IOT from the shared library. KnownAliases / SSL_PROBE_LABELS follow only after iot.<apex> answers over TLS (a later commit), so no confirmed domain is downgraded. IotEntityMappingOn62Test maps the four SP04 sharing entities on the Hibernate 6.2 line.
· kamo-marketing
Voice-assistant paragraphs in the privacy policy and terms (IoT SP09)
· SecurityService
Kamo OAuth authorization server tables and the iot.alexa client (IoT SP09)
· kamo-shared-library
Iot.share_link and iot.share_code canonical templates, IotShareEmail
· kamo-shared-library
Sharing entities and repositories
· kamo-shared-library
Sharing rules, tokens, one-time codes and masked destinations
· kamo-internal
Connector setup guide and field help on the Advanced-tab card (IoT SP08)
· kamo-internal
The remote-monitoring cards — RPM progress, home readings, clinician limits and the alert list
· kamo-internal
Enrolment wizard with the consent script, connect-a-device, time entry and the billing handoff
· kamo-internal
The Monitoring tab, the practice monitoring queue, and the organization's remote-monitoring switch

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