marifold 0.69.0 → 0.71.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 (58) hide show
  1. package/dist/bridge-template/.env.example +6 -0
  2. package/dist/bridge-template/HOSTING.md +477 -0
  3. package/dist/bridge-template/README.md +176 -0
  4. package/dist/bridge-template/api/bridge.ts +5 -0
  5. package/dist/bridge-template/dist/Store.d.ts +51 -0
  6. package/dist/bridge-template/dist/Store.d.ts.map +1 -0
  7. package/dist/bridge-template/dist/Store.js +170 -0
  8. package/dist/bridge-template/dist/Store.js.map +1 -0
  9. package/dist/bridge-template/dist/index.d.ts +4 -0
  10. package/dist/bridge-template/dist/index.d.ts.map +1 -0
  11. package/dist/bridge-template/dist/index.js +189 -0
  12. package/dist/bridge-template/dist/index.js.map +1 -0
  13. package/dist/bridge-template/dist/serve.d.ts +2 -0
  14. package/dist/bridge-template/dist/serve.d.ts.map +1 -0
  15. package/dist/bridge-template/dist/serve.js +18 -0
  16. package/dist/bridge-template/dist/serve.js.map +1 -0
  17. package/dist/bridge-template/package.json +16 -0
  18. package/dist/bridge-template/setup/setup.py +319 -0
  19. package/dist/bridge-template/setup.sh +8 -0
  20. package/dist/bridge-template/vendor/workspace-protocol/dist/identity.d.ts +15 -0
  21. package/dist/bridge-template/vendor/workspace-protocol/dist/identity.d.ts.map +1 -0
  22. package/dist/bridge-template/vendor/workspace-protocol/dist/identity.js +93 -0
  23. package/dist/bridge-template/vendor/workspace-protocol/dist/identity.js.map +1 -0
  24. package/dist/bridge-template/vendor/workspace-protocol/dist/index.d.ts +3 -0
  25. package/dist/bridge-template/vendor/workspace-protocol/dist/index.d.ts.map +1 -0
  26. package/dist/bridge-template/vendor/workspace-protocol/dist/index.js +19 -0
  27. package/dist/bridge-template/vendor/workspace-protocol/dist/index.js.map +1 -0
  28. package/dist/bridge-template/vendor/workspace-protocol/dist/types.d.ts +97 -0
  29. package/dist/bridge-template/vendor/workspace-protocol/dist/types.d.ts.map +1 -0
  30. package/dist/bridge-template/vendor/workspace-protocol/dist/types.js +55 -0
  31. package/dist/bridge-template/vendor/workspace-protocol/dist/types.js.map +1 -0
  32. package/dist/bridge-template/vendor/workspace-protocol/package.json +9 -0
  33. package/dist/bridge-template/vercel.json +16 -0
  34. package/dist/commands/WorkspaceClient.d.ts +6 -0
  35. package/dist/commands/WorkspaceClient.d.ts.map +1 -0
  36. package/dist/commands/WorkspaceClient.js +72 -0
  37. package/dist/commands/WorkspaceClient.js.map +1 -0
  38. package/dist/commands/agent.d.ts.map +1 -1
  39. package/dist/commands/agent.js +9 -3
  40. package/dist/commands/agent.js.map +1 -1
  41. package/dist/commands/ask.d.ts.map +1 -1
  42. package/dist/commands/ask.js +10 -5
  43. package/dist/commands/ask.js.map +1 -1
  44. package/dist/commands/config.js +11 -8
  45. package/dist/commands/config.js.map +1 -1
  46. package/dist/commands/init.d.ts.map +1 -1
  47. package/dist/commands/init.js +10 -20
  48. package/dist/commands/init.js.map +1 -1
  49. package/dist/commands/schedule.d.ts.map +1 -1
  50. package/dist/commands/schedule.js +20 -8
  51. package/dist/commands/schedule.js.map +1 -1
  52. package/dist/commands/workspace.d.ts +9 -0
  53. package/dist/commands/workspace.d.ts.map +1 -0
  54. package/dist/commands/workspace.js +223 -0
  55. package/dist/commands/workspace.js.map +1 -0
  56. package/dist/index.js +9 -1
  57. package/dist/index.js.map +1 -1
  58. package/package.json +8 -6
