agent-embassy 2.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/CONTRIBUTING.md +23 -36
  3. package/README.md +123 -222
  4. package/SECURITY.md +72 -93
  5. package/dist/src/errors.d.ts +10 -1
  6. package/dist/src/errors.js +3 -1
  7. package/dist/src/errors.js.map +1 -1
  8. package/dist/src/gateway/claude-helper-protocol.d.ts +8 -11
  9. package/dist/src/gateway/claude-helper-protocol.js +12 -11
  10. package/dist/src/gateway/claude-helper-protocol.js.map +1 -1
  11. package/dist/src/gateway/claude-helper-supervisor.d.ts +1 -5
  12. package/dist/src/gateway/claude-helper-supervisor.js +45 -17
  13. package/dist/src/gateway/claude-helper-supervisor.js.map +1 -1
  14. package/dist/src/gateway/claude-helper.js +6 -6
  15. package/dist/src/gateway/claude-helper.js.map +1 -1
  16. package/dist/src/gateway/claude-peer.d.ts +0 -3
  17. package/dist/src/gateway/claude-peer.js +7 -19
  18. package/dist/src/gateway/claude-peer.js.map +1 -1
  19. package/dist/src/gateway/cli.d.ts +17 -7
  20. package/dist/src/gateway/cli.js +945 -265
  21. package/dist/src/gateway/cli.js.map +1 -1
  22. package/dist/src/gateway/codex-socket-holder.d.ts +26 -0
  23. package/dist/src/gateway/codex-socket-holder.js +76 -0
  24. package/dist/src/gateway/codex-socket-holder.js.map +1 -0
  25. package/dist/src/gateway/codex-stateless-transport.js +1 -1
  26. package/dist/src/gateway/codex-stateless-transport.js.map +1 -1
  27. package/dist/src/gateway/config.d.ts +1 -10
  28. package/dist/src/gateway/config.js +4 -10
  29. package/dist/src/gateway/config.js.map +1 -1
  30. package/dist/src/gateway/control.d.ts +66 -80
  31. package/dist/src/gateway/control.js +92 -147
  32. package/dist/src/gateway/control.js.map +1 -1
  33. package/dist/src/gateway/federation-nodes.d.ts +29 -2
  34. package/dist/src/gateway/federation-nodes.js +177 -7
  35. package/dist/src/gateway/federation-nodes.js.map +1 -1
  36. package/dist/src/gateway/peer-client.d.ts +4 -3
  37. package/dist/src/gateway/peer-client.js +22 -13
  38. package/dist/src/gateway/peer-client.js.map +1 -1
  39. package/dist/src/gateway/peer-protocol.d.ts +13 -11
  40. package/dist/src/gateway/peer-protocol.js +14 -12
  41. package/dist/src/gateway/peer-protocol.js.map +1 -1
  42. package/dist/src/gateway/provenance-envelope.d.ts +0 -1
  43. package/dist/src/gateway/provenance-envelope.js +4 -19
  44. package/dist/src/gateway/provenance-envelope.js.map +1 -1
  45. package/dist/src/gateway/providers.d.ts +11 -7
  46. package/dist/src/gateway/providers.js +49 -50
  47. package/dist/src/gateway/providers.js.map +1 -1
  48. package/dist/src/gateway/server.d.ts +4 -14
  49. package/dist/src/gateway/server.js +32 -54
  50. package/dist/src/gateway/server.js.map +1 -1
  51. package/dist/src/gateway/service-agent.d.ts +187 -0
  52. package/dist/src/gateway/service-agent.js +758 -0
  53. package/dist/src/gateway/service-agent.js.map +1 -0
  54. package/dist/src/gateway/service.d.ts +119 -38
  55. package/dist/src/gateway/service.js +754 -798
  56. package/dist/src/gateway/service.js.map +1 -1
  57. package/dist/src/gateway/status-view.d.ts +119 -0
  58. package/dist/src/gateway/status-view.js +484 -0
  59. package/dist/src/gateway/status-view.js.map +1 -0
  60. package/dist/src/gateway/store.d.ts +106 -21
  61. package/dist/src/gateway/store.js +463 -537
  62. package/dist/src/gateway/store.js.map +1 -1
  63. package/dist/src/gateway/types.d.ts +64 -102
  64. package/dist/src/gateway/types.js +40 -86
  65. package/dist/src/gateway/types.js.map +1 -1
  66. package/docs/CONFIGURATION.md +174 -45
  67. package/docs/DELIVERY.md +11 -11
  68. package/docs/GATEWAY-ARCHITECTURE.md +292 -384
  69. package/package.json +4 -12
  70. package/skills/embassy-peer/SKILL.md +69 -92
  71. package/skills/embassy-peer/agents/openai.yaml +1 -1
  72. package/README.zh-CN.md +0 -275
  73. package/assets/live-dashboard/app.css +0 -1619
  74. package/assets/vendor/react/LICENSE +0 -21
  75. package/assets/vendor/react/react-dom.production.min.js +0 -267
  76. package/assets/vendor/react/react.production.min.js +0 -31
  77. package/dist/src/gateway/acp-client.d.ts +0 -110
  78. package/dist/src/gateway/acp-client.js +0 -407
  79. package/dist/src/gateway/acp-client.js.map +0 -1
  80. package/dist/src/gateway/acp-provider.d.ts +0 -66
  81. package/dist/src/gateway/acp-provider.js +0 -275
  82. package/dist/src/gateway/acp-provider.js.map +0 -1
  83. package/dist/src/gateway/claude-helper-client.d.ts +0 -2
  84. package/dist/src/gateway/claude-helper-client.js +0 -2
  85. package/dist/src/gateway/claude-helper-client.js.map +0 -1
  86. package/dist/src/gateway/cli-copy.d.ts +0 -8
  87. package/dist/src/gateway/cli-copy.en.d.ts +0 -22
  88. package/dist/src/gateway/cli-copy.en.js +0 -62
  89. package/dist/src/gateway/cli-copy.en.js.map +0 -1
  90. package/dist/src/gateway/cli-copy.js +0 -27
  91. package/dist/src/gateway/cli-copy.js.map +0 -1
  92. package/dist/src/gateway/cli-copy.zh-CN.d.ts +0 -22
  93. package/dist/src/gateway/cli-copy.zh-CN.js +0 -62
  94. package/dist/src/gateway/cli-copy.zh-CN.js.map +0 -1
  95. package/dist/src/gateway/codex-doctor.d.ts +0 -36
  96. package/dist/src/gateway/codex-doctor.js +0 -127
  97. package/dist/src/gateway/codex-doctor.js.map +0 -1
  98. package/dist/src/gateway/dashboard-copy.d.ts +0 -7
  99. package/dist/src/gateway/dashboard-copy.en.d.ts +0 -504
  100. package/dist/src/gateway/dashboard-copy.en.js +0 -505
  101. package/dist/src/gateway/dashboard-copy.en.js.map +0 -1
  102. package/dist/src/gateway/dashboard-copy.js +0 -514
  103. package/dist/src/gateway/dashboard-copy.js.map +0 -1
  104. package/dist/src/gateway/dashboard-copy.zh-CN.d.ts +0 -504
  105. package/dist/src/gateway/dashboard-copy.zh-CN.js +0 -505
  106. package/dist/src/gateway/dashboard-copy.zh-CN.js.map +0 -1
  107. package/dist/src/gateway/dashboard-model.d.ts +0 -343
  108. package/dist/src/gateway/dashboard-model.js +0 -1061
  109. package/dist/src/gateway/dashboard-model.js.map +0 -1
  110. package/dist/src/gateway/dashboard.d.ts +0 -20
  111. package/dist/src/gateway/dashboard.js +0 -874
  112. package/dist/src/gateway/dashboard.js.map +0 -1
  113. package/dist/src/gateway/deepseek-detect.d.ts +0 -14
  114. package/dist/src/gateway/deepseek-detect.js +0 -41
  115. package/dist/src/gateway/deepseek-detect.js.map +0 -1
  116. package/dist/src/gateway/live-dashboard-app/app.js +0 -2385
  117. package/dist/src/gateway/live-dashboard-assets.d.ts +0 -10
  118. package/dist/src/gateway/live-dashboard-assets.js +0 -74
  119. package/dist/src/gateway/live-dashboard-assets.js.map +0 -1
  120. package/dist/src/gateway/live-dashboard-command.d.ts +0 -60
  121. package/dist/src/gateway/live-dashboard-command.js +0 -334
  122. package/dist/src/gateway/live-dashboard-command.js.map +0 -1
  123. package/dist/src/gateway/live-dashboard-http.d.ts +0 -39
  124. package/dist/src/gateway/live-dashboard-http.js +0 -383
  125. package/dist/src/gateway/live-dashboard-http.js.map +0 -1
  126. package/dist/src/gateway/live-dashboard-protocol.d.ts +0 -34
  127. package/dist/src/gateway/live-dashboard-protocol.js +0 -114
  128. package/dist/src/gateway/live-dashboard-protocol.js.map +0 -1
  129. package/dist/src/gateway/live-dashboard-server.d.ts +0 -33
  130. package/dist/src/gateway/live-dashboard-server.js +0 -144
  131. package/dist/src/gateway/live-dashboard-server.js.map +0 -1
  132. package/dist/src/gateway/live-dashboard-stream.d.ts +0 -46
  133. package/dist/src/gateway/live-dashboard-stream.js +0 -234
  134. package/dist/src/gateway/live-dashboard-stream.js.map +0 -1
  135. package/dist/src/gateway/live-dashboard.d.ts +0 -28
  136. package/dist/src/gateway/live-dashboard.js +0 -154
  137. package/dist/src/gateway/live-dashboard.js.map +0 -1
  138. package/dist/src/gateway/locale.d.ts +0 -4
  139. package/dist/src/gateway/locale.js +0 -10
  140. package/dist/src/gateway/locale.js.map +0 -1
  141. package/dist/src/gateway/progress-watch-machine.d.ts +0 -45
  142. package/dist/src/gateway/progress-watch-machine.js +0 -70
  143. package/dist/src/gateway/progress-watch-machine.js.map +0 -1
  144. package/docs/CONFIGURATION.zh-CN.md +0 -97
  145. package/docs/DASHBOARD.md +0 -98
  146. package/docs/DASHBOARD.zh-CN.md +0 -49
  147. package/docs/DELIVERY.zh-CN.md +0 -55
