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.
- package/CHANGELOG.md +77 -0
- package/CONTRIBUTING.md +23 -36
- package/README.md +123 -222
- package/SECURITY.md +72 -93
- package/dist/src/errors.d.ts +10 -1
- package/dist/src/errors.js +3 -1
- package/dist/src/errors.js.map +1 -1
- package/dist/src/gateway/claude-helper-protocol.d.ts +8 -11
- package/dist/src/gateway/claude-helper-protocol.js +12 -11
- package/dist/src/gateway/claude-helper-protocol.js.map +1 -1
- package/dist/src/gateway/claude-helper-supervisor.d.ts +1 -5
- package/dist/src/gateway/claude-helper-supervisor.js +45 -17
- package/dist/src/gateway/claude-helper-supervisor.js.map +1 -1
- package/dist/src/gateway/claude-helper.js +6 -6
- package/dist/src/gateway/claude-helper.js.map +1 -1
- package/dist/src/gateway/claude-peer.d.ts +0 -3
- package/dist/src/gateway/claude-peer.js +7 -19
- package/dist/src/gateway/claude-peer.js.map +1 -1
- package/dist/src/gateway/cli.d.ts +17 -7
- package/dist/src/gateway/cli.js +945 -265
- package/dist/src/gateway/cli.js.map +1 -1
- package/dist/src/gateway/codex-socket-holder.d.ts +26 -0
- package/dist/src/gateway/codex-socket-holder.js +76 -0
- package/dist/src/gateway/codex-socket-holder.js.map +1 -0
- package/dist/src/gateway/codex-stateless-transport.js +1 -1
- package/dist/src/gateway/codex-stateless-transport.js.map +1 -1
- package/dist/src/gateway/config.d.ts +1 -10
- package/dist/src/gateway/config.js +4 -10
- package/dist/src/gateway/config.js.map +1 -1
- package/dist/src/gateway/control.d.ts +66 -80
- package/dist/src/gateway/control.js +92 -147
- package/dist/src/gateway/control.js.map +1 -1
- package/dist/src/gateway/federation-nodes.d.ts +29 -2
- package/dist/src/gateway/federation-nodes.js +177 -7
- package/dist/src/gateway/federation-nodes.js.map +1 -1
- package/dist/src/gateway/peer-client.d.ts +4 -3
- package/dist/src/gateway/peer-client.js +22 -13
- package/dist/src/gateway/peer-client.js.map +1 -1
- package/dist/src/gateway/peer-protocol.d.ts +13 -11
- package/dist/src/gateway/peer-protocol.js +14 -12
- package/dist/src/gateway/peer-protocol.js.map +1 -1
- package/dist/src/gateway/provenance-envelope.d.ts +0 -1
- package/dist/src/gateway/provenance-envelope.js +4 -19
- package/dist/src/gateway/provenance-envelope.js.map +1 -1
- package/dist/src/gateway/providers.d.ts +11 -7
- package/dist/src/gateway/providers.js +49 -50
- package/dist/src/gateway/providers.js.map +1 -1
- package/dist/src/gateway/server.d.ts +4 -14
- package/dist/src/gateway/server.js +32 -54
- package/dist/src/gateway/server.js.map +1 -1
- package/dist/src/gateway/service-agent.d.ts +187 -0
- package/dist/src/gateway/service-agent.js +758 -0
- package/dist/src/gateway/service-agent.js.map +1 -0
- package/dist/src/gateway/service.d.ts +119 -38
- package/dist/src/gateway/service.js +754 -798
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/status-view.d.ts +119 -0
- package/dist/src/gateway/status-view.js +484 -0
- package/dist/src/gateway/status-view.js.map +1 -0
- package/dist/src/gateway/store.d.ts +106 -21
- package/dist/src/gateway/store.js +463 -537
- package/dist/src/gateway/store.js.map +1 -1
- package/dist/src/gateway/types.d.ts +64 -102
- package/dist/src/gateway/types.js +40 -86
- package/dist/src/gateway/types.js.map +1 -1
- package/docs/CONFIGURATION.md +174 -45
- package/docs/DELIVERY.md +11 -11
- package/docs/GATEWAY-ARCHITECTURE.md +292 -384
- package/package.json +4 -12
- package/skills/embassy-peer/SKILL.md +69 -92
- package/skills/embassy-peer/agents/openai.yaml +1 -1
- package/README.zh-CN.md +0 -275
- package/assets/live-dashboard/app.css +0 -1619
- package/assets/vendor/react/LICENSE +0 -21
- package/assets/vendor/react/react-dom.production.min.js +0 -267
- package/assets/vendor/react/react.production.min.js +0 -31
- package/dist/src/gateway/acp-client.d.ts +0 -110
- package/dist/src/gateway/acp-client.js +0 -407
- package/dist/src/gateway/acp-client.js.map +0 -1
- package/dist/src/gateway/acp-provider.d.ts +0 -66
- package/dist/src/gateway/acp-provider.js +0 -275
- package/dist/src/gateway/acp-provider.js.map +0 -1
- package/dist/src/gateway/claude-helper-client.d.ts +0 -2
- package/dist/src/gateway/claude-helper-client.js +0 -2
- package/dist/src/gateway/claude-helper-client.js.map +0 -1
- package/dist/src/gateway/cli-copy.d.ts +0 -8
- package/dist/src/gateway/cli-copy.en.d.ts +0 -22
- package/dist/src/gateway/cli-copy.en.js +0 -62
- package/dist/src/gateway/cli-copy.en.js.map +0 -1
- package/dist/src/gateway/cli-copy.js +0 -27
- package/dist/src/gateway/cli-copy.js.map +0 -1
- package/dist/src/gateway/cli-copy.zh-CN.d.ts +0 -22
- package/dist/src/gateway/cli-copy.zh-CN.js +0 -62
- package/dist/src/gateway/cli-copy.zh-CN.js.map +0 -1
- package/dist/src/gateway/codex-doctor.d.ts +0 -36
- package/dist/src/gateway/codex-doctor.js +0 -127
- package/dist/src/gateway/codex-doctor.js.map +0 -1
- package/dist/src/gateway/dashboard-copy.d.ts +0 -7
- package/dist/src/gateway/dashboard-copy.en.d.ts +0 -504
- package/dist/src/gateway/dashboard-copy.en.js +0 -505
- package/dist/src/gateway/dashboard-copy.en.js.map +0 -1
- package/dist/src/gateway/dashboard-copy.js +0 -514
- package/dist/src/gateway/dashboard-copy.js.map +0 -1
- package/dist/src/gateway/dashboard-copy.zh-CN.d.ts +0 -504
- package/dist/src/gateway/dashboard-copy.zh-CN.js +0 -505
- package/dist/src/gateway/dashboard-copy.zh-CN.js.map +0 -1
- package/dist/src/gateway/dashboard-model.d.ts +0 -343
- package/dist/src/gateway/dashboard-model.js +0 -1061
- package/dist/src/gateway/dashboard-model.js.map +0 -1
- package/dist/src/gateway/dashboard.d.ts +0 -20
- package/dist/src/gateway/dashboard.js +0 -874
- package/dist/src/gateway/dashboard.js.map +0 -1
- package/dist/src/gateway/deepseek-detect.d.ts +0 -14
- package/dist/src/gateway/deepseek-detect.js +0 -41
- package/dist/src/gateway/deepseek-detect.js.map +0 -1
- package/dist/src/gateway/live-dashboard-app/app.js +0 -2385
- package/dist/src/gateway/live-dashboard-assets.d.ts +0 -10
- package/dist/src/gateway/live-dashboard-assets.js +0 -74
- package/dist/src/gateway/live-dashboard-assets.js.map +0 -1
- package/dist/src/gateway/live-dashboard-command.d.ts +0 -60
- package/dist/src/gateway/live-dashboard-command.js +0 -334
- package/dist/src/gateway/live-dashboard-command.js.map +0 -1
- package/dist/src/gateway/live-dashboard-http.d.ts +0 -39
- package/dist/src/gateway/live-dashboard-http.js +0 -383
- package/dist/src/gateway/live-dashboard-http.js.map +0 -1
- package/dist/src/gateway/live-dashboard-protocol.d.ts +0 -34
- package/dist/src/gateway/live-dashboard-protocol.js +0 -114
- package/dist/src/gateway/live-dashboard-protocol.js.map +0 -1
- package/dist/src/gateway/live-dashboard-server.d.ts +0 -33
- package/dist/src/gateway/live-dashboard-server.js +0 -144
- package/dist/src/gateway/live-dashboard-server.js.map +0 -1
- package/dist/src/gateway/live-dashboard-stream.d.ts +0 -46
- package/dist/src/gateway/live-dashboard-stream.js +0 -234
- package/dist/src/gateway/live-dashboard-stream.js.map +0 -1
- package/dist/src/gateway/live-dashboard.d.ts +0 -28
- package/dist/src/gateway/live-dashboard.js +0 -154
- package/dist/src/gateway/live-dashboard.js.map +0 -1
- package/dist/src/gateway/locale.d.ts +0 -4
- package/dist/src/gateway/locale.js +0 -10
- package/dist/src/gateway/locale.js.map +0 -1
- package/dist/src/gateway/progress-watch-machine.d.ts +0 -45
- package/dist/src/gateway/progress-watch-machine.js +0 -70
- package/dist/src/gateway/progress-watch-machine.js.map +0 -1
- package/docs/CONFIGURATION.zh-CN.md +0 -97
- package/docs/DASHBOARD.md +0 -98
- package/docs/DASHBOARD.zh-CN.md +0 -49
- package/docs/DELIVERY.zh-CN.md +0 -55
|
@@ -1,40 +1,34 @@
|
|
|
1
1
|
# Embassy Gateway Architecture
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
supports macOS, the only platform exercised end to end
|
|
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
|
|
8
|
+
This document uses three evidence labels:
|
|
9
9
|
|
|
10
10
|
- **Official**: documented by Anthropic or OpenAI.
|
|
11
|
-
- **Implemented**: present in this
|
|
12
|
-
- **Observed**: established by a bounded, read-only local
|
|
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.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
54
|
-
|
|
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
|
|
71
|
-
task is busy
|
|
72
|
-
|
|
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,
|
|
95
|
-
registry record
|
|
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
|
|
105
|
-
explicitly marked `codex-*` gateway
|
|
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
|
|
109
|
-
|
|
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 │
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
|
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,
|
|
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
|
|
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-
|
|
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
|
|
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
|
-
|
|
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
|
|
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.
|
|
229
|
-
|
|
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
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
|
292
|
-
|
|
293
|
-
|
|
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
|
|
325
|
-
|
|
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.
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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
|
|
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
|
|
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:`,
|
|
429
|
-
|
|
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
|
|
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
|
|
470
|
-
only a participant-scoped conversation
|
|
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,
|
|
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
|
|
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`
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
555
|
-
|
|
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
|
|
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
|
|
571
|
-
`
|
|
572
|
-
|
|
573
|
-
-
|
|
574
|
-
|
|
575
|
-
-
|
|
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
|
|
578
|
-
- `
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
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
|
-
|
|
588
|
-
|
|
589
|
-
|
|
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
|
-
|
|
593
|
-
`serve`, `health`, `status`, `
|
|
594
|
-
`
|
|
595
|
-
`
|
|
596
|
-
`
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
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,
|
|
603
|
-
|
|
604
|
-
|
|
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
|
|
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
|
-
`
|
|
614
|
-
|
|
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
|
|
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.
|
|
624
|
-
fails with both identities or neither.
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
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
|
-
|
|
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
|
|
660
|
-
|
|
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`,
|
|
673
|
-
|
|
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
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
745
|
-
sandbox policy. Offline `TurnStartParams` schema evidence from
|
|
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
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
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
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
940
|
-
logical routes
|
|
941
|
-
may resume once only after their exact registration
|
|
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
|
|
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-
|
|
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
|
|
871
|
+
- create its control socket plus metadata files only inside its
|
|
963
872
|
separate controller-owned mode-0700 state directory;
|
|
964
|
-
- attach to
|
|
965
|
-
- optionally execute fixed `ssh
|
|
966
|
-
no shell and no model-supplied command or
|
|
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
|
|
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.
|
|
995
|
-
|
|
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
|
-
|
|
998
|
-
filesystem root and configured temporary roots are still
|
|
999
|
-
deliberately broad Claude workspaces
|
|
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.
|
|
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
|
|
1020
|
-
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
1047
|
-
|
|
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
|
|
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
|
|