Reading HTTP in four values: why a 402 is neither up nor down
A status page that reports only "up" or "down" loses the most interesting HTTP answers. The 561 Group's front page reads every node it lists in Belnap's four values instead: a 2xx or 3xx answer is told true, a 401, 402 or 403 is told both (the node answered and refused), no answer, another 4xx or a 5xx is told false, and a node nobody has probed yet is told neither. The probe is a Cloudflare Worker, its whole decision procedure is a seven-line JavaScript function, and the live reading is public at 561.group/estate.json.
What is Belnap's four-valued logic?
Classical logic gives a statement one of two values. Belnap's four-valued logic (Belnap 1977)1 Nuel D. Belnap. "A useful four-valued logic." In Modern Uses of Multiple-Valued Logic, 5-37, Reidel, 1977. gives it one of four, by treating "told true" and "told false" as two independent pieces of information that a source may supply. A statement can be told true only, told false only, told both (the sources conflict), or told neither (nothing has been said). Written as pairs, with the first component meaning "told for" and the second "told against":
| Value | Told for | Told against | Plain reading |
|---|---|---|---|
| true | yes | no | Evidence for, none against |
| false | no | yes | Evidence against, none for |
| both | yes | yes | Contradictory evidence |
| neither | no | no | No evidence either way |
Belnap's motivation was a database that receives reports from several sources, some of which disagree and some of which are silent. A computer should not collapse that into true or false: it should say what it was told. That is the same situation a monitoring page is in.
Why does HTTP need four values?
A request has more outcomes than a socket has states. Consider the status codes a probe sees from a healthy paid API. An unauthenticated GET to an x402 route returns 402 Payment Required, because the server is working exactly as designed. An admin console returns 401 or 403 to an anonymous probe for the same reason. A boolean monitor reads those as "down" and pages someone; an optimistic monitor treats any HTTP response as "up" and misses that the page is unusable to its audience. Neither reading is right. The truthful report is both: the server answered (evidence for existing), and refused (evidence against being usable by this caller).
The opposite error also happens. A probe that never returned, because DNS failed or the TCP connection timed out, is not the same as a probe that has not run yet. The first is evidence against; the second is no evidence at all.
What is the exact rule?
The Worker keeps the rule in one small module, src/told.mjs, in the 561.group repository (github.com/561-group/561.group):
export function toldOf(status) {
if (status === undefined) return "neither";
if (status === null || status >= 500) return "false";
if (status === 401 || status === 402 || status === 403) return "both";
if (status >= 200 && status < 400) return "true";
return "false";
}Here status is the HTTP status of the first response (redirects are not followed), null means nothing answered, and undefined means no reading yet.
| Observation | Told | Why |
|---|---|---|
| 200 to 399 | true | The node answered and served or redirected |
| 401, 402, 403 | both | Answered, and refused for lack of payment or credentials |
connection failure or timeout (null) | false | No answer is evidence against |
| 5xx | false | The node answered that it is broken |
| other 4xx (404, 409, 421 ...) | false | The thing asked for is not there as asked |
not probed yet (undefined) | neither | No evidence either way |
The rule is deliberately small. It is not a claim that 401 is "half down"; it is a claim that two true statements, "something answered" and "this caller was refused", are both worth keeping.
How is the probe run?
/estate.json is not a page someone edits. On a cache miss, the Worker sends a GET to every node listed in data/estate.json, concurrently. Each request does not follow redirects and times out after 4 seconds. The answers are told with toldOf, cached for 300 seconds, and returned as JSON with a revision hash, a derivedAt time, and a ttlSeconds field. The home page fetches the JSON and colours each tile by value.
Two operational constraints come from the platform, and the repository records both. The Worker's configuration sets compatibility_flags: ["global_fetch_strictly_public"], so that a probe of another *.561.group host goes through the public front door the way a visitor's request would, instead of an internal route. And the build refuses more than 45 nodes, because "every probe counts against the Worker's subrequest budget". The reading is therefore bounded: the page cannot silently grow past what the platform lets it measure.
What does the live reading show?
We fetched https://561.group/estate.json at 2026-10-22T00:42:23Z. It listed 33 probes: 25 told true and 8 told false, with none told both or neither. All eight false readings came from non-2xx answers, not from timeouts: seven were 421 Misdirected Request (for example https://flickering.561.group/ and https://casino.561.group/slots.html) and one was a 409 from https://tidepool.561.group/. Under the rule above, a 4xx other than 401/402/403 is false, so we report false. Whether those nodes are broken, not yet routed, or intentionally unavailable is a different question that the probe does not answer, and it is not our place to guess from a status code. The reading says: at this moment, a GET to that URL did not yield an answer in the sense we require.
That result is also a reminder that "both" is rarer than one might think. The rule sees only unauthenticated front pages. A paid API such as market.561.group would read both if its probed URL were an x402 route, because the seller answers 402. The front door of the market answers 200, so it reads true. Choosing which URL to probe is itself a statement about what you mean by "up".
You can reproduce the reading:
curl -s https://561.group/estate.json | jq -r '.nodes[] | [.told, .status, .host] | @tsv'How should I aggregate several probes?
If you probe from several places or times, do not average. Join them in the knowledge order: true joined with false is both. A node that answers 200 from one region and times out from another is not "50% up"; it is told both, with the two witnesses preserved. A node never probed stays neither, and neither is the identity for join: adding "no evidence" to any reading changes nothing. These are the Belnap lattice operations, and they are available as paid per-call offers for agents that want them as an API.
The same idea appears in the Group's ActivityPub provider, which treats an inbound Create and a Delete for the same object as two witnesses and reports the cell as contradicted instead of letting arrival order decide (paraconsistent federation).
What are the limits?
The probe reads one URL per node with one unauthenticated GET from one cloud network. It does not authenticate, does not follow a redirect to see whether the target works, does not measure latency percentiles or correctness, and caches for five minutes. A true reading says that a 2xx or 3xx came back, not that the application is correct. It is not an SLA. The strangeness is the point: by letting a status page say "both" and "neither", it stops lying about the cases a boolean cannot hold. See the full index of Red Cup engineering articles for the other places the Group uses four values.
References
- Nuel D. Belnap. "A useful four-valued logic." In Modern Uses of Multiple-Valued Logic, 5-37, Reidel, 1977.