@ours.network/install 1.2.1-nightly.4 → 1.2.1-nightly.40

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 (57) hide show
  1. package/GATEWAY_SETUP.md +178 -0
  2. package/README.md +453 -9
  3. package/ROOTLESS_PODMAN_VALIDATION.md +210 -0
  4. package/assets/Dockerfile +14 -12
  5. package/assets/Dockerfile.gateway +5 -0
  6. package/assets/docker-compose.yaml +8 -1
  7. package/assets/fleet-settings.example.json +48 -0
  8. package/assets/gateway-entrypoint.sh +37 -0
  9. package/assets/release-lock.json +1139 -4470
  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 +12 -2
  18. package/assets/scripts/runtime/README.md +7 -0
  19. package/assets/scripts/runtime/client-setup.mjs +80 -6
  20. package/assets/scripts/runtime/dependency-ready.mjs +13 -0
  21. package/assets/scripts/runtime/diagnostics.mjs +72 -0
  22. package/assets/scripts/runtime/entrypoint.sh +5 -1
  23. package/assets/scripts/runtime/health-messenger.mjs +7 -6
  24. package/assets/scripts/runtime/notifications-gateway.mjs +90 -0
  25. package/assets/scripts/runtime/recover.mjs +165 -0
  26. package/assets/scripts/runtime/start-cowork.sh +5 -0
  27. package/assets/scripts/runtime/start-messenger.sh +29 -4
  28. package/assets/scripts/runtime/start-telegram.sh +5 -0
  29. package/assets/scripts/runtime/supervise.mjs +35 -0
  30. package/assets/sources.json +54 -28
  31. package/install.mjs +7 -2
  32. package/lib/boot-readiness.mjs +43 -0
  33. package/lib/build-transition.mjs +3 -0
  34. package/lib/client-cli.mjs +127 -0
  35. package/lib/container-engine.mjs +127 -0
  36. package/lib/diagnostics.mjs +1 -0
  37. package/lib/docker-conversion-runtime.mjs +3 -2
  38. package/lib/docker-layout-installation.mjs +2 -1
  39. package/lib/docker-recovery.mjs +55 -0
  40. package/lib/docker-runtime-repair.mjs +160 -0
  41. package/lib/effects.mjs +323 -132
  42. package/lib/gateway-transition.mjs +87 -0
  43. package/lib/gateway.mjs +158 -0
  44. package/lib/legacy-migration.mjs +13 -2
  45. package/lib/orchestrate-uninstall.mjs +18 -6
  46. package/lib/orchestrate.mjs +193 -43
  47. package/lib/plan.mjs +27 -11
  48. package/lib/podman-preflight.mjs +40 -0
  49. package/lib/server-onboarding.mjs +164 -16
  50. package/lib/server-ports.mjs +17 -0
  51. package/lib/setup-options.mjs +99 -28
  52. package/lib/setup.mjs +35 -17
  53. package/lib/target.mjs +24 -8
  54. package/lib/uninstall.mjs +2 -2
  55. package/lib/usage.mjs +21 -7
  56. package/lib/workspace-setup.mjs +83 -0
  57. package/package.json +4 -2
package/README.md CHANGED
@@ -1,3 +1,32 @@
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
+ ### Fleet task commands from a harness command sandbox
12
+
13
+ After Fleet configuration, and again on update, the installer asks the exact Fleet it installed to run `ours-fleet managed-cli setup`. Fleet owns that policy; the installer writes no Codex rule or Claude setting itself, and the step starts no agent session and no model. It runs only when the installed Fleet declares the `managed-cli.setup-v1` capability; the Fleet release this installer pins does. With an earlier Fleet (a retained or development installation) it is skipped (and `--fleet-task-workflow` reports that it was not applied), and it never fails an installation: an unsupported combination is printed with Fleet's reason and left exactly as sandboxed as before.
14
+
15
+ Nothing is prepared unless an Agent declares `managed_cli: [task-workflow]`. To opt agents in from the installer, name them:
16
+
17
+ ```sh
18
+ ours-install client --config /private/profile.json --integrations codex,fleet \
19
+ --fleet-settings /private/fleet-settings.json --fleet-task-workflow FleetCoordinator
20
+ ```
21
+
22
+ `--fleet-task-workflow` takes a comma-separated list of Fleet agent names, requires the Fleet integration and conflicts with `--disable-fleet-agents-setup`. It adds that one key to each named Agent and changes no permission. The prepared scope is the packaged Coordinator task workflow (`task create|start|finish|block|unblock|review|list|show`, `room show|members`, template/plan/help inspection) for one pinned Fleet configuration; `task start` and `task finish` provision and retire rooms and agents, which is real authority, and the installer prints that scope. The result is generated configuration only. It is not evidence that a command reached a supervisor: `ours-fleet managed-cli status` and `ours-fleet doctor` report what running agents actually observed. Fleet's `docs/validation/managed-cli-permissions.md` lists the qualified harnesses and platforms.
23
+
24
+ The separate `ours-fleet setup-tunnel <encoded-v2-grant>` step is supported by the pinned Fleet release. Its private App payload remains scoped, signed-root-bound and expiring. Fleet prints its QR and private single-use connection code immediately after successful setup; scan or paste it into the App. `ours-fleet link-device` remains available for additional devices or expired codes. Do not put the expiring tunnel payload in the permanent installer command.
25
+
26
+ 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 flow does not add a native gateway.
27
+
28
+ See [one-connection setup, localhost:4050 and systemd migration](GATEWAY_SETUP.md) for the gateway-only client workflow.
29
+
1
30
  # @ours.network/install — one installer for the whole stack