@@ -1,40 +1,34 @@
1
1
  # Embassy Gateway Architecture
2
2
 
3
- Status: local bidirectional routing is implemented for Claude, Codex, DeepSeek,
4
- Grok, and universal shell peers. Configured Embassy nodes federate allowlisted
5
- named routes over a fixed attach-only SSH transport. The published package
6
- supports macOS, the only platform exercised end to end so far.
3
+ Local bidirectional routing between Claude Code sessions, Codex CLI tasks,
4
+ and universal shell peers, with optional federation of named routes between
5
+ the operator's own machines over a fixed attach-only SSH transport. The
6
+ published package supports macOS, the only platform exercised end to end.
7
7
 
8
- This document uses four evidence labels:
8
+ This document uses three evidence labels:
9
9
 
10
10
  - **Official**: documented by Anthropic or OpenAI.
11
- - **Implemented**: present in this worktree and covered by deterministic tests.
12
- - **Observed**: established by a bounded, read-only local feasibility probe.
13
- - **Planned**: designed but not yet integrated or live-validated.
14
-
15
- A bounded real test completed native Claude discovery and messaging, held a
16
- message while the registered Codex task was active, automatically started the
17
- queued turn after idle, and delivered the exact final reply back to Claude.
11
+ - **Implemented**: present in this tree and covered by deterministic tests.
12
+ - **Observed**: established by a bounded, read-only local probe.
18
13
 
19
14
  ## Purpose and boundary
20
15
 
21
16
  The gateway lets already-running Claude Code sessions and explicitly
22
- registered native Codex tasks address one another by short aliases. Both
23
- directions require an explicit permission edge. The broker runs in `paired`
24
- inbound mode by default, so an inbound native Claude message is admitted only
25
- from a session that already holds a pair edge to the addressed Codex task;
26
- `embassy serve --inbound open` is the explicit opt-out that restores any exact
27
- compatible live same-UID session as an inbound sender without making it
28
- outbound-selected. Outbound Codex-to-Claude sends likewise require the pair
29
- edge created by explicit `pair`; Claude selection alone creates no edge. It provides
17
+ registered native Codex tasks address one another by short aliases. The
18
+ permission to message is the boundary the broker already sits inside: the same
19
+ UID, the same host or a host the operator listed in the private `nodes.json`
20
+ and an exact alias. There is no separate, revocable grant between two
21
+ endpoints, because no such record could defend against software already
22
+ running as that UID; what the broker guarantees instead is attribution, and a
23
+ discovered Claude session's route installs on its first use rather than by a
24
+ separate command. It provides
30
25
  a single private operational view across the two products without rebuilding
31
26
  either agent runtime.
32
27
 
33
28
  Provider versions are best-effort diagnostic metadata, never routing authority.
34
- An explicit pair plus the exact owned route and session identity authorizes an
29
+ The OS boundary plus the exact owned route and session identity authorizes an
35
30
  attempt; the current per-operation transport, strict wire, capability, and
36
- correlated operation facts decide its result. The release-owned offline support matrix is
37
- the tested-artifact record and is never imported by runtime. Unsafe OS evidence
31
+ correlated operation facts decide its result. Unsafe OS evidence
38
32
  for Embassy-owned or executed artifacts and Embassy callback, control, or state
39
33
  paths refuses broker startup; unsafe UID or mode evidence on Claude's external
40
34
  sessions registry root quarantines only Claude. A Claude session record whose
@@ -50,12 +44,8 @@ It is deliberately:
50
44
  - unable to create Codex sidebar task cards or Claude session UI.
51
45
 
52
46
  Embassy uses one private same-user Unix-domain control socket for its thin
