One day on the pager

A Tuesday with a bad night in it, and every control the app has, in the order it came up.

Nothing below is unusual. It is one ordinary day of one person carrying one phone, and it happens to use every field a send takes and every control the phone has over what arrives. The times are local; the sends are the real ones.

the six channels this day runs on

None of them was made that morning. A channel is a name, an address and a short list of defaults: how loud it is allowed to get, what it does at night, and who it rings if nobody answers. The address is the whole integration. Whatever holds it can post to that channel and to nothing else, so the deploy script and the database monitor never have to know about each other.

channelwhat posts to ithighest priorityand
deploysthe release script, before and afternormalthe script mutes it for the length of a restart
buildsCI, on a red runloudquiet hours 19:00–07:00
jobsthe queue consumer and the nightly importloud—
api-healththe health check, whenever it flapsloudstorm control folds a flap into one card
pagingthe monitors that mean get uppagerepeats every 2 m, escalates to a partner
backupsthe nightly backup, when it landspagea vital: the alert is the beat not arriving

Four of the six are ordinary channels, sitting there waiting to be written to. The other two are vitals, which work the other way round: nothing posts to say something broke, something posts to say it is still alive, and the alert is the beat that never comes. Both kinds share one list on the phone. The vitals go at the top as a wall of dots, because there is only ever one thing to know about each.

The list. A dot each for the vitals, with the schedule each one is held to, and amber once one is late. This is the demo seed, not this Tuesday’s phone, which is why it says so at the top. Click to enlarge.
One of them opened. The address at the top is what the crontab line holds, and everything under it is what this phone does when a beat goes missing. Click to enlarge.

Nothing in the commands below chooses a sound, a repeat, an escalation partner or a quiet window. All of that lives on the channel and is set once. A send asks for a level and passes the text. priority=page asks for the top of the channel’s range, and the highest priority in the table above decides whether it gets there.

Tuesday

07:12

the deploy silences its own channel

The restart is going to produce every error it always produces. The script mutes the channel for ten minutes before it starts. The mute has its own end time, so a script that dies halfway through cannot leave you deaf.

$ curl -X POST https://api.tissue.systems/v1/mast/channels/mc_2b7d…/mute \
    -H "Authorization: Bearer $TISSUE_TOKEN" \
    -d '{"until": "2026-09-15T14:22:00Z"}'
a channel mute, from an account token rather than a channel key
07:41

the deploy finishes and says so, quietly

Nothing needs to happen. You want the line in the feed for the next time you are working out when something changed.

$ curl -d silent=1 -d body="deployed 2026.09.029 · s10, s33" "$MAST_URL"
silent: stored, no push
09:05

a build breaks while you are making coffee

Not a page. It can wait until you sit down, but you want it on the lock screen and not buried in a list you check twice a day, and you want the run one tap away instead of going to look for it.

$ curl -d priority=loud \
    -d title="nightly build failed" \
    -d url="https://github.com/…/actions/runs/35055965614" \
    -d url_title="Open the run" "$MAST_URL"
priority=loud · url · url_title
10:20

a queue starts failing, four hundred times

Every consumer retry posts. Without a key that is four hundred notifications. With one it is a single card counting up, and the phone buzzes once.

$ curl -d key=queue-consumer -d priority=loud \
    -d title="consumer cannot reach redis" "$MAST_URL"
{"ok":true,"state":"deduped","duplicate":true}
key: the answer comes back deduped
11:02

a batch job reports what happened an hour ago

The job ran at ten past ten and only got around to telling you at eleven. Send the time the thing happened, not the time the request left.

$ curl -d timestamp=2026-09-15T17:10:00Z \
    -d title="import finished" \
    -d body="18,204 rows · 2 rejected" "$MAST_URL"
timestamp: unix time or ISO-8601
13:00

you go into a meeting

One tap on the phone. Everything is silent for an hour, pages included, and a banner stays up the whole time so you cannot forget you turned it on. A channel marked as breaking through still rings, and the pause screen names it while the window runs.

# the app → Pause pages → 1 hour
# banner: pages paused · until 14:00
# still rings: Home · smoke
a pause, and the one exemption, set on the phone and never from a token
13:38

something pages you anyway

It is held, not dropped, and the feed says so while the pause runs. Nothing else in Mast does this to a page. A mute would have delivered it at full volume, and quiet hours would have let it through too.

# 202 {"ok":true,"state":"queued"}
# the feed: held · pages paused until 14:00
held: a line in the message's own log
14:00

the meeting ends and the held page rings

You could have ended the pause early from the banner and had it ring straight away. Either way nothing was lost, and the card says how long it sat.