2
31
 
3
32
  Requires Node.js 22+ and npm. Install the selected release channel:
@@ -10,8 +39,8 @@ ours-install
10
39
  With no arguments, the wizard explains each step and offers keyboard choices.
11
40
  Use arrow keys and Enter to choose, and Space to select integrations. Recommended
12
41
  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
42
+ input is used for your name and explicitly customized paths or ports. Docker is recommended for the integrated full-stack gateway on every platform;
43
+ native packages remain available for server-only setup. On Windows run the
15
44
  installer inside WSL with Docker Desktop integration. This recommendation concerns
16
45
  packaging and isolation, not a guarantee that emulated x64 is faster on ARM Macs.
17
46
 
@@ -45,7 +74,7 @@ installers. `--integrations none` explicitly skips client integrations. Use
45
74
  Fleet's own wizard is available only in interactive setup; CLI Fleet setup requires
46
75
  its JSON settings file. Fleet stays stopped until you review and activate it.
47
76
 
48
- The server installs the daemon/SDK/CLI, main MCP, Telegram, Cowork and Messenger.
77
+ The server installs the daemon/SDK/CLI, Telegram, Cowork and Messenger.
49
78
  It starts the daemon, preserves the existing Human identity (or creates it once),
50
79
  then starts its applications. Full-stack setup issues a separate local client
51
80
  credential and configures the selected Codex, Claude Code and Fleet integrations.
@@ -56,6 +85,28 @@ The installer embeds exact component versions and SHA-512 values for its release
56
85
  An existing server's retained release selects matching local clients. Secrets are
57
86
  read from protected files, and client setup never copies the daemon API master.
58
87
 
88
+ ## Runtime and client packages
89
+
90
+ Server selections include `@ours.network/daemon`, whose `ours-daemon` binary owns
91
+ runtime startup and service administration. Docker runs `ours-daemon serve`;
92
+ native installations install its systemd/launchd service. Existing retained
93
+ installations can still stop and restore their older CLI-owned runtime.
94
+
95
+ Client selections contain the thin SDK and CLI, plus MCP when a harness needs
96
+ it. They exclude the daemon and its native database/ADAPT dependencies. Fleet
97
+ runs beside the harnesses on the client machine and calls the daemon HTTP API.
98
+ Client commands report an unavailable server and never start one automatically.
99
+
100
+ The checked-in source policy and packed installer retain the existing server
101
+ release set (SDK `3.8.1-nightly.11`, CLI `2.8.1-nightly.9`, daemon
102
+ `3.8.1-nightly.3`). The isolated host CLI uses published SDK
103
+ `3.8.1-nightly.13` and CLI `2.8.1-nightly.11`, including the shared gateway
104
+ profile resolver. `releases/nightly.json` binds both selections to verified
105
+ SHA512 integrities. Gateway-only Fleet/MCP release adoption is still pending;
106
+ see [GATEWAY_SETUP.md](GATEWAY_SETUP.md). Mixed or unselected nested ours
107
+ versions remain rejected within each selected graph. An explicit `--sources`
108
+ override remains available for development.
109
+
59
110
  ## Migrate an existing global installation
60
111
 
61
112
  The wizard detects the old `~/.ours/config.json` and offers to keep its identities,
@@ -137,8 +188,8 @@ native package acquisition, and reports completion only after service readiness.
137
188
  If a Docker service fails, the error includes its last 50 log lines (bounded in
138
189
  size) and a command to inspect the logs. A failed daemon prevents consumers from