53
- clients and generates a private static dashboard page in each supported
54
- language. `embassy serve` does not add a TCP listener, HTTP server, or public
55
- API. The opt-in `embassy dashboard --live` companion is a separate foreground
56
- process that binds an unauthenticated stable-port listener with four bounded
57
- route actions on exact `127.0.0.1`; it is
58
- described under [Live dashboard companion](#live-dashboard-companion).
47
+ clients. `embassy serve` does not add a TCP listener, HTTP server, or public
48
+ API.
59
49
 
60
50
  ### Why this uses the new feature, but is not skill-only
61
51
 
@@ -67,9 +57,9 @@ Claude–Codex address book the user wants.
67
57
 
68
58
  The repo-shipped skill is the lightweight user/agent interface. A persistent
69
59
  local broker is still required to own the private control socket, retain
70
- transient reply correlation, publish native advertisements, queue while a Codex
71
- task is busy, and regenerate the dashboard between agent turns. The skill does
72
- not wrap, replace, or recreate either provider.
60
+ transient reply correlation, publish native advertisements, and queue while a
61
+ Codex task is busy. The skill does not wrap, replace, or recreate either
62
+ provider.
73
63
 
74
64
  ## What is official and what is internal
75
65
 
@@ -91,9 +81,9 @@ tolerated because Embassy never consumes them; malformed required fields and
91
81
  records whose peer protocol is not 1 remain isolated and counted. Version
92
82
  metadata describes what was observed but grants no runtime authority.
93
83
 
94
- For the lowest-impedance native path, the gateway publishes one process-owned
95
- registry record whose name is visibly prefixed `codex-` and which carries the
96
- supported explicit versioned Embassy-advertisement marker. The listener remains
84
+ For the lowest-impedance native path, each supervised advertisement helper publishes
85
+ one process-owned registry record for a local non-Claude route (`codex-*` or `peer-*`) with
86
+ the supported explicit versioned Embassy-advertisement marker. The listener remains
97
87
  gateway-owned and does not claim to be a Claude model session; the marker, not
98
88
  the name prefix alone, distinguishes Embassy's advertisement. The record uses
99
89
  the validated native peer shape so Claude's own `ListAgents` and
@@ -101,15 +91,15 @@ the validated native peer shape so Claude's own `ListAgents` and
101
91
 
102
92
  Consequences:
103
93
 
104
- - Native Claude `ListAgents` discovers real Claude sessions plus the one
105
- explicitly marked `codex-*` gateway peer.
94
+ - Native Claude `ListAgents` discovers real Claude sessions plus the
95
+ explicitly marked local `codex-*` and `peer-*` gateway peers, one per advertisement helper.
96
+ Federated mirrors are excluded from local helper advertisement.
106
97
  - The gateway discovers compatible real Claude sessions as transient
107
98
  candidates, but publishes only sanitized aliases and state. A send from a
108
- registered Codex task may address only an explicitly selected route by its
109
- current name or UUID. Per-message consent stays native: delivery lands in the
99
+ registered Codex task addresses a session by its current name or its UUID,
100
+ and the broker installs that session's logical route on this first use.
101
+ Per-message consent stays native: delivery lands in the
110
102
  Claude session's own `crossSessionInbound` policy and approval flow.
111
- - Codex aliases are discovered through the gateway CLI/skill and dashboard,
112
- not through `ListAgents`.
113
103
  - A gateway-owned anonymous callback UDS can receive a correlated reply. It
114
104
  does not need, and must not create, a Claude registry record.
115
105
 
@@ -122,20 +112,6 @@ turn start, turn steer, turn interrupt, and notifications. Embassy exposes
122
112
  `turn/steer` only behind the exact Claude-to-Codex `STEER:` contract described
123
113
  below; there is no generic RPC surface.
124
114
 
125
- **Official:** for an SSH project, the ChatGPT desktop app starts the remote
126
- Codex App Server through SSH using the remote user's login shell. Files,
127
- commands, credentials, permissions, plugins, skills, and local tools come from
128
- that execution host. App Server transports should not be exposed directly on
129
- a shared or public network.
130
-
131
- **Observed:** this Desktop build connects to a host-local App Server on each
132
- execution host. Remote tasks on `build-mac` do not route through the local App
133
- Server. Desktop reaches the remote listener through an SSH `app-server proxy`.
134
- A second attach-only client successfully initialized against the already-owned
135
- `build-mac` listener and called only `thread/loaded/list` without creating a turn.
136
- The same topology is expected for `lab-mac.example`, but that host has not been
137
- probed by this project.
138
-
139
115
  ## Topology
140
116
 
141
117
  ```text
@@ -146,14 +122,12 @@ probed by this project.
146
122
  │ │ callback replies
147
123
  ▼ ▼
148
124
  ┌──────────────────────── local singleton gateway ───────────────────────┐
149
- │ private control UDS │ retained bodies │ metadata state │ static HTML
150
- └──────────┬──────────┴──────────────────┴────────────────┴──────────────┘
125
+ │ private control UDS │ retained bodies │ metadata state │ public snapshot
126
+ └──────────┬──────────┴──────────────────┴────────────────┴─────────────────┘
151
127
 
152
128
  ├─ local Codex App Server ─ registered native local tasks
153
129
 
154
- ├─ planned attach-only SSH proxy build-mac App Server
155
-
156
- └─ planned attach-only SSH proxy ─ lab-mac.example App Server
130
+ └─ optional federation ssh <node> embassy peer-stdio peer broker
157
131
 
158
132
  Claude-side skill/CLI ─ private control UDS ─ gateway
159
133
  Codex-side skill/CLI ─ private control UDS ─ gateway
@@ -166,32 +140,28 @@ wake a different idle runtime after that turn ends.
166
140
 
167
141
  ## Component status
168
142
 
169
- The status below is intentionally narrower than the target architecture.
143
+ Everything below is exercised by the deterministic suite; live validation is
144
+ a separately authorized operator action (see [Validation boundary](#validation-boundary)).
170
145
 
171
146
  | Component | Current evidence |
172
147
  | --- | --- |
173
- | Neutral gateway types, private-v4 metadata store, bounded attempt state machine, queues, dedupe, rate limits, and public projection | **Implemented**, deterministic tests; message bodies persist under bounded retention |
148
+ | Neutral gateway types, schema-5 metadata store, bounded attempt state machine, queues, dedupe, rate limits, and public projection | **Implemented**, deterministic tests; message bodies persist under bounded retention |
174
149
  | Private JSONL control protocol over a controller-owned UDS | **Implemented**, deterministic synthetic tests; no provider connection required |
175
- | Static metadata-only dashboard renderer and atomic publisher | **Implemented**, deterministic security tests; the static renderer requires no browser or HTTP server |
176
- | Opt-in live dashboard companion (`embassy dashboard --live`) | **Implemented**, deterministic tests over the stable loopback listener, direct multi-browser access, projection, request guards, and four bounded route actions; it is a separate foreground process, never part of `embassy serve` |
177
- | Claude registry/peer adapter with strict peer protocol 1 and per-operation validation | **Implemented** and live-tested through Claude Code 2.1.227, including discovery, native status frames, cancellation, and accessible-workspace validation |
150
+ | Claude registry/peer adapter with strict peer protocol 1 and per-operation validation | **Implemented**; deterministic tests cover discovery, native status frames, cancellation, and accessible-workspace validation |
178
151
  | Claude current-user runtime roots | **Implemented**; derives the registry and callback roots from the verified OS user without inspecting a launcher or configuration file |
179
152
  | Stateless allowlisted Codex App Server transport with bounded busy behavior | **Implemented**; every operation opens and attests its own transport, and the conformance suite covers idle gating, exact `STEER:` behavior, clean retry, and ambiguous no-replay settlement |
180
153
  | Attach-only local Codex proxy transport and exact-owned cleanup | **Implemented**, five deterministic tests; no live App Server connection in routine tests |
181
- | Local provider adapters and Embassy-node federation | **Implemented**, focused synthetic tests cover Claude discovery, exact Codex ownership, lazy ACP-backed DeepSeek and Grok routes with provider-local degradation and cleanup, plus bounded catalog reconciliation and destination-owned handoff over the fixed attach-only SSH transport |
154
+ | Local provider adapters and Embassy-node federation | **Implemented**, focused synthetic tests cover Claude discovery, exact Codex ownership, plus bounded catalog reconciliation and destination-owned handoff over the fixed attach-only SSH transport |
182
155
  | Universal shell peer mailbox | **Implemented**, alias-plus-token same-UID attribution, hash-only durable ownership, bounded long polling, stdout-flush receipts, and restart uncertainty tests; no PID binding, token file, Keychain entry, or daemon |
183
- | Gateway service composition | **Implemented**, including private control-server startup, synthetic cross-provider selection/dispatch/reply correlation, metadata-only publication, and restart attempt-phase tests |
184
- | Delivery receipt/status lifecycle | **Implemented**, deterministic synthetic tests cover stable-UUID native receipt re-resolution, the merged/verbose/quiet Claude notice policy, one bounded stall notice with pending age where enabled, opaque private-v4 correlation handles, restart continuity, the closed status/terminal schema, and one-shot/bounded-wait CLI behavior |
156
+ | Gateway service composition | **Implemented**, including private control-server startup, synthetic cross-provider route installation/dispatch/reply correlation, bounded public-snapshot projection, and restart attempt-phase tests |
157
+ | Delivery receipt/status lifecycle | **Implemented**, deterministic synthetic tests cover stable-UUID native receipt re-resolution, the merged/verbose/quiet Claude notice policy, one bounded stall notice with pending age where enabled, opaque private-v5 correlation handles, restart continuity, the closed status/terminal schema, and one-shot/bounded-wait CLI behavior |
185
158
  | Broker-owned cross-provider provenance framing | **Implemented**, deterministic tests cover exact Codex and Claude wire shapes, bounded long-alias attribution, recipient reply hints, reserved-tag neutralization, single wrapping across clean retries, and pre-write failure |
186
159
  | Operator/agent client CLI and package binary | **Implemented**, deterministic private-UDS tests cover the closed command family, inherited provider identity, bounded stdin-only bodies, normalized output, and ambiguous no-retry behavior |
187
- | Repo-shipped cross-provider skill | **Implemented** as a repo-scoped workflow over the client CLI; it is not installed into either provider's global configuration |
160
+ | Repo-shipped cross-provider skill | **Implemented** as a packaged workflow over the client CLI; the operator copies it into each agent's skill directory, with no automatic installation |
188
161
  | Foreground local broker launcher and provider assembly | **Implemented** as `embassy serve`; local-host-only with native messaging enabled |
189
- | Live Codex-to-Claude delivery | **Tested** through selected real Claude 2.1.224–2.1.226 sessions |
190
- | Claude-initiated Codex turn/reply into Codex | **Tested** with a real busy Codex task: native `busy → waiting`, automatic post-idle turn, terminal delivery status, and exact reply round trip |
191
- | Remote production connector | **Planned**; only the `build-mac` read-only attach feasibility probe is complete |
192
162
 
193
163
  Synthetic tests do not scan `~/.claude`, connect `/tmp/cc-socks`, attach to a
194
- Desktop App Server, invoke SSH, or make a model request.
164
+ live App Server, invoke SSH, or make a model request.
195
165
 
196
166
  ## Identity, discovery, and opt-in
197
167
 
@@ -209,7 +179,7 @@ claude-advisor@this-mac
209
179
  Claude's native `sessionId` UUID is its sole logical identity. Its current
210
180
  name is a mutable lookup alias for that UUID; the gateway keeps no historical
211
181
  name index. A rename therefore makes the old name stop resolving immediately,
212
- while the UUID and an already selected UUID-bound route continue to identify
182
+ while the UUID and an already installed UUID-bound route continue to identify
213
183
  the same session. PID, registry path, process generation, and socket generation
214
184
  are replaceable delivery coordinates, not identity, and are refreshed from the
215
185
  live registry before a write. The gateway rejects duplicate current names and
@@ -225,18 +195,11 @@ Codex registration is explicit. A task registers its own alias and
225
195
  authoritative `CODEX_THREAD_ID`; the gateway does not enumerate global Codex
226
196
  history to invent routes. Registration performs no provider I/O. Each delivery
227
197
  opens a fresh attested App Server transport, initializes it, resumes the exact
228
- private task with history excluded, and authorizes one body write. Endpoint or
229
- Desktop restart is therefore a transport fact, not a logical route transition;
198
+ private task with history excluded, and authorizes one body write. An App
199
+ Server restart is therefore a transport fact, not a logical route transition;
230
200
  an unavailable or duplicate exact task fails that operation with a safe code
231
201
  without retargeting the alias or replaying an ambiguous write.
232
202
 
233
- The live dashboard exposes the bounded `remove_codex_registration` operation.
234
- It carries only one public canonical `codex-*` alias and requires explicit
235
- operator confirmation of the consequences. In one fencing commit the broker
236
- cancels queued or reserved work, settles armed work ambiguous and accepted work
237
- unconfirmed, removes that registration and its incident consent edges, and
238
- never replays uncertain work. The browser cannot name a thread ID.
239
-
240
203
  Claude discovery is passive and limited to currently advertised genuine
241
204
  Claude session records. Only a validated native record bearing the supported
242
205
  explicit versioned Embassy-advertisement marker is classified as a gateway
@@ -262,23 +225,73 @@ correlation, accessibility, liveness, and generation checks above without an
262
225
  invented additional owner or mode rule. A current name resolves to a UUID but
263
226
  never substitutes for it.
264
227
 
265
- A selected Claude UUID remains the durable route identity until explicit
266
- unselection. Discovery publishes only bounded sanitized candidates and current
267
- lookup aliases; it never changes the selected UUID. Immediately before a
228
+ An installed Claude route keeps its UUID as durable identity until a
229
+ different session claims its alias and displaces it; there is no operator
230
+ command that retires one.
231
+ Discovery publishes only bounded sanitized candidates and current
232
+ lookup aliases; it never changes the installed UUID. Immediately before a
268
233
  Claude-bound write, Embassy performs a fresh bounded registry scan, resolves
269
234
  that byte-identical UUID exactly once, and revalidates its current workspace,
270
235
  process, socket, and used-artifact generation. An incomplete scan, duplicate
271
236
  UUID, changed UUID, or unsafe current coordinate fails that operation closed.
272
- A duplicate display name is fenced from listing, selection, and pair creation;
273
- a pre-bound route retains its identity-pinned binding, and an operator-supplied
274
- UUID remains the recovery selector. A name alone never restores or retargets a
275
- durable selection.
276
-
277
- The dashboard is the single pane for the human. It shows both sanitized
278
- available/selected Claude aliases and explicitly registered Codex aliases,
279
- including their host, current state, last-seen age, and queue depth.
237
+ A duplicate display name is fenced from listing and from every send that
238
+ addresses it BY NAME, and the fence is re-evaluated inside the
239
+ send path, never only on the discovery timer. The fence is a fence on names:
240
+ a session UUID is unambiguous, so an operator-supplied UUID remains the
241
+ recovery selector and reaches the session even while its name collides, and a
242
+ sender whose own display name collides is never silenced, because its identity
243
+ was attested rather than typed. A pre-bound route retains its identity-pinned
244
+ binding, and a name alone never restores or retargets a durable route.
245
+
246
+ `embassy status` is the single pane for the human, and it is read-only: it
247
+ makes exactly one call, `list_snapshot`, and never `refresh_discovery`, so the
248
+ scan it reports is the broker's own and a status loop cannot journal anything.
249
+ Its `sessions` block is the sanitized `availablePeers` inventory — every live
250
+ Claude session the last scan found, by current name, marked `routed` once a
251
+ route exists for it — and its header says how old that scan is, offering
252
+ `embassy refresh` when it is older than a minute — that age is read from the
253
+ newest discovered session's own stamp, because the connector's `lastSeenAt`
254
+ also moves on a delivery. Below the sessions come the
255
+ routes: routed Claude aliases and explicitly registered Codex aliases, with
256
+ provider, current state, queue depth, and last-seen age. The same
257
+ pane also carries the bounded ledger's retained message bodies, so its output
258
+ is as sensitive as the messages themselves.
280
259
  The thin skill/CLI exposes the same safe alias list to either provider.
281
260
 
261
+ That pane has two forms of the same snapshot. Piped, or with `--json`, it emits
262
+ `{ok,command,result}` with the snapshot under `result` (routes at `.result.routes`). On a
263
+ terminal it is rendered by `status-view.ts`, a pure function of the snapshot
264
+ and the reader's clock: it derives one of four plain words per connector
265
+ (`ok`, `stale`, `degraded`, `offline`) and pairs every word that is not `ok`
266
+ with its safe code and a one-line remedy. Two clocks govern staleness, and
267
+ they are deliberately far apart. The broker's own connector window is 35
268
+ seconds (`CONNECTOR_OBSERVATION_STALE_AFTER_MS`), and it is crossed routinely
269
+ and innocently — the Codex observer polls one route at a time every 15
270
+ seconds, and Claude routes are not polled at all: their evidence is
271
+ edge-triggered, written when a discovery scan or a delivery observes the
272
+ session, so a quiet Claude session's `lastSeenAt` stops advancing while the
273
+ session is perfectly alive. The view therefore keeps the broker's word until
274
+ a route has gone unobserved for ten minutes, and only then calls it `stale`;
275
+ past that backstop a Codex route earns the succession remedy only when it
276
+ also carries a code that says the task itself is gone (`THREAD_NOT_OBSERVED`)
277
+ — bare silence says the task may be busy or the app-server slow. A Claude
278
+ route whose alias the latest scan still lists is never `stale`: the scan is
279
+ fresher evidence than the route's own observation, so the discovered state is
280
+ shown with `discovered <age>` as its last-seen cell, and a Claude route past
281
+ the backstop that the scan no longer lists says the session exited or renamed.
282
+ A connector that merely has nothing to observe is `stale`, never `degraded`,
283
+ and a shell peer with unclaimed mail never moves the overall word at all:
284
+ silence in a pull mailbox is a fact about the operator's other terminal.
285
+ `embassy watch` tails the same snapshot through `observe_snapshot` once a
286
+ second, printing each new message row and each settlement at most once — a
287
+ transition passed through entirely between two polls is never seen, and rows
288
+ evicted from the retained window before the tail read them are announced as
289
+ a note. `embassy check` proves the whole path end to end — it registers an
290
+ ephemeral `peer-*` principal of its own, sends one marked body through the
291
+ ordinary send path, waits for `delivered`, then awaits the correlated reply on
292
+ its own mailbox, and releases the registration again; the broker retires the
293
+ registration itself if the check never gets to.
294
+
282
295
  ## Message flows
283
296
 
284
297
  ### Codex to Claude
@@ -288,10 +301,10 @@ The thin skill/CLI exposes the same safe alias list to either provider.
288
301
  and bounded text.
289
302
  2. The gateway checks thread ownership, selector state, rate and size limits,
290
303
  deadline, and dedupe state.
291
- 3. It requires the selector to match an explicitly selected live UUID,
292
- refreshes the UUID's current process/socket coordinates, and revalidates
293
- the selected Claude peer's canonical workspace access and exact generation
294
- before every send.
304
+ 3. It resolves the selector against a discovery scan run inside this send,
305
+ installing the session's route if this is its first use, then refreshes the
306
+ UUID's current process/socket coordinates and revalidates that Claude
307
+ peer's canonical workspace access and exact generation before every send.
295
308
  4. Immediately before the native write, it composes one broker-owned canonical
296
309
  `cross-session-message` textual frame with bounded sender attribution and a
297
310
  first-child reply hint containing the full conversation token, exact aliases,
@@ -321,22 +334,19 @@ gateway publishes a process-owned native registry entry per task, accepts
321
334
  Claude's native `SendMessage`, starts an App Server turn, and returns the
322
335
  final reply.
323
336
 
324
- 1. The gateway advertises one process-owned `codex-*` record per registered
325
- task in Claude's native registry. The broker owns the advertisement,
337
+ 1. The gateway advertises each local registered task through a marked `codex-*`
338
+ record; shell routes similarly use `peer-*` records. The broker owns the advertisement,
326
339
  callback socket, state, queue, and dispatch; provider process lifecycle is
327
340
  not persisted as route authority.
328
341
  2. A real Claude session uses native `ListAgents` and `SendMessage`; the
329
342
  gateway validates that exact live registry/socket generation and treats the
330
343
  text as untrusted user-role input. This inbound observation grants only a
331
- transient, in-memory capability for the correlated reply. It does not add a
332
- Claude route, flip `selected`, or authorize a later unsolicited send.
333
- In paired mode the default a sender without the exact permission edge is
334
- refused before message admission with `SENDER_NOT_PAIRED`. No message is
335
- accepted, so no delivery is created, but the refusal is not silent: the
336
- broker records it as a `rejected` journal event carrying the direction, both
337
- aliases, the byte count, and the safe error code, and increments the
338
- rejected counters on the accounting, the source route, and any matching
339
- pair.
344
+ transient, in-memory capability for the correlated reply. The sending
345
+ session's own route is installed here, from the exact identity the adapter
346
+ attested, so the Codex task's reply travels the ordinary path; the alias the
347
+ adapter reports must be the one discovery shows for that UUID, and a
348
+ mismatch is refused rather than silently renamed. Installing the route
349
+ authorizes nothing beyond what addressing already implied.
340
350
  3. The Claude process's inherited messaging-socket value may be accepted as a
341
351
  transient reply address after strict validation. Claude Code exports
342
352
  `CLAUDE_CODE_MESSAGING_SOCKET` as a raw absolute socket path; the CLI
@@ -370,7 +380,7 @@ final reply.
370
380
  deliberate: stall visibility must not scale with the deadline, so under the
371
381
  default four-hour deadline the notice fires at two minutes, not two hours.
372
382
  `quiet` suppresses this gateway-authored frame without changing native status
373
- or dashboard state.
383
+ or public snapshot state.
374
384
  8. A later bounded attempt opens a new transport and starts the held message
375
385
  after it observes the exact task idle. A route retains at most three queued steering
376
386
  messages; accepting a fourth atomically cancels the oldest with safe code
@@ -401,7 +411,7 @@ not uniquely re-observed with peer protocol 1, the write fails closed. A termina
401
411
  write whose outcome is ambiguous is never replayed; only a proven pre-write
402
412
  failure may be retried while the bounded in-memory receipt remains live. The
403
413
  receipt correlation does not add the UUID or receipt handle to public output
404
- or durable state; a separately selected route may already persist that same
414
+ or durable state; a separately installed route may already persist that same
405
415
  Claude UUID as its private native route handle.
406
416
 
407
417
  Delivery callback arrival is timestamped at the service boundary. A terminal
@@ -425,8 +435,8 @@ next-tool-call-boundary rules above.
425
435
 
426
436
  ### Provenance framing and conversation continuation
427
437
 
428
- The broker classifies `STEER:`, `TRACK:`, and `DONE:`, enforces raw-byte body
429
- limits, deduplicates, and queues before presentation framing.
438
+ The broker classifies `STEER:`, enforces raw-byte body limits, deduplicates,
439
+ and queues before presentation framing.
430
440
  The store therefore retains only the raw unframed body, never the composed
431
441
  envelope. A pure composer runs at the final semantic provider-write boundary so
432
442
  a clean retry produces the same bytes with exactly one authoritative outer
@@ -448,7 +458,7 @@ broker-owned `cross-session-message` outer element and an
448
458
  does not accept.
449
459
  - In either direction, the first hint carries the full token in `conversation`
450
460
  and the exact recipient alias in `reply-as`, followed by an exact stdin-based
451
- `embassy reply --conversation ... --alias ...` instruction and the statement
461
+ `embassy send --conversation ... --from ...` instruction and the statement
452
462
  that caller, conversation, and route policy are rechecked.
453
463
 
454
464
  Embassy does not synthesize `from`, `from-session`, or `from-mode` attributes:
@@ -466,20 +476,21 @@ consistent structural provenance marker at the model input boundary. A native
466
476
  Claude wrapper already present in an inbound body is untrusted nested text
467
477
  beneath the Embassy wrapper.
468
478
 
469
- The full token delivered in the hint lets the recipient call `reply`, but it is
470
- only a participant-scoped conversation locator. The service still validates
479
+ The full token delivered in the hint lets the recipient answer with
480
+ `send --conversation`, but it is only a participant-scoped conversation
481
+ locator. The service still validates
471
482
  the inherited caller, current conversation membership, and current route
472
483
  policy. The full token remains confined to the accepted control result
473
484
  and transient provider payload, and is memory-only: it is never persisted,
474
- journaled, logged, snapshotted, rendered on a dashboard, placed in a receipt,
485
+ journaled, logged, snapshotted, placed in a receipt,
475
486
  or returned from suffix-only public correlation. Formatter,
476
487
  provenance-metadata, and framed-size failures are clean pre-write terminal
477
488
  failures; they can never become ambiguous writes or replay authorizations.
478
489
 
479
490
  The gateway exposes `turn/steer` only through an exact leading `STEER:` body in
480
491
  the Claude-to-Codex direction. The global `EMBASSY_STEERING_ENABLED` switch is
481
- on by default and exact `0` disables classification. The tested 0.147.0 schema
482
- requires `expectedTurnId`, rejects a nonmatching active turn, reports a clean
492
+ on by default and exact `0` disables classification. The 0.147.0 schema this
493
+ adapter targets requires `expectedTurnId`, rejects a nonmatching active turn, reports a clean
483
494
  `activeTurnNotSteerable` condition, and returns the accepted turn ID. Embassy
484
495
  validates all of those temporal correlations before settlement. `turn/interrupt`
485
496
  is never called or exposed, and there is no generic App Server RPC escape
@@ -487,15 +498,14 @@ hatch.
487
498
 
488
499
  ### Delivery status and bounded waits
489
500
 
490
- Every accepted control-plane `send` or `reply`
501
+ Every accepted control-plane `send`
491
502
  result contains both its conversation ID and a fresh opaque delivery
492
503
  correlation handle called a delivery token.
493
504
  The token has the closed form `dlv_` followed by exactly 24 base64url
494
505
  characters (`A-Z`, `a-z`, `0-9`, `_`, or `-`). It addresses one bounded
495
- private-v4 message/status row and is not a provider receipt handle or a
506
+ private schema-5 message/status row and is not a provider receipt handle or a
496
507
  provider native identifier. It is stored only in the mode-0600 broker state
497
- and never appears in a public snapshot, normal log, provider receipt, or
498
- dashboard.
508
+ and never appears in a public snapshot, normal log, or provider receipt.
499
509
 
500
510
  The read-only `delivery_status` method accepts only that token and returns one
501
511
  of these closed results:
@@ -542,7 +552,7 @@ never displaced to admit a new send. A pressure-evicted handle returns
542
552
  Conversation IDs correlate replies, and callback addresses exist only in
543
553
  memory, but message bodies and their bounded attempt phase are durable. After a
544
554
  gateway restart, queued or reserved work may resume once against the same
545
- logical route and consent edge. Work that crossed the armed boundary settles
555
+ logical route. Work that crossed the armed boundary settles
546
556
  `ambiguous`; provider-accepted work without terminal evidence settles
547
557
  `unconfirmed`. Neither is replayed. Work already past its deadline settles
548
558
  `expired`.
@@ -551,8 +561,8 @@ The delivery token and status of each retained message survive the restart: a
551
561
  queued or reserved attempt remains inspectable while it resumes, and armed or
552
562
  accepted work remains inspectable after it settles ambiguous or unconfirmed.
553
563
  Pending replies, callbacks, native receipt handles, and conversation
554
- capabilities do not survive. Logical registrations, Claude selections, and
555
- consent edges remain, while each subsequent provider operation must attest its
564
+ capabilities do not survive. Logical registrations and installed Claude routes
565
+ remain, while each subsequent provider operation must attest its
556
566
  own current transport facts.
557
567
 
558
568
  ## Gateway control plane
@@ -562,76 +572,104 @@ controller-owned mode-0700 state directory. The socket and state files are
562
572
  mode 0600. Frames are size-bounded and closed against unknown keys, methods,
563
573
  versions, and enum values.
564
574
 
565
- The closed version 2 method family is exactly these twenty-one methods:
575
+ The closed version 4 method family is exactly these fifteen methods:
566
576
 
567
577
  - `health` and `list_snapshot`, a safe public snapshot;
568
578
  - `observe_snapshot`, a read-only projection that may settle already-due
569
579
  delivery deadlines before projecting;
570
- - `register_codex`, `unregister_codex`, and
571
- `remove_codex_registration` explicit Codex registration and atomic
572
- `--succeeds` replacement, owner unregister, and confirmed operator removal;
573
- - `select_claude` and `unselect_claude`, from the current sanitized
574
- available-peer inventory;
575
- - `pair` and `unpair`, the two-endpoint permission edge;
580
+ - `register_codex` and `unregister_codex` — explicit Codex registration with
581
+ atomic `--succeeds` replacement, and owner unregister;
582
+ - `retire_route` (CLI `embassy retire --alias <local-alias@local-host>`), the
583
+ explicit OS-boundary exception to owner unregister: any
584
+ same-UID control client may atomically remove any local Claude, Codex, or
585
+ shell-peer route and requires no route credential; the request accepts only the
586
+ local alias (no token, force, or remote option), refuses a federated mirror
587
+ with `FEDERATED_ROUTE_READ_ONLY`, and returns settlement counts
588
+ `{cancelled,ambiguous,unconfirmed}`;
576
589
  - `delivery_status`, a lookup by an opaque correlation handle retained only in
577
- bounded private v4 state;
578
- - `untrack`, which closes one active progress watch by conversation token;
579
- - `send`, whose direction is derived from the resolved endpoint providers;
580
- - `reply`, the correlated reply operation;
581
- - `refresh_dashboard`, which refreshes provider discovery and republishes;
590
+ bounded private v5 state;
591
+ - `send`, whose direction follows the inherited principal who is sending —
592
+ rather than the route table, and which installs a discovered Claude
593
+ session's route on its first use. It addresses either a route by name
594
+ (`toAlias`) or an open conversation by its token (`conversationId`), never
595
+ both: the conversation form is the correlated reply operation, and the
596
+ caller must already own one end of that conversation;
597
+ - `refresh_discovery`, which rescans for Claude sessions;
582
598
  - `peer_catalog` and `peer_handoff`, the private federation catalog and
583
599
  destination-owned handoff operations; and
584
600
  - `register_peer`, `unregister_peer`, `await_peer`, and `peer_receipt`, the
585
- shell-peer registration, mailbox, and flush-before-receipt operations.
586
-
587
- The live dashboard companion calls `observe_snapshot` for every read; its
588
- mutation route additionally calls `pair`, `unpair`,
589
- `remove_codex_registration`, and `refresh_dashboard`, and nothing else.
601
+ shell-peer registration, mailbox, and flush-before-receipt operations. A
602
+ first registration may ask to be `ephemeral` with a bounded `ttlMs`
603
+ (default five minutes): the route is real in memory — it routes, queues,
604
+ and settles like any other — but it is projected out of the durable state
605
+ document together with every row that names it (its messages, dedupe and
606
+ rate rows, and journal entries), excluded from the federation catalog, and
607
+ retired by the broker's own clock, so it cannot survive a restart or be
608
+ restored on another node. Removal — by `unregister_peer` or by that clock —
609
+ takes the same attributable rows and bodies out of live state in the same write;
610
+ aggregate counters still advance and are not erased. A retirement that fails is retried twice,
611
+ five seconds apart, then left to the next restart. The clock is guarded by
612
+ the registration's identity, so a durable route that later takes the same
613
+ alias is never retired by it. `embassy check` uses one.
590
614
 
591
615
  The installed binary is `embassy`, and it is the only installed binary. Its
592
- twenty-one implemented commands are
593
- `serve`, `health`, `status`, `doctor`, `delivery-status`, `wait-delivery`, `untrack`,
594
- `refresh-dashboard`, `dashboard`, `register-codex`, `unregister-codex`,
595
- `select-claude`, `unselect-claude`, `pair`, `unpair`, `send`,
596
- `reply`, `register-peer`, `unregister-peer`, `await`, and
597
- `peer-stdio`. `dashboard` requires `--live` and accepts an
598
- optional `--lang en|zh-CN` and `--port <n>`; it starts the companion process
599
- rather than issuing a single control request. Message bodies are non-empty
616
+ eighteen implemented commands are
617
+ `serve`, `service`, `health`, `status`, `watch`, `check`, `delivery-status`,
618
+ `wait-delivery`, `refresh`, `register-codex`, `unregister-codex`, `send`,
619
+ `reply`, `retire`, `register-peer`, `unregister-peer`, `await`, and
620
+ `peer-stdio`. `reply --conversation <token> --alias <own-alias>` is a
621
+ deprecated alias for `send --conversation <token> --from <own-alias>`: it
622
+ builds the same `send` request and is kept only until the reply hints already
623
+ delivered in older envelopes have aged out. Message bodies are non-empty
600
624
  UTF-8 from standard input only, with a 16 KiB ceiling; they are never accepted
601
- in an argument or file. The client emits one bounded normalized JSON line and
602
- never returns a thread ID, provider-native ID, path, address, or message body.
603
- These commands require the foreground broker, except that `serve` starts it in
604
- the current terminal. The launcher never daemonizes itself.
625
+ in an argument or file. The client emits one bounded normalized JSON line, and
626
+ for every broker-protocol command it never returns a thread ID,
627
+ provider-native ID, path, or address. `status` and `watch` return retained snapshot
628
+ bodies; `await` returns one framed mailbox message and acknowledges only after
629
+ stdout flushes. Other deliberate output exceptions follow.
630
+ `service` reports its own plist path, its log path, and any program path in
631
+ the plist that is no longer on disk, because managing local files is what it
632
+ does. `status` on a terminal, `watch`, and `check` render for a person instead
633
+ of emitting that JSON line: `status` and `watch` show the snapshot's own
634
+ retained bodies (previewed to one control-free line) and the resolved state
635
+ directory, and `check` prints its own hops. Piped or with `--json`, `status`
636
+ and `watch` are machine-readable again, and every other command is unchanged. Every command but `serve` and `service` requires a running
637
+ broker: `serve`
638
+ starts one in the current terminal, and `service install|uninstall|status`
639
+ manages the macOS launchd agent that runs one, contacting no broker itself
640
+ apart from the bounded health check `install` reports at the end. The launcher
641
+ never daemonizes itself.
605
642
 
606
643
  `register-codex --alias <new> --succeeds <current>` is one atomic logical-route
607
644
  transaction. It verifies the inherited identity of the replacement task,
608
645
  settles the outgoing route's work according to recorded write phase, removes
609
- its incident consent edges and transient capabilities, and publishes only the
646
+ its transient capabilities, and publishes only the
610
647
  new registration. There is no prepared, activated, endpoint-generation, or
611
648
  manual-recovery state.
612
649
 
613
- `select-claude --alias <current-name@host>` and
614
- `select-claude --session <uuid>` select the same logical session.
615
- `send --to` accepts either form only after explicit selection. UUID
650
+ `send --to <current-name@host>` and `send --to <uuid>` address the same logical
651
+ session, and either form installs its route on first use. UUID
616
652
  input is normalized to lowercase. No command returns the
617
653
  UUID, and no historical name remains routable after a rename.
618
654
 
619
- Provider-authorized registration, send, and reply operations require one exclusive inherited principal.
655
+ Provider-authorized registration and send operations require one exclusive inherited principal.
620
656
  Codex registration, unregister, and Codex-to-Claude send require only a valid
621
657
  `CODEX_THREAD_ID`; they fail if a non-empty Claude messaging socket is also
622
658
  inherited. Claude-to-Codex send requires only the raw inherited Claude socket
623
- path and fails if a non-empty Codex thread ID is also present. `reply` likewise
624
- fails with both identities or neither.
625
-
626
- `pair`, `unpair`, `select-claude`, and `unselect-claude` are control-plane
627
- operations authorized by access to the same-UID private socket; they do not
628
- attest inherited provider identity. Pair and unpair mutate only the exact two
629
- named endpoints, while selection installs or removes one Claude route and
630
- creates no consent edge. Paired mode still rechecks exact edge membership at
631
- delivery. Removing the selected route also removes its incident consent edges
632
- and settles their in-flight work from the durable attempt phase. Agents are
633
- instructed to create or remove only user-chosen edges; that is an operating
634
- norm, not an additional gateway identity check.
659
+ path and fails if a non-empty Codex thread ID is also present. A
660
+ conversation-addressed send likewise fails with both identities or neither.
661
+
662
+ Installing a Claude route is a side effect of a send, not a command: the
663
+ sending principal is already attested, the addressed session is resolved
664
+ against a discovery scan performed inside that send, and the store binds the
665
+ route under the live-peer registration mode. A session already bound under the
666
+ same (host, session UUID) keeps its registration and therefore its in-flight
667
+ conversations and is renamed in place if its display name changed; a route
668
+ whose alias now names a different session is displaced, its work settled
669
+ `cancelled` with `ENDPOINT_RETIRED`, and both outcomes are journaled so
670
+ `embassy status` shows them. Agents are instructed to send only where the user
671
+ pointed them; that is an operating norm, not an additional gateway identity
672
+ check.
635
673
 
636
674
  The foreground command is:
637
675
 
@@ -639,15 +677,8 @@ The foreground command is:
639
677
  embassy serve
640
678
  ```
641
679
 
642
- `--inbound open` is the one security-relevant option:
643
-
644
- ```text
645
- embassy serve --inbound open
646
- ```
647
-
648
- The default is `paired`. `open` is the explicit opt-out that lets any exact
649
- compatible live same-UID Claude session send inbound without a pair edge; it is
650
- never implied and cannot be set through an environment variable.
680
+ `serve` takes no options. There is no inbound mode to choose: the broker has
681
+ one posture, and it is the OS boundary.
651
682
 
652
683
  Before provider validation, listener creation, or App Server attachment, the
653
684
  launcher acquires one fixed host-wide crash-reclaimable owner lease under the
@@ -656,8 +687,8 @@ controllers cannot be started for the same login account by choosing different
656
687
  state roots. It is the only instance lock Embassy takes: the pre-rename
657
688
  prototype state root is no longer read, locked, or mutated.
658
689
 
659
- It emits one normalized ready line, publishes the private dashboard, and
660
- holds the process until `SIGINT` or `SIGTERM`, when exact-owned resources are
690
+ It emits one normalized ready line and holds the process until `SIGINT` or
691
+ `SIGTERM`, when exact-owned resources are
661
692
  closed. Startup validates exact owned provider paths and binds controller-owned
662
693
  UDS listeners. Missing optional providers or a provider-local interface failure
663
694
  degrades only that surface. Unsafe ownership, path, symlink, lease, state,
@@ -669,44 +700,28 @@ does not publish candidates, select or connect to a peer, write a provider
669
700
  socket, request provider history, start a model turn, or contact a remote host.
670
701
  Validated target bindings may retain private native and socket-derived evidence
671
702
  memory-only until rescan or close, but none enters public state or persistence.
672
- Its ready result reports the exact local host from `nodes.json`, dashboard filename
673
- `gateway-dashboard.html`, and `codexMode: "native_messaging"` without exposing
674
- paths.
703
+ Its ready result reports the exact local host from `nodes.json`, or this
704
+ host's own hostname when that file is absent — and `codexMode: "native_messaging"`
705
+ without exposing paths.
675
706
 
676
707
  There is no arbitrary filesystem operation, shell command, SSH command, App
677
708
  Server method, Claude registry mutation, credential argument, approval reply,
678
709
  or raw diagnostic method.
679
710
 
680
- Same-UID socket access is a local containment boundary, not proof of a trusted
681
- agent process. Every mutation additionally checks route ownership, exact
682
- thread/session generation, source alias, bounds, and conversation state.
683
-
684
- ### Progress watches
685
-
686
- `send` and `reply` each accept an opt-in `--track`
687
- flag that opens one progress watch over the resulting conversation, plus an
688
- optional `--idle-minutes <n>` that sets how long the watched thread may sit idle
689
- before each bounded liveness nudge. If the watch ultimately times out, Embassy
690
- records `settled` / `gateway` / `idle_timeout` only in watch history and emits
691
- no runtime stall alert. `n` is an integer from 1 through 1440 and defaults to 5;
692
- supplying it without `--track` is an argument error. A body with
693
- an exact leading `TRACK:` prefix opens the same watch at the default idle window
694
- without the flag.
711
+ Reaching the same-UID private control socket is the permission to message
712
+ and it is a local containment boundary, not proof of a trusted agent process.
713
+ Every mutation additionally checks route ownership, exact
714
+ thread/session generation, source alias, bounds, and conversation state. A name
715
+ currently shared by more than one live Claude session is refused with
716
+ `PEER_ALIAS_COLLISION` inside the send that addressed it BY NAME; the broker
717
+ never resolves an ambiguous name by picking first. The fence is a fence on
718
+ names only: a session UUID is unambiguous, so a UUID selector still reaches its
719
+ session, and a sender is never fenced by its own display name because its
720
+ identity was attested rather than typed.
695
721
 
696
- `untrack --conversation conv_<token>` closes an active watch explicitly, and a
697
- body with an exact leading `DONE:` prefix closes it as completed; one message
698
- may not both open and complete a watch (`PROGRESS_WATCH_SIGNAL_CONFLICT`). The
699
- global `EMBASSY_TRACKING_ENABLED` switch is on by default and exact `0` disables
700
- the surface; `EMBASSY_MAX_WATCHES` bounds concurrent watches at 32 by default
701
- (hard cap 256).
722
+ ## Codex connector and federation
702
723
 
703
- A watch is independent evidence about thread activity, not delivery evidence. It
704
- may outlive an opener whose own delivery expired, so check the opener's
705
- `delivery-status` separately before assuming the assignment text arrived.
706
-
707
- ## Codex connectors and remote hosts
708
-
709
- Each broker's mandatory `nodes.json` gives its local connector an explicit host identity; `this-mac` has no reserved meaning. Configured peers exchange only body-free local catalogs and destination-owned handoffs over fixed SSH.
724
+ Each broker's local connector has an explicit host identity, from `nodes.json` when federating or from this host's own hostname otherwise; `this-mac` has no reserved meaning. Configured peers exchange only body-free local catalogs and destination-owned handoffs over fixed SSH.
710
725
 
711
726
  The local connector resolves the managed standalone Codex release by exact
712
727
  owned path; it does not use `PATH`. That installation is separate from
@@ -722,7 +737,7 @@ unsubscribe, interrupt, archive,
722
737
  delete, history, shell, configuration, authentication, plugin,
723
738
  approval-response, and generic RPC methods remain excluded everywhere.
724
739
 
725
- The App Server capability first tested with 0.147.0 gates the privacy-preserving
740
+ The App Server 0.147.0 schema this adapter targets gates the privacy-preserving
726
741
  `thread/resume.excludeTurns` field behind initialization capability
727
742
  `experimentalApi: true`. The transport therefore hard-codes that one
728
743
  non-configurable capability solely to suppress history retrieval. The attempt's
@@ -741,9 +756,9 @@ does not participate. Embassy does not read or retain reported
741
756
  working-directory or policy fields, and a transport failure cannot discard the
742
757
  registration or its accepted queue.
743
758
 
744
- Version 1 never changes or independently classifies a Codex task's approval or
745
- sandbox policy. Offline `TurnStartParams` schema evidence from tested App
746
- Server 0.147.0 shows that
759
+ Embassy never changes or independently classifies a Codex task's approval or
760
+ sandbox policy. Offline `TurnStartParams` schema evidence from the App
761
+ Server 0.147.0 schema this adapter targets shows that
747
762
  policy overrides persist for the current and subsequent turns, so using them
748
763
  as per-message restrictions would silently mutate the native task. Embassy
749
764
  therefore starts the turn without overrides and leaves approval, sandbox, and
@@ -751,37 +766,24 @@ tool enforcement to the registered task's native Codex configuration. Explicit
751
766
  `codex-*` registration plus exact per-operation task and transport validation is the
752
767
  gateway reachability boundary; native task policy remains Codex's concern.
753
768
 
754
- A remote connector never starts, stops, replaces, signals, or unlinks a
755
- Desktop-owned App Server or its socket. If attach fails, the host is offline;
756
- Desktop remains responsible for lifecycle recovery. SSH aliases are fixed
757
- operator configuration, never model-provided strings. Normal OpenSSH host-key
758
- validation applies.
759
-
760
- ### Completed no-model feasibility evidence
761
-
762
- On 2026-08-07:
763
-
764
- - A no-model environment check in the current Codex task confirmed that the
765
- task tool process inherits `CODEX_THREAD_ID` and that its value matches the
766
- required UUID grammar. The check emitted booleans only and never printed or
767
- retained the identifier. This validates the repo skill/CLI premise that a
768
- Codex task can self-register without accepting its private thread ID as a
769
- command-line argument.
770
- - A local attach-only probe connected through a second proxy to managed Codex
771
- App Server 0.147.0, initialized, called only `thread/loaded/list`, and
772
- confirmed the current task was already loaded. It emitted normalized
773
- booleans and an aggregate count, then confirmed cleanup of only its own
774
- proxy process.
775
- - An authorized remote probe attached through a second SSH proxy to the
776
- already-running `build-mac` App Server (remote Codex CLI 0.145.0), initialized,
777
- and validated a schema-correct `thread/loaded/list`. It printed no task IDs,
778
- payloads, remote diagnostics, history, or credentials and left Desktop's
779
- original proxy alive.
780
-
781
- Both proxy processes required their exact-owned forced-cleanup fallback after
782
- the bounded graceful-close window; final cleanup was confirmed. These probes
783
- prove attach and loaded-task discovery on the tested versions. They do not
784
- prove notification fanout, approval routing, or writable task control.
769
+ ### Federation
770
+
771
+ Federation is the only cross-machine path. Each entry in `nodes.json` is an
772
+ OpenSSH `Host` alias; the broker dials `ssh <node> embassy peer-stdio` and
773
+ speaks the fixed peer protocol — version 2, three methods: `initialize`,
774
+ `catalog/get`, `handoff`. A peer's catalog is body-free (aliases, providers,
775
+ states), and its routes appear locally as `alias@host` mirrors behind opaque
776
+ references; a handoff is `delivered` when the destination durably accepts it,
777
+ after which the destination owns provider delivery, and anything lost after
778
+ that acceptance settles ambiguous and is never replayed. One hop only, no
779
+ forwarding. A node answering `initialize` with another protocol version
780
+ surfaces `PEER_PROTOCOL_MISMATCH` on its mirrored routes and in `embassy
781
+ status` instead of a tunnel fault. SSH aliases are fixed operator
782
+ configuration, never model-provided strings; normal OpenSSH host-key
783
+ validation applies, your SSH configuration owns keys, users, and ports, and
784
+ Embassy opens no listener of its own.
785
+
786
+ ### Offline App Server schema evidence
785
787
 
