@geohar/un-bien 0.8.0 → 0.14.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 (175) hide show
  1. package/README.md +258 -320
  2. package/dist/actions/handlers.js.map +1 -1
  3. package/dist/actions/registry.js.map +1 -1
  4. package/dist/bin/launcher.js +38 -0
  5. package/dist/bin/launcher.js.map +1 -0
  6. package/dist/commands/deps.d.ts +156 -0
  7. package/dist/commands/deps.js +2 -0
  8. package/dist/commands/deps.js.map +1 -0
  9. package/dist/commands/fork_link.d.ts +4 -0
  10. package/dist/commands/fork_link.js +45 -0
  11. package/dist/commands/fork_link.js.map +1 -0
  12. package/dist/commands/housekeeping.d.ts +34 -0
  13. package/dist/commands/housekeeping.js +275 -0
  14. package/dist/commands/housekeeping.js.map +1 -0
  15. package/dist/commands/info.d.ts +43 -0
  16. package/dist/commands/info.js +127 -0
  17. package/dist/commands/info.js.map +1 -0
  18. package/dist/commands/lifecycle.d.ts +45 -0
  19. package/dist/commands/lifecycle.js +708 -0
  20. package/dist/commands/lifecycle.js.map +1 -0
  21. package/dist/commands/pairing.d.ts +26 -0
  22. package/dist/commands/pairing.js +167 -0
  23. package/dist/commands/pairing.js.map +1 -0
  24. package/dist/commands/register.d.ts +12 -0
  25. package/dist/commands/register.js +255 -0
  26. package/dist/commands/register.js.map +1 -0
  27. package/dist/commands/relay.d.ts +28 -0
  28. package/dist/commands/relay.js +81 -0
  29. package/dist/commands/relay.js.map +1 -0
  30. package/dist/commands/session_ops.d.ts +52 -0
  31. package/dist/commands/session_ops.js +75 -0
  32. package/dist/commands/session_ops.js.map +1 -0
  33. package/dist/config.d.ts +30 -2
  34. package/dist/config.js.map +1 -1
  35. package/dist/daemon/install.d.ts +36 -37
  36. package/dist/daemon/install.js +49 -73
  37. package/dist/daemon/install.js.map +1 -1
  38. package/dist/enrich_tool_args.d.ts +9 -0
  39. package/dist/enrich_tool_args.js +171 -0
  40. package/dist/enrich_tool_args.js.map +1 -0
  41. package/dist/extension_ui_bridge.js.map +1 -1
  42. package/dist/image_codec.d.ts +22 -0
  43. package/dist/image_codec.js +149 -0
  44. package/dist/image_codec.js.map +1 -0
  45. package/dist/index.d.ts +32 -127
  46. package/dist/index.js +957 -4042
  47. package/dist/index.js.map +1 -1
  48. package/dist/launch.d.ts +42 -0
  49. package/dist/launch.js +206 -0
  50. package/dist/launch.js.map +1 -0
  51. package/dist/launcher/launcher.d.ts +6 -0
  52. package/dist/launcher/launcher.js +207 -0
  53. package/dist/launcher/launcher.js.map +1 -0
  54. package/dist/mcp/mesh_result.js.map +1 -1
  55. package/dist/mcp/mesh_server.js.map +1 -1
  56. package/dist/mesh/canonical.js +1 -1
  57. package/dist/mesh/canonical.js.map +1 -1
  58. package/dist/mesh/client.js.map +1 -1
  59. package/dist/mesh/encoding.js.map +1 -1
  60. package/dist/mesh/self_revoke.js.map +1 -1
  61. package/dist/mesh/siblings.js.map +1 -1
  62. package/dist/mesh/verify.js +1 -1
  63. package/dist/mesh/verify.js.map +1 -1
  64. package/dist/pairing/crypto.js +0 -1
  65. package/dist/pairing/crypto.js.map +1 -1
  66. package/dist/pairing/peer_trust.d.ts +20 -0
  67. package/dist/pairing/peer_trust.js +82 -0
  68. package/dist/pairing/peer_trust.js.map +1 -0
  69. package/dist/pairing/qr.js.map +1 -1
  70. package/dist/pairing/storage.js +4 -3
  71. package/dist/pairing/storage.js.map +1 -1
  72. package/dist/panel_bridge.d.ts +3 -1
  73. package/dist/panel_bridge.js +11 -10
  74. package/dist/panel_bridge.js.map +1 -1
  75. package/dist/paths.d.ts +22 -8
  76. package/dist/paths.js +37 -10
  77. package/dist/paths.js.map +1 -1
  78. package/dist/protocol/codec.js.map +1 -1
  79. package/dist/rooms.d.ts +16 -0
  80. package/dist/rooms.js +23 -4
  81. package/dist/rooms.js.map +1 -1
  82. package/dist/session/bridge.js.map +1 -1
  83. package/dist/session/broker.js.map +1 -1
  84. package/dist/session/broker_remote.js.map +1 -1
  85. package/dist/session/capabilities.d.ts +11 -0
  86. package/dist/session/capabilities.js +35 -0
  87. package/dist/session/capabilities.js.map +1 -0
  88. package/dist/session/cwd_lock.js +3 -3
  89. package/dist/session/cwd_lock.js.map +1 -1
  90. package/dist/session/debug_log.js +15 -5
  91. package/dist/session/debug_log.js.map +1 -1
  92. package/dist/session/envelope.js.map +1 -1
  93. package/dist/session/global_config.d.ts +1 -1
  94. package/dist/session/global_config.js +1 -1
  95. package/dist/session/global_config.js.map +1 -1
  96. package/dist/session/ipc.d.ts +2 -2
  97. package/dist/session/ipc.js.map +1 -1
  98. package/dist/session/leader_election.js.map +1 -1
  99. package/dist/session/local_config.d.ts +37 -0
  100. package/dist/session/local_config.js +47 -1
  101. package/dist/session/local_config.js.map +1 -1
  102. package/dist/session/mesh_node.d.ts +1 -1
  103. package/dist/session/mesh_node.js +1 -1
  104. package/dist/session/mesh_node.js.map +1 -1
  105. package/dist/session/peer.js.map +1 -1
  106. package/dist/session/peer_inventory.js.map +1 -1
  107. package/dist/session/peer_limits.js.map +1 -1
  108. package/dist/session/received_images.d.ts +63 -0
  109. package/dist/session/received_images.js +273 -0
  110. package/dist/session/received_images.js.map +1 -0
  111. package/dist/session/relay_lifecycle.d.ts +219 -0
  112. package/dist/session/relay_lifecycle.js +764 -0
  113. package/dist/session/relay_lifecycle.js.map +1 -0
  114. package/dist/session/rpc_envelope.d.ts +28 -0
  115. package/dist/session/rpc_envelope.js +10 -1
  116. package/dist/session/rpc_envelope.js.map +1 -1
  117. package/dist/session/rpc_handlers.d.ts +53 -0
  118. package/dist/session/rpc_handlers.js +268 -0
  119. package/dist/session/rpc_handlers.js.map +1 -0
  120. package/dist/session/rpc_inbound.d.ts +42 -7
  121. package/dist/session/rpc_inbound.js +56 -1
  122. package/dist/session/rpc_inbound.js.map +1 -1
  123. package/dist/session/setup_wizard.js.map +1 -1
  124. package/dist/session/tools.js.map +1 -1
  125. package/dist/session/wizard.js.map +1 -1
  126. package/dist/state_migration.d.ts +45 -0
  127. package/dist/state_migration.js +104 -0
  128. package/dist/state_migration.js.map +1 -0
  129. package/dist/subagent_rooms.d.ts +68 -0
  130. package/dist/subagent_rooms.js +919 -0
  131. package/dist/subagent_rooms.js.map +1 -0
  132. package/dist/test_hooks.d.ts +180 -0
  133. package/dist/test_hooks.js +98 -0
  134. package/dist/test_hooks.js.map +1 -0
  135. package/dist/transport/peer_channel.d.ts +18 -1
  136. package/dist/transport/peer_channel.js +42 -12
  137. package/dist/transport/peer_channel.js.map +1 -1
  138. package/dist/transport/pi_forward_client.js.map +1 -1
  139. package/dist/transport/relay_client.d.ts +24 -2
  140. package/dist/transport/relay_client.js +6 -3
  141. package/dist/transport/relay_client.js.map +1 -1
  142. package/dist/ui/footer.js.map +1 -1
  143. package/docs/daemon.md +170 -179
  144. package/package.json +13 -11
  145. package/service-templates/launchd.plist.template +8 -7
  146. package/service-templates/systemd.service.template +5 -4
  147. package/service-templates/task-launcher.vbs.template +5 -5
  148. package/service-templates/task-scheduler.xml.template +1 -1
  149. package/dist/bin/supervisord.js +0 -86
  150. package/dist/bin/supervisord.js.map +0 -1
  151. package/dist/daemon/client.d.ts +0 -20
  152. package/dist/daemon/client.js +0 -131
  153. package/dist/daemon/client.js.map +0 -1
  154. package/dist/daemon/control_protocol.d.ts +0 -176
  155. package/dist/daemon/control_protocol.js +0 -63
  156. package/dist/daemon/control_protocol.js.map +0 -1
  157. package/dist/daemon/cron_log.d.ts +0 -45
  158. package/dist/daemon/cron_log.js +0 -71
  159. package/dist/daemon/cron_log.js.map +0 -1
  160. package/dist/daemon/cron_registry.d.ts +0 -80
  161. package/dist/daemon/cron_registry.js +0 -198
  162. package/dist/daemon/cron_registry.js.map +0 -1
  163. package/dist/daemon/id.d.ts +0 -24
  164. package/dist/daemon/id.js +0 -36
  165. package/dist/daemon/id.js.map +0 -1
  166. package/dist/daemon/registry.d.ts +0 -63
  167. package/dist/daemon/registry.js +0 -153
  168. package/dist/daemon/registry.js.map +0 -1
  169. package/dist/daemon/rpc_child.d.ts +0 -194
  170. package/dist/daemon/rpc_child.js +0 -419
  171. package/dist/daemon/rpc_child.js.map +0 -1
  172. package/dist/daemon/supervisor.d.ts +0 -93
  173. package/dist/daemon/supervisor.js +0 -666
  174. package/dist/daemon/supervisor.js.map +0 -1
  175. /package/dist/bin/{supervisord.d.ts → launcher.d.ts} +0 -0