139
190
  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.
191
+ Server runtime installation selects SDK/CLI, Telegram, Cowork and
192
+ Messenger. MCP runs locally with the Codex and Claude Code plugins and calls the daemon API through the SDK.
142
193
  The daemon starts first; consumers are checked through their owning readiness
143
194
  interfaces. Messenger uses its existing identity prerequisite and never creates an
144
195
  identity. Its failure is reported after attempting unrelated consumers. Package
@@ -195,9 +246,9 @@ relative to the profile file. Select any nonempty subset of integrations; Fleet
195
246
  When Fleet settings are supplied, the installer passes them to Fleet's strict
196
247
  noninteractive `init --settings`; otherwise it opens Fleet's existing wizard.
197
248
  Fleet validates and publishes its own configuration. Client setup verifies the selected
198
- daemon and packaged OURS MCP before registering native integrations. It acquires
249
+ authenticated daemon API before registering native integrations. It acquires
199
250
  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
251
+ not administer daemon state or invoke Docker. Codex and Claude Code include the local stdio MCP server as a dependency. Exact npm client
201
252
  installation needs no Python; Git source builds use the supplied source recipes
202
253
  and their build prerequisites. Normal native clients need neither Docker nor
203
254
  server maintenance tools.
@@ -213,7 +264,8 @@ an existing override and does not edit shell configuration or running sessions.
213
264
  Repeat with `ours-install client install` to reuse imported inputs and installed
214
265
  package records. Existing settings and Fleet-generated files are preserved. A
215
266
  prepared profile for the same endpoint/instance may supply a valid replacement
216
- issued token; another server is refused without changing the default. Invalid
267
+ issued token; a different server instance is refused without changing the default. A verified
268
+ new gateway address for the same instance updates the shared profile for new clients. Invalid
217
269
  server access is rejected before import. An acquisition or component failure
218
270
  reports incomplete setup and keeps the saved configuration for retry. Selected
219
271
  native integrations whose executable is unavailable are incomplete, not installed.
@@ -224,7 +276,7 @@ Interactive no-argument installation asks for package/Docker server mode, a
224
276
  prepared client profile, or `client`. Client setup offers the saved server when
225
277
  present. On first setup it asks for the server HTTP endpoint, issued-token file,
226
278
  source manifest and integration choices, obtains the instance UUID from
227
- `/selection`, displays it, then checks authenticated daemon and MCP access before
279
+ `/.well-known/ours` at the gateway, displays it, then checks authenticated daemon API access before
228
280
  saving anything. No UUID must be entered manually. The 2.0 HTTP flow assumes the
229
281
  approved trusted same-host deployment; selection metadata is not cryptographic
230
282
  server authentication. Fleet collects missing setup answers through its own wizard.
@@ -281,3 +333,395 @@ maintenance reads both formats. Format2 is not readable by older maintenance too
281
333
  so this change does not promise executable downgrade support. Restore validates the
282
334
  original archive records unchanged, then writes markers for the active target runtime.
283
335
  Mixed/incomplete context and component record sets are refused before activation.
