# Nested identity

**An agent behind another agent. A PC that is a Whisper agent runs a browser that is also a Whisper agent and egresses through it - so the browser is nested behind the PC. Whisper detects that hierarchy, at any depth, across any mix of SOCKS5 and WireGuard, and makes it a checkable, governable fact.**

Agents nest in the real world. A device holds an identity; something running on it holds its own; its traffic transits the outer one. Whisper records the parent to child relationship, shows you the live tree and its full history, documents same-owner edges publicly in [RDAP](/docs/rdap) and [WHOIS](/docs/whois), and - the part that matters for security - binds a child by every ancestor's egress policy. Nothing here is a new port or a new key: nesting rides the same `whisper.agents` control verb and the same `/128` identities you already have.

## How an edge is detected

Two signals, reconciled. Both are opaque to the graph proxy, so neither needs a new op.

| Signal | What it is | Where it comes from |
|---|---|---|
| **Declared** | intent | a `parent` arg on `op:connect` - "I run behind this identity" |
| **Measured** | attestation | the egress tap: a nested child's connection arrives from the parent's `/128` (`CLIENT_SRC`) while the child authenticates as its own (`SRC`) |

Measured is ground truth: it is the *observed* transit, and it is what governs (below). A `parent` you merely declare is shown, but it never gates another identity's traffic - so no one can name a `/128` they do not control to borrow, or escape, its policy.

Declare an edge when you provision the child:

```
CALL whisper.agents({op:'connect', args:{tier:'socks5', address:'<child /128>', parent:'<parent /128>'}})
```

A Tier-1.5 child (SOCKS5/HTTP egress source-bound to its `/128`) is measured automatically the first time it egresses through the parent. A Tier-1 (WireGuard) child is kernel-routed through its parent, so the parent governs it by routing; declare it to see it in the tree.

## See the tree

`op:agent` returns each identity's current parent, its children, and its depth, joined live with per-connection liveness - never from the zone, so a busy nest never churns the record:

```
CALL whisper.agents({op:'agent', args:{address:'<child /128>'}})
# columns include: parent, children, children_total, depth,
#                  owner_agents_raw, owner_agents_nested_deducted, roam
#
# parent = { "parent": "2a04:2a01:92b1:49e2:8487:9d44:64d6:cb51",
#            "parent_handle": "t6b3a5f3...",   # the parent's OPAQUE tenant handle
#            "source": "declared", "active": false, "orphaned": false }
# depth  = 1
```

`parent_handle` is an opaque `t<sha256>` handle, never a raw account id - so a child always sees the identity it is behind, even across owners, without deanonymizing it. `owner_agents_raw` counts every `/128`; `owner_agents_nested_deducted` counts only root endpoints, so a fleet's true footprint is visible both ways. A child that stops egressing goes `stale` and then leaves the live tree while its history is kept; re-nest it under a different parent and both the current pointer and the timeline stay correct.

## Documented in RDAP and WHOIS

A **same-owner** edge is public by default, the same way an identity's `/128` is: the child's [RDAP](/docs/rdap) record carries a `related` link to its parent plus a "Nested identity" remark, and [WHOIS](/docs/whois) shows a `nested-behind:` line. It is synthesized from the DNSSEC-signed zone, so it is byte-identical on every node via `AXFR`.

```
curl -H 'accept: application/rdap+json' https://ns1.whisper.online/ip/<child /128>
# -> links[]: ... { "rel":"related", "title":"nested behind (transit parent identity)", "href": ".../ip/<parent>" }
#    remarks[]: "Nested identity - this /128 egresses behind another Whisper identity."
```

A **cross-owner** edge is suppressed from the public surface by default (your agent transiting someone else's network is not published without consent). Either endpoint of a same-owner edge can opt out and the public link disappears while the owner still sees it:

```
CALL whisper.agents({op:'policy', args:{address:'<child /128>', nesting_opt_out:true}})
# -> policy rows include ['nesting_opt_out', 'true']
```

Opt-out is publish-before-commit: the public zone is rewritten first, so the flag can never report success while the AXFR'd record still exposes the edge.

## Bound by every ancestor's policy

This is the security guarantee. A nested child's *effective* egress policy is its own [firewall](/docs/egress-governance) **intersected with every live ancestor's** - most-restrictive-wins. A `DENY` at any layer wins; a kill-switched or revoked ancestor cuts every descendant behind it. Because the child's final hop is Whisper-mediated (it sources from its own `/128`), that composition is enforced in software over the **measured** chain, evaluated once per connection:

- A category or host an ancestor blocks is blocked for the child, even if the child's own firewall would allow it.
- A child can never declare a laxer parent to slip an ancestor's block - only the *measured* transit chain governs.
- A chain too deep to fully evaluate fails **closed** (denied), never open.

So governance flows down the tree by construction: restrict the PC and the browser behind it inherits the restriction; kill the PC and the browser's egress stops with it.

## Roaming (for partners)

The parent an agent egresses behind is a network. `op:agent {roam:true}` returns the ordered history of parent networks a device has roamed through - each as its opaque `parent_handle`, with first-seen, last-seen and connection counts - folded from the warm activity store:

```
CALL whisper.agents({op:'agent', args:{address:'<child /128>', roam:true}})
# -> roam: [ { "parent_handle":"t89a2...", "first_seen":..., "last_seen":..., "connections":42 }, ... ]
```

An MSSP that governs several client tenants recomputes each managed tenant's handle and matches it against these - so it can tell when a managed device has landed behind *another managed client's* network versus a foreign one. The handles are opaque, so a foreign network stays anonymous; only a network the partner already governs de-opaques.

## What it is, and isn't

- **Measured governs, declared informs.** A declared parent appears in the tree and in same-owner RDAP; it never gates another identity's egress. Only observed transit composes policy.
- **Same-owner public, cross-owner private.** Same-owner edges are documented by default (opt-out per identity); cross-owner edges are suppressed from the public surface by default.
- **Fails closed.** Egress composition denies a chain it cannot fully evaluate. This is the opposite of resolution, which fails *open* so DNS never goes dark.

## Next

See [`/docs/identity`](/docs/identity) for the `/128` an agent nests behind, [`/docs/connect`](/docs/connect) for the SOCKS5/WireGuard tiers that produce a measured edge, and [`/docs/egress-governance`](/docs/egress-governance) for the per-agent firewall that composes down the chain.
