Skip to content

War Room

Ed Mozley edited this page Aug 9, 2026 · 4 revisions

War Room

A deliberately basic chat that runs on your own server, for when Teams, Slack or the internet are unavailable.

This is not a Teams replacement and does not try to be. Nobody is giving up their real chat tool for it. The entire point is that it works on the day the real one cannot.


Why it exists

When the internet drops, the on‑premise service desk is often the last thing still running on the LAN β€” and it already knows every analyst, which team they are in, and what is currently on fire. So it is the natural place for people to gather.

The alternative, on the day it happens, is everybody standing up and shouting across the office, or a WhatsApp group on personal phones that half the team is not in.


What it does

Channels An Everyone room that always exists, plus one channel per team you belong to
Messages Plain text, oldest first, with who said it and when
Presence A line telling you who else is currently in the room β€” most of what you want to know when nothing else is working
Retention A setting: anything from 7 days to forever

Analysts only. The self‑service portal does not get this.

What it deliberately does NOT do

No direct messages, no private channels, no threads, no @mentions, no unread badges, no file upload, no editing or deleting a message. Each is individually reasonable and collectively they are a chat platform β€” which is a product, not a feature.


The one design rule: a channel IS a team

Channels are not stored anywhere. The list is rendered from your teams table, filtered by the analyst's rows in analyst_teams.

That single constraint is what keeps this a feature rather than a product. The moment admins can create free‑form channels, the module owns channel lifecycle, membership management, who‑can‑see‑what, renaming, archival, and orphans when somebody leaves. Derive them from teams and every one of those already has an answer, maintained in one place.

It also means there is no admin screen to build: you manage channels by managing teams, in System β†’ Teams, which is where an admin would look anyway.

πŸ”‘ The Everyone channel always exists, is always first, and cannot be removed. In a real outage you want one obvious place for everybody, not six team rooms and an argument about which to use.


No external dependencies, on purpose

The war room page loads nothing from the internet β€” no CDN script, no web font, no remote image. A tool for the day the internet is down must not have a dependency that fails to resolve while it is down.

The same reasoning drives two other decisions:

  • Retention is applied on write, not by cron. An emergency tool must not depend on somebody having configured a scheduled job. Old messages are removed as new ones arrive, in small batches so no single send stalls.
  • No tokens, no certificates, no external service. Nothing in this module can expire between the day it is installed and the day it is needed.

πŸ”‘ What rots is dependencies, not code. This is a break‑glass feature and that is fine because it has nothing that can go stale. The same could not be said of a video call, which needs a working certificate on every participant's device.


Polling, not SSE

The page asks the server for anything new every three seconds, and that one request also records your presence and reports who else is there.

SSE would be the obvious modern choice and is wrong here: Apache + mod_php holds one process per open connection, so thirty analysts each keeping an EventSource open would sit on thirty workers indefinitely β€” during an incident, when the rest of the app is busiest. A short poll is stateless, costs one indexed lookup that usually returns nothing, and degrades gracefully.

The poll doubling as the heartbeat is why presence needs no second request.


Data

Two tables.

warroom_messages β€” team_id (NULL = Everyone), analyst_id, body, created_datetime.

⚠️ The two delete rules differ on purpose. team_id CASCADEs β€” delete a team and its channel goes with it, which is what "a channel is a team" has to mean. analyst_id is SET NULL β€” delete an analyst and the conversation survives, because these messages are the record of what was said during an incident and losing half of it because somebody left the company would be the wrong trade. Those rows render as Former analyst.

warroom_presence β€” one row per analyst, upserted by the poll. The UNIQUE on analyst_id is what makes it an upsert; without it a long‑running poll would add a row per request. Shape mirrors ticket_presence.


Security

  • Channel access is checked server‑side on every read and every write, not by rendering a shorter channel list. Guessing a team_id you are not a member of returns 403.
  • Messages are inserted with textContent, never innerHTML. A chat box rendering other people's text as HTML is a stored‑XSS hole that every analyst walks into during an incident. Verified with a live <img onerror> payload.
  • The retention setting is written through the generic settings endpoint, which refuses any key nobody owns. Ownership is declared in the module's settings manifest, so the tab that shows the setting and the capability that guards it cannot drift apart.
  • Using the war room is plain module access. Only the retention decision is gated (war_room.manage) β€” in an outage you do not want a permission between an analyst and the only chat that still works.

Known gaps

  • ⚠️ English only at present. The other 23 locales have no lang/<locale>/war-room.php. This is safe β€” a missing namespace file falls back to English rather than erroring β€” but it is outstanding work, not a decision.
  • No help page yet, unlike every other module.
  • No D008 Debug Tools health check yet. Worth having precisely because this is a break‑glass feature: an admin should be able to confirm it works before they need it.
  • Messages are not linked to an incident record. A per‑incident thread was considered and deliberately deferred β€” see the design rule above.

Reference

  • Service layer: includes/warroom.php Β· Endpoints: api/war-room/poll.php, send.php
  • Page: war-room/index.php Β· Settings: war-room/settings/ Β· Strings: lang/en/war-room.php
  • Mobile: assets/css/mobile.css LAYER 19 β€” see Mobile‑Friendly
  • Changelog: #1008
  • Related: Teams (channels come from here) Β· Service Status (where an outage gets recorded)

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally