Trust domains
Separate them, and the promise stops being a promise
A privacy guarantee that depends on an operator choosing not to run a query is a policy. A privacy guarantee that depends on two organizations holding two halves is a structure. Only one of those survives a bad quarter.
The instinct behind this page was stated plainly by the owner of this design: "it would all be in containers or virtual machines, same thing, and all of them would be in different ones." That instinct is right, and it has a precise justification that is worth stating carefully - because the justification is what tells you which separations matter, and a separation that does not matter is decoration.
Isolation is what makes the privacy guarantee structural rather than a policy promise. A system that holds observations and identity keys in one place has a privacy policy. A system that holds them in two places, under two organizations, with a court between them, has an architecture - and the difference is that the second one still protects people on the day the first organization is careless, is subpoenaed for something else, is acquired, or is simply told by someone senior to run the query anyway.
This page is part of a design document. Nothing described here has been built or deployed, and the deployment sketch at the end is illustrative - a shape to argue with, not a system to run.
The property everything else serves
No single domain compromise yields an identity bound to a location.
Every rule on this page exists to make that sentence true, and the sentence is falsifiable: name a domain, take all of it, and see what you have. That is the test a reviewer should apply, and the co-location matrix below is where the design either survives it or does not.
The heavy dashed line is the whole design. Left of it, nothing can name a person; right of it, nothing knows where anyone was. The crossed-out path is the one an ordinary engineering decision - "let the console call the custodian directly, it will be faster" - would create, and creating it deletes the guarantee without changing a single line of the privacy policy.
The six domains
For each domain the four questions that matter are the same: what does it hold, what can it therefore learn alone, what does a total compromise of it yield, and what must it never be able to reach. A domain whose fourth answer is empty is not a domain.
| Domain | What it holds | What it learns alone | Total compromise yields | Must never reach |
|---|---|---|---|---|
| Anchor / edge | The current-interval identifier set, live radio observations, dwell timers, and its own event-signing key. No identities. No history beyond the interval it is in. Runs with the network down. | That an enrolled device was within range of this one place, this long, in this interval. Never which device. | Presence without identity at a single known location, one interval of the identifier set - which expires in minutes - and the ability to lie: to fabricate an event or suppress a real one. | Any key material, any identity, any other anchor's observations, and any identifier set older than the one it is currently matching. |
| Set distributor | The current-interval identifier set across all enrolled devices, the anchor fleet's addresses, and a signing key held in a hardware module. | How many devices are enrolled, and which anchors are online. Nothing about identities. Nothing about encounters. | The enrollment count, which the design already treats as public, and - only if the hardware module falls with it - the ability to push a forged set, making anchors ignore a real device or alarm on a stranger. That is an integrity attack, not a privacy one. | Observations, identities, and any interval other than the current one. Retained past sets are a linkage corpus, so it must not have them to lose. |
| Operator console | Zone events keyed by identifier, tier state, dispatch queues, case references, officer accounts. No keys. | A pseudonymous movement record: which identifier was near which anchor, when, and how often. Over months this is a rich behavioral profile attached to no name. | That entire pseudonymous corpus, plus the ability to escalate tiers and file events that never happened. It does not yield a name, and there is no configuration change that makes it yield one. | Key material, the custodian by any route, and the audit store as a reader. It may append to the record of what it did; it may not read it back or amend it. |
| Key custodian | The deposited public halves of every device enrollment key and the roster binding them to people. An independent body - a different organization, not a different container. | Who is enrolled. Not where anybody has been: it receives no observation stream at all, not a filtered one and not an aggregated one. | The enrollment roster, which for a supervised population is largely a public registry fact already, and the ability to resolve any identifier presented to it. An attacker holding the custodian and nothing else has no observations to resolve. | The observation stream, the console, the anchor fleet, and the ability to initiate a resolution. It answers; it never asks. |
| Audit store | Append-only, hash-chained records of every resolution - who requested, under what authority, what was disclosed, what the outcome was - signed at the writer. Held by the oversight body. | The complete history of authorized resolutions. This is identity bound to place and time, for the subset already lawfully resolved. | Read access to the one dataset in the system that does bind names to locations. See the finding below: this is unavoidable, and pretending otherwise would be the dishonest version of this page. | Nothing outward at all. It initiates no connection, it answers no query from the operator, and it is separate from every domain whose conduct it records. |
| Court interface | Petitions, issued orders, the court's own signing key, and every exigency invocation with its notification clock running. | Which identifiers somebody wanted resolved, and the reasons given. Not where those identifiers were observed beyond what the petition states, and not who they belong to. | The ability to mint authorizations. This is the highest-value target in the system, which is why the custodian verifies the signature itself and why every issued order is published into the audit chain within a fixed window - a minted order that no judge signed shows up as an order with no matching petition. | The observation stream and the key material. The court authorizes a resolution it cannot itself perform. |
The honest exception, stated where a reviewer will look for it
The audit store is the one domain whose compromise does yield identity bound to a location. That is not a flaw to be engineered away; an audit record that omitted what was disclosed would not be an audit record. What the design can do is bound it: the store is small because resolutions are rare and court-gated, it is encrypted at rest to the oversight body's key, the operator holds no read credential, and it is not co-located with the observation corpus that would let a reader extend a resolved identity forwards and backwards through everything else the system ever saw. The claim at the top of this page is about the domains that make the system run. The audit store is the domain that makes it accountable, and accountability has a price that should be named rather than hidden.
Which pairs are dangerous
Enumerating domains is the easy half. The argument for separation lives in the pairs: a reviewer will not ask whether the boxes are drawn, they will ask what happens if two of them end up on the same host, in the same account, or under the same administrator. All fifteen pairs are below, including the ones that are fine - because a matrix in which everything is dangerous is a matrix nobody believes.
| Co-located pair | Verdict | What the pair yields that neither half had |
|---|---|---|
| Anchor + key custodian | Defeats it | An anchor knows its own location to the meter and observes identifiers in real time. Keys turn each of those into a name. This is identity bound to location, live, at the edge, with no court, no console and no log - and the anchor is the most physically exposed domain in the system, sitting in a school foyer where somebody can put hands on it. |
| Set distributor + key custodian | Defeats it | The distributor holds the complete set of identifiers currently valid for every enrolled device; the custodian can map every one of them to a person. Together they are a standing directory that converts any observation, made by anyone, anywhere, into an identity - without either half ever observing anything itself. It de-anonymizes the whole population at once, which makes it arguably worse than the console pairing. |
| Operator console + key custodian | Defeats it | Observations plus keys equal identity bound to location, retrospectively, in bulk, for everyone the system has ever seen. This is precisely the standing location database the architecture claims not to have, and it is the pair the whole design exists to prevent. |
| Operator console + court interface | Defeats it, through the front door | Not a data merge - an authority merge. The operator issues its own orders, the custodian honors them because they are correctly signed, and identity comes back through the legitimate channel with a clean audit trail. Every technical control performs exactly as designed. This is the pair most likely to be created by accident, because "the authorization service" sounds like operator infrastructure. |
| Court interface + key custodian | Defeats the check | Self-issued orders resolved by the same hands. With no observation stream it yields the enrollment roster rather than anyone's location - but it removes the independence that makes every order meaningful, and it is one hop from catastrophe if either half later gains a feed. |
| Operator console + audit store | Defeats the check | The record of what the operator did, in the operator's hands. No identity gain, and yet it dissolves the enforceability of every other separation: a resolution that can be un-logged is an unauthorized resolution that nobody can ever detect. |
| Key custodian + audit store | Defeats the check | The custodian can erase its own resolutions. The one party that must never resolve without a record becomes the party that decides what the record says. |
| Court interface + audit store | Defeats the check | A court that both authorizes and keeps the record of its own authorizations has no independent check on either. Weaker than the operator case - courts already publish - but the oversight body should hold the store precisely so that neither the asker nor the answerer is also the archivist. |
| Anchor + audit store | Defeats the check | The resolution history sitting on a box in a public building. No new inference, and an appalling custody arrangement for the most sensitive dataset in the system. |
| Anchor + court interface | Defeats the check | An edge node able to mint its own authorization. It still needs the custodian, but it turns every physically accessible anchor into a route to a valid order. |
| Set distributor + audit store | Defeats the check | Custody again: no inference gain, wrong hands for the record. |
| Set distributor + court interface | Defeats the check | Forged sets and self-issued orders from one position. An integrity attack with an authorization attached to it. |
| Anchor + set distributor | Degrades unlinkability | The anchor gains the ability to retain past sets and link this week's identifiers to each other at its own location. Still no identity - but the interval-only rule that limits an anchor's memory stops being enforced by anything but the anchor's own configuration. |
| Anchor + operator console | No new information | The console already receives every observation the anchor makes. Co-locating them is an availability and integrity decision, not a privacy one. |
| Set distributor + operator console | No new information | Both are keyless, and the console already knows enrollment status for the identifiers it sees. Worth keeping apart for blast radius and for the retention rule above, but nothing here reaches a name. |
Every pair that defeats the guarantee contains the key custodian, or manufactures an order that reaches it.
That is the finding a reviewer should take away, and it is what turns a diagram into a requirement. The custodian is not one isolated service among six. It is the single domain whose co-location with anything on the observation side produces an identity bound to a location - which is why it must be a different organization, and why every other separation on this page is a lesser matter of blast radius by comparison.
What containers do not buy
The instinct to put everything in its own container is right, and containers are a real control: they bound a process, they make the flow rules writable and reviewable, and they turn an ambient "we would never do that" into an artefact somebody has to deliberately edit. What they do not do has to be said just as plainly, because a design that overclaims here is worse than one that never separated anything.
- Same-host containers share a kernel. A container is a set of namespaces and cgroups over one running kernel, not a machine. A kernel privilege-escalation bug crosses every container on that box at once, and so does a hypervisor escape across every virtual machine on that host. Two domains on one kernel are one domain with a bookkeeping convention. Virtual machines are stronger than containers here and still not a trust boundary you would bet a child's address on.
- Co-tenancy on one operator's infrastructure is not organizational separation. Different containers, different namespaces, even different clusters - if they sit in one cloud account, under one administrative root, with one identity provider, one secrets manager, one backup destination and one on-call engineer who can reach all of it at three in the morning, then one compromised administrator holds both halves. So does one insider, one subpoena served on one company, and one acquisition.
- The key custodian being a separate container while being the same company defeats the whole design. This is the failure that will actually happen, because it is cheaper, it demonstrates beautifully, and every diagram still looks correct. Split custody is a claim about who can be compelled and who can be fired, not about which process id holds which file. If the same people can deploy both sides, there is one side.
- A shared observability sink quietly re-merges everything. Six isolated domains that all ship their logs to one aggregator have built the merged database in the one place nobody threat-models. Debug logging that includes an identifier on one side and a subject reference on the other reconstructs the join in a search box.
- Shared backups and snapshots are co-location with a delay. If the console's volume snapshots and the custodian's key volume land in the same bucket, the bucket is the merged domain - and it is usually the least-monitored surface in the estate.
- A break-glass credential is a permanent credential. An operator-held emergency route to the custodian, kept for the day something goes badly wrong, is not a contingency. It is the co-location, stored in a password manager, waiting.
None of this argues against containers. It argues that the container is the innermost of several boundaries and the weakest of them, and that the strength of the design comes from the outermost one - a different legal entity with its own counsel - with cryptography carrying the load in between.
The industry evidence for taking this seriously is not hypothetical. In the adjacent product category, 70+ tested children's tracking devices - tens of millions of units - funnel through just three shared backends.Stykas & Solferini, Black Hat (Aug. 7, 2026)
An attacker could locate a watch, alter emergency contacts, falsify location, and activate microphones and cameras with no visible indication. Different vendors, different apps, different privacy policies, one trust domain - and reaching that one backend meant reaching all of them. Whatever the diagrams said, the shared backend was where the boundary really ran.
A deployment sketch
Illustrative files sit in the repository under deploy/ - compose.operator.yml, compose.custodian.yml and network-policy.yaml. They are not run by anything; they exist so that the separations argued for above have a concrete shape to disagree with. The most important thing about them is that there are two compose files, and that whoever deploys the first cannot deploy the second.
Allowed flows
Default-deny in both directions first, then name every permitted flow. A policy that denies only inbound traffic leaves every service free to call out, and calling out is how a compromised console reaches a custodian whose address it was never meant to know.
| From | To | What crosses | Enforced by |
|---|---|---|---|
| Anchor | Set distributor | A pull of the signed current-interval identifier set. The distributor never pushes, so it needs no route to the edge at all. | Cryptographic - the set carries the distributor's signature and the anchor verifies it before matching. Network policy is the second line. |
| Anchor | Console ingest | Signed zone events, keyed by identifier. One direction only. | Network policy. The console cannot open a connection to an anchor, so a compromised console cannot task the edge. |
| Console | Court gate | A petition naming one identifier, one interval, one case, signed by a named officer. | Network policy, plus a signature that ties the request to a person who can be asked about it later. |
| Court gate | Key custodian | A court order for a single resolution, single-use, nonce-bound, with a maximum age. | Cryptographic - the custodian verifies the court's signature against a key in its own hardware module. Network reach is not authority: an attacker who obtains a route still cannot make it answer. |
| Console, court gate, custodian | Audit store | Append-only resolution, order and request records, hash-chained and signed at the writer. | Cryptographic - the chain makes a rewrite detectable and a truncation visible as a broken link. An append-only network endpoint alone would not survive an operator with storage access. |
| Oversight body | Audit store | Read access to the chain. | A credential the operator does not hold and cannot mint. |
| Console | Key custodian | DENIED. No route, no configuration key, no address. The empty custodian setting in the operator compose file is deliberate and is not runtime-configurable. | Network policy only - which is exactly why the organizational split has to carry this one. |
| Any domain | The internet | DENIED by default. Egress is enumerated per domain and every entry is one host. | Default-deny egress policy. |
Where the boundary is cryptographic rather than merely network
This distinction is the one worth arguing about, because a network rule is a configuration and configurations drift. Three flows above survive a mistake in the policy file, and one does not.
Cryptographic, and so self-defending
- The custodian answers only against a court signature it verifies with its own key. Give it a route it should not have and it still refuses.
- The identifier set is signed, so a hostile distribution path cannot make an anchor alarm on a stranger - the worst it achieves is denial.
- The audit chain is signed and linked, so write access to the storage does not confer the ability to rewrite history undetectably.
Network only, and so the weak link
- The console's inability to reach the custodian is a firewall rule. Nothing cryptographic prevents a console from asking; only the absence of a valid order prevents an answer.
- That is a real control and a fragile one - one merged namespace, one helpful platform team, one migration, and the route exists.
- It is the specific reason the custodian must be a different organization. When the network control fails, what remains is that the people who run the console do not have credentials to a system they do not own.
deploy/network-policy.yaml - deny first, then enumerate
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: sp-operator
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
Everything else in that file is an exception to these six lines, and each exception is one selector to one port. The rule the file is written around: if a flow is not named, it does not exist - and if a flow is named and cannot be justified in the table above, it should be deleted rather than documented.
deploy/compose.custodian.yml - the half that is somebody else's
environment:
SP_COURT_VERIFY_KEY: pkcs11:object=court-authorization
SP_ORDER_SINGLE_USE: "true"
SP_ORDER_MAX_AGE_MINUTES: "60"
SP_REPLAY_NONCE_REQUIRED: "true"
SP_AUDIT_WRITE_BEFORE_ANSWER: "true"
The last setting is the one that matters most and is easiest to lose in a performance review: the audit record is written before the answer is returned, so a resolution that cannot be logged does not happen. An order that is single-use, age-limited and nonce-bound is also the difference between an authorization and a reusable key - a report with no nonce bound to it is replayable, and a replayable authorization is a standing one. [28]
How to test this design
- Name a domain and take all of it. Walk the table: for each domain, what does the attacker now hold, and can they produce a name attached to a place? If any single answer is yes, the separation is wrong.
- Draw the org chart, not the network diagram. Ask who can deploy the custodian and who can deploy the console. If the answer is the same person, the same team, or the same company, the design in this document is not the design in production.
- Follow the backups. Ask where each volume's snapshots land and who can read them. Shared destinations reconstitute a dangerous pair on a schedule.
- Follow the logs. Ask what the observability platform ingests from both sides, and whether any field on the observation side and any field on the key side can be joined in its search box.
- Ask for the break-glass procedure. If one exists that lets the operator reach the custodian without a court, the exception is the architecture - the same failure the identity page describes for an unbounded exigency path.
- Read the audit store's access list. If the operator can read it, the record of what the operator did is not evidence.
The point of all of it
A design document earns its keep when it makes the wrong build visibly wrong. Any of the six tests above can be run against a deployment in an afternoon by somebody with no access to the code, and each of them fails loudly rather than quietly. That is the standard this page is written to: not that the architecture is clever, but that a reviewer who does not trust it can check.
How this works
1:03 · narrated · captioned · nothing here is sent anywhere when you press play. Prefer to read it? The transcript is below.
Transcript
| At | Section | What it says |
|---|---|---|
| 0:02 | What it is | Six trust domains, each holding one part of the system. This page is about which of them may never sit together, and why that separation is the guarantee. |
| 0:12 | Structure, not policy | Isolation is what makes the privacy guarantee structural rather than a policy promise. A promise lasts until somebody senior asks for the query to be run anyway. |
| 0:21 | What containers buy | Same-host containers share one kernel. Two domains on one kernel are one domain with a bookkeeping convention, and a shared log sink re-merges them quietly. |
| 0:32 | The organizational line | A custodian that is a separate container inside the same organization is not a custodian. Split custody is about who can be compelled and who can be fired. |
| 0:42 | The finding | Every co-location pair that defeats the guarantee contains the key custodian, or manufactures an order that reaches it. That is what makes it a requirement. |
| 0:52 | How to test it | Name a domain, take all of it, and see whether you hold a name attached to a place. Then draw the org chart rather than the network diagram. |