# the card: asked for page · held 22 min
the per-message log
16:10

a disk is filling, slowly

This is a page, because at some hour it will take the database with it. It is not an every-thirty-seconds page, though. Set the repeat to the pace of the problem, and give it a stopping point so it does not follow you into next week.

$ curl -d priority=page -d key=disk-var \
    -d retry=600 -d expire=86400 \
    -d title="/var 91% full" "$MAST_URL"
retry · expire · key
17:45

you clear 40 GB and the alert closes itself

The same key, one more field. Every device the page reached gets the quiet update, a partner's phone included, and the card says how long it was open.

$ curl -d key=disk-var -d resolve=1 \
    -d title="/var back to 61%" "$MAST_URL"
{"ok":true,"state":"resolved","open_for":5700}
resolve: the answer carries open_for, in seconds
19:00

quiet hours start on the chatty channel

The build channel has nothing to say at night that cannot wait until morning. Quiet hours deliver silently instead of dropping the message, so the feed is still complete when you look.

# the app → the channel → Quiet hours → 19:00–07:00
per channel · a page ignores it
22:31

twenty alerts in five minutes

A flapping health check. Past the threshold the pushes collapse into one summary that counts up, and the individual messages are all still in the feed. A page is never the thing folded away.

# one notification: 34 messages · api-health
# answers come back {"state":"suppressed"}
storm control: twenty in five minutes, to begin with

Wednesday

02:58

the one you bought the app for

Replication has stopped. This sounds with the ringer switch off, through a Focus, through the channel's mute and through its quiet hours, and it repeats every two minutes until a human says otherwise.

$ curl -d priority=page -d key=repl-lag \
    -d sound=klaxon -d title="db-01" \
    -d body="no WAL segment in 11 minutes" "$MAST_URL"
priority=page · sound · key
03:04

you acknowledge it from the Dynamic Island

The repeat stops. Nothing is resolved and nothing is pretending to be. The incident is still open; you have only said that a person is awake. The log keeps both times.

# 03:04 acked · 6 min after fired
# still open
acknowledge is not resolve
03:31

the second one, and nobody answers it

After the escalation window the page moves to your partner's phone and rings there. It goes one person deep, on purpose. They can resolve it from their side, and it closes on yours.

# 03:31 escalated · partner
# nothing on the send says this — escalation
# is a property of the channel, set in the app
the escalation partner
06:00

the backup does not run

Nothing failed, so nothing sent, and that is the failure everything else misses. The vital was expecting a beat at 03:17, never got one, and paged on its own once the grace ran out.

17 3 * * *  /usr/local/bin/backup.sh \
    && curl -fsS https://mast.tissue.dev/mk_8e2a…
a vital channel: a period, and how late is still not a problem
07:20

the beat arrives and closes its own page

You rerun the job by hand. The first beat after a silence closes the page that silence opened, with nothing extra to send.

# 07:20 the crontab line runs
# 202 {"ok":true,"state":"alive"} — no card, so no id
# the open page: resolved · open for 1 h 20 m
one beat, two jobs
08:15

you read the day back

All of it is in one feed, including the sends that made no sound: the silent deploy line, the folded storm, the page that was held through the meeting. Each card carries its own history.

# fired → escalated → acked → resolved
# with the time of each
the per-message incident log, kept for thirty days

All eighteen land in the same list, in the order they happened, including the ones that made no sound.

A day read back. Open and resolved sit on the cards themselves, a folded count sits beside the channel that flapped, and the wall above answers the question a quiet pager cannot answer by being quiet. Click to enlarge.

where each control appeared

So that nothing is hiding in the story.

controlin the day
title, bodythroughout
priority09:05 loud · 02:58 page
key10:20, 16:10, 17:45, 02:58
resolve17:45
acknot in this day. It makes an ordinary alert wait for a human the way a page does
retry, expire16:10
url, url_title09:05
sound02:58
silent07:41
timestamp11:02
channel mute07:12, expiring on its own
quiet hours19:00
the ceilingset on all six channels, though none of them had to clamp anything down on this day
pause, and breaking through one13:00, 13:38, 14:00
storm control22:31
vitals06:00, 07:20
the escalation partner03:31

Two of the eighteen moments are a person answering, and one is a job answering on its own. The rest of the day the phone is silent, which is where a pager sits almost all of the time.

recipesA problem, and the send that does it. Every field above, taken one at a time. sending alertsEvery way to send, every field used above, and the sounds. Mast PagerThe app that rings: levels on the phone, making a page wake you, and answering one from the lock screen.

The overview and the price are on the Tissue Mast page, and everything else is in the docs.