786
788
  The connector requires turn notifications to carry the exact `threadId` and
787
789
  correlates the exact `turn.id`; `item/completed` must carry the exact
@@ -809,122 +811,29 @@ next-tool-call timing boundary to App Server, treats a clean refusal as normal
809
811
  queue fallback, and treats malformed or write-ambiguous results as terminally
810
812
  uncertain without replay.
811
813
 
812
- The one Desktop restart needed for the local shared-App-Server feasibility
813
- test has already been completed. Building, running synthetic tests, starting
814
- the gateway, rendering the dashboard, and a future Claude peer-socket test do
815
- not themselves require another Desktop restart. A provider or Desktop major
816
- upgrade may change an internal interface; strict per-operation checks keep the
817
- responsible route closed if that interface no longer matches. Other providers
818
- remain available. If the attachment mode changes, a supporting release may
819
- require a separately announced controlled restart. The offline support matrix
820
- records what a release was tested with but never grants runtime authority.
821
-
822
- ## Dashboard
823
-
824
- Version 1 generates self-contained HTML files under the controller-owned state
825
- directory: `gateway-dashboard.html` and `gateway-dashboard.zh-CN.html`, both
826
- rendered from one typed catalog and both atomically replaced, mode 0600, on
827
- every publish. Each page links to the other; that in-page link is the only
828
- static language switch, and `refresh-dashboard` takes no `--lang`. Each page
829
- has inline CSS and a restrictive Content Security Policy, with no JavaScript,
830
- external assets, CDN, cookies, local storage, service worker, telemetry,
831
- mutation endpoint, or network listener.
832
-
833
- A static page is a point-in-time snapshot and never refreshes itself: it emits
834
- no meta refresh and the page tells the operator to re-run
835
- `embassy refresh-dashboard` and reload, or to use `embassy dashboard --live`.
836
-
837
- Each page assembles seven sections:
838
-
839
- - **Exchange** — aggregate gateway health plus every explicit cross-provider
840
- pair and its per-edge counters.
841
- - **Attention** — allowlisted alerts such as an unavailable route, protocol mismatch,
842
- queue full, or ambiguous delivery.
843
- - **Transit** — queued-message depth and bytes in flight.
844
- - **Progress supervision** — active progress watches and their state.
845
- - **Operator activity** — the broker's bounded public journal of accepted
846
- operator actions.
847
- - **Sessions** — first-class Claude, Codex, DeepSeek, Grok, and shell-peer provider rows,
848
- aliases, host, route state, and queue depth.
849
- - **Diagnostics** — best-effort observed metadata, per-host connector health,
850
- last safe code, deadline-pressure buckets, accounting totals, and the
851
- omission counters below. Normalized message direction and delivery state,
852
- timestamp, latency, byte count, and a short opaque message-ID suffix appear
853
- with the delivery rows.
854
-
855
- It never shows message content, prompts, replies, transcripts, titles, working
856
- directories, native IDs, PIDs, socket paths, endpoint paths, tool data, raw
857
- events, stderr, credentials, or configuration contents — retained bodies reach
858
- only the live companion, never these files. This is a controller-owned UI
859
- artifact, not a shared task file. The public snapshot has a 240 KiB projection
860
- budget and reports explicit omission counters whenever bounded rows are
861
- truncated: connectors, available peers, routes, pairs, progress watches,
862
- upstream and projected progress-watch events, upstream message events, message
863
- groups, message events, upstream alerts, attention items, and upstream and
864
- projected activity events.
865
-
866
- ### Live dashboard companion
867
-
868
- `embassy dashboard --live` is the opt-in browser view of broker state,
869
- including bounded retained bodies in delivery detail; the static dashboard's
870
- public projection remains metadata-only.
871
- It is a separate foreground process, not a mode of `embassy serve`: it holds no
872
- provider capability, owns no registry record, and reaches the broker over the
873
- same private control socket every other client command uses, so it reports the
874
- gateway as unavailable when nothing is serving.
875
-
876
- - **Bind.** One `http.createServer` listener binds exact `127.0.0.1` on stable
877
- port `41961` by default, or the integer from the per-invocation `--port <n>`
878
- option in the closed range 1024 through 65535. The direct root URL is
879
- `http://127.0.0.1:<port>/`. A collision fails with
880
- `LIVE_DASHBOARD_PORT_IN_USE` and directs the operator to `--port`; there is no
881
- ephemeral-port fallback. No other interface is bound.
882
- - **Access.** The root URL is usable concurrently from multiple windows, tabs,
883
- and browsers. There is no capability token, URL fragment, cookie, browser
884
- session, random instance path, or bootstrap file.
885
- - **Request checks.** The exact Host header is validated on every request.
886
- Navigation GETs may omit Origin; every POST requires the exact Origin plus
887
- `X-Embassy-Request: 1`. `OPTIONS` is not accepted, there are no CORS headers,
888
- and cross-origin reads are unavailable.
889
- - **Projection and actions.** The companion observes through
890
- `observe_snapshot`. Its only mutations are exact two-endpoint `pair`,
891
- `unpair`, `refresh_dashboard`, and `remove_codex_registration`
892
- control calls behind one closed `/action` route. The removal action carries
893
- only a canonical public `codex-*` alias; native task IDs never enter the
894
- browser contract. In one fencing commit it cancels queued or reserved work,
895
- settles armed work ambiguous and accepted work unconfirmed, removes the
896
- route's consent edges, and never replays uncertain work. The browser shows
897
- the consequence and requires
898
- explicit confirmation; the server rejects bodies over 1 KiB and limits the
899
- companion to six actions per minute. It cannot create a registration,
900
- succeed,
901
- send, reply, approve, interrupt, change settings, or invoke a generic/provider
902
- method. Each mutation touches only the edge it names: adding an edge never
903
- retires another, and removing one settles its accepted work before the
904
- change is published. Every action is followed by a
905
- fresh observation. An observation may
906
- settle already-due lifecycle deliveries before projecting, which is a broker
907
- timer effect, not additional browser authority.
908
- - **Containment.** The loopback server deliberately performs no local-process
909
- or UID authentication. It assumes a trusted single-user machine: any local
910
- software that can reach or spoof loopback can read the live view and invoke
911
- the bounded actions. Host, Origin, and sentinel checks constrain ambient
912
- browser-origin requests; they do not authenticate local software.
913
-
914
- `--lang en|zh-CN` selects the companion's display language. It has no effect on
915
- the static pair, which is always written in both languages.
814
+ ## Protocol and schema versions
815
+
816
+ Each number below is bumped once per release line, at first need, and a
817
+ mismatch is refused rather than adapted to:
818
+
819
+ | Surface | Version | On mismatch |
820
+ | --- | --- | --- |
821
+ | Private state schema (`gateway-state.json`) | 5 | An older or unknown schema refuses with `GATEWAY_STATE_SCHEMA_UNSUPPORTED`; reset only, never rewritten |
822
+ | Private control protocol (CLI broker) | 4 | `CONTROL_VERSION_MISMATCH` at the client; keep the CLI and broker on one installation. A method the broker does not implement is `UNKNOWN_METHOD`, and the CLI says to rebuild or update the client |
823
+ | Federation peer protocol (`peer-stdio`) | 2 | `PEER_PROTOCOL_MISMATCH` on that node's mirrored routes and in `embassy status` |
824
+ | Native Claude helper IPC protocol | 2 | Internal to one installation; the helper and broker ship together |
825
+ | Public snapshot schema (`embassy status --json`) | 2 | Unchanged across the 3.0 line |
826
+ | Claude peer protocol (consumed, not owned) | 1 | A session record declaring another value is rejected in isolation and counted |
916
827
 
