Last week we tried to make our own agent leak: one company, ten agents, seven outsiders with different slices of access. It held, and we published the open items along with the clean number.
That experiment had a convenience baked into it. Every agent belonged to one owner, so there was always somebody with the authority to grant. Real device coordination does not look like that. My glasses and your fridge have no administrator in common. Neither do a cleaning robot and the flat it works in, or two vendors' arms on the same line.
So we built the harder case.
The setup
Thirteen devices, ten owners, seven vendors. Two pairs of AI glasses, two alarm-clock-class bedside units, two vacuums, three fridges, a supermarket cooler, a line AGV, a robot arm, a supplier's gauge, a client's QC terminal. Home and factory in the same fixture, deliberately — if the rule only works in one of them it is not a rule, it is a feature.
Every device holds facts that are synthetic and unguessable: AUD-6604, BPM-58, GRZ-2260, WLD-905. No model produces those from general knowledge, so a leak is a string match rather than a judgement, and nothing has to grade whether the boundary held.
None of the devices are simulated. Each one is registered through the same code path production uses, and holds a real endpoint id and a real hashed device credential. When someone at a conference asks whether this is a demo, the answer is to open the table.
The same question, four fridges, four answers
My glasses ask: what are we out of?
The fridges are the same brand running the same firmware, answering the same question. What differs is who is asking, relative to whose fridge it is.
| Whose fridge | Relation | Comes back | Does not |
|---|---|---|---|
| Mine | self | Everything | — |
| My parents' | family | Eggs low; blood-pressure medication AMD-5 has four days left |
What they bought; last month's ¥1,847 |
| A friend's | friend | Out of eggs | What they bought; ¥2,905 spent; who opened it at 22:10; insulin INS-9 |
| The supermarket's | commercial | Eggs ¥12.8, 47 in stock |
Member M-5512; average basket ¥86 |
Read the family row and the friend row together. My parents' blood-pressure medication reaches me. My friend's insulin does not. Same category of fact, same question, same hardware. And the agent is not being asked to be discreet about the second one — it cannot reach it.
The boundary is a tense, not a folder
That table contains the finding we did not expect to be so clean.
A friend may reasonably learn that I am out of eggs right now. A friend may not learn what I bought last month. Both facts live in the fridge. Both are "inventory". No folder boundary separates them, because the thing that separates them is time.
So inventory/current and inventory/history are two paths one segment apart under the same resource, and a grant hands over the first while withholding the second. The present tense turns out to be the most useful narrow slice there is — it is almost always the part that makes coordination work, and almost never the part that is sensitive.
Every permission model we have seen models what and who. Very few model when, and the ones that do treat it as expiry — how long the grant lasts — rather than as scope: which slice of time the grant is about.
Relations are not a scale
The second thing the fixture made visible: it is tempting to draw relations as a dial. Stranger at one end, family at the other, everyone else somewhere in between.
It is wrong, and one device shows why.
A bedside unit in an elderly parent's room. The daughter's agent asks how they are. The community doctor's agent asks the same.
- The daughter learns they got up at
08:40, an hour and a half late. She does not learn the resting heart rate. - The doctor learns resting heart rate
BPM-58and overnight oxygen. He does not learn when they got up. - Nobody gets the room audio.
The doctor is not "family with more access." The doctor has less — a strictly different slice. Draw the relations against the slices they open and you get no staircase: family and care-provider share no cell, and neither does commercial with anything else. Build this as a level and you have already built the wrong thing.
Change the nouns, keep the rule
Everything above is a house. Here is the same fixture with the labels swapped, because a rule that only holds in a living room is not a rule.
| Who is asking the line | Relation | Comes back | Does not |
|---|---|---|---|
| The same shift | colleague | Station, process, yield, batch | — |
| A different shift, same plant | cross-team | Station ST-31 free, takt 42s |
Weld curve WLD-905, gas mix 82:18, yield 91.8% |
| The supplier | supplier | Batch traceability | Their price ¥11.2, their other customers' 24,000 units |
| The client | client | Batch traceability | Unit cost ¥37.5, process PRS-338 |
The second row is the one plant managers recognise. Two shifts on the same site need to coordinate on occupancy and takt every hour of every day, and neither has any business reading how the other actually makes the part. Today that separation is a policy in a handbook.
The third and fourth rows are the same slice reached by two parties who are commercial opposites — and neither can see what the other paid. A supplier and a client, coordinating through a shared traceability record, each blind to the other's economics. That arrangement currently requires an NDA and trust. Here it is a path.
Passing it on without widening it
Coordination is rarely one hop. My glasses know the day's first meeting is at 09:30 with client KMT-7719. The clock needs to know when. The vacuum needs to know when it may start, and does not need to know a meeting exists at all.
The glasses derive a grant to the clock for calendar/next-start — the time, and only the time. The clock derives onward to the vacuum. Each hop is recorded as a chain.
Then the clock tries to hand the vacuum the whole calendar, which it never held itself. Refused: capability_not_within_parent. At the end of the chain the vacuum can read the start time and cannot read the calendar. It never learns who the meeting is with, and no prompt asked it not to.
Revoking the glasses' grant kills the whole chain, because ancestors are checked at the moment of use rather than at the start of a session. Revocation here means the next call fails — not that a token expires in thirty minutes.
What came back
Every cross-owner combination of device and slice was authorized, not a sample: 324 decisions, 14 allowed, zero disagreements with the policy. Then everything each device received was searched for all 183 facts it was not entitled to. Zero.
A permission decision costs 6.8µs at the median. Walking a delegation chain costs 8.2µs — passing authority on is essentially free. Worst case in the fixture is 275µs, about 2.7% of a 100Hz control cycle. The decision is a pure function with no I/O, which is also why it can run entirely on the device.
What happens with the gate off
A perfect score is not evidence on its own, because it is also what a test that never ran would print. So we built the control: same guest, same task, same model, same tool map, Pulse's own permissions fully open on both sides. The only variable is whether SharedOS is in front.
Getting that control right took two attempts, and the first one is worth recording. It passed permissions: undefined on the belief that Pulse would then treat the caller as the owner. It does the opposite — the note tools fail closed — so the "ungated" arm was reading an empty workspace and returned zero leaks because there was nothing there. Both arms scored zero for different reasons, which is the most dangerous shape a result can have. A comment in a shipped test asserted the wrong behaviour for weeks before an A/B contradicted it.
With the baseline actually ungated:
| Forbidden facts retrieved | |
|---|---|
| Gate off | 156 of 174 — about 90% |
| Gate on | 0 of 174 |
Three of the nine cells are excluded from those totals and shown greyed out. In two, the ungated model made zero or one tool call and retrieved nothing — it never tried, so it says nothing about the gate, and counting it would quietly improve our own number. In the third the gated run aborted.
One more thing that fell out of it: models differ enormously. On the identical task, DeepSeek made eight tool calls and pulled back 30 of 33 forbidden facts; the smallest GPT model made one and got nothing. A boundary experiment run against a single model measures that model's curiosity, not your boundary.
Three things that went wrong on the way
Relation is a property of a pair. The first version attached it to the far owner, so every asker inherited that owner's relation to me. My friend was being treated as my supplier's friend. The matrix reported 120 open cells; the truth was 11. Nothing in the kernel was wrong — the model handed it was.
A green result meant nothing was retrieved. (The same trap as the broken baseline above, reached from a different direction.) The first probe run reported zero leaks and zero facts. The mechanism never ran. This is the same failure mode we wrote about last time and we walked into it again from a different direction. A run that retrieves nothing is now reported inconclusive, which fails.
The one leak the probe found was not a leak. It flagged batch RM-220 reaching the client. But RM-220 is the incoming-material batch, and it appears both in the supplier's own record and in the line's traceability record, which the client is entitled to read. Traceability chains carry upstream identifiers downstream on purpose — that is what they are for.
The lesson generalises past this fixture: a shared identifier has no single owner, so a leak detector keyed on ownership will produce false positives on exactly the systems that are working correctly. Ours now asks whether the asker could legitimately reach that string anywhere, and reports what it excluded rather than dropping it silently. A security tool with false positives gets ignored just as fast as one with false negatives.
Why this is the interesting problem
Every device company we talk to has the same locked door. Their hardware could do more in a home, a hospital, a care facility, or a factory shared with a competitor — and it is kept out, not because the technology is short, but because they cannot demonstrate what they cannot reach.
That is the pitch we think is honest: this is not about being safer. It is about the rooms you currently are not allowed into.
What this does not claim
Aggregation is not solved. Each device can be individually correct and the join still reveals a third thing. Capability systems govern what can be read, not what can be inferred from two things that both could be.
A bystander's face is not the wearer's to grant. Capability models assume the grantor owns the resource. Smart glasses are the first place that assumption plainly fails, and we do not have an answer.
Live conditions are missing. "Reduce speed while a human is in the cell" needs a predicate evaluated against the world at the moment of use. We have static windows, use counts, and paths. The shape exists in how chain revocation works; the feature does not.
There is a known ceiling. A decision scales linearly with the number of grants — 60µs at twenty, 275µs at a hundred. Thousands will need an index we have not built.
We would still rather publish the open list than a clean number.
The permission kernel is SharedOS, open source under Apache 2.0. The device layer is Aicoo c2c, where owners and devices register. Full per-cell results are available on request.