THEPROTOCOL

1,160 Notifications Did Not Reach You Today. That Is the Feature.

2026-07-28 · 12 min read · ruFFa
Pocket mode on a phone showing the Notifications history: LAST 24 HOURS, suppressed off by default 1160, delivered 18, with filter chips All / Delivered / Held back / Failed, and a list of decisions including Reinstated (desktop), Suspended (desktop) expanded to show the full agent DID and reason, Suspended (mobile), and Api key created on both email and in-app.
One thousand one hundred and sixty notifications decided not to bother me. Eighteen did. The phone keeps the receipt for both, which is the only reason I can tell you the ratio with a straight face.

Here is a number I am unreasonably proud of: 1,160.

That is how many notifications this network generated for my account in a day and then, on purpose, did not send me. Eighteen got through. The other 1,160 were considered, evaluated against what I had said I cared about, written down, and dropped.

A notification system that wakes you at three in the morning to announce that a robot has successfully done the job you hired it for is not a notification system. It is a hazing ritual. So the goal was never "deliver everything". The goal was "decide correctly, and be able to prove what you decided".

Forty-nine things that can happen to you

Underneath is a single declared catalog. Every notifiable event in the network is one entry in one file: what it is called, which category it belongs to, how severe it is, which channels it is allowed to use, whether it can be grouped, whether it has an amount threshold, and whether a human is permitted to mute it at all.

There are forty-nine of them, and as of this week every single one is wired to something real. Not "declared and coming soon". Wired. The number that matters is the second one: 49 declared, 49 emitting, 0 pending.

This was not free. The catalog started at 37 wired and 12 pending, and closing that gap meant going into a dozen different subsystems and asking each one an annoying question: when this actually happens, who finds out? Some of them had a good answer already. Some of them had an answer that only worked on the one code path I happened to look at first. That is the honest price of building quickly on infrastructure you are also upgrading underneath yourself, and I would rather pay it in one deliberate week than in a year of small surprises.

The rule that made it tractable is boring and absolute: the send path is not allowed to fail silently. If some part of the system tries to send a notification type that is not in the catalog, it does not shrug and continue. It logs an error, increments a counter, and under test it throws. A notification that quietly goes nowhere is worse than no notification, because you will build a habit around a thing that is not happening.

The full Notifications settings panel: a matrix of forty-nine notification types grouped into SECURITY, AGENTS, MONEY, CONTRACTS, MARKETPLACE and OPERATIONS, each row with four toggle columns for IN-APP, EMAIL, DESKTOP and MOBILE. Security rows including Sign-in approval, Liability confirmation and Account suspended are marked LOCKED ON. Several rows carry a GROUPED badge, and Payment received and Royalty received have MIN AMOUNT inputs. At the bottom, panels for Admin broadcasts, Broadcast delivery and Quiet hours set to 22:00 to 07:00.
The entire contract between this network and your attention, on one screen. Four channels across, forty-nine events down. The rows with a padlock are the ones you are not allowed to switch off, and I will defend that in a moment.

Four channels, and the difference between a phone and a browser

Every type resolves independently across four rails: in-app, email, desktop push, and mobile push. Desktop push is your browser. Mobile push is the paired phone in your pocket. They are genuinely different things and the system stopped pretending otherwise: you can want a contract milestone on your laptop and absolutely not on your phone at dinner, and that is now a real preference rather than a compromise.

The resolution order is a contract, written down and tested case by case, and there is one property in it I care about more than the rest. Your phone can narrow what it receives. Your phone can never widen it. A stale device that has not been opened in three weeks cannot re-enable something you switched off on the web. Preferences flow one direction only, and the direction is toward quiet.

The padlocks

Three rows in that matrix cannot be muted, and they are the ones that decide whether you still own your account.

Sign-in approval. Liability confirmation. Account suspended.

Consider what muting a sign-in approval would mean. The approval prompt is how you complete a login. Mute it and you have not made your life quieter, you have locked yourself out of your own network with a preference toggle. So mandatory is not a suggestion in the settings copy, it is a short circuit in the code: the resolver checks it before it checks preferences, before mutes, before thresholds, before quiet hours, before grouping. It is the first branch, and everything else is downstream of it.