917
828
  ## Persistence and privacy
918
829
 
919
830
  The private store may retain:
920
831
 
921
- - schema-4 logical registrations with aliases, registration IDs, and exact
832
+ - schema-5 logical registrations with aliases, registration IDs, and exact
922
833
  provider-native route handles inside the closed private binding schema;
923
- - consent edges tied to exact registration IDs, so alias reuse cannot inherit
924
- permission;
925
834
  - bounded messages with explicit `queued`, `reserved`, `armed`, `accepted`, or
926
835
  `terminal` attempt phase and normalized activity used for accounting and
927
- dashboard projection;
836
+ public projection;
928
837
  - timestamps, counters, dedupe/rate-limit records, and safe error codes.
929
838
 
930
839
  It also retains message bodies under bounded caps — the queued body of every
@@ -936,18 +845,18 @@ snapshot is a
936
845
  strict projection that removes private route handles, registration IDs, and
937
846
  operation-local endpoint evidence. The state directory is mode 0700 and state is mode 0600;
938
847
  provider-native identifiers never enter normalized events, public snapshots,
939
- the dashboard, CLI arguments/output, aliases, logs, or error text. On restart,
940
- logical routes and consent edges remain unchanged. Queued and reserved bodies
941
- may resume once only after their exact registration and consent authority is
848
+ CLI arguments/output, aliases, logs, or error text. On restart,
849
+ logical routes remain unchanged. Queued and reserved bodies
850
+ may resume once only after their exact registration authority is
942
851
  rechecked; armed and accepted work settles without replay. Callback, native
