உள்ளடக்கத்திற்குச் செல்லவும்
KAMOUniverse

மாற்று பதிவேடு

என்ன புதிது.

அது கப்பல்கள் போல், தளத்தில் ஒவ்வொரு மேம்பாடு.

  1. · kamo-internal

    Suggest logo, colours and backgrounds from the member's site

    The Basics step already asks for a website. This reads it and offers what it finds on the three branding steps that follow, as suggestions the member can take, take one of, or ignore entirely. Nothing is ever applied without a click. The scan state lives OUTSIDE WizardFormData deliberately: that object is stringified into sessionStorage on every keystroke, and a result holds several megabytes of base64 -- persisting it would serialize all of it on every character typed anywhere in the wizard, and blow the quota besides. It fires once when the member leaves Basics, three steps before Logo, so the answer is usually waiting by the time they arrive. Every suggestion says WHERE IT CAME FROM -- "Header image", "Carousel slide 2", "CSS --brand-primary", "From your logo". That chip is the whole design: it is the difference between a system that guessed and one that looked, and it is what makes an automatic read of somebody's website feel like evidence rather than a stranger going through their files. The suggestion surfaces are tinted and hairline-edged so a member can tell at a glance which half of the screen is theirs and which half is ours. Failure renders NOTHING. No website, an unreachable site, a plain page -- these steps are optional and already work, so a member whose site gives up nothing has done nothing wrong. The one exception is a rescan they asked for themselves, and a 429, which is the one failure they can act on. The logo rail is per-slot, not per-step: the compact slot is SVG-only because the backend writes it to a fixed logo.svg key whatever the real type is, so offering it a PNG would be offering a broken logo.

  2. · kamo-shared-library

    Append-only clinical notes with amendment and cosign (SP7)

    Identity is split from content from the first row. ClinicalNote is what other records point at — an order raised from the note, an amendment to it, a disclosure of it — while every revision is a separate immutable ClinicalNoteVersion. Retrofitting append-only onto a mutable note table is a migration nightmare AND a live malpractice exposure for as long as it takes, so the split exists now rather than when someone asks for an audit trail. Amendments and addenda are different things and the distinction is legal. An addendum appends and the note stands. An amendment corrects, and it creates a new version WITH THE ORIGINAL RETAINED AND STILL RENDERABLE — a record that cannot show what it used to say is not a corrected record, it is an altered one, and that is how a chart stops being defensible. §164.526 patient-requested amendments are a third state again: the patient disagrees, the clinician may decoder, and either way both the request and the outcome become part of the record. Append-only is enforced by a throwing @PreUpdate/@PreRemove, never by @Column(updatable = false). Hibernate SILENTLY OMITS a non-updatable column from the UPDATE, so the write appears to succeed and the value just does not change — the worst of both worlds. The database half is a REVOKE, because entity callbacks are bypassed by bulk JPQL and native SQL. Cosign is modelled because a note pending a supervisor's countersignature is not a finished record, and under incident-to rules the supervision is also what makes the visit billable — so isFinal() is a clinical AND a financial predicate. aiAssisted is on the version because it cannot be backfilled: once a note is signed, nothing distinguishes what an ambient scribe drafted from what a clinician typed, and AB 3030-style rules require the disclosure. HPI, ROS and exam stay as prose on purpose — forcing narrative into discrete fields produces worse notes and worse data. Structure earns its place in the assessment and plan, where each problem links to its orders and its diagnosis, which is what makes the note, the order and the claim fall out of one act of documentation. 1742 tests green.

  3. · kamo-shared-library

    Prescriptions and orders with real loop closure (SP7)

    MedicationRequest keys the drug to RxCUI and never to a vendor identifier. FDB's GCN_SEQNO, Medi-Span's GPI, MEDID, HICL_SEQNO are the vendor's moat, and keying internal data to them is the difference between a drug-database switch being a data migration and being a contract renegotiation. intent is enforced rather than decorative: a proposal is what decision support or an AI produces, and treating one as an order sends a prescription nobody signed. reported carries the other asymmetry — a patient-reported medication is NOT ours to refill but absolutely counts for interaction checking, because a drug interaction does not care who wrote it. isControlledSubstance is denormalised because EPCS, PDMP checks and two-person access control all branch on it and none of those paths should join a drug database to find out. The sig text is kept verbatim beside the structured fields, because a parsed sig is an interpretation and the prescriber's words are the legal instruction. ServiceRequest carries closureStatus SEPARATE from FHIR status, and that is the point of the table. Missed follow-up on abnormal results and unclosed referrals are the top two ambulatory diagnostic-error categories, and an order placed and never resulted is invisible in most systems precisely because nothing is wrong — no error fires, no queue fills, it just sits. A request can be completed in FHIR terms because the lab ran it, while the loop is open because nobody reviewed the result or told the patient. Only the second question prevents harm. expectedBy exists so overdue has a definition at all; without it the weekly "ordered and never resulted" digest has nothing to select on. closureReason is required to close without a result, so "closed" can never quietly mean someone got tired of seeing it in the queue. 1728 tests green.

  4. · kamo-shared-library

    Problem list, allergies and observations (SP7)

    The three chart tables where a modelling mistake harms a patient rather than annoying a user. Condition carries TWO independent statuses because conflating them is the classic error. clinicalStatus says whether the patient still has it; verificationStatus says how sure anyone is. A ruled-out differential is active/refuted — collapse them and you either leave a refuted condition on the problem list or delete a real one that was never formally confirmed, which is most working diagnoses. Refuted and entered-in-error are also why a condition is never deleted: "we considered this and it is not so" is information the next clinician needs. AllergyIntolerance carries a no-known-allergy SENTINEL, and this is the safety-critical one. An empty allergy list is ambiguous — "asked, nothing found" and "nobody has asked" are completely different clinical facts, and an absent row means the second while reading like the first. A prescriber seeing a blank list reasonably infers it is safe to prescribe. The sentinel is a real row recording who asked and when, so absence can mean unasked. criticality is kept separate from severity because a mild rash that is early anaphylaxis is low severity and high criticality, and only one of those tells a prescriber to stop. Observation gives value[x] real typed columns rather than one stringified one, because the type is part of what was recorded and quantity search is most of what an Observation is for. interpretation is STORED, never derived: reference ranges vary by method and population, so recomputing against today's range silently reclassifies historical results. dataAbsentReason exists because a cancelled test and one nobody ran otherwise look identical. And a correction SUPERSEDES via supersedesObservationId rather than inserting a second row — two potassiums on a flowsheet is how a clinician acts on the stale one. isUnacknowledgedAbnormal deliberately excludes uninterpreted results: flooding the inbox is how a real abnormal gets lost in the noise, and missed follow-up on abnormal results is a leading ambulatory diagnostic-error category. 1718 tests green.

  5. · kamo-shared-library

    The terminology store (SP4)

    Releases, concepts, the precomputed hierarchy closure, value set definitions and expansions, concept maps, and per-tenant licence entitlement. Releases are never mutated, only superseded. A claim reprocessed a year later must resolve codes valid on its DATE OF SERVICE — overwriting a release in place makes every historical record silently re-interpret itself against a vocabulary that did not exist when it was written, and the record looks unchanged while meaning something different. isActive on a concept is NOT a soft delete. An inactive code must still resolve or every historical record referencing it stops being readable; it means "do not offer this for new entry". ConceptClosure is precomputed and is the largest table here — SNOMED CT US Edition is ~400k active concepts and its closure runs to tens of millions of rows. That is cheaper than the alternative: :below and is-a subsumption are recursive queries, and "find every kind of diabetes" is an ordinary clinical question that must not be a graph walk at query time. Value set DEFINITIONS and EXPANSIONS are separate tables because eCQM value sets bind to a specific expansion version. Regenerating one in place changes what a quality measure counted, retroactively, and its numbers stop reproducing. Expansions keep inactive members too: a picker filters them out but validation of historical data needs them, and dropping them makes old records fail validation. ConceptMapElement.equivalence is never defaulted to equivalent. A SNOMED-to-ICD-10 map is usually wider, narrower or inexact, and treating an approximation as a fact changes what the record asserts — billing narrower than documented is upcoding, the reverse discards specificity the chart had. TenantCodeSystemEntitlement exists because two licences make this a per-tenant question. CPT is licensed per product with royalties and usage reporting, so entitlement must be a countable row rather than a config flag; SNOMED requires a UMLS licensee per organization and has territory-dependent fees. The schema consequence is specific: always store the CODE, gate the DISPLAY. An unentitled tenant gets 99213 with no descriptor — withholding the code would break their claims, withholding the text is what the licence requires. PENDING is not ENTITLED, because paperwork in flight is how a breach happens while everyone believes it is covered. Deployment note recorded on CodeSystemRelease: these are non-PHI, read-mostly, non-tenant-scoped reference tables and the design intent is a dedicated single-node Postgres — hierarchy queries are the shape Yugabyte is worst at. They live here so the model is defined and testable; moving them is a datasource change, not a schema change.

  6. · SecurityService

    Expose the scan on /api/security/org/brand-scan

    Its own controller rather than a method on OrganizationController, which already takes 25 constructor dependencies. The path still lands under the same prefix, so nothing about the wizard's routing changes. The session gate is the security boundary, not boilerplate: Spring Security is anyRequest().permitAll() here and authorization is done in the controller, so an endpoint that makes the platform fetch a caller-supplied URL must resolve-or-401 before anything else. The limiter is Redis-backed, following OrgCreationRateLimiter rather than the in-process design this was planned with -- SecurityService runs replicas, so a per-pod counter is a limit in name only, which is the trap the kamocapcha replay guard already fell into. Keyed on the USER, not the member: one person holds memberships in many orgs and keying on the member would multiply their allowance by how many businesses they are in. Fails open, because a branding step that stops suggesting anything because Redis is unreachable is worse than the burst it was slowing.

  7. · SecurityService

    Orchestrate the scan behind one cached, budgeted call

    One response rather than three endpoints because it is one scan: the logo must be downloaded before its pixels can contribute a colour, and the colours are wanted on a step the member reaches before the backgrounds. Splitting it would mean scanning the site three times. The extractors stay pure readers over a parsed page. This is the only class that decides which candidates are worth a request and the only one that knows the response has a size ceiling -- which is what lets the extractors be tested against fixture HTML with no I/O at all. Assets are deduped on their BYTES, not their URL: the same logo served from /logo.png, a CDN path and the favicon is the normal case, and three identical cards is noise. A 200 is not accepted as HTML on its own -- parked domains, captive portals and JSON error pages all answer 200, and parsing one as a page yields a confidently empty scan. Failure is quiet everywhere: unreachable, unparseable or not-a-website all return 200 with empty lists. These three steps are optional and already work; telling a member their own site failed a scan on the screen where they are building their brand turns a bonus into a defect they did not cause and cannot fix.

  8. · SecurityService

    Take hero imagery, falling back to carousel slides

    The fallback is conditional by design. A site with a hero has already chosen one picture to represent itself; sweeping its carousel too would bury that choice in a grid of eight equal alternatives. The carousel is what a site offers INSTEAD of a hero. That rule was broken in the first cut, and the tests caught it: `body` sat in the hero-container list, so `body img` matched every image on the page including every slide. The hero pass was therefore never empty, the fallback could never fire, and slides arrived mislabelled as heroes with no slide number. `body` is still accepted for a CSS background, where it is one specific declaration about the backdrop -- the asymmetry is the fix, not an oversight. Cloned slides are skipped: loop carousels duplicate their first and last slides for a seamless wrap, and offering the same photograph twice looks broken. Everything is gated on REAL decoded dimensions rather than on where it was found, because a hero container legitimately holds logos and icons, and a sign-in screen backed by a 48px icon is an obvious defect.

  9. · SecurityService

    Reconcile brand colours from five independent sources

    Five because each is authoritative on a different kind of site and silent on the rest: a design system states everything in CSS variables, a PWA in its manifest, a WordPress theme states nothing and has to be inferred from which colour its buttons are, and a hand-built page states nothing anywhere -- only its logo knows. Reconciled, not raced. A declared colour beats an inferred one at equal chroma, but a declared GREY loses to an inferred brand colour, because --primary:#333333 describes the site's text. The selector weighting is the whole idea behind the inferred source: counting colour declarations flat returns #333333 for every site on the internet. No single source reaches full confidence. That headroom is what lets a colour corroborated by several sources outrank any one declaration -- a site can carry a stale theme-color from a rebrand, but not the same stale value in its manifest AND its variables AND its buttons. Secondary returns EMPTY rather than the runner-up when nothing is far enough from primary. Two swatches a member cannot tell apart reads as a malfunction, and the wizard already handles a missing secondary.

  10. · SecurityService

    Find a site's logo from twelve independent signals

    Twelve rather than one because there is no single place a logo lives. A Shopify store declares it in JSON-LD; WordPress puts it in a header img; a React app renders its header after hydration so only the manifest and favicons survive in the initial HTML; a hand-built site has nothing but /favicon.ico. Any one signal finds a logo on about half of real sites. Scored twice on purpose -- what the page CLAIMS, then what the bytes turn out to be. The best possible declaration can still point at a 1x1, and a fine 512px PNG in a footer is probably a payment-provider badge. Two misses the tests caught, both real shapes rather than hypotheticals: selecting on img[src] skipped <img data-src> entirely, which is what a lazy loader ships for an above-the-fold header logo since the src is written by JS we never run; and the inline-SVG size floor rejected an svg the site had itself classed "logo", when a compact mark really can be a hundred bytes of path data. A site's own claim now outranks the floor.

  11. · SecurityService

    Index a site's CSS and parse its page once

    A declaration scanner rather than a CSS grammar: the scan has three questions (custom properties, background images, colours) and none needs the cascade resolved -- it is looking for evidence of intent, not the computed value of any element. Three things it would be easy to get wrong and that the tests pin: @media/@supports/@layer are FLATTENED, not treated as declaration blocks, because responsive sites put most real styling including hero backgrounds behind a media query; declarations split on semicolons outside parens, so a url() data payload does not tear a value in half; and var() resolves exactly one hop, because --a:var(--b);--b:var(--a) is a real thing people write and chasing the chain needs a cycle guard. style="" attributes are collected too. Elementor, Webflow and Squarespace put the hero background on the element rather than in a stylesheet, so reading only <style> and <link> finds no hero on most small-business sites.

  12. · kamo-internal

    File a bug or an enhancement

    The type leads the form, because it changes what the rest of it is asking: 'What happened?' for a defect, 'What would you like to see?' for a request. Two cards with an icon and a one-line hint rather than a dropdown — this is the choice the whole report hangs off, and it decides what the AI is later asked to do. It badges the card and the drawer, filters the list, and picks which default instruction the Send To AI dialog opens with. The copy that assumed every report was a bug is gone with it: 'Report a Bug' is now 'New Report', and 'Submit Bug' is 'Submit Report'.

  13. · SecurityService

    Start the 3-day trial on first entry into the org, not at creation

    Creating an org and opening it are different events. An org can be created and left sitting, and one nobody has walked into must not have spent its evaluation — so creation seeds the trial pending again and entering is what starts the clock. The start moved into PostAuth***Service.create***Id, the single place a real session is minted. Login, the workspace picker, mobile device auth and the post-verification auto-session therefore all start it without each having to remember to, and a future entry path inherits the behaviour. Sudo sessions are excluded: enter-as and impersonation are an administrator opening somebody else's org, which is not the customer arriving and must not spend their trial. The explicit calls in SecurityController and SignInCompletionService are gone as redundant, and the latter no longer needs BillingSetupService at all. Org creation keeps one explicit call. It hands the owner a session before this commit runs, so that mint finds no subscription to start — billing is seeded afterCommit. The afterCommit ratchet caught that the call needed REQUIRES_NEW, without which the write would have been silently discarded and a new org's trial would never have started at all.

  14. · SecurityService

    Rank colours perceptually rather than by RGB frequency

    Counting RGB values across a stylesheet elects the site's off-white page background every time -- it is the most-used colour on almost every site ever built. What the wizard needs is the colour a human would point at and call "their blue", which is a question about chroma, not count. OKLCH rather than HSL because HSL's lightness is not perceptual: hsl(60,100%,50%) and hsl(240,100%,50%) claim the same lightness while one is blinding and the other nearly black. Distance is computed on the Cartesian OKLab axes, not on hue, because hue is meaningless for a near-grey -- two identical greys can sit 180 degrees apart, which is exactly where a secondary-colour choice would otherwise go wrong. Parses the bare "37 99 235" channel triple too. Tailwind emits its custom properties that way so opacity modifiers can compose them, so without it most new marketing sites yield no brand colour at all.

  15. · kamo-shared-library

    Two-phase transaction Bundle executor (SP3)

    Phase 1 resolves every conditional reference and urn:uuid placeholder and touches nothing. Phase 2 writes and reads nothing. The split is the entire point. The naive implementation interleaves them — conditional-create a Patient, read its id back, reference it from the Observation, write that. On YugabyteDB a SELECT after a write in the same transaction triggers a read restart surfaced as SQLSTATE 40001. It fails only under concurrency, so it passes every test, ships, and then presents in production as a random deadlock. A retry loop then appears to fix it while only winning the race more often, converting an error into occasional silent slowness that stays for years. Splitting the phases makes it impossible rather than rare. That is pinned by a test asserting no resolve ever follows a write, and it is mutation-proven: rewriting the executor as the naive per-entry loop failed with the actual interleaving printed — RESOLVE:Patient, WRITE:Patient, WRITE:Observation, RESOLVE:Condition. A conditional reference matching more than one resource is a 412, never "pick the first". Duplicate patient records are ordinary rather than theoretical, and picking silently attaches an observation, an order or a note to whichever duplicate the index returned. The server does not guess which record clinical data belongs to. Because resolution is a separate phase, an ambiguous conditional aborts before ANY write happens, which is what makes the Bundle genuinely atomic rather than **************** Bundles above 500 entries are refused with 413 rather than attempted. One resource write already spans ten-plus tablets, so an unbounded Bundle is an unbounded distributed transaction: it would lock a large part of the cluster, hold those locks, and very likely time out anyway having blocked everyone else's writes. Splitting is what the client would have to do regardless. 1700 tests green.

  16. · SecurityService

    Read real image dimensions from the bytes

    Nothing here trusts the page. An HTML width attribute is a layout instruction, not a fact about the file -- a 2400px logo is routinely declared width="180" and a tracking pixel width="600" -- so scoring on the page's numbers ranks a spacer above a real logo. Hand-written rather than ImageIO because ImageIO reads none of the three formats that matter most here: SVG (the best answer for the compact slot), ICO (what /favicon.ico always is) and WebP (what a modern site serves everything as). Two details worth keeping: an ICO is a container and the LARGEST entry is reported, since the first is usually 16x16; and an SVG is measured by viewBox rather than width, which is routinely "100%" and says nothing about the artwork's proportions.

  17. · SecurityService

    Ask for different work for a bug and an enhancement

    A bug says 'find out why and fix it' and points at the logs; an enhancement says 'this already works, build the better version' and points at the current behaviour instead. Sending an enhancement request off to hunt through logs for a fault that was never there wastes the entire run. Both are pinned byte-for-byte against the console's copies.

  18. · MediaService

    File a bug or an enhancement

    The type is refused rather than defaulted on a write: an enhancement quietly filed as a bug sends the AI session hunting for a fault that was never there. It is also an optional list filter, since the two are different work rather than different severities.

  19. · kamo-shared-library

    A report is a bug or an enhancement

    The distinction is not cosmetic — it decides what the AI session is asked to do. Pointing a session at logs hunting a fault that was never there wastes the whole run, so an enhancement gets its own instruction rather than the bug one with a word swapped. BUG is ordinal 0 and must stay there: the column's DEFAULT 0 is what makes every row filed before the distinction existed read as a bug. Both list queries gain it as an optional filter.

  20. · SecurityService

    Guarded outbound fetching for the website brand scan

    The member types the URL, so every request this issues is an SSRF primitive, and there is no egress NetworkPolicy and no forward proxy in this cluster -- Redis answers unauthenticated on 6379 and MinIO on 9000 with root credentials. The application guard is the control. Reuses PublicHostGuard and the DNS pin from SafeImageFetcher but does not reuse the class itself: it caps at 256KB and refuses redirects, and both are wrong for a website. Redirects are the one relaxation, walked by hand with every hop re-checked, because acme.com -> www -> https is three hops before any HTML exists. A hop may never downgrade https to http, and that rule is asserted directly rather than through a socket -- driven through MockWebServer it would pass on the TLS handshake failing and keep passing if the rule were deleted.

  21. · kamo-shared-library

    The FHIR search compiler (SP3)

    Compiles a search into one SQL statement over the typed index tables. Every rule below fails by returning the WRONG ROWS rather than by raising anything, which is why each has a test that says why. Every criterion becomes an EXISTS, never a JOIN, and that is a correctness decision rather than a performance one. Index tables are multi-valued: an Observation with three categories has three token rows, so an inner join returns it three times and a page of twenty could hold six distinct resources. Adding DISTINCT to paper over it then breaks keyset pagination, because the sort columns must join the select list and duplicates still consume page slots. Paging is keyset, never OFFSET. OFFSET re-scans and discards, so cost grows with depth — and worse, a resource written between two page requests shifts every later row, so a client walking a Bundle silently SKIPS records. For a bulk export or a reconciliation that is missing clinical data with no error. logical_id is a mandatory tiebreaker: without it two resources sharing a timestamp order arbitrarily, so one appears twice and another never. Date search is INTERVAL OVERLAP, because a FHIR date IS an interval — eq2026 means "overlaps 2026", not "equals midnight on 1 January". A null range_high means STILL RUNNING and extends forever; coalescing it to the low bound makes an active condition stop matching ge the day after it started. Token has three forms that are three different questions: bare code, system|code, and |code meaning explicitly NO system. Collapsing the third lets |male match a male coded in an unrelated system. Quantity compares ONLY the canonical column, composite matches both halves on ONE row — pairing across tables would match a systolic code to a diastolic value and report a hypertensive reading that was never taken. :contains and :above are REFUSED rather than served slowly. Both need a match no b-tree can use, so they would scan the largest tables in the schema; an honest error beats an endpoint that works in testing and times out on real data, and the CapabilityStatement declares them unsupported. Deleted resources are excluded — tombstones keep answering vread and _history, but a search asks what IS, and returning them puts deleted problems back on a problem list. Only enum-controlled table names are interpolated; every client value is bound, and a test pins that with an injection string. Mutation-proven: treating a date as an instant and swapping keyset for OFFSET each failed with their own explanation before the revert. 1689 tests green.

  22. · InitializerService

    A system report is a bug or an enhancement

    ALTER rather than folded into the CREATE, because the table already exists everywhere this has reached. DEFAULT 0 is BUG, which is what every row filed before the distinction existed was. No CHECK constraint, for the same reason as status: the enum can grow. Applied to yb-tserver:5433 by hand as well, since this service has no CI.

  23. · kamo-internal

    Remind the dispatched session to push its work

    Matches the server copy in SystemBugAiDispatchService, which is what the dialog actually shows once the preflight lands — this one covers the moment before it. The test pins the whole sentence rather than a fragment, because a partial match would let the two drift by a word and still pass.

  24. · SecurityService

    Remind the dispatched session to push its work

    A session that fixes a bug and leaves the change sitting on the dev machine has not fixed anything anybody else can see. The sentence is split out of the entity so a test can pin it byte-for-byte: kamo-internal carries its own copy for the moment before the preflight lands, and the console then replaces it with this one, so drift would show as the text changing under the operator mid-compose.

  25. · SecurityService

    Start the 3-day trial when the org is created

    The trial was seeded pending and started on the owner's first sign-in, because an org was unreachable until its DNS verified — starting the clock at creation would have burned the trial while the owner was still configuring a domain and could not get in. An org is usable the moment it exists now, so that reason is gone, and deferring the start would hand out a working org whose trial has not begun. Seeding is also how a broken org gets healed, though, and the boot-time backfill runs with nobody present on orgs whose owners have never had a working workspace. TrialStart marks the difference: NOW everywhere somebody is actually there (creation, and the owner's own bootstrap-billing), ON_FIRST_SIGN_IN for the backfill, which keeps the pending seed so maybeStartTrialOnLogin still gives them all three days when they arrive. The 13 orgs already sitting pending are deliberately not backfilled, for the same reason.

  26. · KlusterServices

    An access canary that actually logs in, and alerts that reach a person

    Two gaps, both proven against the live cluster today. FIRST: every existing KamoDesktop alert measures whether a COMPONENT is alive. All four were green during the 2026-08-25 outage — RDP endpoint ready, agent ready, guacamole 1/1, guacd 1/1 — while nobody could connect, because the SSO token's expires was emptying the session's connection list. Liveness cannot see that. So the canary walks the whole path a person walks: SecurityService mints a real link, it is redeemed at the PUBLIC gateway url (exercising DNS, the certificate and Traefik routing rather than bypassing them), the desktop must appear in the connection directory, and a session opened on an EARLIER cycle must still list its connections. That last stage is the point. The outage left fresh logins working and broke only reconnects, so a probe that logs in fresh each time would never have seen it. Verified end to end against the live gateway with a deliberately short window: fresh login green, held session red, exactly the outage signature. It does NOT open a real RDP session. xrdp keys sessions on <user, colour-depth> and colour-depth is pinned so logins REJOIN one desktop — a probe logging in as a real member would attach to their live session and disconnect them every few minutes. Two TCP checks plus the existing endpoint-readiness alert cover that leg without evicting anyone. SECOND, and the real reason a broken desktop could only be found by trying it: Alertmanager's only receiver was one literally named "null", and every alert in the cluster was routed into it. The rules were firing correctly; nothing was listening. Alerts now go to SysAdmin@KamoCRM.com via the cluster's own postfix and to a member's chat thread. Delivered as an AlertmanagerConfig CR because **************** is applied by hand and is not a CI target — routing that exists only after someone re-runs a script is the same class of problem as the rule nobody applied. New alerts cover a broken login, broken reconnects specifically, an expiring certificate, and a STALE canary — a probe that stops reporting must never read as healthy, since silence is the failure mode this whole pipeline removes. The probe ships as a ConfigMap on a stock python image (standard library only), so there is no second image to keep patched for the life of the cluster, and CI gates the deploy on its 17 unit tests: a canary that silently reports success is worse than no canary.

  27. · MediaService

    Deliver cluster alerts into a member's chat thread

    Alertmanager webhook receiver at /api/internal/alerts/chat, so a KamoDesktop alert reaches a person instead of a dashboard. This is the BEST-EFFORT leg. Email is the guaranteed one, sent independently by Alertmanager, so an unresolvable chat thread — renamed member, deleted alias, fresh database — degrades to "email only" rather than to silence. A notifier that can fail closed and take the alert with it is worse than no notifier, because it still reads as coverage. Hence 200-with-an-outcome rather than a non-2xx Alertmanager would retry forever against a member who does not exist. A sender member distinct from the recipient is required: findOrCreateChatSession only matches two-participant sessions, so a self-addressed alert would mint a fresh session every time it fired and bury the chat list. That case is reported and skipped instead. Auth accepts X-Internal-Auth or Authorization: Bearer with the same secret — an AlertmanagerConfig webhook can only set Authorization, never a custom header. Compared constant-time and failing closed when unconfigured, deliberately not the looser equals() used by this service's older internal endpoints, where a blank configured secret matches a blank supplied header.

  28. · SecurityService

    Internal probe endpoint so the access canary tests the real path

    The KamoDesktop canary needs a link minted the way the icon mints one. It holds the same signing key, so it could build its own — and would then be testing itself. The outage it exists to catch lived entirely in how SecurityService builds the link, and a self-minting canary would have sailed past it green. So **************** walks the icon's path: same provision gate, same agent preflight, same buildAutoConnectUrl. If the icon would hand a user a broken link, the probe gets the same broken link. It reports the stage it reached rather than a bare boolean, so an alert can name the broken link instead of the symptom, and it answers 200 either way — "the desktop is broken" and "SecurityService is unreachable" are different alerts with different runbooks, and a 5xx would collapse them. Gated on X-Internal-Auth, constant-time, failing closed when unconfigured. The body is a credential (the url embeds the member's encrypted Linux password), which is also why this is not an open health endpoint. The probe member is whoever has the lowest-id usable dev-machine account, filtered in Java over findAll() rather than by a hand-written query: a pinned member id rots, and a bespoke JPQL string can only be proven by booting the service. This class exists to keep KamoDesktop up; it must not be why SecurityService fails to start.

  29. · kamo-shared-library

    The FHIR read path — read, vread, history and ETag (SP3)

    Completes the round trip: a resource can now be written, read back, read at a specific revision, and have its history walked. The JSON is SERVED rather than assembled. A resource fans out across many tables — an Observation has components, performers, reference ranges, codings, identifiers — so rebuilding it per request would make GET latency proportional to how complicated the resource is, and the sickest patients with the richest records would be the slowest to open. Two primary-key lookups instead, regardless of size. The cost is staleness, paid knowingly: projectorVersion on every version row is what lets a reprojection sweep find rows an upgraded mapper left behind, which is what stops a US Core bump quietly serving last year's shape until certification testing finds it. FhirReadOutcome exists because FHIR needs THREE answers where a naive lookup gives two. A resource that never existed is 404; one that was deleted is 410 Gone and still answers vread and history. Collapsing them tells a caller that a record they legitimately held a reference to never existed — which turns a deletion into a phantom, and when the caller is another system reconciling a chart, it reconciles against a fiction. A pointer row with no matching version throws rather than reporting 404. Those two are written in one transaction, so it cannot happen through the writer; if it is ever seen, something wrote outside it, and answering 404 would hide that while quietly losing a resource. isCurrentVersion is the optimistic-concurrency check, and a null If-Match answers false rather than true. Two clinicians editing the same problem list at once is ordinary, and last-write-wins silently discards one of their assessments — defaulting a missing precondition to "matches" is how that protection gets disabled without anyone deciding to. 1674 tests green.

  30. · kamo-shared-library

    The FHIR write path — projection and atomic writer (SP3)

    A resource write is not one row: it is the domain rows, the canonical JSON, up to eight kinds of search-index row, a reference link per reference, and a security label per sensitivity. They land in ONE transaction or the resource becomes findable before it is labelled, and that window is exactly as long as the gap between two writes. FhirProjection lets a per-resource mapper be a PURE FUNCTION — resource in, projection out, no database, no transaction, no ordering. That is what makes 35 mappers tractable to test, and it means the eventual StructureDefinition generator only has to emit something that builds one of these; it never has to understand persistence. The writer decides ORDER, the mapper decides CONTENT, and keeping that line is what stops the Yugabyte-specific rules being duplicated into every mapper. FhirResourceWriter is the order, and every step is there because doing it differently fails intermittently or silently: The version is claimed FIRST, with an UPDATE that returns the new value. Reading then writing is a read-after-write in one transaction, which Yugabyte aborts with 40001 under concurrency — it presents as a random deadlock and a retry loop appears to fix it while fixing nothing. Stale index rows are swept BEFORE new ones are written, and every index type is swept even when this write produces none of that type, because the PREVIOUS version may have had them. An update that only inserts leaves a resolved problem or a removed allergy still answering a search — a wrong clinical answer, not a slow one. The label sweep deliberately excludes manuallyAssigned rows: a clinician's explicit restriction must survive reprocessing, which is the whole reason SecurityLabelAssignment records who set it. Everything is batched. This platform lost weeks to a per-row audit transaction that made a 1,427-row grid take 23 seconds, and a resource write has the same shape at higher volume. Not @Transactional and not a stereotype, both deliberate. A resource write is frequently one entry in a transaction Bundle, so a nested boundary would either split the Bundle or silently mark the outer transaction rollback-only; the caller owns it. And a shared-lib @Component needing an unscanned repository has crash-looped services four times. Mutation-proven: moving the sweep after the writes and widening the label delete to include manual labels produced three failures across the two ordering tests and the label-scope test before the revert. 1666 tests green.

உங்கள் உலகம் காத்திருக்கிறது.

நிமிடங்களில் இலவசமாகத் தொடங்கவும். நீங்கள் தயாராக இருக்கும் போது பொருட்களை சேர்க்கவும் - எந்த மாற்றமும் இல்லை, புதிய உள்நுழைவுகள் இல்லை.

எப்பொழுதும் இலவசம் — கடன் அட்டை தேவையில்லை