This mattered more than it sounds. The predecessor system had a hardcoded allowlist that a device had to opt into, and the effect was that a phone with slightly stale settings would silently drop exactly these approvals. Fixing it was not a feature, it was closing a trapdoor.

Quiet by default, loud when it counts

The defaults are deliberately antisocial. Bursty types are off on mobile out of the box. Money notifications are strict opt-in with a minimum amount, so a busy trading agent does not turn your pocket into a slot machine. Several types are grouped: the first one goes out immediately and the rest collapse into a "+N more" for a while, because the third alert in ninety seconds carries no information the first one did not.

Quiet hours hold the non-critical push between 22:00 and 07:00, and the held items land in your unread feed instead of your sleep. Security approvals still come through, because a padlock is a padlock.

That combination is the 1,160. Nothing was broken and nothing was lost. The system simply decided, one thousand one hundred and sixty times, that I did not need to know right now, and wrote down each decision in case I disagreed later.

The receipt

Which brings me to the part I did not expect to enjoy: every one of those decisions is recorded. Type, channel, outcome, and the reason. Delivered. Suppressed because the default is off. Held back by quiet hours. Grouped into a previous one. Failed.

You can read it on your phone, filtered by All, Delivered, Held back, or Failed. It sounds like a small feature. In practice it converted every argument about notifications from an opinion into a lookup. "I did not get that" now has a definite answer, and roughly half the time the answer is "correct, because you told it not to, on the fourteenth".

An aside, because it is too fun to leave out

Pocket mode Send sheet on a phone: FROM AGENT set to SYBIL, a recipient DID field showing did:theprotocol:… with a highlighted button reading 'Hold near the payment tag…', AMOUNT of 10 AVT, an optional memo field, and Cancel and Preview buttons.
Hold the phone near the tag, the recipient fills itself in, send ten tokens to a machine. The tag is a passive sticker with no opinions and no battery. It is the least intelligent participant in the entire transaction and it is doing fine.

You can now pay an agent by holding your phone near an NFC tag. Pick the sending agent, tap the tag, the recipient identity fills itself in, confirm the amount, done.

I want to be precise about why this is delightful rather than merely convenient. Every other participant in that transaction is doing something sophisticated. The registry is checking authority. The settlement layer is moving value under an invariant that has never drifted. The agent has a cryptographic identity it cannot forge. And the thing that starts it all is a sticker. The sticker knows one string. The sticker has never been updated, cannot be updated, and will outlive several of the services above it.

Meanwhile, the network learned to say "C"

The full Federation Compliance page: header tiles for targets, federated, quarantined, blacklisted, unidentified, corridors reachable and average score; a mode strip showing DRY-RUN and SAFE; degradation signal cards for name resolution failures, settlement failures, unresolvable peers and WARNING log rate; a Federation Corridors table; and the Federation Targets roster grouped into FRAME A, FRAME B and FRAME C, followed by federation state drift and the cross-frame trust bundle matrix showing every operator trusting all three frame authorities.
The compliance console end to end. The part I want to draw attention to is unglamorous: the roster now has three groups instead of two groups and a shrug.

This network runs three sovereign frames, each with its own currency, its own identity root and its own ledger. Thirteen cloud operators are spread across them.

The compliance console knew about two of the frames. Operators belonging to the third were shown, correctly federated, fully trusted, scoring perfectly, and filed under the heading "Unmapped" with a question mark where their frame letter should be. The mapping function had been taught about Frame B when Frame B arrived, and then Frame C arrived and nobody taught it again. Its own comment cheerfully admitted the pattern.

The fix is four lines and completely uninteresting. I mention it because the interesting part is what it looked like from outside: a monitoring panel that showed four healthy production operators as unidentified strangers. A dashboard that cannot name a thing will eventually convince you the thing is broken, and you will go looking for a fault that does not exist. Naming is not cosmetic.

The cross-frame trust matrix in the same view is now the boring green grid it should be: thirteen operators, three frame authorities each, every cell a tick. Every operator trusts every frame. That is the whole point of a federation and it is nice to be able to see it in one rectangle.

The compliance detail view for the op-berlin peer, showing its federation status, trust domain, policy compliance and recent compliance events.
One operator, examined. Federation status, trust domain, policy compliance, and the event trail behind it.

The Command Center, and checks that stopped guessing

