@ours.network/install 1.2.1-nightly.3 → 1.2.1-nightly.31

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 (49) hide show
  1. package/GATEWAY_SETUP.md +178 -0
  2. package/README.md +353 -9
  3. package/ROOTLESS_PODMAN_VALIDATION.md +210 -0
  4. package/assets/Dockerfile +17 -13
  5. package/assets/Dockerfile.gateway +5 -0
  6. package/assets/docker-compose.yaml +4 -1
  7. package/assets/fleet-settings.example.json +48 -0
  8. package/assets/gateway-entrypoint.sh +37 -0
  9. package/assets/release-lock.json +1167 -4499
  10. package/assets/release.json +37 -19
  11. package/assets/scripts/build/build-common.mjs +2 -1
  12. package/assets/scripts/build/build-sdk.mjs +6 -1
  13. package/assets/scripts/maintenance/build-context.mjs +1 -1
  14. package/assets/scripts/maintenance/daemon-owner.mjs +15 -0
  15. package/assets/scripts/maintenance/docker-layout-conversion.mjs +7 -3
  16. package/assets/scripts/maintenance/release-graph.mjs +18 -1
  17. package/assets/scripts/maintenance/state-operation.mjs +4 -2
  18. package/assets/scripts/runtime/client-setup.mjs +80 -6
  19. package/assets/scripts/runtime/diagnostics.mjs +72 -0
  20. package/assets/scripts/runtime/entrypoint.sh +1 -1
  21. package/assets/scripts/runtime/health-messenger.mjs +7 -6
  22. package/assets/scripts/runtime/notifications-gateway.mjs +82 -0
  23. package/assets/scripts/runtime/start-messenger.sh +25 -4
  24. package/assets/scripts/runtime/supervise.mjs +35 -0
  25. package/assets/sources.json +54 -28
  26. package/install.mjs +7 -2
  27. package/lib/build-transition.mjs +3 -0
  28. package/lib/client-cli.mjs +127 -0
  29. package/lib/container-engine.mjs +127 -0
  30. package/lib/diagnostics.mjs +1 -0
  31. package/lib/docker-conversion-runtime.mjs +3 -2
  32. package/lib/docker-layout-installation.mjs +2 -1
  33. package/lib/docker-runtime-repair.mjs +160 -0
  34. package/lib/effects.mjs +298 -132
  35. package/lib/gateway-transition.mjs +87 -0
  36. package/lib/gateway.mjs +158 -0
  37. package/lib/legacy-migration.mjs +13 -2
  38. package/lib/orchestrate.mjs +135 -43
  39. package/lib/plan.mjs +27 -11
  40. package/lib/podman-preflight.mjs +40 -0
  41. package/lib/server-onboarding.mjs +164 -16
  42. package/lib/server-ports.mjs +17 -0
  43. package/lib/setup-options.mjs +88 -27
  44. package/lib/setup.mjs +35 -18
  45. package/lib/target.mjs +24 -8
  46. package/lib/uninstall.mjs +2 -2
  47. package/lib/usage.mjs +19 -6
  48. package/lib/workspace-setup.mjs +83 -0
  49. package/package.json +4 -2