@@ -0,0 +1,6 @@
1
+ # Use a TCP Redis endpoint. A REST-only Redis URL is not compatible.
2
+ # Use TLS (rediss://) for a remote Redis service. Do not commit real values.
3
+ MARIFOLD_BRIDGE_REDIS_URL=rediss://default:REPLACE_ME@redis.example.com:6379
4
+ # Generate a random secret with at least 32 characters. This registers hosts;
5
+ # joining a workspace uses a separate, single-use host invitation.
6
+ MARIFOLD_BRIDGE_REGISTRATION_TOKEN=REPLACE_WITH_A_RANDOM_SECRET_OF_AT_LEAST_32_CHARACTERS
@@ -0,0 +1,477 @@
1
+ # Bridge hosting guide
2
+
3
+ Updated September 11, 2026. These are manual deployment recipes for the existing
4
+ bridge. The standalone package has passed local smoke tests; Vercel, Cloudflare,
5
+ AWS, and Aliyun live deployments remain unverified. Review current provider plans,
6
+ limits, regions, and costs before creating resources.
7
+
8
+ Only deploy the bridge package. Never upload the parent `.marifold` directory,
9
+ profiles, model credentials, or the local Marifold application service. The bridge
10
+ is an authenticated encrypted relay; the personal application service remains on
11
+ the owner's device.
12
+
13
+ ## Choose a deployment
14
+
15
+ For a Linux VM, start with the [guided installer](#guided-linux-installer).
16
+ The manual systemd recipe below remains available for installations without Docker.
17
+
18
+ | Provider | Recipe for the current code | Additional infrastructure |
19
+ | --- | --- | --- |
20
+ | Vercel | Prepared Node function project | TLS TCP Redis |
21
+ | Cloudflare | Named Tunnel to a persistent Node service | Always-on Linux server, domain, TCP Redis |
22
+ | AWS | EC2 Linux instance with systemd and Caddy HTTPS | Domain, persistent TCP Redis |
23
+ | Aliyun / Alibaba Cloud | ECS Linux instance with systemd and Caddy HTTPS | Domain, persistent TCP Redis |
24
+
25
+ Cloudflare Tunnel provides the public HTTPS endpoint; it does not host the Node
26
+ process. The current package contains no Workers or Containers deployment adapter.
27
+ Those products would need separate integration and validation. Similarly, these
28
+ AWS and Aliyun recipes use virtual machines, not Lambda or Function Compute.
29
+
30
+ Choose a region and hostname reachable from the owner's actual devices. A provider
31
+ name does not guarantee reachability from mainland China. Test both HTTPS and
32
+ paired WebSocket traffic from home and office before relying on a deployment.
33
+
34
+ ## Guided Linux installer
35
+
36
+ On Aliyun ECS, AWS EC2, or another Linux server with systemd, install Docker Engine
37
+ and its Compose plugin using the [official distribution instructions](https://docs.docker.com/engine/install/).
38
+ Python 3 is also required. The installer checks these prerequisites; it does not
39
+ replace existing Docker packages, change cloud security groups, or configure DNS.
40
+ The server needs access to container/package registries for the first build.
41
+
42
+ If you pulled the Marifold repository onto the server:
43
+
44
+ ```sh
45
+ pnpm install --frozen-lockfile
46
+ pnpm -r build
47
+ node packages/cli/dist/index.js workspace bridge install
48
+ ```
49
+
50
+ If the `marifold` command already points to that build, the final command is simply:
51
+
52
+ ```sh
53
+ marifold workspace bridge install
54
+ ```
55
+
56
+ The CLI requests sudo when necessary and runs the installer on **this server**.
57
+ You do not need to start the personal Marifold application service on the ECS.
58
+ Alternatively, upload just a prepared bridge package (excluding `.env`, `.vercel`
59
+ and `node_modules`), enter that directory on the server, and run:
60
+
61
+ ```sh
62
+ sudo bash setup.sh
63
+ ```
64
+
65
+ The standalone route does not require Node or pnpm on the VM: the bridge runtime
66
+ and dependency installation run inside the Node container. Its image build uses
67
+ npm independently of your development package-manager choice.
68
+
69
+ The wizard asks for:
70
+
71
+ 1. **Redis:** create a dedicated persistent Redis on loopback port 32144 (default),
72
+ or enter an existing Redis URL through a hidden prompt. Existing Redis remains
73
+ unchanged; its credentials and permissions must already be configured. Startup
74
+ checks connectivity, not the full persistence/ACL policy. If using the server's
75
+ existing Redis, `127.0.0.1:6379` works because these Linux containers use host
76
+ networking.
77
+ 2. **HTTPS:** create Caddy configuration for a domain you control, or keep an
78
+ existing HTTPS proxy/Cloudflare Tunnel. For Caddy, point DNS at this server and
79
+ allow TCP 80/443 in the cloud security group. Existing ingress should forward
80
+ to `http://127.0.0.1:32143` on the same server.
81
+ 3. **Review and start:** confirm the destination and selected services. The wizard
82
+ generates secrets, builds/starts containers, waits for bridge HTTP and Redis
83
+ connectivity checks, and prints the next steps. It does not print secrets.
84
+
85
+ The installation lives at `/opt/marifold-bridge` with owner-only access:
86
+
87
+ | File/resource | Purpose |
88
+ | --- | --- |
89
+ | `compose.json` | Named project and restart policies |
90
+ | `bridge.env` | Protected bridge connection URL and generated registration token |
91
+ | `registration-token` | Protected copy of the token for the host creation prompt |
92
+ | `package/` | Compiled bridge inputs and container build files; no personal state |
93
+ | `redis.conf` | Dedicated Redis settings, only when selected |
94
+ | Named Redis volume | Persistent AOF/snapshot data; never automatically deleted |
95
+ | Named Caddy volumes | Certificate/configuration state, only when selected |
96
+
97
+ Read the generated registration token **locally**, then enter it on your host Mac:
98
+
99
+ ```sh
100
+ sudo cat /opt/marifold-bridge/registration-token
101
+ ```
102
+
103
+ Docker is enabled at boot and containers use `restart: unless-stopped`. They return
104
+ after reboot unless deliberately stopped. Dedicated Redis uses AOF with every-second
105
+ fsync and a no-eviction policy with a 128 MiB data limit; provision monitoring and
106
+ backups before relying on it. Persistence is not a backup. Other projects' Redis
107
+ processes and configuration are left alone.
108
+
109
+ Management commands:
110
+
111
+ ```sh
112
+ sudo docker compose -f /opt/marifold-bridge/compose.json ps
113
+ sudo docker compose -f /opt/marifold-bridge/compose.json logs --tail 50 bridge
114
+ sudo docker compose -f /opt/marifold-bridge/compose.json restart bridge
115
+ ```
116
+
117
+ After editing `bridge.env`, use `up -d` to recreate containers with changed values;
118
+ `restart` alone does not reload container environment configuration. Review logs
119
+ locally before sharing them; never print `docker compose config` without `--quiet`
120
+ because expanded configuration may contain secrets.
121
+
122
+ If startup fails, the installer exits with failure and keeps generated files and
123
+ data. Fix the connection, registry access, or port problem and retry:
124
+
125
+ ```sh
126
+ marifold workspace bridge install --start
127
+ # Or, from the prepared package:
128
+ sudo bash setup.sh --start
129
+ ```
130
+
131
+ This reuses the original configuration/token. A normal install refuses an existing
132
+ directory, containers, or volumes instead of overwriting them. It does not migrate
133
+ an existing manual systemd deployment or upgrade an older installed package.
134
+ Never use `docker compose down -v` unless you intend to destroy Redis and certificate
135
+ volumes. Public HTTPS and real pairing remain separate acceptance checks after
136
+ local health succeeds.
137
+
138
+ Installer validation covers generated configuration, permissions, input rejection,
139
+ secret exclusion, collision protection and startup command sequencing in disposable
140
+ fixtures. Docker execution, reboot recovery and live certificates still require
141
+ the first Linux server trial; they have not been verified on this development Mac.
142
+ Developers can run the installer fixtures with
143
+ `python3 -B -m unittest discover -s apps/bridge/setup -p 'test_*.py'` from the repository.
144
+
145
+ ## Common package and Redis prerequisites
146
+
147
+ Prepare once on the development Mac, or use your existing prepared directory:
148
+
149
+ ```sh
150
+ marifold workspace bridge prepare ~/.marifold/bridge
151
+ cd ~/.marifold/bridge
152
+ npm install --ignore-scripts
153
+ ```
154
+
155
+ Preparation refuses an existing destination; do not rerun it over a prepared
156
+ package. It includes compiled `dist/`, `vendor/`, a manifest, and Vercel files.
157
+ Linux deployments use `node dist/serve.js` and ignore `vercel.json` and the
158
+ Vercel-specific API entry point. Install dependencies on the destination OS;
159
+ do not transfer your Mac's `node_modules`.
160
+
161
+ Use a Redis service that supports the current `ioredis` client's TCP commands,
162
+ Lua scripts, and pub/sub. A REST-only endpoint or an unvalidated cluster endpoint
163
+ is insufficient. Start with a dedicated single-primary Redis-compatible endpoint;
164
+ the bridge does not configure Redis Cluster discovery. Enable persistence,
165
+ non-evicting storage, and backups: host identities and revocations must survive
166
+ restarts. Use TLS (`rediss://`) for remote Redis and allow access only from the
167
+ bridge's network where possible. Never expose an unauthenticated Redis port.
168
+
169
+ All deployments require:
170
+
171
+ ```dotenv
172
+ MARIFOLD_BRIDGE_REDIS_URL=rediss://default:REPLACE_ME@redis.example.com:6379
173
+ MARIFOLD_BRIDGE_REGISTRATION_TOKEN=REPLACE_WITH_A_RANDOM_SECRET
174
+ ```
175
+
176
+ Generate a separate registration secret using `openssl rand -hex 32`. Store it
177
+ securely; it registers hosts and is not the guest invitation. Percent-encode
178
+ reserved characters in Redis URL credentials. Do not commit real environment
179
+ files or copy secrets into support messages.
180
+
181
+ ## Vercel
182
+
183
+ ### Using pnpm instead of npm
184
+
185
+ The prepared package can use pnpm. Choose one package manager for this deployment
186
+ directory. If switching from npm, move its `package-lock.json` and `node_modules`
187
+ aside before reinstalling; do not deploy two competing lockfiles.
188
+
189
+ ```sh
190
+ cd ~/.marifold/bridge
191
+ pnpm install --ignore-scripts
192
+ pnpm --version
193
+ ```
194
+
195
+ Keep the resulting `pnpm-lock.yaml` and use the same exact pnpm version in
196
+ deployment. For example, with pnpm **11.17.0**, replace only `installCommand` in
197
+ the prepared `vercel.json` with:
198
+
199
+ ```json
200
+ "installCommand": "npx --yes pnpm@11.17.0 install --frozen-lockfile --ignore-scripts"
201
+ ```
202
+
203
+ This is a property to edit in the existing JSON object, not a replacement for the
204
+ whole configuration. Match the version to the one used to create your lockfile.
205
+ The explicit version avoids relying on Vercel's preinstalled pnpm default; the
206
+ build needs registry access to fetch that version. Preserve all routing/function
207
+ settings. On a Linux VM with that pnpm version installed, replace `npm ci` with
208
+ `pnpm install --frozen-lockfile --ignore-scripts` and transfer `pnpm-lock.yaml`
209
+ instead of `package-lock.json`. The runtime command remains `node dist/serve.js`.
210
+
211
+ See [pnpm install](https://pnpm.io/cli/install) and
212
+ [Vercel package-manager selection](https://vercel.com/docs/package-managers).
213
+
214
+ ### Deploy the project
215
+
216
+ Use the [complete project configuration in README](README.md#standalone-project-configuration).
217
+ The short sequence, after preparing the package, is:
218
+
219
+ 1. Run `npx vercel link` inside the prepared directory. Select the account and
220
+ create/select the bridge project; no Git repository is required.
221
+ 2. Provision TLS TCP Redis and add the two variables above to the project's
222
+ Production environment settings.
223
+ 3. Select Other as the framework, Node 24.x, and enable Fluid compute. Keep the
224
+ generated empty build command, install command, function duration, and rewrites.
225
+ 4. Ensure Deployment Protection permits machine access to `/health`, `/v1/hosts`,
226
+ and `/v1/connect` without an interactive login.
227
+ 5. Run `npx vercel --prod`, then use the stable production project URL for pairing.
228
+ Redeploy after changing deployment environment variables.
229
+
230
+ WebSocket connections can end at function duration limits and reconnect to another
231
+ instance, so external Redis coordination is required. See
232
+ [Vercel WebSockets](https://vercel.com/docs/functions/websockets).
233
+
234
+ ## Shared Linux service for AWS, Aliyun, and Cloudflare origins
235
+
236
+ Use a dedicated Ubuntu/Debian-style Linux VM with systemd. Install Node.js **24.x**
237
+ and npm using a supported [Node installation method](https://nodejs.org/en/download).
238
+ Use a system-wide executable, not an interactive user's shell-only version-manager
239
+ setup. Check `node --version` and `command -v node`.
240
+
241
+ Transfer only the prepared package to a new deployment directory, such as
242
+ `/opt/marifold-bridge`. Exclude `.env`, `.env.*`, `.vercel`, and `node_modules`;
243
+ keep `.env.example` as a template if wanted. Retain `package-lock.json`, then run
244
+ `npm ci --ignore-scripts` on the VM as the deployment user. The deployment user
245
+ owns installation; the runtime account only needs read access to the package.
246
+
247
+ On a fresh VM, create a dedicated runtime account and environment file:
248
+
249
+ ```sh
250
+ sudo useradd --system --user-group --no-create-home --shell /usr/sbin/nologin marifold-bridge
251
+ sudo install -m 600 /dev/null /etc/marifold-bridge.env
252
+ sudoedit /etc/marifold-bridge.env
253
+ ```
254
+
255
+ These creation commands are for a fresh installation; preserve existing accounts
256
+ and environment files during updates. Fill the file using your actual values:
257
+
258
+ ```dotenv
259
+ MARIFOLD_BRIDGE_REDIS_URL=rediss://default:REPLACE_ME@redis.example.com:6379
260
+ MARIFOLD_BRIDGE_REGISTRATION_TOKEN=REPLACE_WITH_YOUR_GENERATED_SECRET
261
+ HOST=127.0.0.1
262
+ PORT=32143
263
+ ```
264
+
265
+ The Node entry point does not automatically read `.env`. This service unit loads
266
+ the protected environment file. Create
267
+ `/etc/systemd/system/marifold-bridge.service`:
268
+
269
+ ```ini
270
+ [Unit]
271
+ Description=Marifold personal workspace bridge
272
+ Wants=network-online.target
273
+ After=network-online.target
274
+
275
+ [Service]
276
+ Type=simple
277
+ User=marifold-bridge
278
+ Group=marifold-bridge
279
+ WorkingDirectory=/opt/marifold-bridge
280
+ EnvironmentFile=/etc/marifold-bridge.env
281
+ ExecStart=/usr/bin/node /opt/marifold-bridge/dist/serve.js
282
+ Restart=on-failure
283
+ RestartSec=5
284
+ TimeoutStopSec=20
285
+ NoNewPrivileges=true
286
+ PrivateTmp=true
287
+ ProtectHome=true
288
+ ProtectSystem=strict
289
+
290
+ [Install]
291
+ WantedBy=multi-user.target
292
+ ```
293
+
294
+ Replace `/usr/bin/node` with the system-wide path verified above if different.
295
+ Ensure the runtime user can traverse/read the deployment directory. Then:
296
+
297
+ ```sh
298
+ sudo systemctl daemon-reload
299
+ sudo systemctl enable --now marifold-bridge
300
+ sudo systemctl status marifold-bridge
301
+ curl --fail http://127.0.0.1:32143/health
302
+ ```
303
+
304
+ Health identifies the process; pairing is still needed to verify Redis delivery.
305
+ Check service logs locally when diagnosing errors and redact credentials before
306
+ sharing them. Keep port 32143 private. Choose **one** ingress method below:
307
+ Caddy for direct HTTPS, or Cloudflare Tunnel on the same VM.
308
+
309
+ ### Direct HTTPS with Caddy
310
+
311
+ Point a domain such as `bridge.example.com` at the VM's stable public IP. Install
312
+ Caddy with its official system service. Configure `/etc/caddy/Caddyfile`:
313
+
314
+ ```caddyfile
315
+ bridge.example.com {
316
+ reverse_proxy 127.0.0.1:32143
317
+ }
318
+ ```
319
+
320
+ Replace the example hostname. Allow inbound TCP 80 and 443 for this recipe, and
321
+ ensure DNS resolves correctly for certificate issuance. Leave 32143 and Redis
322
+ closed to public inbound traffic. Caddy handles HTTPS and reverse proxying; see
323
+ the [reverse proxy guide](https://caddyserver.com/docs/quick-starts/reverse-proxy)
324
+ and [service setup](https://caddyserver.com/docs/running#using-the-service).
325
+
326
+ ```sh
327
+ sudo caddy validate --config /etc/caddy/Caddyfile
328
+ sudo systemctl reload caddy
329
+ curl --fail https://bridge.example.com/health
330
+ ```
331
+
332
+ Use the whole hostname for this relay, without stripping `/v1/connect` or adding
333
+ a path prefix. Avoid browser login gates, interactive bot challenges, and caching
334
+ on bridge endpoints. Exercise WebSocket pairing after configuring the proxy.
335
+
336
+ ## AWS EC2
337
+
338
+ 1. Create a Linux EC2 instance in the chosen region, with persistent disk and a
339
+ stable public address for direct HTTPS. Use a dedicated SSH key or your existing
340
+ approved administrative access. Review instance, disk, IP, and transfer charges.
341
+ 2. Configure its security group: admin access only from your management network;
342
+ TCP 80/443 for Caddy ingress; no public 32143 or Redis port. Keep outbound access
343
+ needed for DNS, package installation, certificates, and the Redis endpoint.
344
+ [EC2 security groups](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-security-groups.html)
345
+ 3. Provision a dedicated compatible Redis endpoint in the same region, preferably
346
+ on a private network reachable from the instance. Enable TLS/authentication,
347
+ persistence and backups; verify service command/endpoint compatibility before
348
+ selecting a managed Redis offering. Do not assume all serverless cache products
349
+ are interchangeable with this client's single-primary connection.
350
+ 4. Install the shared Linux service, point your DNS hostname at the stable address,
351
+ and configure Caddy as above. Keep the environment file outside the package.
352
+ 5. Perform the acceptance checklist below, including an instance/service restart.
353
+
354
+ An AWS load balancer is optional and not required for this one-owner initial
355
+ recipe. Adding one requires separate TLS, WebSocket timeout, and cost review.
356
+
357
+ ## Aliyun / Alibaba Cloud ECS
358
+
359
+ 1. Select a Linux ECS instance, persistent disk, region, public bandwidth, and
360
+ stable public IP/EIP appropriate to your devices. Review the region's domain
361
+ and hosting prerequisites before purchase. For mainland hosting, consult
362
+ [Alibaba Cloud's ICP filing guidance](https://www.alibabacloud.com/help/en/icp-filing/basic-icp-service/product-overview/what-is-an-icp-filing)
363
+ and confirm the requirements for your intended endpoint with the provider.
364
+ 2. Configure the ECS security group for restricted administrative access and
365
+ Caddy TCP 80/443. Keep the Node port and Redis private. See
366
+ [ECS security group rules](https://www.alibabacloud.com/help/en/ecs/user-guide/security-group-rules).
367
+ 3. Provision persistent compatible TCP Redis in the same region/VPC. Check TLS,
368
+ authentication, non-eviction, backups, Lua/pub/sub support, and endpoint mode.
369
+ A managed Redis-compatible product still needs this compatibility check.
370
+ 4. Install the shared Linux service and Caddy. Configure DNS for the endpoint and
371
+ complete applicable provider domain setup before certificate issuance/testing.
372
+ 5. Test from the actual mainland networks and devices, then complete acceptance.
373
+ Direct is the intended connection mode when this endpoint is reachable.
374
+
375
+ This recipe does not use Aliyun Function Compute or require Cloudflare in front
376
+ of Aliyun. The same Node service layout can be adapted to another Linux VM
377
+ provider, with that provider's firewall, DNS, and storage setup.
378
+
379
+ ## Cloudflare Tunnel
380
+
381
+ This recipe needs an always-on server running the shared Linux service. It can
382
+ be an AWS/Aliyun VM or another server. Cloudflare supplies the public endpoint and
383
+ outbound tunnel, while that server supplies compute and reaches Redis.
384
+
385
+ 1. Add/control the intended domain in Cloudflare and install the bridge service
386
+ on the origin VM. Confirm local health on `127.0.0.1:32143`.
387
+ 2. Create a named Cloudflare Tunnel in the dashboard. Install `cloudflared` on the
388
+ **same VM** using the dashboard's OS-specific connector instructions; run it
389
+ as a persistent service. Treat its connector token as a secret.
390
+ 3. Add a published application route for `bridge.example.com`, service type HTTP,
391
+ service URL `localhost:32143`, with no path restriction. Cloudflare terminates
392
+ public TLS; the origin HTTP hop is local to the VM. Do not use the private
393
+ Marifold application service as the tunnel origin.
394
+ 4. Keep inbound Node/Redis ports closed. Allow the connector's documented outbound
395
+ traffic. An origin using only Tunnel does not need public inbound 80/443 or a
396
+ Caddy installation. Retain your separate administrative access.
397
+ 5. Ensure the bridge hostname allows noninteractive HTTP/WebSocket clients. The
398
+ current client does not supply Cloudflare Access service-token headers and
399
+ cannot complete browser challenges. Scope any exception to this relay hostname;
400
+ do not remove protections from unrelated applications.
401
+ 6. Verify public health, pair two devices, and restart the connector to exercise
402
+ reconnect. Use the stable hostname rather than a temporary Quick Tunnel URL.
403
+
404
+ Follow the [official named-tunnel setup](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/).
405
+ Cloudflare proxies WebSockets but connections can close during network updates;
406
+ see [WebSocket behavior](https://developers.cloudflare.com/network/websockets/).
407
+ This route does not guarantee reachability from every mainland network.
408
+
409
+ ## Local device proxies
410
+
411
+ Bridge proxy support is **planned, not implemented**. The intended setting is
412
+ local to each device's workspace connection: Direct by default, with an optional
413
+ explicit proxy applied consistently to registration, pairing and WebSocket
414
+ reconnects. Home and office may use different values for the same endpoint.
415
+
416
+ This is independent of hosting provider and model-provider proxies. Never put a
417
+ Mac's proxy address in the server environment file: `127.0.0.1` there means the
418
+ server. Do not synchronize local proxy credentials. CLI/Web create/join and later
419
+ connection editing should expose the setting when implemented. For now a VPN/TUN
420
+ that routes the service process may supply connectivity; provider proxy settings
421
+ do not configure workspace bridge traffic.
422
+
423
+ ## Acceptance, updates, and recovery
424
+
425
+ For an installer-managed Linux deployment, pull and build Marifold on the server,
426
+ then run the updater:
427
+
428
+ ```sh
429
+ cd ~/repos/marifold
430
+ git pull --ff-only
431
+ pnpm install --frozen-lockfile
432
+ pnpm -r build
433
+ node packages/cli/dist/index.js workspace bridge update
434
+ ```
435
+
436
+ With a linked CLI, the last command is `marifold workspace bridge update`. For a
437
+ freshly prepared standalone package, use `sudo bash setup.sh --update` instead.
438
+ `workspace bridge install --start` remains a restart/recovery command, not an upgrade.
439
+
440
+ The updater targets the existing `/opt/marifold-bridge` installation. It builds a
441
+ new release while the current container runs, preserves the installed Dockerfile
442
+ (including a China registry mirror), and replaces only the bridge container. It
443
+ preserves the environment file, registration token, Redis container/data and Caddy
444
+ configuration. Connections briefly reconnect. Failed deployment or health checks
445
+ trigger a rollback to the retained previous image. Build failures leave the running
446
+ container and active configuration intact. Release directories and rollback
447
+ configuration remain under `/opt/marifold-bridge`; the updater prints their location.
448
+ This updater does not manage manual systemd or Vercel deployments.
449
+
450
+ The v0.70.3 relay prevents repeated delivery of unacknowledged packets within a
451
+ connection. Updated devices enable concurrent chunks only when both relay
452
+ connections support this behavior. Upgrade the ECS bridge as well as both Macs
453
+ to enable the throughput improvement; older relays retain sequential transfers.
454
+ After updating, `/health` includes `"deliveryReplay":"on-reconnect"`.
455
+
456
+ For every provider, verify before relying on the bridge:
457
+
458
+ - HTTPS health responds without a login/challenge; then authenticated registration
459
+ and pairing succeed. Health alone is not a Redis or end-to-end execution test.
460
+ - The guest reads shared host data and receives a host-authenticated model answer.
461
+ - An approved file write reaches the selected device; its artifact downloads.
462
+ - Separate sessions run concurrently; the same session rejects competing runs.
463
+ - Connection/service/ingress restart recovers without duplicate side effects.
464
+ - Host offline behavior, guest executor opt-out, and device revocation work.
465
+ - Redis metadata survives a planned restart and has a recovery procedure. Test
466
+ backup/restore on disposable data before applying it to a real workspace.
467
+
468
+ Retain the prior package for rollback. For a Linux update, stage dependencies in
469
+ a separate release directory, stop/switch/restart the service during a planned
470
+ interruption, and preserve the environment file and Redis data. For Vercel, retain
471
+ the prior deployment and environment configuration. Recheck pairing and reconnect
472
+ after a change. Do not flush Redis as a troubleshooting shortcut: losing host and
473
+ revocation metadata can break existing workspaces.
474
+
475
+ The remaining live acceptance work is shared with the repository's
476
+ `docs/workspaces.md`. No provider resources are created by preparing this package
477
+ or by saving this guide.
@@ -0,0 +1,176 @@
1
+ # Personal workspace bridge
2
+
3
+ This package routes encrypted messages between one owner's Marifold devices. It
4
+ contains no provider credentials, profile database, model endpoint, or agent tools.
5
+ The host service must stay online. Redis stores host public identities, revocations,
6
+ connection generations and encrypted delivery queues; it is required for production.
7
+
8
+ See [Hosting options and setup](HOSTING.md) for Vercel, Cloudflare Tunnel, AWS EC2,
9
+ and Aliyun ECS recipes, local device proxy design, and deployment acceptance.
10
+
11
+ For a guided Linux server install, run `marifold workspace bridge install`, or
12
+ `sudo bash setup.sh` inside a prepared package on that server. The
13
+ [installer guide](HOSTING.md#guided-linux-installer) covers Docker prerequisites,
14
+ Redis/HTTPS choices, automatic startup, and token retrieval.
15
+
16
+ ## Vercel test deployment
17
+
18
+ 1. Use `marifold workspace bridge prepare ./marifold-bridge` to create a standalone
19
+ deployment directory. Review its README, environment template and `vercel.json`.
20
+ It contains compiled bridge/protocol code and pinned direct dependencies, with
21
+ no personal Marifold data. Run `npm install --ignore-scripts` inside it to create
22
+ the dependency lockfile before deploying.
23
+ 2. Provision a **TCP Redis** service with TLS, persistent storage, and enough
24
+ connections for the relay. A REST-only Redis endpoint is insufficient. Keep the
25
+ Redis service and Vercel region close together. Retain host/revocation metadata
26
+ across restarts; use a non-evicting database and provider backups.
27
+ 3. In Vercel, select Node.js 24 and enable **Fluid compute**. Add the two variables
28
+ from `.env.example` as encrypted project environment variables. Use a random
29
+ registration token (for example `openssl rand -hex 32`), and copy it securely
30
+ to the host's workspace creation prompt. It is not a guest invitation.
31
+ 4. From the prepared directory, use the Vercel dashboard or `vercel` CLI to review
32
+ the account, project, region and deployment. Review Vercel and Redis plan limits
33
+ and spending controls before confirming creation. This package does not create
34
+ paid resources automatically. Deployment Protection must permit `/health`,
35
+ `/v1/hosts` and `/v1/connect`; Marifold authenticates these endpoints itself.
36
+ 5. Check `https://<deployment>/health` returns `marifold-bridge`. On the host, run
37
+ `marifold workspace create Home --bridge https://<deployment>` and enter the
38
+ registration token. On the other Mac, run `marifold workspace add <url>` and
39
+ enter the single-use invitation. Add `--executor` to enable remote tools.
40
+
41
+ The source checkout can also be deployed with `apps/bridge` as the project root;
42
+ include files outside that root for the workspace protocol dependency and use the
43
+ checked-in build command. The prepared standalone directory needs no monorepo.
44
+
45
+ Vercel WebSockets are currently beta. Its Node function exports an `http.Server`;
46
+ connections reconnect before/after function rotation and may reach another
47
+ instance. Redis, not process memory, coordinates delivery. Test rotation and a
48
+ host–MacBook round trip before relying on the deployment.
49
+
50
+ Official references: [Vercel WebSockets](https://vercel.com/docs/functions/websockets),
51
+ [Vercel function limits](https://vercel.com/docs/functions/limitations).
52
+
53
+ ### Standalone project configuration
54
+
55
+ For a package already prepared at `~/.marifold/bridge`, start with:
56
+
57
+ ```sh
58
+ cd ~/.marifold/bridge
59
+ npm install --ignore-scripts
60
+ npx vercel link
61
+ ```
62
+
63
+ Sign in if prompted, select your account/team, and create or select a project such
64
+ as `marifold-bridge`. Link this prepared directory, not the whole `.marifold`
65
+ directory. A Git repository is not required for CLI deployment. Review the chosen
66
+ account and plan before creating resources.
67
+
68
+ Use these settings for the **standalone prepared package**:
69
+
70
+ | Setting | Value |
71
+ | --- | --- |
72
+ | Framework preset | Other (`framework: null` in the generated configuration) |
73
+ | Project root | The prepared bridge directory |
74
+ | Node.js | 24.x |
75
+ | Fluid compute | Enabled |
76
+ | Install command | `npm install --ignore-scripts` |
77
+ | Build command | Empty; the prepared runtime is already compiled |
78
+ | Function | `api/bridge.ts`, with `maxDuration: 300` |
79
+ | Routing | Keep the generated rewrite from `/(.*)` to `/api/bridge` |
80
+ | Function region | Choose near your TCP Redis service |
81
+ | Deployment Protection | The bridge URL must accept service requests without an interactive Vercel login |
82
+
83
+ Keep the generated `vercel.json`; do not replace it with settings for a static
84
+ website. Source-checkout builds use the different build command described above.
85
+
86
+ Provision TLS TCP Redis with persistent, non-evicting metadata storage and backups.
87
+ In the Vercel project's environment settings, add both variables for **Production**:
88
+
89
+ | Variable | Value |
90
+ | --- | --- |
91
+ | `MARIFOLD_BRIDGE_REDIS_URL` | Your provider's full `rediss://…` TCP connection URL, including credentials |
92
+ | `MARIFOLD_BRIDGE_REGISTRATION_TOKEN` | A separate random secret used to register hosts |
93
+
94
+ Generate the registration token locally with `openssl rand -hex 32`. Store it
95
+ securely for the host's workspace creation prompt; do not paste it into shared
96
+ documentation. The `.env.example` file is a template, and a local `.env` does not
97
+ replace Vercel project environment configuration. Add Preview variables separately
98
+ only if you intend to test preview deployments.
99
+
100
+ After reviewing configuration and costs, deploy from the prepared directory:
101
+
102
+ ```sh
103
+ npx vercel --prod
104
+ curl --fail 'https://<your-project>.vercel.app/health'
105
+ ```
106
+
107
+ The health response should identify `marifold-bridge`. Use the stable project
108
+ production URL for workspace creation and joining. Health alone does not validate
109
+ pairing or WebSocket reconnects; finish the two-device acceptance test above.
110
+ Redeploy after changing production environment variables for them to take effect.
111
+
112
+ CLI references: [link a project](https://vercel.com/docs/cli/link),
113
+ [deploy](https://vercel.com/docs/cli/deploy), and
114
+ [environment variables](https://vercel.com/docs/environment-variables).
115
+
116
+ Prefer pnpm? Follow [Using pnpm instead of npm](HOSTING.md#using-pnpm-instead-of-npm)
117
+ to create its lockfile and replace the generated npm install command with a
118
+ version-pinned pnpm command.
119
+
120
+ ## Device connectivity and proxies
121
+
122
+ Proxy support for workspace bridge connections is **planned, not implemented**.
123
+ The current bridge client does not use Marifold's model-provider proxy settings.
124
+ Do not add a bridge proxy key to `.env` or use a `--proxy` workspace option yet.
125
+
126
+ The planned setting belongs to each device's local workspace connection. Default
127
+ to **Direct**; allow an explicit proxy for a connection that needs one. Apply the
128
+ same choice to host registration, pairing, WebSocket traffic and reconnects. Make
129
+ it available in CLI and Web UI before the first create/join connection, with later
130
+ editing supported. Keep proxy addresses and any credentials local and protected;
131
+ never synchronize them through the host or expose credentials in status output.
132
+
133
+ Choose based on reachability from that device, not the hosting provider's name:
134
+
135
+ | Deployment and device situation | Connection choice |
136
+ | --- | --- |
137
+ | A Vercel bridge is unreachable directly from this Mac | Configure a local proxy once support exists |
138
+ | An Aliyun or another bridge is reachable directly | Direct |
139
+ | Home and office have different network requirements | Configure each device independently |
140
+
141
+ An Aliyun deployment still needs a compatible HTTPS/WebSocket relay and persistent
142
+ Redis; the [Aliyun ECS recipe](HOSTING.md#aliyun--alibaba-cloud-ecs) has not been
143
+ live-tested. The prepared Vercel configuration is not a generic deployment
144
+ configuration for every provider.
145
+
146
+ The server's `.env` configures the bridge runtime, including Redis and host
147
+ registration. It does not configure the Macs connecting to it. A proxy address
148
+ such as `127.0.0.1:7890` on Vercel refers to the Vercel runtime, not your Mac.
149
+ Provider proxies remain independent from bridge proxies. A VPN/TUN that routes
150
+ the Marifold service's traffic may already provide connectivity; a successful
151
+ browser page load alone does not prove that background WebSocket traffic works.
152
+
153
+ ## Local smoke test
154
+
155
+ After building, set the two environment variables and run `node dist/serve.js`.
156
+ It binds loopback port 32143 by default; `PORT` and `HOST` override that for a
157
+ reviewed standalone deployment. Local `http://127.0.0.1:32143` is accepted by
158
+ Marifold. Every non-loopback bridge URL must use HTTPS.
159
+
160
+ `MARIFOLD_TEST_REDIS_BIN=/path/to/redis-server pnpm --filter @marifold/bridge test`
161
+ starts a disposable loopback Redis with persistence disabled for the integration
162
+ test. It never connects to a configured production Redis database.
163
+
164
+ ## Retention and recovery
165
+
166
+ Delivery queues retain up to 128 frames per recipient for five minutes. Endpoint
167
+ messages expire after one minute; a reconnect retransmits the same operation ID
168
+ in a fresh encrypted frame. The authoritative endpoint journal prevents repeated
169
+ effects. Redis loss can make existing workspaces unreachable; restore its metadata
170
+ from backup or create and pair a new workspace explicitly. Rotating the registration
171
+ token affects future host registrations; existing membership is revoked by the host.
172
+
173
+ TLS protects each connection. HPKE and endpoint signatures additionally protect
174
+ message contents from the bridge. The bridge can observe device/workspace IDs,
175
+ routing, packet sizes, timing and public keys. Logs must never include request
176
+ bodies, authorization headers, invitation tokens or decrypted application data.
@@ -0,0 +1,5 @@
1
+ import { createBridge, RedisRelayStore } from '../dist/index.js';
2
+ export default createBridge(
3
+ new RedisRelayStore(process.env.MARIFOLD_BRIDGE_REDIS_URL!),
4
+ process.env.MARIFOLD_BRIDGE_REGISTRATION_TOKEN!,
5
+ );