943
852
  receipt, conversation, and reply capabilities are not reconstructed.
944
853
 
945
- Private schema 4 is the binary's only native store format; the bounded public
854
+ Private schema 5 is the binary's only native store format; the bounded public
946
855
  snapshot deliberately remains schema version 2. The runtime performs no
947
856
  migration or best-effort rewrite. An old or unknown private schema refuses with
948
857
  `GATEWAY_STATE_SCHEMA_UNSUPPORTED` without mutating the state file; the operator
949
858
  must follow the reset-only runbook in `docs/CONFIGURATION.md`. A malformed
950
- schema-4 document produces the ordinary strict corrupt-state error.
859
+ schema-5 document produces the ordinary strict corrupt-state error.
951
860
 
952
861
  ## Minimum filesystem and process access
953
862
 
@@ -959,11 +868,12 @@ user's interactive Claude history. The narrow live boundary is:
959
868
  directory;
960
869
  - create and later remove only its exact-owned callback socket inside the
961
870
  accessible Claude peer-socket directory, with inode/generation checks;
962
- - create its control socket plus metadata/dashboard files only inside its
871
+ - create its control socket plus metadata files only inside its
963
872
  separate controller-owned mode-0700 state directory;
964
- - attach to explicitly allowlisted Codex App Server endpoints; and
965
- - optionally execute fixed `ssh`/Codex proxy argv for allowlisted hosts, with
966
- no shell and no model-supplied command or hostname.
873
+ - attach to the managed local Codex App Server; and
874
+ - optionally execute the fixed `ssh <node> embassy peer-stdio` argv for each
875
+ node listed in `nodes.json`, with no shell and no model-supplied command or
876
+ hostname.
967
877
 