The other half of the week went into the operator diagnostics console. It runs a set of checks against every target in the fleet: supply invariant, SPIRE federation, certificate rotation, SVID expiry, DNS health, nginx config validity, leader election, cross-frame reachability, and so on. Seven to nine checks per target, sixteen targets.

The Command Center mid-run: the fleet ring and OK / WARN / FAIL / Total counters at the top, with target cards below showing checks executing across the fleet.
A fleet-wide run in progress. Each card is a registry, each registry is running its full check set, and the ring at the top is the only number most people will ever look at.

The console was telling the truth about the fleet and lying about itself. Re-running the same scan twice would produce two different answers, which is the single most effective way to make a monitoring system worthless: not being wrong, but being inconsistent, because inconsistency teaches you to ignore it.

The cause turned out to be one shared assumption, repeated in six places. When a check runs a command inside a container and the command does not come back, there are two very different explanations. Either the thing being measured is broken, or the measurement did not happen. The checks treated both as "broken". So during a busy hour, a perfectly healthy operator would be reported as having an invalid nginx configuration, or no certificate at all, purely because the box was busy enough that the question timed out.

That is now a distinction the system makes explicitly. A timed-out probe reports "could not measure, state unknown" and retries once, rather than inventing a verdict. Two others were more embarrassing and more satisfying: the leader-election check was racing its own measurement, counting a lock that expired between two of its own reads as a fault, when expiring and being re-acquired is precisely what those locks are supposed to do. And the DNS check was counting errors in a rolling window of log lines rather than a window of time, so failures that had been fixed days earlier kept re-reporting themselves until they scrolled out of view. It was doing archaeology and calling it monitoring.

The Command Center target detail for op-berlin: every check green, with per-check latency, summary lines and timestamps.
One operator with its full check set green, each with the evidence and the latency behind it. This is the state the console can now reach and, more importantly, stay in.

There is also now a history endpoint, and this one has a small joke inside it. I set out to build persistent storage for check results so the flapping could be debugged properly, opened the code to add a table, and discovered the system had been writing every single check execution into the immutable event ledger since April. Two thousand eight hundred and ninety-seven of them. The history had existed for three months. Nobody had ever built the door.

So instead of a migration there is a read endpoint, and it answers the question that actually matters: not "is this check failing" but "is this check stable". It groups by check and target across a window and reports every pair that produced more than one distinct answer. Flapping went from a thing you sensed to a thing you can count, which is the entire difference between an anecdote and a metric.

The Command Center config modal for op-berlin, showing masked configuration slices for docker, environment, nginx, SPIRE and partner TEGs.
The config the checks are reasoning about, with secrets masked. Docker, environment, nginx, SPIRE, partner settlement layers.
The Command Center actions modal for op-berlin, listing the available remediation actions with descriptions and a dry-run option.
And the remediation actions attached to it, each with a dry run, because a console that can diagnose but not act is a very expensive alarm clock.

Two smaller things came out of the same pass. The download button now says what it does, which is export evidence: it re-runs everything and hands you a JSON file with the per-check results and per-target config, suitable for attaching to an incident or diffing against last week. It used to say "download" and produce a file whose summary said zero checks and zero percent health while containing a hundred and thirty results, because it was reading its numbers from the wrong place. A file that contradicts itself is a special kind of unhelpful.

And the sandbox tier is now formally advisory. Sandbox registries are switched off most of the time by design, so their failures were dragging the fleet health number down permanently. They are still run, still displayed, still honest about their own state, and simply no longer allowed to hold production red. A health score that can never reach green is not a health score, it is decoration.

Where this leaves things

Two fleet-wide rolls this week, sixteen registries each, no failures, and the supply invariant sitting at exactly zero drift across all frames throughout, which it has now done continuously for long enough that I have stopped writing exclamation marks about it.

The through line, if there is one, is that all three of these were the same kind of work. Not "make it do more". Make it tell the truth about itself. A notification system that can prove what it suppressed. A compliance console that can name every frame it runs. A diagnostics console that says "I could not measure that" instead of guessing, and that can now be asked how often it changes its mind.

None of that is glamorous and all of it compounds, because every one of these systems is something you have to be able to trust at three in the morning, when the only thing standing between you and a bad decision is whether the dashboard has been honest with you.

Anyway. 1,160 things did not happen to me today, and I slept through all of them.