feat(desktop): name the computer an agent lives on - #6259
Conversation
An owner signs into Buzz on several computers. Persona definitions (kind:30175) sync and insert on arrival, but managed-agent records (kind:30177) are a deliberate no-op on inbound no-match, because they carry device-local secrets that must never ride the relay. So every computer that receives a persona mints its own keypair: one name, N pubkeys, N computers. Mentions then route by pubkey. The `p`-tag match in buzz-acp reaches exactly one of the N -- whichever pubkey the sending client happened to resolve -- so a mention aimed at a sleeping computer dies in silence, and nothing in the UI ever said which computer an agent lived on. This adds the missing noun: - `device_identity`: a stable per-install id and a human label, minted once, persisted 0600 to `<app-data>/agents/device.json`. The id is an opaque uuid v4, never derived from hardware. The label is seeded from the OS host name -- which routinely contains a real person's name -- so it is user-editable and sanitized before it is published. - The label rides the agent's own kind:30177 projection, and is accepted on the way back in only when the event author matches the owner the agent's NIP-OA profile cryptographically declares. A peer cannot stamp a label onto someone else's agent. - The mention dropdown names the computer, and a mention that resolves to another computer's keypair says so instead of dead-ending. - A settings card to rename this computer. Deliberately out of scope: moving agent secrets between computers, and making the relay-side single-connection exclusion real. Both are Stage 1 and Stage 2 of docs/agent-identity-sync.md on branch design/tailnet-agent-mesh; this is that document's Stage 0. Signed-off-by: Michael Feth <michael@jira-flow.com>
EvidenceTwo agents share the display name Before —
|


The problem
An owner signs into Buzz on several computers. They create an agent on one. It
appears on all of them, wearing the same name. They mention it, and often
nothing answers — while a different copy of the same agent, on a computer that
happens to be awake, answers a different mention minutes later. Nothing in the
UI ever says which computer an agent actually lives on, whether it is running,
or whether it is configured.
Why it happens
Persona definitions (kind:30175) sync between an owner's computers and insert on
arrival. Managed-agent records (kind:30177) deliberately do not — inbound
no-match is a hard no-op, because a managed agent carries device-local secrets
and "an agent that does not already exist locally has no secret key to run
with" (
desktop/src-tauri/src/commands/personas/inbound.rs:553-557).So each computer that receives a persona and instantiates it mints its own
keypair. One name, N pubkeys, N computers.
Turns then route by pubkey: the
p-tag match incrates/buzz-acp/src/filter.rs:390-395, on by default because--subscribedefaults to
mentions. A mention therefore reaches exactly one of the N —whichever pubkey the sending client happened to resolve. If that one is asleep,
the mention dies silently.
Evidence from a real fleet
Measured from one owner's local kind:30177 retention stores (aggregate only; the
stores are keyed per
(owner pubkey, relay URL)):One name,
Bumble, holds 7 distinct pubkeys in a single scope.In one channel, the member roster is 1 human and 26 agents; 23 of those resolve
to managed-agent directory entries carrying only 13 distinct names — four
Winnies, three Airys, three Todds, and so on.
The dead mention is directly observable in the harness logs: one Winnie logged
steer-acks in that channel on 2026-08-18, while a second Winnie and a second
Ernie last touched the same channel on 2026-08-15 only to log
subscribed to channel …and then go quiet.desktopInstanceId, the only per-process discriminator on a runtime receipt, isprovably not a machine identity — all 40 receipts on the machine carry the single
value
xyz.block.buzz.app, and its own doc comment says it exists to separatetwo Buzz apps on one machine, never two machines.
What this PR does
device_identity— a stable per-install id and a human label, minted onceand persisted
0600to<app-data>/agents/device.json. The id is an opaqueuuid v4, never derived from hardware. A corrupt file is preserved as
device.json.corruptand replaced rather than failing the caller.contains a real person's name, and it is published in a world-readable
kind:30177 event — so it is user-editable, length-capped, and refuses control
characters.
only when the kind:30177 author matches the owner the agent's NIP-OA profile
cryptographically declares (
nostr_convert/agent_directory.rs:115). A peercannot stamp a label onto someone else's agent.
another computer's keypair says so instead of dead-ending in silence.
It deliberately does not repurpose
desktop_instance_id, whose ownershipcheck must keep working for the two-Buzzs-on-one-machine case.
What this PR does not do
Moving agent secrets between computers, and making the relay-side
single-connection exclusion real, are Stage 1 and Stage 2 of
docs/agent-identity-sync.mdon branchdesign/tailnet-agent-mesh. This is thatdocument's Stage 0, which it recommends building first precisely because it
needs no decision about secrets. Note that unifying identity onto a
default-off, per-process exclusion would be strictly worse than today — see §3
of that document.
Note for reviewers: overlapping open PRs
This branch is rebased on
mainas of the 8 commits through #5874, whichincluded #6224 (
bound remote agent mention authorization). That PR and this oneboth add to
desktop/src/shared/api/tauriRelayAgents.tsand the e2e bridge; therebase conflict was resolved by keeping both entry points and having
revalidateRelayAgentsreuse this PR'sfromRawRelayAgentnormalizer, so thedevice fields flow through both paths.
Still-open PRs that touch overlapping files, none of which this PR depends on:
#6077 (7 files), #6126 (17 files), #6013, #6037. This PR adds no
mention-receipt work (kind 44102) and no agent-grouping changes — it is additive
to both — so it can merge in any order; whichever lands second resolves the
textual overlap.
Verification
Run on Windows against this branch, after the rebase:
cargo check --manifest-path desktop/src-tauri/Cargo.toml --all-targetscargo test --manifest-path desktop/src-tauri/Cargo.tomlpnpm --dir desktop exec tsc --noEmitpnpm --dir desktop testpnpm --dir desktop check:px-textjust file-size-checkThe one Rust failure is
claude_spawn_uses_the_probed_cli_executable, whichpasses in isolation (
--lib <name>: 1 passed). It mutates the process-globalPATHviastd::env::set_varand races the other tests in the same process. Itis pre-existing and untouched by this branch — the diff contains no occurrence of
CLAUDE_CODE_EXECUTABLEand noruntime.rschange.Likewise
channels.spec.ts › shows cached profile labelsfailed once in acombined Playwright run and then passed 3/3 in isolation; it asserts a cached
user-profile label with a 1s timeout and is unrelated to this change.
just test(relay integration, needs Postgres + Redis) was not run — thischange touches no relay crate.