NexLotus Request a briefing

02 / Patient Experiences

The lift is down. Nobody told the third floor.

A staff console that can tell every device in the building what is out of service, in every shipped language, within a deliberately closed set of things it is safe to say on a channel that cannot promise arrival.

Notice kinds

A closed set of 5. Three others were deleted from the type, not disabled behind a flag.

Delivery

Polling, not push. Up to 10 seconds late, and to an offline device not at all.

Translation

Once, at send time, stored per language, so what a reader saw is fixed and auditable.

The staff communications console. Panels for an operational notice, announcing to the building, scheduled messages, sent messages, patient handouts, problems reported by visitors, and operational metrics. A working pane on the right is empty until a panel is opened.
The whole console. Seven panels on the left, one working pane on the right, and no dashboard anywhere: a station screen at three in the morning needs a list of things a person can do, not a summary of things that have already happened. Development build, seeded data, no deployment.

Five things a hospital can say here. And three it deliberately cannot.

A closed set is not a limitation that was accepted reluctantly. It is what makes the channel translatable without a provider, renderable offline, and impossible to misuse under pressure, because there is no free-text field on the surface that matters most.

Operational notice kinds available in the system
KindWhat it meansFree text
SERVICE_INTERRUPTION Something in the building has stopped working: a lift, an entrance, a service. none
AREA_CLOSED A wing, entrance or department is shut. none
DEPARTMENT_BUSY A department is under unusual load. An operational fact, never a clinical one. none
CUSTOM The one place a staff member can write their own words, translated at send time. yes
RESOLVED Stands a notice down. A notice that was raised must be closable by the same closed vocabulary. none

Every reader sees the scope printed on the notice itself, in their own language: Operational information only. This is not an emergency notification system.

The three that were removed

LOCKDOWN, EVACUATE and SHELTER were deleted from the notice kinds. Not hidden behind a permission, not disabled by configuration. They were removed from the type, so no deployment can turn them back on and no operator can reach them under pressure.

The reasoning is delivery, not squeamishness. This channel polls; it cannot promise a message arrives, and it certainly cannot promise it arrives in time. A channel with that property must not carry a message whose entire value depends on arriving. The free-text field is refused at the API boundary if it contains emergency wording, and the refusal names the overhead page instead.

The consequence, stated deliberately

Because nothing this channel now carries is time-critical, its delivery latency is an operational fact rather than a clinical-safety question, and no sign-off on that latency is required before deployment. That is the trade the closed set buys. It is not a substitute for an overhead page, the building's own emergency systems, or a person walking the room, and the software says so on every notice it renders.

Delivery

The message that has to be right is sent as a key, never as text.

A notice travels as an identifier. Every device renders it from its own shipped dictionary, in its own language, with no network call to a translation provider and no dependence on one being reachable. The one message that must arrive intact never leaves the building's own vocabulary.

Why not translate it

A translated sentence can be late, wrong, or absent. A key cannot: the string it renders shipped with the device and was reviewed before release. Notices are the surface where being late is worse than being plain.

Touches no reader data

Raising or standing down a notice reads and writes nothing belonging to any reader, a property pinned by a test, because it is the kind of thing that quietly stops being true during a refactor.

First on the console

The notice panel sits at the top of the staff console, above everything else, because it is the only surface that has to be right when somebody is in a hurry.

One notice key rendered by many devices A staff member raises a notice. The identifier travels to every device, and each one renders the text from its own locally shipped dictionary in its own language. Staff raises a notice SERVICE_INTERRUPTION An identifier · no sentence, no translation Device · EN Service interruption From local dictionary Device · FR Interruption de service From local dictionary Device · +11 more Each renders its own language RTL where it applies No translation provider is contacted The notice renders with the network down, the provider down, or the account expired. Nothing about it is remote.
One key out, thirteen locally rendered strings in. The English and French strings are human-verified; the rest carry the auto-translated disclaimer.

Free text is translated once, at send time, and never again.

When a staff member does need their own words, the message is translated into every shipped language at the moment it is sent, and each rendering is stored. Two things follow from that, and both matter more than they first appear.

First, what a reader saw is fixed and auditable. It cannot silently change later because a provider changed how it translates. Second, nobody stands in a corridor waiting on a translation API while the message they needed ten seconds ago is still in flight. A language that fails to translate falls back to the English source: worse to read, never wrong.