package/README.md CHANGED
@@ -1,22 +1,20 @@
1
1
  > **Derived from [remote-pi](https://github.com/jacobaraujo7/remote_pi)** by Jacob
2
2
  > Moura, used under the MIT License (preserved in [`LICENSE`](LICENSE)). This tree
3
- > is part of the [un-bien](../README.md) monorepo and is mid-rework — the content
4
- > below still carries upstream branding and is being rebranded to un-bien.
3
+ > is part of the [Un Bien](../README.md) monorepo.
5
4
 
6
5
  <p align="center">
7
- <img src="https://raw.githubusercontent.com/georgeharker/un-bien/main/app/icons/un-bien-macos-1024.png" width="160" alt="un-bien logo" />
6
+ <img src="https://raw.githubusercontent.com/georgeharker/un-bien/main/app/icons/un-bien-macos-1024.png" width="160" alt="Un Bien logo" />
8
7
  </p>
9
8
 
10
- <h1 align="center">un-bien</h1>
9
+ <h1 align="center">Un Bien</h1>
11
10
 
12
11
  > Extend the [Pi coding agent](https://github.com/earendil-works/pi) with two
13
- > superpowers: agents that talk to each other on the same machine, and a mobile
14
- > app that drives Pi from your phone.
12
+ > superpowers: **remote-control Pi from your phone** (native iOS/macOS app over a
13
+ > relay you host), and a **local agent mesh** where several Pi sessions talk to
14
+ > each other.
15
15
 
16
- **Homepage:** <https://remote-pi.jacobmoura.work>
17
-
18
- `/remote-pi` is a single slash command that wires both at once. Run it; the
19
- first time it asks a couple of questions and you are done.
16
+ `/unbien` is a single slash command that wires both at once. Run it; the first
17
+ time it asks a couple of questions and you are done.
20
18
 
21
19
  ## Protocol & Security
22
20
 
@@ -32,22 +30,28 @@ document — this README only covers user-facing setup.
32
30
  Install the extension (one-time):
33
31
 
34
32
  ```bash
35
- pi install npm:remote-pi
33
+ pi install npm:@geohar/un-bien
36
34
  ```
37
35
 
38
36
  Then in any Pi terminal:
39
37
 
40
38
  ```text
41
- /remote-pi
39
+ /unbien
42
40
  ```
43
41
 
44
42
  The first run shows a short interactive wizard (agent name, default session,
45
- whether to auto-start the relay). On every following run, `/remote-pi` joins
46
- the local agent session and starts the relay automatically — no extra typing.
43
+ whether to use the relay on this terminal). On every following run, `/unbien`
44
+ joins the local agent session and starts the relay automatically — no extra
45
+ typing.
46
+
47
+ > **You must configure a relay before the mobile app can connect.** Un Bien ships
48
+ > pointing at **nobody's** infrastructure — there is no built-in default relay.
49
+ > Self-host one (see [The relay](#the-relay)) and point the extension at it with
50
+ > `/unbien set-relay <url>`.
47
51
 
48
52
  ### Try the agent network in 30 seconds
49
53
 
50
- Open **two** Pi terminals in the same directory and run `/remote-pi` in each.
54
+ Open **two** Pi terminals in the same directory and run `/unbien` in each.
51
55
  Both join the same session. Now just talk to the LLM — it has the tools.
52
56
 
53
57
  In terminal A (say it ended up named `agent-A`):
@@ -78,8 +82,7 @@ normalize it.
78
82
 
79
83
  ## What it does
80
84
 
81
- Remote Pi adds two independent layers on top of Pi. You can use either, or
82
- both:
85
+ Un Bien adds two independent layers on top of Pi. You can use either, or both:
83
86
 
84
87
  ### 1) Agent network (local broker, optional cross-PC relay)
85
88
 
@@ -97,7 +100,7 @@ current turn, and receive any later reply through the inbox/turn flow with
97
100
  `re` correlating it to the original message id.
98
101
 
99
102
  Peers on the same machine talk over a Unix domain socket at
100
- `~/.pi/remote/sessions/<session-name>/broker.sock`. When sibling PCs are paired,
103
+ `~/.local/state/un-bien/sessions/<session-name>/broker.sock`. When sibling PCs are paired,
101
104
  a leader-capable Extension or MCP participant bridges the opaque cross-PC
102
105
  addresses over the relay; local-only use stays on UDS when relay access is off.
103
106
  Useful for splitting work across roles (`backend`, `frontend`, `tests`,
@@ -109,22 +112,19 @@ over — the failover is invisible to the LLMs.
109
112
 
110
113
  ### 2) Mobile app (over the relay)
111
114
 
112
- The companion mobile app lets you send prompts to Pi and read its responses
113
- from your phone. The phone and the Pi process find each other through a
114
- **relay**: a small WebSocket server that ferries messages between them.
115
- Pairing is one-time and per device, via QR code.
115
+ The companion native iOS/macOS app lets you **attach to a running Pi session or
116
+ launch a new one** and drive it from your phone: send prompts, read responses,
117
+ answer Pi's interactive prompts, and switch models. The phone and the Pi process
118
+ find each other through a **relay**: a small WebSocket server that ferries
119
+ messages between them. Pairing is one-time and per device, via QR code.
116
120
 
117
121
  Communication uses WebSocket over TLS to the relay. Fields such as `ct` are
118
122
  wire containers, not a systemwide end-to-end confidentiality guarantee: current
119
123
  Pi-forward, cross-PC, app, and control envelopes visible to the relay are not
120
124
  fully opaque or E2E encrypted. A relay operator can see routed plaintext
121
125
  protocol content and metadata; see [`rpc-envelope`](../docs/rpc-envelope.md) for the exact
122
- trust boundaries.
123
-
124
- **Get the app** — all current download options (Google Play, App Store, and
125
- direct builds while public releases roll out):
126
-
127
- <https://remote-pi.jacobmoura.work/#get-the-app>
126
+ trust boundaries. **Host the relay yourself** to keep that operator role in your
127
+ own hands.
128
128
 
129
129
  ---
130
130
 
@@ -150,23 +150,25 @@ under "App actions".
150
150
  It is **not** a generic slash-command picker. The Pi SDK does not expose
151
151
  programmatic invocation for most builtins (those live in the TUI's
152
152
  interactive loop), so the app exposes only the actions that have a clean
153
- SDK call. The [`pi-telegram`](https://github.com/llblab/pi-telegram) adapter
154
- follows the same pattern.
153
+ SDK call.
155
154
 
156
155
  ### Images
157
156
 
158
- The app can attach **one image** (camera or gallery) to a message. It's
159
- compressed on the device and rides **inline** in the `user_message` the
160
- optional `images` field carries `{ data: <base64>, mime }`. The pi-extension
161
- turns it into the SDK's multimodal content (an `ImageContent` followed by the
162
- caption `TextContent`) and calls `sendUserMessage(content)`, so the model sees
163
- the picture plus your text.
157
+ Un Bien **displays images produced during a session** when a tool or the agent
158
+ emits an image, the extension surfaces it to the app as a preview (customType
159
+ `un-bien:received-image`), capped at 10 MB.
160
+
161
+ On the inbound side, the wire and extension also support **image ingest**: a
162
+ `user_message` may carry an optional `images` field (`{ data: <base64>, mime }`),
163
+ which the extension turns into the SDK's multimodal content (an `ImageContent`
164
+ followed by the caption `TextContent`) and feeds to `sendUserMessage(content)`.
165
+ The mobile app does **not** yet expose an attach control, so this path is
166
+ available to clients but not surfaced in the app today.
164
167
 
165
168
  Whether a model accepts images is surfaced as a `vision` flag on each
166
- `WireModel` (derived from the SDK's `Model.input` including `"image"`); the app
167
- greys out the attach button when the active model is text-only.
169
+ `WireModel` (derived from the SDK's `Model.input` including `"image"`).
168
170
 
169
- The **relay is unchanged** — the image travels inside the same application
171
+ The **relay is unchanged** — an image travels inside the same application
170
172
  message container as the text, so there's no binary channel (large files are a
171
173
  future track). Base64 or a field named `ct` is not an E2E confidentiality
172
174
  boundary; the current Relay visibility follows the trust model above. Text-only
@@ -179,39 +181,39 @@ messages are unaffected.
179
181
  Requirements: Node 20+, Pi (the host coding agent).
180
182
 
181
183
  ```bash
182
- pi install npm:remote-pi
184
+ pi install npm:@geohar/un-bien
183
185
  ```
184
186
 
185
- The extension self-registers the `/remote-pi` slash command and deploys an
187
+ The extension self-registers the `/unbien` slash command and deploys an
186
188
  agent skill that teaches the LLM how to use `list_peers`, `agent_send`, and the
187
189
  event-driven inbox/reply flow.
188
190
 
189
191
  To verify:
190
192
 
191
193
  ```text
192
- /remote-pi config
194
+ /unbien config
193
195
  ```
194
196
 
195
197
  It should print the effective relay URL and where it came from
196
- (`env` / `config` / `default`).
198
+ (`env` / `config` / `unset`).
197
199
 
198
200
  ---
199
201
 
200
- ## Using `/remote-pi`
202
+ ## Using `/unbien`
201
203
 
202
204
  The bare command is the everyday entry point:
203
205
 
204
206
  ```text
205
- /remote-pi
207
+ /unbien
206
208
  ```
207
209
 
208
210
  Behavior depends on whether there's a local config for this directory:
209
211
 
210
- | State | What happens |
211
- | ------------------------------------------ | ---------------------------------------------------------------------------------------- |
212
- | First run (no `.pi/remote-pi/config.json`) | Interactive wizard → saves config → joins agent session → starts relay (if you opted in) |
213
- | Returning user, auto-start enabled | Joins agent session + starts relay automatically, then prints status |
214
- | Returning user, auto-start disabled | Prints status only; join/relay must be run manually |
212
+ | State | What happens |
213
+ | ---------------------------------------- | ---------------------------------------------------------------------------------------- |
214
+ | First run (no `.pi/un-bien/config.json`) | Interactive wizard → saves config → joins agent session → starts relay (if you opted in) |
215
+ | Returning user, auto-start enabled | Joins agent session + starts relay automatically, then prints status |
216
+ | Returning user, auto-start disabled | Prints status only; join/relay must be run manually |
215
217
 
216
218
  The wizard asks three questions:
217
219
 
@@ -220,36 +222,36 @@ The wizard asks three questions:
220
222
  an address from this name. Defaults to the directory name.
221
223
  2. **Default session** — the name of the agent-network room for this
222
224
  directory. Multiple terminals in the same directory join the same session.
223
- 3. **Auto-start relay (for mobile app access)?** — `Yes` if you want
224
- `/remote-pi` to also connect to the relay so the mobile app can reach this
225
- Pi. `No` for local-only use (agent network without mobile access).
225
+ 3. **Use the relay on this terminal?** — `Yes` if you want `/unbien` to also
226
+ connect to the relay so the mobile app (and paired PCs) can reach this Pi.
227
+ `No` for local-only use (agent network without mobile access).
226
228
 
227
- Re-run the wizard later with `/remote-pi setup`.
229
+ Re-run the wizard later with `/unbien setup`.
228
230
 
229
231
  ---
230
232
 
231
233
  ## Pairing a mobile device
232
234
 
233
- Once the relay is up (`/remote-pi relay status` shows `started` or `paired`):
235
+ Once the relay is up (`/unbien relay status` shows `started` or `paired`):
234
236
 
235
237
  ```text
236
- /remote-pi pair
238
+ /unbien pair
237
239
  ```
238
240
 
239
- A QR code is printed in the terminal. Scan it with the Remote Pi mobile app.
241
+ A QR code is printed in the terminal. Scan it with the Un Bien mobile app.
240
242
  Pairing is **per machine** — once a device is paired, every Pi process on
241
- this machine accepts it (it lives in `~/.pi/remote/peers.json`).
243
+ this machine accepts it (it lives in `~/.local/state/un-bien/peers.json`).
242
244
 
243
245
  To list paired devices:
244
246
 
245
247
  ```text
246
- /remote-pi devices
248
+ /unbien devices
247
249
  ```
248
250
 
249
251
  To remove one:
250
252
 
251
253
  ```text
252
- /remote-pi revoke <shortid>
254
+ /unbien revoke <shortid>
253
255
  ```
254
256
 
255
257
  The shortid is the first 8 chars shown by `devices`.
@@ -259,60 +261,36 @@ The shortid is the first 8 chars shown by `devices`.
259
261
  ## The relay
260
262
 
261
263
  The relay is the network boundary. TLS protects transit, but the Relay can see
262
- routed plaintext protocol content and metadata; use a relay you trust or
263
- self-host. There is no systemwide or PC-mesh E2E guarantee. For Pi-to-Pi
264
+ routed plaintext protocol content and metadata; **use a relay you trust or
265
+ self-host**. There is no systemwide or PC-mesh E2E guarantee. For Pi-to-Pi
264
266
  forwarding, the Relay currently permits a route when any correctly signed Owner
265
267
  blob lists both canonical Pi keys. That does not prove the Owner paired with or
266
268
  controls either Pi.
267
269
 
268
- ### Upgrade order (Relay 0.3 first, then Extension 0.6)
269
-
270
- Upgrade the **Relay to 0.3 first**: an old Extension can consume the new
271
- Relay's UUID errors. Extension 0.6 carries a one-release legacy wire-label
272
- shim, so mixed new/old Extensions interoperate when both select the same unique
273
- colon-free signed nickname label, or when neither has one and both use the
274
- canonical standard-padded key prefix. Delimiter or collision cases, like
275
- divergent nickname views, are unsupported and may be silently dropped by the
276
- old receiver. Upgrade all Extension/MCP participants in one maintenance window.
277
- The shim does not replace the receiver-local aliases returned by `list_peers`;
278
- addresses remain opaque.
279
-
280
- Extension 0.6 accepts an old Relay's lowercase 32-hex trusted error ID only as
281
- a narrow shim for an old Relay or Relay rollback; that shim is not why
282
- Relay-first is safe.
283
-
284
- You have two options:
285
-
286
- ### Option A — Use the community relay
287
-
288
- `https://relay-rp1.jacobmoura.work` (default). Zero setup. Good for trying
289
- things out or for casual use. (The extension converts to `wss://…`
290
- internally when opening the connection — both schemes point at the same
291
- endpoint.)
270
+ **Un Bien ships with no default relay** `/unbien config` reports `unset` until
271
+ you configure one, and the extension refuses to connect until you do.
292
272
 
293
- Caveats:
273
+ ### Self-host the relay
294
274
 
295
- - Shared infrastructure availability is best-effort.
296
- - **There is no IP allow-listing or VPN gating**.
297
-
298
- ### Option B — Self-host (recommended for privacy)
299
-
300
- Run the relay yourself in Docker and put it behind a VPN like
275
+ Run the relay yourself and put it behind a VPN like
301
276
  [Tailscale](https://tailscale.com), [WireGuard](https://www.wireguard.com),
302
277
  or your own VPC. Because the relay's network-level protection is just TLS +
303
278
  keypair authentication, layering a VPN on top means **only your devices** can
304
279
  even reach the WebSocket port — defense in depth.
305
280
 
306
- Quick Docker outline (see the
307
- [relay README](https://github.com/jacobaraujo7/remote_pi/blob/main/relay/README.md#self-hosted-relay-recommended-for-privacy)
308
- for the full setup, environment variables, and reverse-proxy guidance):
281
+ Build and run from the `relay/` crate in this monorepo (see the
282
+ [relay README](../relay/README.md) for environment variables and reverse-proxy
283
+ guidance):
309
284
 
310
285
  ```bash
286
+ # From the monorepo root:
287
+ docker build -t un-bien-relay ./relay
311
288
  docker run -d \
312
- --name remote-pi-relay \
289
+ --name un-bien-relay \
313
290
  -p 3000:3000 \
291
+ -v un-bien-data:/data \
314
292
  --restart unless-stopped \
315
- ghcr.io/jacobaraujo7/remote-pi-relay:latest
293
+ un-bien-relay
316
294
  ```
317
295
 
318
296
  Bind the container to your VPN interface, terminate TLS in a reverse proxy,
@@ -323,7 +301,7 @@ and point both your Pi and your phone at the resulting `https://…` URL.
323
301
  Once your relay is reachable, tell the extension:
324
302
 
325
303
  ```text
326
- /remote-pi relay url https://relay.yourdomain.tld
304
+ /unbien relay url https://relay.yourdomain.tld
327
305
  ```
328
306
 
329
307
  The URL **must** be `http://` or `https://` — `ws://` / `wss://` are
@@ -331,21 +309,23 @@ rejected at validation. The extension converts to WebSocket internally when
331
309
  it opens the connection. Same canonical form for the mobile app and any
332
310
  self-hosting docs: paste the URL your reverse proxy exposes.
333
311
 
334
- This writes `~/.pi/remote/config.json` with `{ "relay": "..." }`. Resolution
335
- order (highest precedence first):
312
+ This writes the `relay` field into the global config at
313
+ `~/.pi/extensions/un-bien.json`. Resolution order (highest precedence first):
314
+
315
+ 1. `UNBIEN_RELAY` environment variable (CI / one-off overrides)
316
+ 2. `relay` field in `~/.pi/extensions/un-bien.json`
336
317
 
337
- 1. `REMOTE_PI_RELAY` environment variable (CI / one-off overrides)
338
- 2. `~/.pi/remote/config.json`
339
- 3. The built-in default (`https://relay-rp1.jacobmoura.work`)
318
+ There is **no built-in default** when neither is set, the extension refuses
319
+ to connect and prompts you to configure a relay.
340
320
 
341
321
  Verify the active URL and its source with:
342
322
 
343
323
  ```text
344
- /remote-pi config
324
+ /unbien config
345
325
  ```
346
326
 
347
- If you change the URL while connected, run `/remote-pi relay stop` then
348
- `/remote-pi relay start` (or `/remote-pi relay` to toggle).
327
+ If you change the URL while connected, run `/unbien relay stop` then
328
+ `/unbien relay start` (or `/unbien relay` to toggle).
349
329
 
350
330
  The mobile app has its own relay-URL setting in its preferences pane — keep
351
331
  both pointing at the same relay.
@@ -394,16 +374,16 @@ original message id.
394
374
 
395
375
  The wire format is a 5-field envelope `{ from, to, id, re, body }` serialized
396
376
  as one JSON line per message. The leader's broker writes an `audit.jsonl`
397
- log at `~/.pi/remote/sessions/<name>/audit.jsonl` for postmortem inspection.
377
+ log at `~/.local/state/un-bien/sessions/<name>/audit.jsonl` for postmortem inspection.
398
378
 
399
379
  Useful commands:
400
380
 
401
- | Command | What it does |
402
- | ------------------------- | ----------------------------------------------------- |
403
- | `/remote-pi` | Join the local mesh (and start the relay, if enabled) |
404
- | `/remote-pi peers` | List local + cross-PC mesh peers, grouped by PC |
405
- | `/remote-pi rename <new>` | Rename this agent in the current session |
406
- | `/remote-pi stop` | Leave the local mesh and disconnect the relay |
381
+ | Command | What it does |
382
+ | ---------------------- | ----------------------------------------------------- |
383
+ | `/unbien` | Join the local mesh (and start the relay, if enabled) |
384
+ | `/unbien peers` | List local + cross-PC mesh peers, grouped by PC |
385
+ | `/unbien rename <new>` | Rename this agent in the current session |
386
+ | `/unbien stop` | Leave the local mesh and disconnect the relay |
407
387
 
408
388
  Name collisions inside a session get a numeric suffix automatically
409
389
  (`backend`, `backend#2`, `backend#3`). The broker assigns it and returns the
@@ -415,71 +395,30 @@ real name to the peer.
415
395
 
416
396
  ### Local session (one Pi, one terminal)
417
397
 
418
- | Command | Description |
419
- | ---------------------------------------- | ------------------------------------------------------------------------------ |
420
- | `/remote-pi` | Connect (join local mesh + start relay), or run setup on first use |
421
- | `/remote-pi setup` | Run the setup wizard and update local config |
422
- | `/remote-pi status` | Show local mesh + relay status |
423
- | `/remote-pi stop` | Stop everything for **this** terminal (mesh + relay) |
424
- | `/remote-pi pair` | Show QR code + copy-paste pairing URI for a new mobile device |
425
- | `/remote-pi devices` | List paired mobile devices (online/offline per device) |
426
- | `/remote-pi revoke <shortid>` | Revoke a paired device by its shortid |
427
- | `/remote-pi set-relay <url>` | Persist a new relay URL (http:// or https://) |
428
- | `/remote-pi relay [start\|stop\|status]` | Relay-only control — leaves local mesh membership untouched (no verb = toggle) |
429
- | `/remote-pi relay url <url>` | Same as `set-relay` |
430
- | `/remote-pi config` | Show the effective relay URL and its source (env / config / default) |
431
-
432
- ### Daemon fleet (one supervisor, N background Pis — see [Daemon mode](#daemon-mode))
433
-
434
- | Command | Description |
435
- | ---------------------------------------------- | ----------------------------------------------------------------------------- |
436
- | `/remote-pi create <cwd> [--name X]` | Register a folder as a daemon |
437
- | `/remote-pi remove <id>` | Unregister a daemon (local config preserved) |
438
- | `/remote-pi daemons` | List registered daemons + state |
439
- | `/remote-pi daemon start` | Start every registered daemon |
440
- | `/remote-pi daemon stop` | Stop every running daemon (`/remote-pi stop` stops only the local terminal) |
441
- | `/remote-pi daemon restart` | Stop + start all daemons |
442
- | `/remote-pi daemon status` | Detailed runtime status (pid, uptime, restart count) |
443
- | `/remote-pi daemon send <id> "<text>"` | Send a prompt to a specific daemon |
444
- | `/remote-pi cron add <id> "<expr>" "<prompt>"` | Schedule a recurring prompt (`--tz`, `--wake`, `--no-skip-busy`, `--catchup`) |
445
- | `/remote-pi cron list` | List scheduled jobs (schedule, enabled, next run, last status) |
446
- | `/remote-pi cron run <jobId>` | Fire a job now (ignores its schedule) |
447
- | `/remote-pi cron enable\|disable <jobId>` | Toggle a job on/off |
448
- | `/remote-pi cron remove <jobId>` | Delete a job |
449
- | `/remote-pi cron log [<jobId>] [--tail N]` | Read the fire/skip audit log |
450
- | `/remote-pi install` | Install `pi-supervisord` as a system service |
451
- | `/remote-pi uninstall` | Remove the system service (registry preserved) |
398
+ | Command | Description |
399
+ | ------------------------------------- | ------------------------------------------------------------------------------ |
400
+ | `/unbien` | Connect (join local mesh + start relay), or run setup on first use |
401
+ | `/unbien setup` | Run the setup wizard and update local config |
402
+ | `/unbien status` | Show local mesh + relay status |
403
+ | `/unbien stop` | Stop everything for **this** terminal (mesh + relay) |
404
+ | `/unbien pair` | Show QR code + copy-paste pairing URI for a new mobile device |
405
+ | `/unbien devices` | List paired mobile devices (online/offline per device) |
406
+ | `/unbien revoke <shortid>` | Revoke a paired device by its shortid |
407
+ | `/unbien set-relay <url>` | Persist a new relay URL (http:// or https://) |
408
+ | `/unbien relay [start\|stop\|status]` | Relay-only control — leaves local mesh membership untouched (no verb = toggle) |
409
+ | `/unbien relay url <url>` | Same as `set-relay` |
410
+ | `/unbien config` | Show the effective relay URL and its source (env / config / unset) |
411
+
412
+ ### Service install & remote launch (see [Remote launch](#remote-launch))
413
+
414
+ | Command | Description |
415
+ | ------------------- | ---------------------------------------------------------------------------------- |
416
+ | `/unbien install` | Install the **unbien-launcher** daemon as a system service + link the `unbien` CLI |
417
+ | `/unbien uninstall` | Remove the launcher service + CLI links (pairing and config preserved) |
452
418
 
453
419
  All commands above work both as Pi slash commands (interactive) and as
454
- shell-level `remote-pi <subcommand>` when the package is installed
455
- globally (`npm install -g remote-pi`).
456
-
457
- ### Scheduled prompts (`cron`)
458
-
459
- `remote-pi cron` schedules **recurring prompts** to daemons through the
460
- supervisor — e.g. a daily "summarise new PRs". Output flows fire-and-forget to
461
- the mesh/app like any prompt; the cron layer only audits the dispatch.
462
-
463
- - **Schedule** is a cron expression (croner syntax; an optional 6th _seconds_
464
- field is supported), with an optional IANA timezone via `--tz`:
465
-
466
- ```sh
467
- remote-pi cron add a1b2c3d4 "0 9 * * *" "Summarise new PRs" --tz America/Sao_Paulo
468
- ```
469
-
470
- - **Minimum interval is 60s** — more frequent schedules are rejected (guards
471
- token cost + pileup). A fire is **skipped when the daemon is mid-turn**
472
- (`--no-skip-busy` to override); `--wake` starts a stopped daemon first;
473
- `--catchup` runs once on supervisor start if the previous run was missed.
474
- - **Prerequisite**: the supervisor must run as a service (`remote-pi install`).
475
- Without it there is no scheduler, and `cron` commands say so instead of
476
- silently pretending to schedule.
477
- - **Audit**: every fire **and** every skip appends one line to
478
- `~/.pi/remote/cron.jsonl` with a `result` of `delivered`,
479
- `woke_and_delivered`, `deliver_failed`, `skipped_busy`, `skipped_down`, or
480
- `skipped_disabled` — read it with `remote-pi cron log`.
481
-
482
- Step-by-step walkthrough: the [daemon tutorial](https://remote-pi.jacobmoura.work/tutorials/daemon).
420
+ shell-level `unbien <subcommand>` when the package is installed
421
+ globally (`npm install -g @geohar/un-bien`).
483
422
 
484
423
  ### Footer + title
485
424
 
@@ -494,167 +433,190 @@ tabs.
494
433
 
495
434
  ---
496
435
 
497
- ## Daemon mode
436
+ ## Remote launch
498
437
 
499
- When you want a Pi to keep running in the background (responding to
500
- mobile prompts at 3am, processing cron jobs, monitoring a folder while
501
- you're not at the keyboard), promote it to a **daemon** managed by a
502
- single OS-level supervisor.
438
+ The **unbien-launcher** is a lightweight mesh peer **not** a Pi session — that
439
+ lets a paired app reach your machine even when no Pi is running. It reuses the
440
+ machine's un-bien config (identity + relay), joins the machine-level control
441
+ room, advertises `remote_launch`, and spawns a `pi` window via `tmux` (default)
442
+ or `herdr` when the app asks for one.
503
443
 
504
444
  See [`docs/daemon.md`](./docs/daemon.md) for troubleshooting.
505
445
 
506
446
  ### One-time setup
507
447
 
508
448
  ```bash
509
- # Install the package globally so `remote-pi` and `pi-supervisord`
510
- # are on your PATH (`pi install npm:remote-pi` alone makes the Pi
511
- # extension available but does NOT expose the CLI binaries — see
449
+ # Install the package globally so the `unbien` CLI is on your PATH
450
+ # (`pi install npm:@geohar/un-bien` alone makes the Pi extension available
451
+ # but does NOT expose the CLI binary — see
512
452
  # https://docs.npmjs.com/cli/v10/configuring-npm/package-json#bin).
513
- npm install -g remote-pi
453
+ npm install -g @geohar/un-bien
514
454
 
515
- # Install the supervisor as a user-level system service. Linux uses
455
+ # Install the launcher as a user-level system service. Linux uses
516
456
  # systemd --user; macOS uses launchd LaunchAgent. Both auto-start at
517
457
  # login and survive reboots.
518
- remote-pi install
458
+ unbien install
519
459
  ```
520
460
 
461
+ Or from inside Pi: `/unbien install` — same service, plus it links the
462
+ `unbien` CLI into `~/.local/bin` so no global npm install is needed.
463
+
521
464
  The `install` command:
522
465
 
523
- - Writes `~/.config/systemd/user/remote-pi-supervisord.service` (Linux)
524
- or `~/Library/LaunchAgents/dev.remotepi.supervisord.plist` (macOS)
466
+ - Writes `~/.config/systemd/user/unbien-launcher.service` (Linux) or
467
+ `~/Library/LaunchAgents/dev.unbien.launcher.plist` (macOS); on Windows it
468
+ registers a Task Scheduler task (`RemotePiLauncher`) and prompts for
469
+ elevation once
525
470
  - Activates it via `systemctl --user enable --now` or `launchctl bootstrap`
526
- - The supervisor starts immediately and re-starts on every login
471
+ - The launcher starts immediately and re-starts on every login
527
472
 
528
- ### Per-folder workflow
473
+ A standalone package exists for running the launcher in the foreground, no
474
+ service: `npm install -g @geohar/un-bien-launcher` → `unbien-launcher`. Same
475
+ daemon, shipped as its own binary (see the
476
+ [launcher README](../launcher/README.md)).
529
477
 
530
- For each agent you want to keep alive 24/7:
478
+ ### Enable remote launch
531
479
 
532
- ```bash
533
- # 1. Configure the agent interactively first (one time).
534
- cd ~/Movies
535
- pi # /remote-pi → setup wizard, /remote-pi pair, etc
536
-
537
- # 2. Promote to a daemon. The id is derived from the cwd
538
- # (sha256(realpath)[:8]), stable across machines.
539
- remote-pi create ~/Movies --name "Video Editor"
540
- # → Daemon registered: id=4e39152d name="Video Editor" cwd=/Users/x/Movies
480
+ The launcher only spawns sessions where remote launch is **opted in**:
541
481
 
542
- # 3. Start it (supervisor spawns `pi --mode rpc` for this folder).
543
- remote-pi daemon start
544
- ```
482
+ - per directory: `allow_remote_launch: true` in `<cwd>/.pi/un-bien/config.json`
483
+ - or machine-wide: `defaults.allow_remote_launch: true` in the global config
484
+ (`~/.pi/extensions/un-bien.json`)
545
485
 
546
- Now you can:
486
+ The spawn backend is machine-wide: `launch.backend` in the global config —
487
+ `tmux` (default; one shared `un-bien` tmux session, a window per launched Pi)
488
+ or `herdr`. The backend binary must be on PATH: `unbien install` snapshots
489
+ your PATH at install time, so re-run it after installing new tools.
547
490
 
548
- ```bash
549
- remote-pi daemons # list + state
550
- remote-pi daemon status # uptime, pid, restart count
551
- remote-pi daemon send 4e39152d "Cut the first 30 seconds of latest clip"
552
- remote-pi daemon stop # stop all
553
- remote-pi daemon restart # restart all
554
- ```
491
+ ### Launch names
555
492
 
556
- The agent receives the prompt as if a user typed it; its response flows
557
- back through the relay/mesh you configured during interactive setup
558
- mobile app sees it live, other agents on the same machine can see it
559
- via the local UDS mesh.
493
+ A `session_launch` request may carry a `name`. On the tmux backend the
494
+ launched Pi gets it as `pi -n <name>` pi's native session display name
495
+ which the extension resolves as the session-scoped agent name: the mesh
496
+ join, cwd lock, `room_meta`, and the app's session tile all show it instead
497
+ of the path-derived default. It is per-process (never inherited by
498
+ subagents or child processes) and never persisted — a manual `pi` in the
499
+ same directory keeps its configured `agent_name`. `/unbien rename` still
500
+ wins once it runs. The herdr backend uses the name as the workspace label
501
+ only until it can pass args through to pi.
560
502
 
561
503
  ### Removing or uninstalling
562
504
 
563
505
  ```bash
564
- remote-pi remove <id> # unregister one daemon (config preserved)
565
- remote-pi uninstall # remove the supervisor service (registry kept)
506
+ unbien uninstall # remove the launcher service (pairing + config kept)
566
507
  ```
567
508
 
568
- `uninstall` is reversible — re-running `install` later brings every
569
- registered daemon back. To wipe the registry entirely, `rm
570
- ~/.pi/remote/daemons.json`.
509
+ `uninstall` is reversible — re-running `install` later brings the launcher
510
+ back. Pairing and identity live in the state root and are never touched.
571
511
 
572
512
  ### Where to find logs
573
513
 
574
- | Platform | Command |
575
- | -------- | ----------------------------------------------- |
576
- | Linux | `journalctl --user -u remote-pi-supervisord -f` |
577
- | macOS | `tail -f ~/.pi/remote/supervisord.log` |
578
-
579
- Each spawned daemon's stderr is forwarded into the supervisor's log
580
- with a `[<cwd>]` prefix, so a single log stream shows every agent.
581
-
582
- ### Caveats (plan/26 trade-offs)
583
-
584
- - **Tool approval is not gated.** Daemons inherit the same Pi config
585
- the interactive run uses — Bash, Edit, Write etc. all execute without
586
- prompting. Configure Pi's tool permissions to taste before promoting
587
- a folder to daemon.
588
- - **Pairing still happens interactively.** Daemons don't show a QR
589
- themselves; the keypair + paired devices come from the prior `pi`
590
- session in the same folder.
591
- - **Single supervisor.** If `pi-supervisord` crashes all daemons go
592
- down with it. systemd/launchd restarts it within seconds; daemons
593
- come back automatically.
594
- - **One daemon per cwd.** The `roomIdForCwd` derivation makes daemons
595
- by-path; two daemons in the same folder is rejected at `create` time.
514
+ | Platform | Command |
515
+ | -------- | ------------------------------------------------------- |
516
+ | Linux | `journalctl --user -u unbien-launcher -f` |
517
+ | macOS | `tail -f ~/.local/state/un-bien/launcher.log` |
518
+ | Windows | `Get-Content ~\.local\state\un-bien\launcher.log -Wait` |
596
519
 
597
- ---
598
-
599
- ## Configuration files
600
-
601
- | Path | Scope | What's in it |
602
- | -------------------------------------------- | ------------- | --------------------------------------------------------------------------------------------------- |
603
- | `<cwd>/.pi/remote-pi/config.json` | Per-directory | `agent_name`, `session_name`, `auto_start_relay` |
604
- | `~/.pi/remote/config.json` | Per-user | `relay` URL; optional `defaults.auto_start_relay` (machine-wide fallback for per-directory configs) |
605
- | `~/.pi/remote/peers.json` | Per-machine | Paired mobile devices |
606
- | `~/.pi/remote/sessions/<name>/` | Per-session | Broker socket + `audit.jsonl` |
607
- | `~/.pi/remote/skills/agent-network/SKILL.md` | Per-user | Agent skill the LLM reads |
520
+ ### Caveats
608
521
 
609
- Every path above **except the global `config.json`** derives from a single
610
- **state root** by default `~/.pi/remote`. Two environment variables relocate
611
- that state root:
522
+ - **Pairing still happens interactively.** The launcher doesn't show a QR; it
523
+ only trusts owners already paired via `/unbien pair` in a Pi session on
524
+ this machine.
525
+ - **Tool execution is not gated.** A launched Pi inherits the same Pi config
526
+ an interactive run uses — Bash, Edit, Write etc. all execute without
527
+ prompting. Configure Pi's tool permissions to taste before enabling
528
+ `allow_remote_launch`.
529
+ - **Same identity, same relay.** The launcher reads the machine's global
530
+ config — `/unbien config` shows exactly what it will use.
612
531
 
613
- | Variable | Behaviour |
614
- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
615
- | `REMOTE_PI_DIR` | Absolute path to the state root. No suffix appended — set this to an XDG-style location like `~/.config/pi/remote-pi`. Takes priority over every other variable. |
616
- | `REMOTE_PI_HOME` | Stand-in for `$HOME`; state lives at `<REMOTE_PI_HOME>/.pi/remote`. Kept for backward compatibility. |
617
-
618
- When both are set, `REMOTE_PI_DIR` wins. Use these to put remote-pi **state**
619
- (sessions, daemon registries, cwd locks, paired identity) on a specific disk,
620
- inside an XDG directory, or anywhere your system's conventions dictate.
532
+ ---
621
533
 
622
- The global `config.json` (relay URL + defaults) is resolved separately, so it
623
- can sit beside the coding agent's own settings rather than the state tree:
534
+ ## Configuration & settings
624
535
 
625
- | Variable | Behaviour |
626
- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
627
- | `PI_CODING_AGENT_DIR` | The Pi host's settings root (default `~/.pi`). `config.json` lives at `<PI_CODING_AGENT_DIR>/remote/config.json`, so with the default agent dir the path stays exactly `~/.pi/remote/config.json`. Takes priority for config. |
536
+ ### Files
628
537
 
629
- When `PI_CODING_AGENT_DIR` is unset, `config.json` falls back to the state root
630
- above (so a pure `REMOTE_PI_DIR` relocation still keeps config beside the rest).
538
+ | Path | Scope | What's in it |
539
+ | ------------------------------------------------------ | ----------------- | ------------------------------------------------------------------------------ |
540
+ | `~/.pi/extensions/un-bien.json` | Per-user (global) | `relay` URL, `defaults`, `identity`, `debug` — the global settings (see below) |
541
+ | `<cwd>/.pi/un-bien/config.json` | Per-directory | `agent_name`, `auto_start_relay`, `allow_remote_launch` |
542
+ | `~/.local/state/un-bien/identity.json` | Per-machine | Paired identity keypair (file identity backend; `0600`) |
543
+ | `~/.local/state/un-bien/peers.json` | Per-machine | Paired mobile devices |
544
+ | `~/.local/state/un-bien/sessions/<name>/` | Per-session | Broker socket + `audit.jsonl` |
545
+ | `~/.local/state/un-bien/skills/agent-network/SKILL.md` | Per-user | Agent skill the LLM reads |
631
546
 
632
- ### Global defaults
547
+ ### Global settings — `~/.pi/extensions/un-bien.json`
633
548
 
634
- The global `~/.pi/remote/config.json` can include a `defaults` block that acts
635
- as a fallback for every per-directory config that doesn't set the field:
549
+ This is a Pi **extension config** (it lives beside the coding agent's own
550
+ settings, under `PI_CODING_AGENT_DIR/extensions/`, not in the state tree). All
551
+ fields are optional:
636
552
 
637
553
  ```jsonc
638
554
  {
555
+ // Relay URL in canonical http(s):// form. No default — unset means the
556
+ // extension refuses to connect. Set via `/unbien set-relay <url>`.
639
557
  "relay": "https://relay.yourdomain.tld",
640
- "defaults": {
641
- "auto_start_relay": true,
642
- },
558
+
559
+ // Machine-wide fallback for every per-cwd config that doesn't set the field.
560
+ // Pin auto-start once instead of dropping a file into every repo.
561
+ "defaults": { "auto_start_relay": true },
562
+
563
+ // Remote-launch backend a `session_launch` request uses on THIS machine:
564
+ // "tmux" (default; one shared tmux session, a window per launched Pi) or
565
+ // "herdr". Machine-wide choice — whether remote launch is enabled at all
566
+ // stays the per-cwd `allow_remote_launch` flag.
567
+ "launch": { "backend": "tmux" },
568
+
569
+ // Machine-identity storage. `storage` selects the PRIMARY backend for this
570
+ // Pi's long-term Ed25519 seed: "keychain" (OS-secured, default) or "file"
571
+ // (a 0600 seed file — the SSH-private-key model: cat-able, portable,
572
+ // works headless). `path` overrides the file-backend location
573
+ // (default ~/.local/state/un-bien/identity.json). The unselected backend is still
574
+ // READ to recover an existing identity, but never written.
575
+ "identity": { "storage": "keychain", "path": "~/.local/state/un-bien/identity.json" },
576
+
577
+ // File-based diagnostic logs. Off by default. Read from config (not env)
578
+ // because the daemon fork usually runs detached without a shell's env.
579
+ "debug": { "envelope": false, "panels": false },
643
580
  }
644
581
  ```
645
582
 
646
- This lets you pin `auto_start_relay` once beside `relay` — instead of
647
- dropping a `config.json` into every repo. A per-directory file (or the
648
- `REMOTE_PI_DIRECT_CONFIG` env var) still overrides the global default.
583
+ ### Per-directory settings`<cwd>/.pi/un-bien/config.json`
584
+
585
+ Written by the `/unbien` setup wizard:
586
+
587
+ | Field | Default | Meaning |
588
+ | --------------------- | -------------- | ------------------------------------------------------------------------------------------ |
589
+ | `agent_name` | directory name | Presentation leaf name for this agent (senders still use the opaque `list_peers` address) |
590
+ | `auto_start_relay` | `true` | On a fresh terminal, `/unbien` auto-joins the mesh and starts the relay |
591
+ | `allow_remote_launch` | `false` | Honor `session_launch` requests from a paired owner (spawn a new Pi session). Opt-in only. |
592
+
593
+ ### Environment variables
594
+
595
+ | Variable | Purpose |
596
+ | ---------------------- | -------------------------------------------------------------------------------------------------------------------------- |
597
+ | `UNBIEN_RELAY` | Relay URL override (highest precedence, ahead of the config file). CI / one-off use. |
598
+ | `UNBIEN_STATE_DIR` | Absolute override of the **state** dir itself. The knob for relocating state. Highest precedence of the state knobs. |
599
+ | `UNBIEN_DIR` | Legacy absolute override of the **state** dir itself (no suffix appended). Lower precedence than `UNBIEN_STATE_DIR`. |
600
+ | `UNBIEN_HOME` | Legacy stand-in `$HOME`; state lives at `<UNBIEN_HOME>/.pi/un-bien`. Lower precedence than `UNBIEN_DIR`. |
601
+ | `PI_CODING_AGENT_DIR` | The Pi host's settings root (default `~/.pi`); the global config lives at `<PI_CODING_AGENT_DIR>/extensions/un-bien.json`. |
602
+ | `UNBIEN_DIRECT_CONFIG` | Inline per-cwd config (JSON) instead of a `.pi/un-bien/config.json` file — CI / one-off use. |
649
603
 
650
- The `defaults` block is read from wherever the global `config.json` resolves, so
651
- it follows a relocated config: with `PI_CODING_AGENT_DIR` set it lives at
652
- `<PI_CODING_AGENT_DIR>/remote/config.json` (see the environment overrides above).
604
+ The state root resolves as `UNBIEN_STATE_DIR` > `UNBIEN_DIR` > `UNBIEN_HOME` >
605
+ `${XDG_STATE_HOME:-~/.local/state}/un-bien` the XDG-style default. `UNBIEN_DIR`/
606
+ `UNBIEN_HOME` relocate un-bien **state** (sessions, cwd locks, paired identity) and stay honored for older deployments. On first use
607
+ of the new default, a one-time best-effort migration moves any legacy
608
+ `~/.pi/un-bien` state (peers.json included) into the new root.
609
+ `PI_CODING_AGENT_DIR` relocates only the global **config**, so it can sit
610
+ beside the coding agent's own settings.
611
+
612
+ **Deprecated**: the relay no longer defaults its SQLite DB to a CWD-relative
613
+ `data/mesh.db` — see the relay README for its `UNBIEN_MESH_DB_PATH` /
614
+ state-root resolution.
653
615
 
654
616
  Override the relay for a single run without persisting:
655
617
 
656
618
  ```bash
657
- REMOTE_PI_RELAY=https://staging.example.tld pi
619
+ UNBIEN_RELAY=https://staging.example.tld pi
658
620
  ```
659
621
 
660
622
  ---
@@ -664,13 +626,16 @@ REMOTE_PI_RELAY=https://staging.example.tld pi
664
626
  **Footer says `🟡 relay waiting for pairing` even though I paired a device.**
665
627
  The icon reflects whether _any_ device has been paired on this machine, not
666
628
  whether one is connected right now. If you really have a paired device in
667
- `/remote-pi devices`, restart Pi — the cache may be stale (fixed in current
629
+ `/unbien devices`, restart Pi — the cache may be stale (fixed in current
668
630
  release; report a bug if it recurs).
669
631
 
670
632
  **Mobile app times out connecting.** Verify the same relay URL is configured
671
633
  on both sides. If you self-host behind a VPN, your phone must also be on the
672
634
  VPN (Tailscale on iOS/Android works fine).
673
635
 
636
+ **`/unbien config` says the relay is `unset`.** Un Bien has no default relay.
637
+ Self-host one and set it with `/unbien set-relay <url>` (or `UNBIEN_RELAY`).
638
+
674
639
  **`agent_request` keeps timing out.** It is deprecated because it blocks the
675
640
  turn while waiting for another agent's content reply. Migrate to `agent_send`;
676
641
  a unicast waits only for the delivery ACK, and the receiver can reply later
@@ -683,44 +648,17 @@ other terminal first.
683
648
 
684
649
  ---
685
650
 
686
- ## Branding
687
-
688
- Official brand assets live in
689
- [`/branding`](https://github.com/jacobaraujo7/remote_pi/tree/main/branding) —
690
- SVG sources for the logo (full, foreground, background, monochrome) plus a
691
- banner. See the
692
- [branding README](https://github.com/jacobaraujo7/remote_pi/blob/main/branding/README.md)
693
- for palette and export sizes.
694
-
695
- <table>
696
- <tr>
697
- <td align="center">
698
- <img src="https://raw.githubusercontent.com/jacobaraujo7/remote_pi/main/branding/logo-full.svg" width="96" alt="logo-full" /><br/>
699
- <sub><code>logo-full</code></sub>
700
- </td>
701
- <td align="center">
702
- <img src="https://raw.githubusercontent.com/jacobaraujo7/remote_pi/main/branding/logo-foreground.svg" width="96" alt="logo-foreground" /><br/>
703
- <sub><code>logo-foreground</code></sub>
704
- </td>
705
- <td align="center">
706
- <img src="https://raw.githubusercontent.com/jacobaraujo7/remote_pi/main/branding/logo-monochrome.svg" width="96" alt="logo-monochrome" /><br/>
707
- <sub><code>logo-monochrome</code></sub>
708
- </td>
709
- </tr>
710
- </table>
711
-
712
- ---
713
-
714
651
  ## Links
715
652
 
716
- - Homepage: <https://remote-pi.jacobmoura.work>
717
- - Source: <https://github.com/jacobaraujo7/remote_pi>
653
+ - Repository: <https://github.com/georgeharker/un-bien>
654
+ - Documentation: <https://docs.georgeharker.com/un-bien>
718
655
  - Pi coding agent: <https://github.com/earendil-works/pi>
719
- - Relay (self-hosting guide): <https://github.com/jacobaraujo7/remote_pi/blob/main/relay/README.md>
720
- - Issues / bugs: <https://github.com/jacobaraujo7/remote_pi/issues>
656
+ - Relay (self-hosting guide): [`../relay/README.md`](../relay/README.md)
657
+ - Upstream project (remote-pi, MIT): <https://github.com/jacobaraujo7/remote_pi>
721
658
 
722
659
  ---
723
660
 
724
661
  ## License
725
662
 
726
- MIT
663
+ MIT — derived from [remote-pi](https://github.com/jacobaraujo7/remote_pi) by
664
+ Jacob Moura. See [`LICENSE`](LICENSE).