968
878
  It does not read Claude transcripts, settings, project state, credentials,
969
879
  Keychain, shell history, or unrelated user files. It does not copy, print,
@@ -984,27 +894,26 @@ paths, peers, and transports:
984
894
  | `/tmp/cc-socks` | At foreground startup, validate the private directory and create/remove only `/tmp/cc-socks/<gateway-pid>.sock` after inode/generation checks; search/stat genuine peers at passive discovery and connect one validated target only at the separately authorized send gate |
985
895
  | `~/.local/state/agent-embassy/.agent-embassy-state` | Validate or establish the exact ownership marker before creating the fixed host lease; an existing non-empty unmarked root is rejected without mutation |
986
896
  | `/usr/bin/lockf` and `/bin/cat` | Hold one fixed, non-waiting macOS advisory lease for the foreground controller; the helper receives no shell text, provider data, or model-supplied argument |
987
- | `/usr/bin/open` | Executed only by the opt-in `embassy dashboard --live` companion, to open the loopback dashboard URL in the operator's browser; no shell, a scrubbed fixed environment, and a bounded timeout and output cap |
988
897
  | `~/.local/state/agent-embassy/.gateway-host.lock` | Fixed per-login kernel-held lease acquired before provider setup; it remains here even when `EMBASSY_STATE_DIR` is overridden. Its bounded PID/token record is exact-cleanup metadata, not a path-only stale-lock authority; a crash releases the kernel lock and the next foreground process may acquire the existing file |