Announcement bodies and every per-language rendering are AES-256-GCM encrypted before they reach the database, not because they are personal information, which they must not be, but because an operator can type anything into a free-text box, and the at-rest posture should not depend on them never making a mistake.

Scheduled announcements

Recurring building-wide messages, with a run-level idempotency guard so two racing scheduler ticks cannot double-send the same run.

Read state

Acknowledgement counts, with the reach caveat rendered from the API payload itself, so the screen cannot drift from what the server believes it is saying.

Metrics

Announcements by category, notices raised and time-to-stand-down, scheduled runs and stale gaps. CSV export for whoever asks.

Retention

Expired announcements, their translations and their acknowledgements are purged on a configured schedule. The audit log keeps its own, longer clock.

Facility scoping

An announcement stamped with a different facility is never served, pinned by a test, because a multi-site deployment is where that goes wrong.

Acknowledgement is a reach estimate, not proof of anything. It stores an announcement id and a timestamp, and there is no column for anything else. Until 5 September 2026 it also stored a permanent identifier the reader's browser had minted, which was removed rather than renamed, so the count is now a count of acknowledgement events rather than of distinct devices. Treat it as an upper bound. Every screen that shows the number says so, in the same sentence as the number.

Stated plainly

The latency, admitted rather than buried.

The reader app polls. There is no push. Both feeds, operational notices and announcements, reach a device up to one poll interval late, and reach a device with no network, a flat battery, or a locked screen not at all.

A device whose polls keep failing backs off rather than retrying at full rate, because when the tunnel drops, every device in the building fails the same request at the same moment, and a synchronised retry storm is what stops the server coming back.

Poll interval

10s

While the tab is on screen. Polling stops entirely while it is hidden.

Backoff ceiling

80s

Where a device with repeatedly failing polls settles, instead of hammering a recovering server.

Push notifications

0

No install, no app store, no push. Both portals are plain responsive websites.

This is the whole argument for the closed set. A channel that cannot promise arrival must not carry a message whose value depends on arriving, so the messages whose value depends on arriving were removed from it. The staff console holds a persistent connection because it lives on a station screen; the reader app does not, because a phone in a waiting room cannot be relied on to hold one.

Seven panels. Nothing else.

The console is served through the public tunnel, so it holds only what a staff member needs for the building and for their own account. It cannot create, reset, or delete anyone else's account. That lives on a listener bound to the hosting device's loopback interface.

Panels available in the staff communications console
PanelWhat it does
Operational notice Raise or stand down one of the closed set of kinds. Sent as a key, never as text. First on the screen, because it is the only surface that has to be right.
Message the building Free text to every device, translated once at send time into every shipped language and stored per language. A failed translation falls back to the English source.
Scheduled announcements Recurring facility-wide announcements, with a run-level idempotency guard against double-sends.
Sent messages Everything this building has been sent, newest first, with the acknowledgement count and its reach caveat rendered from the payload so the screen cannot drift from the server's own claim.
Patient handouts Publish a leaflet or notice anyone in the building can open on their own phone, alongside the announcement feed rather than inside it.
Reported by visitors Problems people have reported with a place: a lift, a corridor, a sign. Grouped by place rather than by reporter, because there is no reporter to group by.
Metrics Announcements by category, notices raised and time-to-stand-down, scheduled runs and stale gaps. CSV export, and the screen says on its face that these are operational and not clinical.

The waiting-room QR is not on this list. It moved to the hosting device, with the reader's poster and the equipment code on separate pages so the one that gets printed for a public wall cannot carry the staff code.

Staff sign in with a local account or with Microsoft. The Entra application is registered in the hospital's own tenant, so their conditional access applies natively and they can revoke it without asking us. Local accounts are argon2id with revocable server-side sessions and a mandatory second factor. There is deliberately no account lockout, because an emergency department must never be locked out, so per-account backoff plus mandatory TOTP is the compensating control.

Next: the record of where the equipment has been.

Asset Tracking is the third module: QR labels, an append-only event log, and a record that says where a machine was last left rather than where it is. It is feature-complete and it has never run in a hospital, and the page says exactly what it has not had.