336
+
337
+ ### Retrying a Docker installation after a source-policy permission failure
338
+
339
+ Setup verifies the selected Docker image before reusing it, including when the image
340
+ already exists. If an older installer left a root-owned, mode-0600
341
+ `/opt/ours/sources.json`, setup repairs only that image file's permissions and
342
+ continues. The selected package bytes, build provenance, image execution settings,
343
+ credentials and stored data are retained. The old materialized Dockerfile's known
344
+ COPY instruction is also corrected for future builds. No manual image or volume
345
+ deletion is required; repeat the original setup with the updated installer.
346
+
347
+ The repair runs isolated verification containers without state mounts or network
348
+ access. Other image verification failures stop setup without replacing the image.
349
+ An interrupted repair can be retried, including after the image tag was replaced.
350
+
351
+ ### Remote clients over HTTPS
352
+
353
+ A client may select an `https://` origin backed by a TLS reverse proxy. Keep the
354
+ daemon listener private and configure the proxy separately with a certificate
355
+ trusted by the client and matching the hostname. Node uses its normal trust
356
+ store; a private CA may be supplied through `NODE_EXTRA_CA_CERTS` before starting
357
+ the client. Certificate verification must remain enabled.
358
+
359
+ The existing issued client credential works over either transport; changing the
360
+ URL scheme does not require a new credential. Keep the server's master on the
361
+ server. Forward `x-ours-api-token` and the `x-ours-*` session headers unchanged,
362
+ and support streamed request/response bodies and long polling. Client requests
363
+ refuse redirects, including HTTPS-to-HTTP redirects: configure the final HTTPS
364
+ origin directly. UUID/capability checks still precede credential-bearing calls.
365
+
366
+ This adds client HTTPS support, not an HTTPS daemon listener, certificate
367
+ provisioning or automatic reverse-proxy configuration. Existing local HTTP and
368
+ SSH-tunnel profiles continue to work.
369
+
370
+ ## One-URL Docker gateway
371
+
372
+ New Docker installations use one loopback-published nginx port (default 3050).
373
+ Clients import one `serverUrl`; their daemon endpoint is `<serverUrl>/daemon` and
374
+ Fleet derives `<serverUrl>/cowork/management/rpc`. Cowork, Messenger and Telegram
375
+ backend ports are internal only. Existing installations retain their prior layout
376
+ on ordinary install/update. Select compatible releases before explicitly migrating:
377
+
378
+ ```sh
379
+ ours-install server gateway-enable --state-dir /private/ours
380
+ ```
381
+
382
+ Migration checks the installed Cowork, Messenger and Telegram capabilities before
383
+ stopping services, verifies authenticated daemon and read-only Cowork management,
384
+ and preserves the existing credential and client integration settings. Failure
385
+ restores routing, client profile and prior service selection. An interrupted
386
+ migration blocks other mutations: repeat `gateway-enable` to finish rollback,
387
+ then run it again to attempt migration. The private `gateway-transition.json`
388
+ retains recovery state until rollback or migration completes. Back up the whole
389
+ installation using the normal server backup procedure before maintenance.
390
+
391
+ For a nested external URL, pass `--server-url https://ours.example/base` during
392
+ Docker install or `gateway-enable`. This sets browser origin and path configuration;
393
+ it does **not** publish a public listener, provide TLS, or install external
394
+ credentials. Configure the authenticated entry first and retain the `/base` path
395
+ when proxying. A compatible release must contain these capabilities:
396
+ `cowork.http-management-v1`, `messenger.gateway-prefix-v1`, and
397
+ `telegram.gateway-listener-v1`. Older releases are rejected without enabling the
398
+ new listener; no package version is silently substituted.
399
+
400
+ ### Notifications
401
+
402
+ When the selected release includes `@ours.network/notifications`, gateway
403
+ installations run it inside the Messenger container, under one supervisor with
404
+ Messenger: a signal stops both, and either one exiting stops the container so
405
+ its normal restart recovers both. It publishes no host port. The gateway routes
406
+ `<serverUrl>/notifications/` to it after the same daemon credential check as
407
+ Telegram, and the service's adapter checks that credential again before acting
408
+ as the installation owner. Browser cookies, origins and fetch metadata are
409
+ removed. Messenger produces message notifications over loopback.
410
+
411
+ `prepare` creates the private configuration once in
412
+ `storage/state/notifications/config.json`: the owner token, separate Fleet and
413
+ Messenger producer tokens, and VAPID keys. Later runs only validate it; rotating
414
+ the owner token or VAPID keys would orphan every browser subscription, so a
415
+ differing configuration is refused rather than rewritten. Back it up with the
416
+ rest of the server state. A local client setup binds the Fleet producer token
417
+ to this server in `~/.ours-client/notifications-producer.json`; Fleet then sends
418
+ agent completions through `<serverUrl>/notifications/`. Remote clients and
419
+ native installations do not produce notifications. The gateway discovery
420
+ document is unchanged, so existing clients keep accepting the server. A
421
+ retained source policy without the package keeps working without notifications.
422
+
423
+ An existing complete installation gains them with its normal update, which
424
+ prepares the credentials after activating the new runtime and hands the Fleet
425
+ producer credential to the local client, then a web-only Fleet restart (agents
426
+ keep running):
427
+
428
+ ```sh
429
+ ours-install all update --mode docker --state-dir /private/ours --identity-name "Your Name" --integrations codex,fleet --fleet-settings /private/fleet.json --compatible
430
+ ours-fleet web restart
431
+ ```
432
+
433
+ ### Authenticated external entry
434
+
435
+ Messenger has no application authentication. Loopback access trusts local users;
436
+ never forward this gateway publicly without authentication. All applications on
437
+ this origin share one trust boundary: scripts or XSS in any application can make
438
+ requests to the others. Cowork's entered server credential grants operator room
439
+ administration and stays in memory until reload. Path prefixes do not isolate
440
+ applications. Only expose this origin to trusted operators.
441
+
442
+ A supported external entry is nginx TLS plus HTTP Basic authentication for browser
443
+ paths, with machine paths left to their existing server-token checks. Create the
444
+ password file using an interactive `htpasswd` prompt and make it readable by the
445
+ proxy worker. Use real certificates and the matching `--server-url`. The inner
446
+ gateway remains bound to `127.0.0.1:3050`; neither backend ports nor the inner
447
+ listener should be externally forwarded. Example for `/base`:
448
+
449
+ ```nginx
450
+ # In http {}:
451
+ map $http_upgrade $ours_connection { default upgrade; '' close; }
452
+ server {
453
+ listen 443 ssl;
454
+ server_name ours.example;
455
+ ssl_certificate /private/tls/fullchain.pem;
456
+ ssl_certificate_key /private/tls/key.pem;
457
+ auth_basic "ours operators";
458
+ auth_basic_user_file /private/ours-users;
459
+ access_log off;
460
+ proxy_set_header Host $http_host;
461
+ proxy_set_header Upgrade $http_upgrade;
462
+ proxy_set_header Connection $ours_connection;
463
+ proxy_http_version 1.1;
464
+ proxy_read_timeout 300s;
465
+ proxy_buffering off;
466
+ location = /base/.well-known/ours { auth_basic off; proxy_pass http://127.0.0.1:3050; }
467
+ location /base/daemon/ { auth_basic off; proxy_pass http://127.0.0.1:3050; }
468
+ location = /base/cowork/management/rpc { auth_basic off; proxy_pass http://127.0.0.1:3050; }
469
+ location /base/tg-connector/ { auth_basic off; proxy_pass http://127.0.0.1:3050; }
470
+ location /base/ { proxy_pass http://127.0.0.1:3050; }
471
+ location / { return 404; }
472
+ }
473
+ ```
474
+
475
+ Browser requests include same-origin proxy credentials. Fleet/SDK use the issued
476
+ `X-Ours-Api-Token`, require no browser login, and reject redirects. An SSH tunnel
477
+ to the loopback gateway is another private transport option, but every client must
478
+ establish its own tunnel and use a URL/origin matching that tunnel; it is not
479
+ automatic remote access. Do not treat a token entered in Cowork as Messenger auth.
480
+
481
+ Publish compatible Cowork, Messenger, Telegram and Fleet artifacts before enabling
482
+ gateway defaults in a qualified installer release. Gateway client setup acquires
483
+ and checks Fleet before activating the managed profile or publishing native
484
+ commands. Migration also checks the installed `ours-fleet version --json`
485
+ capability before stopping services when the selected managed profile includes
486
+ Fleet. Upgrade that executable first. The gate does not inventory differently pinned
487
+ running Fleet instances or remote clients: upgrade every client using this server
488
+ to a compatible Fleet release before cutover.
489
+
490
+ ### Cross-repository gateway qualification
491
+
492
+ Build Cowork, Fleet, Messenger and Telegram checkouts with their locked dependencies.
493
+ The real-service test uses disposable local daemon state, a local test broker,
494
+ nginx Docker containers and Chromium; it never uses installed identities or tokens.
495
+ It requires Linux Docker host networking and `openssl`. Set:
496
+
497
+ ```sh
498
+ OURS_TEST_GATEWAY_SERVICES=1 \
499
+ OURS_TEST_COWORK_ROOT=/checkouts/ours-cowork \
500
+ OURS_TEST_FLEET_ROOT=/checkouts/ours-fleet \
501
+ OURS_TEST_MESSENGER_ROOT=/checkouts/ours-messenger-server \
502
+ OURS_TEST_TELEGRAM_ROOT=/checkouts/ours-tg-connector \
503
+ OURS_TEST_BROWSER=/opt/google/chrome/chrome \
504
+ node --test packages/installer/test/gateway-services.test.mjs
505
+ ```
506
+
507
+ Run `OURS_TEST_DOCKER=1 node --test packages/installer/test/gateway-docker.test.mjs`
508
+ separately for Compose port isolation with a client container. That routing test
509
+ uses echo upstreams; the real-service test covers Fleet room operations, browser
510
+ paths, external authentication and Messenger WebSocket access. Neither test is
511
+ cross-platform Docker Desktop qualification or a live deployment test.
512
+
513
+ ### Rootless Podman (qualification in progress)
514
+
515
+ Container installations can select a local Linux rootless backend explicitly:
516
+
517
+ ```sh
518
+ export PODMAN_COMPOSE_PROVIDER=/absolute/path/to/docker-compose
519
+ ours-install server install --mode docker --container-engine podman \
520
+ --state-dir "$HOME/.ours-server-podman" --identity-name "Your Name"
521
+ ```
522
+
523
+ `mode: docker` remains the legacy container-layout name. `containerEngine` and
524
+ `containerBinding` retain Podman, the invoking UID, native storage paths, socket
525
+ and standalone Compose provider, plus the UID/GID mapping fingerprint. Records without `containerEngine` continue to
526
+ use Docker. Package/client installs reject the engine option. Existing records
527
+ cannot change engines through a flag or ambient connection change; automatic
528
+ Docker-to-Podman data migration is not provided. Legacy local-daemon migration
529
+ (`--migrate-from`) to Podman is also unsupported and is refused before source
530
+ shutdown or managed CLI publication, including migration retries. Use a fresh
531
+ Podman installation, or migrate the legacy daemon with Docker/packages.
532
+
533
+ Podman uses its native build command, with the same resolved Compose build
534
+ context, target, architecture, arguments and secret references, followed by
535
+ Compose `--no-build` startup. It does not require the Docker CLI or daemon.
536
+ The selected standalone Docker Compose provider must be version 2.35 or newer;
537
+ version checks do not replace the behavioral capability probe. `podman-compose`
538
+ is not an interchangeable provider for this implementation. Secrets sourced
539
+ from environment variables use Podman's native build-secret mechanism, never
540
+ build arguments or temporary copies.
541
+
542
+ Prerequisites include a working rootless API socket, sufficient subordinate
543
+ UID/GID mappings, and native volume-subpath isolation. Before setup/start, an
544
+ isolated disposable project tests Compose `!reset`, health waiting, JSON status,
545
+ service DNS, private UID-1000 storage and subpath isolation. A failed check stops
546
+ installation rather than substituting the whole volume. Existing volumes must
547
+ have this installation's project and volume labels. Docker contexts, unrelated
548
+ containers and host directory ownership are not changed.
549
+
550
+ Server boot recovery requires an enabled user `podman.socket`, an enabled
551
+ `podman-restart.service` using `--filter should-start-on-boot=true`, and user
552
+ linger. The standard user-service environment/default socket must select the
553
+ same storage; custom XDG paths and sockets are rejected for server startup.
554
+ The installer diagnoses missing prerequisites and never runs hidden sudo or
555
+ rewrites the vendor unit. Long-running Podman services get `unless-stopped`;
556
+ administration jobs do not. Stop/status remain available when boot prerequisites
557
+ are missing, provided the retained backend is accessible. Runtime-only test
558
+ harnesses may select a separate local socket with `OURS_PODMAN_SOCKET`; its
559
+ storage must match native Podman and the selection cannot change afterward.
560
+
561
+ Gateway images read and validate IPv4/IPv6 nameservers from their container at
562
+ startup. Only the resolver placeholder is replaced; nginx variables and existing
563
+ authentication/routes remain intact. UID 101, read-only rootfs, dropped
564
+ capabilities and tmpfs are retained. Setup and start verify authenticated
565
+ upstream requests in addition to liveness.
566
+
567
+ This change is **not yet a fully qualified server-support release**. Debian's
568
+ Podman 5.4.2 with Compose 5.5.1 failed subpath isolation and has incompatible boot
569
+ recovery semantics. Podman 6.1.2 passed the isolated capability probe on native
570
+ 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,
571
+ SELinux Enforcing, logout/reboot, explicit-stop recovery, and Docker regression
572
+ matrix must all be recorded before declaring a supported engine/provider pair.
573
+ The supplied ARM VM results are historical feasibility evidence, not this
574
+ release's qualification.
575
+
576
+ Opt-in gateway fixture (isolated project, only owned resources removed):
577
+
578
+ ```sh
579
+ OURS_TEST_DOCKER=1 node --test test/gateway-docker.test.mjs
580
+ PODMAN_COMPOSE_PROVIDER=/absolute/path/to/docker-compose \
581
+ OURS_TEST_PODMAN=1 node --test test/gateway-docker.test.mjs
582
+ ```
583
+
584
+ ## Account workspace enrollment (task build)
585
+
586
+ The unmerged account onboarding feature requires matching reviewed installer,
587
+ Fleet and Messenger task builds. Do not advertise it as available in `@latest`.
588
+ Use an owned private directory and a 0600 downloaded payload:
589
+
590
+ ```sh
591
+ node /absolute/task-ours-network/packages/installer/install.mjs \
592
+ --setup-workspace-file /absolute/private/workspace.payload \
593
+ --workspace-fleet-bin /absolute/task-ours-fleet/dist/cli.js \
594
+ --sources /absolute/private/task-sources.json
595
+ ```
596
+
597
+ The wrapper defaults fresh hosts to the full Docker stack and retains existing
598
+ Human roots through the installed SDK with exact instance/private credential
599
+ selection. For an existing supported gateway add `--scope client --config` with
600
+ its private profile. It clears competing inherited daemon selectors before Fleet
601
+ initialization and enrollment; it never copies a browser device bearer to the
602
+ account server. A private stdin payload is also accepted by `--setup-workspace -`.
603
+ Encoding is not encryption; command arguments can enter history/process listings.
604
+ Cloudflare management tokens are forbidden in these payloads.
605
+
606
+ Docker and `cloudflared` are required for the fresh workflow. Supply a complete
607
+ `--fleet-settings` JSON for noninteractive Fleet initialization, or answer its
608
+ terminal prompts. Dry-run never initializes or enrolls Fleet. Read the matching
609
+ ours-app `server/DEPLOYMENT.md` for exact task source-policy generation, external
610
+ Google/Cloudflare prerequisites and account-server setup. On uncertain enrollment
611
+ check account status before requesting a new payload; do not reset identities or
612
+ replay a consumed invitation.
613
+
614
+ Workspace setup accepts exactly https://app.ours.network and the controlled test
615
+ account origin https://app.ours-tunnel.com. To move an existing same workspace to
616
+ the other origin, obtain a fresh payload from the moved account server and add
617
+ `--workspace-migrate-app-origin`. Fleet verifies the signed binding receipt before
618
+ changing local origin trust, preserves identities/config/device records, and
619
+ restarts the runtime. An implicit origin change is rejected. Browser account
620
+ cookies/device IndexedDB must be linked anew on the new origin. See the app
621
+ server/DEPLOYMENT.md switching procedure.
622
+
623
+ 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.
624
+
625
+ ### Private-context Docker script permissions
626
+
627
+ 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.
628
+
629
+ 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.
630
+
631
+ Fresh server setup checks its loopback daemon, Cowork and Messenger ports before
632
+ writing the installation configuration. Occupied implicit defaults advance to a
633
+ free port, skipping reserved sibling ports; the selected values are saved in the
634
+ installation record and used by native/container services, gateway and client
635
+ profile generation. An explicitly requested occupied port fails with a diagnostic.
636
+ Existing installation records keep their ports and identities. A listener acquired
637
+ by another process after preflight still causes startup to fail safely; no foreign
638
+ process is stopped and no retained installation is silently moved.
639
+
640
+ Workspace setup pins Fleet `1.2.0-nightly.52` 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, v2 positional one-use grants and automatic QR/connection-code output from `ours-fleet setup-tunnel`, optional private file/stdin inputs for legacy v1 payloads, a separate `ours-fleet link-device` command for additional devices, 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, accepts the App's device credential on the notification presence socket so an open App receives no push, and defaults agent wakes to `monitor.interrupt: after_tool` (Hermes: no interrupt; role snapshots saved before this default keep no interrupt). Existing installation identities and configured ports remain retained; a retained-port collision fails safely.
641
+
642
+ ### App tunnel setup prerelease sequencing
643
+
644
+ The simplified App flow shows one copyable `ours-fleet setup-tunnel <encoded-v2-grant>`
645
+ command for the installed host, including an SSH session on a VPS. Its argument
646
+ contains only the existing v2 single-use grant (`version`, `appOrigin`, `code`,
647
+ `expiresAt`), never a connector token or broad provider credential. The command can
648
+ appear in shell history and process listings: keep it private and remove it from
649
+ history after use. Base64 is encoding, not encryption. Server-side expiry and
650
+ one-time redemption remain enforced. Fleet checks prerequisites before HTTPS POST
651
+ redemption; it never prints the grant or puts it in a URL, and retains existing
652
+ signed-root binding and scoped connector token-file controls. It prints the QR and
653
+ connection code automatically; the App checks readiness and opens the code field.
654
+ Private `--file`/`--stdin` transports remain optional and are the only transports
655
+ for legacy v1 payloads with long-lived credentials.
656
+
657
+ The nightly installer release manifest selects published Fleet `1.2.0-nightly.52`
658
+ and its actual registry integrity through the normal release manifest/source-policy
659
+ generators. This release includes v2 argument and private-input support. The former
660
+ nightly.41 selection predates this capability. Merge and publish the repinned
661
+ installer before deploying the App; the pin change alone is not an installer
662
+ publication. Existing v1 private-file enrollment remains compatible. The legacy
663
+ App wrapper refuses an older Fleet before reading or redeeming its grant.
664
+
665
+ The notification gateway permits daemon-authenticated producer selection only on
666
+ POST `/api/v1/send` and `/api/v1/delete-target`. The latter retires a removed
667
+ conversation using the credential's fixed user/source scope. Browser inbox
668
+ `/api/v1/delete` continues to require the user credential. The notification
669
+ service must support the scoped lifecycle route; this change does not repin or
670
+ publish an installer release.
671
+
672
+ ## Boot and failure recovery
673
+
674
+ Docker long-running daemon, Telegram, Cowork, Messenger/Notifications and gateway
675
+ containers use `unless-stopped`. They resume when the selected Docker Engine
676
+ starts, and Docker restarts a failed container with its built-in backoff. A manual
677
+ `docker stop`, `ours-install server stop`, or uninstall stays stopped across
678
+ Engine restarts. Administrative/maintenance jobs have no automatic restart.
679
+ Docker health status alone does not restart a container.
680
+
681
+ The four application containers also supervise their locked entrypoints. After
682
+ 90 seconds of startup grace, probes run every 5 seconds with a 15-second timeout.
683
+ Three consecutive local health failures or a child exit stop its entire process
684
+ group (TERM, then KILL within 20 seconds) before restarting. Cowork's detached
685
+ workers carry an explicit supervisor-PID marker; recovery also kills those
686
+ owned groups and waits for them to stop before launching a replacement. Each container start
687
+ allows three automatic child recovery attempts. Exhaustion leaves the container
688
+ running but unhealthy, with its children stopped and a diagnostic in its logs.
689
+ Correct the cause and use `ours-install server restart --state-dir PATH` to
690
+ reset that circuit; `server start` alone does not reset a running circuit holder.
691
+ Consumers wait for authenticated readiness of their pinned daemon instance and
692
+ do not spend their recovery budget on a daemon outage. The gateway uses Docker
693
+ process recovery only; a gateway hang requires operator restart.
694
+
695
+ Re-run `server install` to deliver recovery to an existing installation without
696
+ changing its retained exact packages, identity, credentials, volumes or config.
697
+ This explicit install starts its selected services, as before. `server update`
698
+ with reviewed storage compatibility delivers the new selected release and
699
+ restores only previously running services. Retained runtime scripts are refreshed
700
+ and rebuilt on install; an interrupted refresh retains a rebuild marker. The
701
+ restart policy overlay also covers retained Compose files. Reconciliation updates
702
+ only this project's recorded long-running containers and does not start or
703
+ recreate a deliberately stopped container. `server start` can reconcile policies
704
+ but does not retrofit health supervision into an old image: run install/update.
705
+ Backup, migration and update still stop writers before maintenance, and a failed
706
+ maintenance operation keeps them stopped. `server uninstall` (also `ours-uninstall --state-dir PATH` for a managed
707
+ schema-2 installation) stops and removes managed services, retaining volumes,
708
+ identity and credentials. It refuses unfinished transitions and data purge.
709
+ Recovery introduces no host watchdog or service.
710
+
711
+ Host boot is a separate prerequisite. On Linux, enable the selected Docker
712
+ Engine system service; rootless Docker additionally needs its user service and
713
+ user linger. The installer reports read-only boot diagnostics and does not enable
714
+ or restart host services. A reachable remote Engine or Docker Desktop context
715
+ cannot prove boot recovery on this machine. A custom Unix socket also has an
716
+ unverified boot-service binding; a host unit cannot establish its startup. On macOS/Windows with WSL, enable Docker
717
+ Desktop startup at sign-in; this does not provide pre-login startup. Rootless
718
+ Podman's existing enabled socket/restart-service and linger preflight remains.
719
+ Native package mode retains its package-owned systemd user/launchd services:
720
+ Linux needs user linger for pre-login startup, macOS needs a GUI login session,
721
+ and native hung-process recovery is not supplied by this Docker supervisor.
722
+ Fleet stays on the host with Fleet-owned lifecycle and crash protection. Installing
723
+ the exact Fleet nightly binary does not upgrade or restart an existing supervisor.
724
+ The published no-policy core delivery is a verified recovery omission that
725
+ reproduces the reported symptom. The original incident cause remains unconfirmed:
726
+ actual host Engine availability, installed policies and pre-reboot stopped state
727
+ still require Owner confirmation.