marifold 0.67.0 → 0.70.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bridge-template/.env.example +6 -0
- package/dist/bridge-template/HOSTING.md +477 -0
- package/dist/bridge-template/README.md +176 -0
- package/dist/bridge-template/api/bridge.ts +5 -0
- package/dist/bridge-template/dist/Store.d.ts +51 -0
- package/dist/bridge-template/dist/Store.d.ts.map +1 -0
- package/dist/bridge-template/dist/Store.js +170 -0
- package/dist/bridge-template/dist/Store.js.map +1 -0
- package/dist/bridge-template/dist/index.d.ts +4 -0
- package/dist/bridge-template/dist/index.d.ts.map +1 -0
- package/dist/bridge-template/dist/index.js +189 -0
- package/dist/bridge-template/dist/index.js.map +1 -0
- package/dist/bridge-template/dist/serve.d.ts +2 -0
- package/dist/bridge-template/dist/serve.d.ts.map +1 -0
- package/dist/bridge-template/dist/serve.js +18 -0
- package/dist/bridge-template/dist/serve.js.map +1 -0
- package/dist/bridge-template/package.json +16 -0
- package/dist/bridge-template/setup/setup.py +319 -0
- package/dist/bridge-template/setup.sh +8 -0
- package/dist/bridge-template/vendor/workspace-protocol/dist/identity.d.ts +15 -0
- package/dist/bridge-template/vendor/workspace-protocol/dist/identity.d.ts.map +1 -0
- package/dist/bridge-template/vendor/workspace-protocol/dist/identity.js +93 -0
- package/dist/bridge-template/vendor/workspace-protocol/dist/identity.js.map +1 -0
- package/dist/bridge-template/vendor/workspace-protocol/dist/index.d.ts +3 -0
- package/dist/bridge-template/vendor/workspace-protocol/dist/index.d.ts.map +1 -0
- package/dist/bridge-template/vendor/workspace-protocol/dist/index.js +19 -0
- package/dist/bridge-template/vendor/workspace-protocol/dist/index.js.map +1 -0
- package/dist/bridge-template/vendor/workspace-protocol/dist/types.d.ts +97 -0
- package/dist/bridge-template/vendor/workspace-protocol/dist/types.d.ts.map +1 -0
- package/dist/bridge-template/vendor/workspace-protocol/dist/types.js +55 -0
- package/dist/bridge-template/vendor/workspace-protocol/dist/types.js.map +1 -0
- package/dist/bridge-template/vendor/workspace-protocol/package.json +9 -0
- package/dist/bridge-template/vercel.json +16 -0
- package/dist/commands/WorkspaceClient.d.ts +6 -0
- package/dist/commands/WorkspaceClient.d.ts.map +1 -0
- package/dist/commands/WorkspaceClient.js +72 -0
- package/dist/commands/WorkspaceClient.js.map +1 -0
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +9 -3
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/ask.d.ts.map +1 -1
- package/dist/commands/ask.js +10 -5
- package/dist/commands/ask.js.map +1 -1
- package/dist/commands/channel.d.ts.map +1 -1
- package/dist/commands/channel.js +3 -8
- package/dist/commands/channel.js.map +1 -1
- package/dist/commands/doctor.d.ts +2 -2
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +57 -5
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/profile.d.ts.map +1 -1
- package/dist/commands/profile.js +4 -3
- package/dist/commands/profile.js.map +1 -1
- package/dist/commands/schedule.d.ts.map +1 -1
- package/dist/commands/schedule.js +20 -8
- package/dist/commands/schedule.js.map +1 -1
- package/dist/commands/workspace.d.ts +9 -0
- package/dist/commands/workspace.d.ts.map +1 -0
- package/dist/commands/workspace.js +223 -0
- package/dist/commands/workspace.js.map +1 -0
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- 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.
|