@@ -0,0 +1,178 @@
1
+ # Set up Ours with one client connection
2
+
3
+ Fleet, the `ours` CLI and MCP clients share `~/.ours-client/profile.json`.
4
+ Set the server address once. The address is the HTTP gateway: clients append
5
+ `/daemon`, `/cowork`, `/messenger` or `/tg-connector` as needed. They do not
6
+ connect to server Unix sockets, discover server state directories, or try port
7
+ 3050 after a connection failure. Fleet's local supervisor/harness sockets still
8
+ control local processes; they do not carry server management requests.
9
+
10
+ ## Release availability
11
+
12
+ The nightly manifest selects one published dependency graph: SDK
13
+ `3.8.1-nightly.14`, CLI `2.8.1-nightly.12`, daemon `3.8.1-nightly.6`,
14
+ Fleet `1.2.0-nightly.17`, MCP/Codex/Claude Code `1.2.0-nightly.9`,
15
+ Telegram `1.0.1-nightly.8`, Messenger `1.0.31-nightly.8`, and Cowork
16
+ `1.3.3-nightly.20260926.ceab465`. Exact archive checksums are retained in
17
+ `releases/nightly.json`; the host CLI uses the same SDK/CLI pair.
18
+
19
+ The coordinated installer release must include these pins before using the
20
+ commands below. Its release gate verifies the actual registry and vendored
21
+ package graphs, client acquisition/retry, and rejection of dependency drift.
22
+ These checks do not deploy or certify an existing server installation.
23
+
24
+ The daemon includes ADAPT SDK/native `0.12.1` and the shutdown socket-reset
25
+ correction from [SDK PR70](https://github.com/adapt-toolkit/ours-sdk/pull/70).
26
+ Client profile commands do not control server processes.
27
+
28
+ ## Fresh full-stack setup on localhost:4050
29
+
30
+ For an agent asked to “put the daemon on localhost port 4050”, use **4050 as the
31
+ public gateway port**. The daemon's internal container port stays 3050. Docker
32
+ publishes only the gateway. Run as the user who will run Fleet and the harnesses.
33
+ Use Node.js 22+ and a working Docker Compose installation.
34
+
35
+ ```sh
36
+ npm install -g @ours.network/install@nightly
37
+ ours-install all install --mode docker \
38
+ --state-dir "$HOME/ours-server" --identity-name "Your Name" \
39
+ --port 4050 --integrations codex,claude-code
40
+ ```
41
+
42
+ The installer starts the server, retains or creates its Human identity, issues a
43
+ separate client credential, and imports the connection into `~/.ours-client`.
44
+ For Fleet, add `fleet` to `--integrations` and provide
45
+ `--fleet-settings /absolute/path/fleet-settings.json`; see the Fleet settings
46
+ example in [assets/fleet-settings.example.json](assets/fleet-settings.example.json).
47
+ The example selects Codex for all three jobs; review its model and subscription
48
+ against the installed Fleet catalog before use. The installer prepares Fleet but leaves it
49
+ stopped for review. Use `--dry-run` first to inspect a complete preset.
50
+
51
+ Do not set `OURS_PORT=4050` in a client shell, Fleet role, or user service. The
52
+ single profile contains `serverUrl: "http://127.0.0.1:4050"`, with daemon requests
53
+ at `http://127.0.0.1:4050/daemon` and Cowork management at
54
+ `http://127.0.0.1:4050/cowork/management/rpc`.
55
+
56
+ ## Existing or remote server
57
+
58
+ An older Docker installation needs its gateway enabled on the **server host**:
59
+
60
+ ```sh
61
+ ours-install server gateway-enable --state-dir "$HOME/ours-server"
62
+ ```
63
+
64
+ This is a server migration that may restart its services; perform it in an
65
+ approved maintenance window. Do not change identities or reinstall a populated
66
+ server to change a client address. `--port` on an existing installation cannot
67
+ silently move the retained public port.
68
+
69
+ For a remote host, expose the gateway through an authenticated HTTPS entrypoint
70
+ and select that URL when installing the server, for example
71
+ `--server-url https://ours.example.net/base`. The reverse proxy must preserve the
72
+ base prefix, streaming responses and WebSocket upgrades. Do not expose the
73
+ individual service ports.
74
+
75
+ Obtain an issued credential using `ours-install server access-issue --state-dir
76
+ /absolute/server/root --output /private/credential` on the server. Transfer it
77
+ privately to the client user. Never print the credential or put it into command
78
+ arguments, source control, chat, or a service unit. Use a private directory
79
+ (mode 0700) and credential/profile files (mode 0600).
80
+
81
+ Prepare a client profile (the UUID must be the selected server's actual instance):
82
+
83
+ ```json
84
+ {
85
+ "serverUrl": "https://ours.example.net/base",
86
+ "endpoint": "https://ours.example.net/base/daemon",
87
+ "expectedInstanceId": "11111111-2222-3333-4444-555555555555",
88
+ "credentialPath": "/absolute/private/credential"
89
+ }
90
+ ```
91
+
92
+ `endpoint` is optional for the client resolver; when present it must exactly match
93
+ `serverUrl + "/daemon"` after trailing-slash normalization. Older endpoint-only
94
+ profiles must be migrated explicitly: supply the actual gateway URL, not a
95
+ guessed address derived from a direct daemon port.
96
+
97
+ ```sh
98
+ ours-install client --config /absolute/private/profile.json \
99
+ --integrations codex,claude-code
100
+ ```
101
+
102
+ This imports the profile and issued credential into `~/.ours-client`. All new
103
+ clients use that default without an environment export. A conflicting existing
104
+ server instance is refused and retained; do not delete it to bypass that check.
105
+ To change only the gateway address of the same server, prepare a private profile
106
+ with the new `serverUrl` and matching `/daemon` endpoint, keep its actual
107
+ `expectedInstanceId`, and run the same `ours-install client --config ...` command.
108
+ The installer validates authenticated access before importing the new address;
109
+ it preserves imported Fleet settings. New processes use the new address. A
110
+ failed verification leaves the old selection intact. A different server UUID
111
+ requires a separately planned migration, not an address edit.
112
+
113
+ Changing the retained Docker public listening port in place is not currently
114
+ supported. `--port 4050` above is for a fresh server. For an existing server,
115
+ configure an administered reverse proxy to its gateway at the desired address,
116
+ then use the same-instance client address procedure. Do not edit installation
117
+ state or recreate containers with new identities to work around this limitation.
118
+ Native server installations currently need a separately administered gateway;
119
+ the integrated gateway setup above uses Docker (or supported Podman Compose).
120
+
121
+ ## Fleet and systemd
122
+
123
+ For a new Fleet setup, save the linked JSON example as an absolute private file
124
+ and run as the same user after importing the client profile:
125
+
126
+ ```sh
127
+ ours-fleet init --settings /absolute/path/fleet-settings.json
128
+ ```
129
+
130
+ If the installer already imported Fleet settings, reuse them explicitly:
131
+
132
+ ```sh
133
+ ours-fleet init --settings "$HOME/.ours-client/fleet-settings.json"
134
+ ```
135
+
136
+ These commands work without a terminal. A bare `ours-fleet init` is interactive.
137
+ The installer also accepts `--fleet-settings /absolute/path/fleet-settings.json`
138
+ and passes it to this same noninteractive path. Generated systemd and launchd services persist only the common
139
+ profile path, not a service URL, port, or token. Lingering user services therefore
140
+ use the same selection as CLI and MCP after logout/reboot. Detached Fleet task
141
+ workers inherit the common profile and do not inherit legacy service selectors.
142
+
143
+ `OURS_CONFIG` is an explicit override for the **whole client profile**. Use it only
144
+ when all relevant clients intentionally share that same alternate file. It is
145
+ not a per-service address override. For the normal setup, leave it unset.
146
+
147
+ When migrating existing units, inspect `systemctl --user cat 'ours-fleet-agent@.service'`
148
+ and any per-role drop-ins. Remove stale `OURS_PORT`, `OURS_STATE_DIR`,
149
+ `OURS_API_TOKEN`, `OURS_DAEMON_ID` and Cowork-local overrides from their owning
150
+ configuration. Regenerate Fleet units with `ours-fleet init --settings /absolute/path/fleet-settings.json`
151
+ in the intended profile environment, then restart affected client roles only after approval.
152
+ Already-running clients retain their current connection until restarted; editing
153
+ a profile does not rebind running identities. Do not restart the server for a
154
+ client-only profile correction.
155
+
156
+ ## Verify and diagnose
157
+
158
+ ```sh
159
+ curl --fail http://127.0.0.1:4050/.well-known/ours
160
+ ours config show --json
161
+ ours identity list --json
162
+ ours-fleet doctor
163
+ ours-fleet room list
164
+ ```
165
+
166
+ Discovery must advertise the gateway and expected instance. `ours config show`
167
+ shows only connection metadata and a credential path, never the token. For a
168
+ remote server use its exact prefix in the discovery URL. Test both daemon
169
+ identity access and Cowork room listing: one succeeding does not prove the other.
170
+ Check `command -v ours-mcp` and `ours-mcp version` as well as the MCP binary launched
171
+ by the harness; Fleet can bundle a different MCP version than the global binary.
172
+
173
+ Missing/unsafe profiles, mismatched instances, rejected credentials, HTTP errors
174
+ and timeouts fail on the selected gateway. Fix that selection or service; do not
175
+ add localhost/socket fallbacks. If a task reports `room_probe_unavailable`, check
176
+ Cowork through the gateway. `control_unavailable` means Fleet could not query its
177
+ local role supervisor after its room checks; inspect the role's supervisor and
178
+ control channel separately. An active room alone does not establish agent readiness.
package/README.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## Install first, connect the App afterwards
2
+
3
+ ```sh
4
+ ours-install --username alex --name Alex --surname Taylor --disable-fleet-agents-setup
5
+ ```
6
+
7
+ This permanent command contains no tunnel credentials or enrollment expiry. On a terminal it asks for the remaining installation choices, including Docker, Podman or native packages. The username is local and has no global uniqueness check. A fresh root is named `<username>@<hostname>`; the optional `--hostname` defaults to the machine name. Messenger stores Name and Surname through its supported profile API. Retried setup retains root identities and existing Messenger profiles.
8
+
9
+ `--disable-fleet-agents-setup` installs the Fleet CLI and saves the choice without opening its agent/model wizard or rewriting existing Fleet configuration. Configure agents in the App after device linking. Complete presets still work unattended; partial presets require a terminal and cannot use `OURS_ASSUME_YES`.
10
+
11
+ The separate `ours-fleet setup-tunnel --file /private/setup-payload` step requires the matching Fleet follow-up release. Its private App payload remains scoped, signed-root-bound and expiring. Then run `ours-fleet link-device` and paste its private single-use code into the App. Do not put the expiring tunnel payload in the permanent installer command.
12
+
13
+ Native packages currently provide server-only installation; full Fleet/App onboarding requires the supported gateway (Docker or Podman). The installer refuses unsupported native full-stack setup before changing anything. This PR does not add a native gateway or release unpublished Fleet commands.
14
+
15
+ See [one-connection setup, localhost:4050 and systemd migration](GATEWAY_SETUP.md) for the gateway-only client workflow.
16
+
1
17
  # @ours.network/install — one installer for the whole stack
2
18
 
3
19
  Requires Node.js 22+ and npm. Install the selected release channel:
@@ -10,8 +26,8 @@ ours-install
10
26
  With no arguments, the wizard explains each step and offers keyboard choices.
11
27
  Use arrow keys and Enter to choose, and Space to select integrations. Recommended
12
28
  settings keep advanced ports and package overrides out of the usual flow. Text
13
- input is used for your name and explicitly customized paths or ports. Linux x64
14
- recommends native mode; macOS and Windows recommend Docker. On Windows run the
29
+ input is used for your name and explicitly customized paths or ports. Docker is recommended for the integrated full-stack gateway on every platform;
30
+ native packages remain available for server-only setup. On Windows run the
15
31
  installer inside WSL with Docker Desktop integration. This recommendation concerns
16
32
  packaging and isolation, not a guarantee that emulated x64 is faster on ARM Macs.
17
33
 
@@ -45,7 +61,7 @@ installers. `--integrations none` explicitly skips client integrations. Use
45
61
  Fleet's own wizard is available only in interactive setup; CLI Fleet setup requires
46
62
  its JSON settings file. Fleet stays stopped until you review and activate it.
47
63
 
48
- The server installs the daemon/SDK/CLI, main MCP, Telegram, Cowork and Messenger.
64
+ The server installs the daemon/SDK/CLI, Telegram, Cowork and Messenger.
49
65
  It starts the daemon, preserves the existing Human identity (or creates it once),
50
66
  then starts its applications. Full-stack setup issues a separate local client
51
67
  credential and configures the selected Codex, Claude Code and Fleet integrations.
@@ -56,6 +72,28 @@ The installer embeds exact component versions and SHA-512 values for its release
56
72
  An existing server's retained release selects matching local clients. Secrets are
57
73
  read from protected files, and client setup never copies the daemon API master.
58
74
 
75
+ ## Runtime and client packages
76
+
77
+ Server selections include `@ours.network/daemon`, whose `ours-daemon` binary owns
78
+ runtime startup and service administration. Docker runs `ours-daemon serve`;
79
+ native installations install its systemd/launchd service. Existing retained
80
+ installations can still stop and restore their older CLI-owned runtime.
81
+
82
+ Client selections contain the thin SDK and CLI, plus MCP when a harness needs
83
+ it. They exclude the daemon and its native database/ADAPT dependencies. Fleet
84
+ runs beside the harnesses on the client machine and calls the daemon HTTP API.
85
+ Client commands report an unavailable server and never start one automatically.
86
+
87
+ The checked-in source policy and packed installer retain the existing server
88
+ release set (SDK `3.8.1-nightly.11`, CLI `2.8.1-nightly.9`, daemon
89
+ `3.8.1-nightly.3`). The isolated host CLI uses published SDK
90
+ `3.8.1-nightly.13` and CLI `2.8.1-nightly.11`, including the shared gateway
91
+ profile resolver. `releases/nightly.json` binds both selections to verified
92
+ SHA512 integrities. Gateway-only Fleet/MCP release adoption is still pending;
93
+ see [GATEWAY_SETUP.md](GATEWAY_SETUP.md). Mixed or unselected nested ours
94
+ versions remain rejected within each selected graph. An explicit `--sources`
95
+ override remains available for development.
96
+
59
97
  ## Migrate an existing global installation
60
98
 
61
99
  The wizard detects the old `~/.ours/config.json` and offers to keep its identities,
@@ -137,8 +175,8 @@ native package acquisition, and reports completion only after service readiness.
137
175
  If a Docker service fails, the error includes its last 50 log lines (bounded in
138
176
  size) and a command to inspect the logs. A failed daemon prevents consumers from
139
177
  starting; a failed consumer does not prevent checks of unrelated consumers.
140
- Server runtime installation selects SDK/CLI, main MCP, Telegram, Cowork and
141
- Messenger. Main MCP is injected into the daemon, not started as a second daemon.
178
+ Server runtime installation selects SDK/CLI, Telegram, Cowork and
179
+ Messenger. MCP runs locally with the Codex and Claude Code plugins and calls the daemon API through the SDK.
142
180
  The daemon starts first; consumers are checked through their owning readiness
143
181
  interfaces. Messenger uses its existing identity prerequisite and never creates an
144
182
  identity. Its failure is reported after attempting unrelated consumers. Package
@@ -195,9 +233,9 @@ relative to the profile file. Select any nonempty subset of integrations; Fleet
195
233
  When Fleet settings are supplied, the installer passes them to Fleet's strict
196
234
  noninteractive `init --settings`; otherwise it opens Fleet's existing wizard.
197
235
  Fleet validates and publishes its own configuration. Client setup verifies the selected
198
- daemon and packaged OURS MCP before registering native integrations. It acquires
236
+ authenticated daemon API before registering native integrations. It acquires
199
237
  only selected integration packages and their actual client dependencies. It does
200
- not install main MCP, administer daemon state or invoke Docker. Exact npm client
238
+ not administer daemon state or invoke Docker. Codex and Claude Code include the local stdio MCP server as a dependency. Exact npm client
201
239
  installation needs no Python; Git source builds use the supplied source recipes
202
240
  and their build prerequisites. Normal native clients need neither Docker nor
203
241
  server maintenance tools.
@@ -213,7 +251,8 @@ an existing override and does not edit shell configuration or running sessions.
213
251
  Repeat with `ours-install client install` to reuse imported inputs and installed
214
252
  package records. Existing settings and Fleet-generated files are preserved. A
215
253
  prepared profile for the same endpoint/instance may supply a valid replacement
216
- issued token; another server is refused without changing the default. Invalid
254
+ issued token; a different server instance is refused without changing the default. A verified
255
+ new gateway address for the same instance updates the shared profile for new clients. Invalid
217
256
  server access is rejected before import. An acquisition or component failure
218
257
  reports incomplete setup and keeps the saved configuration for retry. Selected
219
258
  native integrations whose executable is unavailable are incomplete, not installed.
@@ -224,7 +263,7 @@ Interactive no-argument installation asks for package/Docker server mode, a
224
263
  prepared client profile, or `client`. Client setup offers the saved server when
225
264
  present. On first setup it asks for the server HTTP endpoint, issued-token file,
226
265
  source manifest and integration choices, obtains the instance UUID from
227
- `/selection`, displays it, then checks authenticated daemon and MCP access before
266
+ `/.well-known/ours` at the gateway, displays it, then checks authenticated daemon API access before
228
267
  saving anything. No UUID must be entered manually. The 2.0 HTTP flow assumes the
229
268
  approved trusted same-host deployment; selection metadata is not cryptographic
230
269
  server authentication. Fleet collects missing setup answers through its own wizard.
@@ -281,3 +320,308 @@ maintenance reads both formats. Format2 is not readable by older maintenance too
281
320
  so this change does not promise executable downgrade support. Restore validates the
282
321
  original archive records unchanged, then writes markers for the active target runtime.
283
322
  Mixed/incomplete context and component record sets are refused before activation.
323
+
324
+ ### Retrying a Docker installation after a source-policy permission failure
325
+
326
+ Setup verifies the selected Docker image before reusing it, including when the image
327
+ already exists. If an older installer left a root-owned, mode-0600
328
+ `/opt/ours/sources.json`, setup repairs only that image file's permissions and
329
+ continues. The selected package bytes, build provenance, image execution settings,
330
+ credentials and stored data are retained. The old materialized Dockerfile's known
331
+ COPY instruction is also corrected for future builds. No manual image or volume
332
+ deletion is required; repeat the original setup with the updated installer.
333
+
334
+ The repair runs isolated verification containers without state mounts or network
335
+ access. Other image verification failures stop setup without replacing the image.
336
+ An interrupted repair can be retried, including after the image tag was replaced.
337
+
338
+ ### Remote clients over HTTPS
339
+
340
+ A client may select an `https://` origin backed by a TLS reverse proxy. Keep the
341
+ daemon listener private and configure the proxy separately with a certificate
342
+ trusted by the client and matching the hostname. Node uses its normal trust
343
+ store; a private CA may be supplied through `NODE_EXTRA_CA_CERTS` before starting
344
+ the client. Certificate verification must remain enabled.
345
+
346
+ The existing issued client credential works over either transport; changing the
347
+ URL scheme does not require a new credential. Keep the server's master on the
348
+ server. Forward `x-ours-api-token` and the `x-ours-*` session headers unchanged,
349
+ and support streamed request/response bodies and long polling. Client requests
350
+ refuse redirects, including HTTPS-to-HTTP redirects: configure the final HTTPS
351
+ origin directly. UUID/capability checks still precede credential-bearing calls.
352
+
353
+ This adds client HTTPS support, not an HTTPS daemon listener, certificate
354
+ provisioning or automatic reverse-proxy configuration. Existing local HTTP and
355
+ SSH-tunnel profiles continue to work.
356
+
357
+ ## One-URL Docker gateway
358
+
359
+ New Docker installations use one loopback-published nginx port (default 3050).
360
+ Clients import one `serverUrl`; their daemon endpoint is `<serverUrl>/daemon` and
361
+ Fleet derives `<serverUrl>/cowork/management/rpc`. Cowork, Messenger and Telegram
362
+ backend ports are internal only. Existing installations retain their prior layout
363
+ on ordinary install/update. Select compatible releases before explicitly migrating:
364
+
365
+ ```sh
366
+ ours-install server gateway-enable --state-dir /private/ours
367
+ ```
368
+
369
+ Migration checks the installed Cowork, Messenger and Telegram capabilities before
370
+ stopping services, verifies authenticated daemon and read-only Cowork management,
371
+ and preserves the existing credential and client integration settings. Failure
372
+ restores routing, client profile and prior service selection. An interrupted
373
+ migration blocks other mutations: repeat `gateway-enable` to finish rollback,
374
+ then run it again to attempt migration. The private `gateway-transition.json`
375
+ retains recovery state until rollback or migration completes. Back up the whole
376
+ installation using the normal server backup procedure before maintenance.
377
+
378
+ For a nested external URL, pass `--server-url https://ours.example/base` during
379
+ Docker install or `gateway-enable`. This sets browser origin and path configuration;
380
+ it does **not** publish a public listener, provide TLS, or install external
381
+ credentials. Configure the authenticated entry first and retain the `/base` path
382
+ when proxying. A compatible release must contain these capabilities:
383
+ `cowork.http-management-v1`, `messenger.gateway-prefix-v1`, and
384
+ `telegram.gateway-listener-v1`. Older releases are rejected without enabling the
385
+ new listener; no package version is silently substituted.
386
+
387
+ ### Notifications
388
+
389
+ When the selected release includes `@ours.network/notifications`, gateway
390
+ installations run it inside the Messenger container, under one supervisor with
391
+ Messenger: a signal stops both, and either one exiting stops the container so
392
+ its normal restart recovers both. It publishes no host port. The gateway routes
393
+ `<serverUrl>/notifications/` to it after the same daemon credential check as
394
+ Telegram, and the service's adapter checks that credential again before acting
395
+ as the installation owner. Browser cookies, origins and fetch metadata are
396
+ removed. Messenger produces message notifications over loopback.
397
+
398
+ `prepare` creates the private configuration once in
399
+ `storage/state/notifications/config.json`: the owner token, separate Fleet and
400
+ Messenger producer tokens, and VAPID keys. Later runs only validate it; rotating
401
+ the owner token or VAPID keys would orphan every browser subscription, so a
402
+ differing configuration is refused rather than rewritten. Back it up with the
403
+ rest of the server state. A local client setup binds the Fleet producer token
404
+ to this server in `~/.ours-client/notifications-producer.json`; Fleet then sends
405
+ agent completions through `<serverUrl>/notifications/`. Remote clients and
406
+ native installations do not produce notifications. The gateway discovery
407
+ document is unchanged, so existing clients keep accepting the server. A
408
+ retained source policy without the package keeps working without notifications.
409
+
410
+ An existing complete installation gains them with its normal update, which
411
+ prepares the credentials after activating the new runtime and hands the Fleet
412
+ producer credential to the local client, then a web-only Fleet restart (agents
413
+ keep running):
414
+
415
+ ```sh
416
+ ours-install all update --mode docker --state-dir /private/ours --identity-name "Your Name" --integrations codex,fleet --fleet-settings /private/fleet.json --compatible
417
+ ours-fleet web restart
418
+ ```
419
+
420
+ ### Authenticated external entry
421
+
422
+ Messenger has no application authentication. Loopback access trusts local users;
423
+ never forward this gateway publicly without authentication. All applications on
424
+ this origin share one trust boundary: scripts or XSS in any application can make
425
+ requests to the others. Cowork's entered server credential grants operator room
426
+ administration and stays in memory until reload. Path prefixes do not isolate
427
+ applications. Only expose this origin to trusted operators.
428
+
429
+ A supported external entry is nginx TLS plus HTTP Basic authentication for browser
430
+ paths, with machine paths left to their existing server-token checks. Create the
431
+ password file using an interactive `htpasswd` prompt and make it readable by the
432
+ proxy worker. Use real certificates and the matching `--server-url`. The inner
433
+ gateway remains bound to `127.0.0.1:3050`; neither backend ports nor the inner
434
+ listener should be externally forwarded. Example for `/base`:
435
+
436
+ ```nginx
437
+ # In http {}:
438
+ map $http_upgrade $ours_connection { default upgrade; '' close; }
439
+ server {
440
+ listen 443 ssl;
441
+ server_name ours.example;
442
+ ssl_certificate /private/tls/fullchain.pem;
443
+ ssl_certificate_key /private/tls/key.pem;
444
+ auth_basic "ours operators";
445
+ auth_basic_user_file /private/ours-users;
446
+ access_log off;
447
+ proxy_set_header Host $http_host;
448
+ proxy_set_header Upgrade $http_upgrade;
449
+ proxy_set_header Connection $ours_connection;
450
+ proxy_http_version 1.1;
451
+ proxy_read_timeout 300s;
452
+ proxy_buffering off;
453
+ location = /base/.well-known/ours { auth_basic off; proxy_pass http://127.0.0.1:3050; }
454
+ location /base/daemon/ { auth_basic off; proxy_pass http://127.0.0.1:3050; }
455
+ location = /base/cowork/management/rpc { auth_basic off; proxy_pass http://127.0.0.1:3050; }
456
+ location /base/tg-connector/ { auth_basic off; proxy_pass http://127.0.0.1:3050; }
457
+ location /base/ { proxy_pass http://127.0.0.1:3050; }
458
+ location / { return 404; }
459
+ }
460
+ ```
461
+
462
+ Browser requests include same-origin proxy credentials. Fleet/SDK use the issued
463
+ `X-Ours-Api-Token`, require no browser login, and reject redirects. An SSH tunnel
464
+ to the loopback gateway is another private transport option, but every client must
465
+ establish its own tunnel and use a URL/origin matching that tunnel; it is not
466
+ automatic remote access. Do not treat a token entered in Cowork as Messenger auth.
467
+
468
+ Publish compatible Cowork, Messenger, Telegram and Fleet artifacts before enabling
469
+ gateway defaults in a qualified installer release. Gateway client setup acquires
470
+ and checks Fleet before activating the managed profile or publishing native
471
+ commands. Migration also checks the installed `ours-fleet version --json`
472
+ capability before stopping services when the selected managed profile includes
473
+ Fleet. Upgrade that executable first. The gate does not inventory differently pinned
474
+ running Fleet instances or remote clients: upgrade every client using this server
475
+ to a compatible Fleet release before cutover.
476
+
477
+ ### Cross-repository gateway qualification
478
+
479
+ Build Cowork, Fleet, Messenger and Telegram checkouts with their locked dependencies.
480
+ The real-service test uses disposable local daemon state, a local test broker,
481
+ nginx Docker containers and Chromium; it never uses installed identities or tokens.
482
+ It requires Linux Docker host networking and `openssl`. Set:
483
+
484
+ ```sh
485
+ OURS_TEST_GATEWAY_SERVICES=1 \
486
+ OURS_TEST_COWORK_ROOT=/checkouts/ours-cowork \
487
+ OURS_TEST_FLEET_ROOT=/checkouts/ours-fleet \
488
+ OURS_TEST_MESSENGER_ROOT=/checkouts/ours-messenger-server \
489
+ OURS_TEST_TELEGRAM_ROOT=/checkouts/ours-tg-connector \
490
+ OURS_TEST_BROWSER=/opt/google/chrome/chrome \
491
+ node --test packages/installer/test/gateway-services.test.mjs
492
+ ```
493
+
494
+ Run `OURS_TEST_DOCKER=1 node --test packages/installer/test/gateway-docker.test.mjs`
495
+ separately for Compose port isolation with a client container. That routing test
496
+ uses echo upstreams; the real-service test covers Fleet room operations, browser
497
+ paths, external authentication and Messenger WebSocket access. Neither test is
498
+ cross-platform Docker Desktop qualification or a live deployment test.
499
+
500
+ ### Rootless Podman (qualification in progress)
501
+
502
+ Container installations can select a local Linux rootless backend explicitly:
503
+
504
+ ```sh
505
+ export PODMAN_COMPOSE_PROVIDER=/absolute/path/to/docker-compose
506
+ ours-install server install --mode docker --container-engine podman \
507
+ --state-dir "$HOME/.ours-server-podman" --identity-name "Your Name"
508
+ ```
509
+
510
+ `mode: docker` remains the legacy container-layout name. `containerEngine` and
511
+ `containerBinding` retain Podman, the invoking UID, native storage paths, socket
512
+ and standalone Compose provider, plus the UID/GID mapping fingerprint. Records without `containerEngine` continue to
513
+ use Docker. Package/client installs reject the engine option. Existing records
514
+ cannot change engines through a flag or ambient connection change; automatic
515
+ Docker-to-Podman data migration is not provided. Legacy local-daemon migration
516
+ (`--migrate-from`) to Podman is also unsupported and is refused before source
517
+ shutdown or managed CLI publication, including migration retries. Use a fresh
518
+ Podman installation, or migrate the legacy daemon with Docker/packages.
519
+
520
+ Podman uses its native build command, with the same resolved Compose build
521
+ context, target, architecture, arguments and secret references, followed by
522
+ Compose `--no-build` startup. It does not require the Docker CLI or daemon.
523
+ The selected standalone Docker Compose provider must be version 2.35 or newer;
524
+ version checks do not replace the behavioral capability probe. `podman-compose`
525
+ is not an interchangeable provider for this implementation. Secrets sourced
526
+ from environment variables use Podman's native build-secret mechanism, never
527
+ build arguments or temporary copies.
528
+
529
+ Prerequisites include a working rootless API socket, sufficient subordinate
530
+ UID/GID mappings, and native volume-subpath isolation. Before setup/start, an
531
+ isolated disposable project tests Compose `!reset`, health waiting, JSON status,
532
+ service DNS, private UID-1000 storage and subpath isolation. A failed check stops
533
+ installation rather than substituting the whole volume. Existing volumes must
534
+ have this installation's project and volume labels. Docker contexts, unrelated
535
+ containers and host directory ownership are not changed.
536
+
537
+ Server boot recovery requires an enabled user `podman.socket`, an enabled
538
+ `podman-restart.service` using `--filter should-start-on-boot=true`, and user
539
+ linger. The standard user-service environment/default socket must select the
540
+ same storage; custom XDG paths and sockets are rejected for server startup.
541
+ The installer diagnoses missing prerequisites and never runs hidden sudo or
542
+ rewrites the vendor unit. Long-running Podman services get `unless-stopped`;
543
+ administration jobs do not. Stop/status remain available when boot prerequisites
544
+ are missing, provided the retained backend is accessible. Runtime-only test
545
+ harnesses may select a separate local socket with `OURS_PODMAN_SOCKET`; its
546
+ storage must match native Podman and the selection cannot change afterward.
547
+
548
+ Gateway images read and validate IPv4/IPv6 nameservers from their container at
549
+ startup. Only the resolver placeholder is replaced; nginx variables and existing
550
+ authentication/routes remain intact. UID 101, read-only rootfs, dropped
551
+ capabilities and tmpfs are retained. Setup and start verify authenticated
552
+ upstream requests in addition to liveness.
553
+
554
+ This change is **not yet a fully qualified server-support release**. Debian's
555
+ Podman 5.4.2 with Compose 5.5.1 failed subpath isolation and has incompatible boot
556
+ recovery semantics. Podman 6.1.2 passed the isolated capability probe on native
557
+ Linux amd64; production and prerelease-source builds, service lifecycle, rebuild and scoped maintenance also passed. See [validation evidence](ROOTLESS_PODMAN_VALIDATION.md). The full public installer/maintenance matrix,
558
+ SELinux Enforcing, logout/reboot, explicit-stop recovery, and Docker regression
559
+ matrix must all be recorded before declaring a supported engine/provider pair.
560
+ The supplied ARM VM results are historical feasibility evidence, not this
561
+ release's qualification.
562
+
563
+ Opt-in gateway fixture (isolated project, only owned resources removed):
564
+
565
+ ```sh
566
+ OURS_TEST_DOCKER=1 node --test test/gateway-docker.test.mjs
567
+ PODMAN_COMPOSE_PROVIDER=/absolute/path/to/docker-compose \
568
+ OURS_TEST_PODMAN=1 node --test test/gateway-docker.test.mjs
569
+ ```
570
+
571
+ ## Account workspace enrollment (task build)
572
+
573
+ The unmerged account onboarding feature requires matching reviewed installer,
574
+ Fleet and Messenger task builds. Do not advertise it as available in `@latest`.
575
+ Use an owned private directory and a 0600 downloaded payload:
576
+
577
+ ```sh
578
+ node /absolute/task-ours-network/packages/installer/install.mjs \
579
+ --setup-workspace-file /absolute/private/workspace.payload \
580
+ --workspace-fleet-bin /absolute/task-ours-fleet/dist/cli.js \
581
+ --sources /absolute/private/task-sources.json
582
+ ```
583
+
584
+ The wrapper defaults fresh hosts to the full Docker stack and retains existing
585
+ Human roots through the installed SDK with exact instance/private credential
586
+ selection. For an existing supported gateway add `--scope client --config` with
587
+ its private profile. It clears competing inherited daemon selectors before Fleet
588
+ initialization and enrollment; it never copies a browser device bearer to the
589
+ account server. A private stdin payload is also accepted by `--setup-workspace -`.
590
+ Encoding is not encryption; command arguments can enter history/process listings.
591
+ Cloudflare management tokens are forbidden in these payloads.
592
+
593
+ Docker and `cloudflared` are required for the fresh workflow. Supply a complete
594
+ `--fleet-settings` JSON for noninteractive Fleet initialization, or answer its
595
+ terminal prompts. Dry-run never initializes or enrolls Fleet. Read the matching
596
+ ours-app `server/DEPLOYMENT.md` for exact task source-policy generation, external
597
+ Google/Cloudflare prerequisites and account-server setup. On uncertain enrollment
598
+ check account status before requesting a new payload; do not reset identities or
599
+ replay a consumed invitation.
600
+
601
+ Workspace setup accepts exactly https://app.ours.network and the controlled test
602
+ account origin https://app.ours-tunnel.com. To move an existing same workspace to
603
+ the other origin, obtain a fresh payload from the moved account server and add
604
+ `--workspace-migrate-app-origin`. Fleet verifies the signed binding receipt before
605
+ changing local origin trust, preserves identities/config/device records, and
606
+ restarts the runtime. An implicit origin change is rejected. Browser account
607
+ cookies/device IndexedDB must be linked anew on the new origin. See the app
608
+ server/DEPLOYMENT.md switching procedure.
609
+
610
+ Workspace setup automatically reuses the saved managed gateway profile (or explicit `--config`/`OURS_CONFIG`), including a host with a non-default server state directory. It does not reinstall server/client state, create a root, or initialize an existing Fleet configuration. It requires Fleet capability `workspace.enroll.preserve-profile-v1` and passes `--preserve-profile`; Fleet requires the matching Messenger capability before posting signed enrollment. Hosts running older Messenger must first update that host service through its supported installer lifecycle; this wrapper never guesses or replaces its server directory. After Owner releases Messenger/Fleet/installer, app bootstrap pins must be updated before removing the preview guard.
611
+
612
+ ### Private-context Docker script permissions
613
+
614
+ Fresh image builds explicitly make non-secret maintenance/runtime scripts readable by the container user even when the host build context was copied under umask077. Host scripts, selected source policy and state retain their private permissions.
615
+
616
+ For a pending installation using an older image, run the corrected installer against the same pending workspace. It recognizes older shipped script COPY instructions combined with private host script modes, updates those image COPY modes and rebuilds the daemon image from the retained source selection. It leaves host data and enrollment state intact and qualifies the rebuilt image before preparing services. A private retry marker retains the rebuild requirement after interruption; it is removed only after verification succeeds. This rebuild resolves third-party transitive dependencies using the existing testing-delivery rules; selected Ours versions and integrity remain release-checked. It is not a byte-identical image permission-layer repair.
617
+
618
+ Fresh server setup checks its loopback daemon, Cowork and Messenger ports before
619
+ writing the installation configuration. Occupied implicit defaults advance to a
620
+ free port, skipping reserved sibling ports; the selected values are saved in the
621
+ installation record and used by native/container services, gateway and client
622
+ profile generation. An explicitly requested occupied port fails with a diagnostic.
623
+ Existing installation records keep their ports and identities. A listener acquired
624
+ by another process after preflight still causes startup to fail safely; no foreign
625
+ process is stopped and no retained installation is silently moved.
626
+
627
+ Workspace setup pins Fleet `1.2.0-nightly.39` with its recorded registry integrity. This release includes first-time Fleet setup through the host API for the account App, direct App access to the workspace services with the device credential, separate `ours-fleet setup-tunnel` and `ours-fleet link-device` commands, host prerequisite checks before the one-time workspace proof (`setup-tunnel --check`), `setup-tunnel --resume` to finish a setup that stopped after the host saved its private pending setup record (not when the proof's answer was lost, or after the setup window expired with an unconfirmed non-default port), and persists the available Fleet loopback port used by the workspace tunnel. It also accepts a setup-generated configuration left behind after the host enrollment state was removed, runs the host Codex when it is at least as new as the packaged one, offers only the Codex models the signed-in account can use, and removes the temporary setup contact after confirmation. Fleet serves no pages and opens no browser: every screen is in the App. Fleet also serves the workspace notification routes, produces notifications through the server gateway with the imported producer credential, and defaults agent wakes to `monitor.interrupt: after_tool` (Hermes: no interrupt). Existing installation identities and configured ports remain retained; a retained-port collision fails safely.