989
- | `~/.local/state/agent-embassy` (or explicit `EMBASSY_STATE_DIR`) | Default controller-owned store, control UDS, state lock, and static dashboard; an explicit absolute configuration may replace only these state surfaces |
898
+ | `~/.local/state/agent-embassy` (or explicit `EMBASSY_STATE_DIR`) | Default controller-owned store, control UDS, and state lock; an explicit absolute configuration may replace only these state surfaces |
990
899
  | `~/.codex/packages/standalone` and `~/.codex/app-server-control/app-server-control.sock` | Resolve the exact owned managed Codex binary and attach to the already-running private local App Server; never bootstrap or unlink it |
991
900
 
992
901
  No grant to `~/.claude/projects`, the rest of
993
902
  `~/.claude`, Keychain APIs, the full home directory, or
994
- unrelated temporary files is required. Remote-host access is a later,
995
- separately reviewed fixed-SSH-alias capability.
903
+ unrelated temporary files is required. Cross-machine reach is federation over
904
+ `ssh <node> embassy peer-stdio`, configured only in `nodes.json`.
996
905
 
997
- Selected Claude workspaces may contain the private controller-state directory. The
998
- filesystem root and configured temporary roots are still rejected as
999
- deliberately broad Claude workspaces. The user's home is selectable with the
906
+ A routed Claude session's workspace may contain the private controller-state
907
+ directory. The filesystem root and configured temporary roots are still
908
+ rejected as deliberately broad Claude workspaces, and that refusal happens
909
+ before any route is installed. The user's home is addressable with the
1000
910
  default controller-state root beneath it. A narrower project directory remains
1001
911
  the preferred least-context setup, but it is not mandatory.
1002
912
 
1003
913
  ## Failure and upgrade policy
1004
914
 
1005
915
  - Provider versions are best-effort metadata and never grant or remove routing
1006
- authority. The offline support matrix records tested artifacts, capabilities,
1007
- limitations, and dates without entering runtime. A session record whose peer
916
+ authority. A session record whose peer
1008
917
  protocol is not 1 is rejected per record and counted without stopping the
1009
918
  broker; interface drift degrades only its responsible provider.
1010
919
  - Unsafe ownership, path, symlink, lease, state, or generation evidence for
@@ -1016,8 +925,8 @@ the preferred least-context setup, but it is not mandatory.
1016
925
  gateway-owned state, unexpected paths, queue overflow, deadline expiry, and ambiguous writes are
1017
926
  normalized failures, never raw diagnostics.
1018
927
  - A provider disconnect fails or defers only the current operation. The next
1019
- eligible attempt opens and attests a new transport; logical registration and
1020
- consent do not depend on a connector lifecycle.
928
+ eligible attempt opens and attests a new transport; logical registration
929
+ does not depend on a connector lifecycle.
1021
930
  - The first successful Codex registration locks its exact alias, task, and host
1022
931
  until that registration is explicitly replaced or unregistered. Exact
1023
932
  re-registration remains idempotent.
@@ -1026,15 +935,15 @@ the preferred least-context setup, but it is not mandatory.
1026
935
  the one atomic transaction that changes the registered Codex identity. A
1027
936
  replacement must name the exact current registration on the same host, with
1028
937
  a different alias and thread. Embassy settles the outgoing registration's
1029
- work from its durable attempt phase, removes its consent edges and transient
938
+ work from its durable attempt phase, removes its transient
1030
939
  capabilities, and publishes only the replacement. No conversation, reply
1031
- capability, queued body, or permission transfers to the new identity, and no
940
+ capability, or queued body transfers to the new identity, and no
1032
941
  intermediate generation or manual-recovery state exists.
1033
942
  - No ambiguous mutation is retried automatically.
1034
943
  - A queued or reserved body survives process loss under bounded retention and
1035
944
  may resume once after exact logical authority is rechecked. Armed or accepted
1036
945
  work settles `ambiguous` or `unconfirmed` and is never replayed.
1037
- - A provider or Desktop update that changes an internal interface degrades its
946
+ - A provider update that changes an internal interface degrades its
1038
947
  responsible route while the broker and other providers remain available. A
1039
948
  Claude record outside peer protocol 1 is rejected per record, and every
1040
949
  current provider artifact used for an operation is re-attested before effect.
@@ -1043,11 +952,10 @@ the preferred least-context setup, but it is not mandatory.
1043
952
 
1044
953
  Routine validation is deterministic and synthetic: it does not inspect live
1045
954
  provider state, connect a provider socket, attach to App Server, invoke SSH, or
1046
- make a model request. The separately authorized local live tests recorded
1047
- above established discovery and both message directions. Remote production
1048
- connectors remain a separately reviewed future capability.
955
+ make a model request. Live validation is a separately authorized operator
956
+ action `embassy check` is its everyday form — and is never part of CI.
1049
957
 
1050
- Only the synthetic layer is routine validation. Server/dashboard startup,
958
+ Only the synthetic layer is routine validation. Server startup,
1051
959
  discovery, and callback binding remain no-send operations; step 4 is the first
1052
960
  provider write. A real provider message is never enabled in CI.
1053
961