@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,210 @@
1
+ # Rootless Podman integration validation
2
+
3
+ Status: implementation under review; **not a qualified server-support release**.
4
+ Validation date: 2026-09-25. No production deployment, migration, restart or merge.
5
+
6
+ ## Scope and source baseline
7
+
8
+ The supplied `rootless-podman-support.zip` was inspected as design/evidence,
9
+ not executable authority. SHA-256:
10
+ `0eafc865566be71bcb87d432a98384da47e6d153e4395d9cfe721b4def168e96`.
11
+ It contains six substantive Markdown documents and no implementation patches.
12
+ Its historical ARM VM observations do not establish current Linux amd64 support.
13
+
14
+ All referenced repositories were fetched into isolated checkouts at these
15
+ `prerelease` commits. SDK, CLI and daemon share the SDK repository; the MCP
16
+ repository also supplies Codex and Claude Code integrations.
17
+
18
+ | Repository | Commit |
19
+ | --- | --- |
20
+ | ours-network | a9ae420d2878a304911e94d53d0cdd2cff06f299 |
21
+ | ours-sdk | 737e1028cabadcbb8837548923c7a92b49e9e575 |
22
+ | ours-cowork | 2d2ff8979756678068b8f6b85c619f6d811010ff |
23
+ | ours-messenger-server | 5e2ca2be5789a5300fcbed08a0dd26b9b59780ef |
24
+ | ours-tg-connector | 21f4bf92b52e1d61261987f298bcc76248cddb78 |
25
+ | ours-fleet | 717f6d8a09e6173b9b8a017fc5efb08d71927524 |
26
+ | ours-mcp | ce6e3e0298b83aec0257c4ae3773d785d8b38889 |
27
+
28
+ Runtime launch/build/maintenance and gateway packaging belong to ours-network;
29
+ no source changes were required in the other repositories. Host Fleet and harness
30
+ integrations continue using their existing client paths. Their unrelated feature
31
+ suites are not implied by the installer results below.
32
+
33
+ ## Corrections established by testing
34
+
35
+ - This prerelease already checks authenticated gateway readiness during install.
36
+ The added lifecycle check covers full starts and preserves intentionally stopped
37
+ applications during partial runtime restoration.
38
+ - Podman 5.4.2 with Compose 5.5.1 exposes the whole named volume instead of the
39
+ requested subpath. Its vendor restart unit also lacks `should-start-on-boot`.
40
+ The new preflight rejects this combination rather than weakening isolation.
41
+ - Local and API Podman must select the same rootless store. Compose is passed an
42
+ explicit Unix socket; native builds use the retained root/runroot/driver.
43
+ Both report the same retained UID/GID mapping fingerprint.
44
+ - Podman image IDs need Docker-compatible `sha256:` normalization at the adapter
45
+ boundary. Missing-object detection must not reinterpret inspect error 125 as
46
+ permission to create or replace an object.
47
+ - Compose excludes maintenance profiles from ordinary configuration output and
48
+ implicitly builds absent administration images. Native build resolution enables
49
+ all profiles and builds the selected missing administration image first.
50
+ - Fresh schema-2 installations omit embedded `networkMcp`; the shared rebuild
51
+ validator previously required it. Validation now accepts absence and still
52
+ rejects malformed or conflicting embedded configuration, while retaining
53
+ standalone-profile, credential, provenance and strict legacy conversion checks.
54
+ - Gateway COPY destinations must remain accessible to UID 101 on both engines.
55
+ Runtime DNS comes from validated container nameservers, retaining nginx variables.
56
+
57
+ ## Observed environment and results
58
+
59
+ Native Debian 13 Linux amd64, unprivileged UID 1000, subordinate UID/GID ranges,
60
+ SELinux disabled, standalone Docker Compose 5.5.1. Podman 6.1.2 was built from
61
+ upstream tag commit `04f3aa430e6df81bea059978bc5bafbc846ba3e7` with systemd and
62
+ seccomp support, using netavark/aardvark-dns 2.1.0. A separate task store and API
63
+ socket isolated all Podman tests from the installed 5.4.2 default backend.
64
+
65
+ - Behavioral preflight: PASS on 6.1.2, including UID-1000 private storage,
66
+ volume subpath isolation, DNS, Compose reset/wait/JSON status.
67
+ - Native synthetic secret/cache build: PASS; secret absent from command arguments,
68
+ build output, image inspect/history and final filesystem.
69
+ - Production runtime: native build of the shipped exact package/integrity policy,
70
+ runtime metadata and gateway artifact capability qualification PASS.
71
+ - Development runtime: native build from all exact prerelease commits above,
72
+ including private SDK/submodule acquisition through an environment build secret,
73
+ native compilation and gateway capability qualification PASS. The source-built
74
+ five-service lifecycle and authenticated gateway also PASS.
75
+ - Real runtime: prepare, access-init/access-issue, test Human identity, all five
76
+ services, authenticated gateway readiness, stop/status/start PASS.
77
+ - Maintenance: server backup/restore and Cowork backup/reset/restore PASS; all
78
+ services subsequently restarted with authenticated gateway readiness.
79
+ - Rebuild: the initial fresh-layout validator defect was reproduced and fixed;
80
+ retained transition recovery PASS after rebuilding the maintenance helper. A
81
+ second complete rebuild using final code and the real installation lock PASS,
82
+ including offline state validation and authenticated gateway readiness.
83
+ - Gateway runtime: Docker 2/2 and Podman 2/2 PASS for root/nested paths,
84
+ authentication, WebSockets and changed backend IPs. Combined Podman native
85
+ capability/build-secret and gateway tests: 4/4 PASS.
86
+ - Installer suite: 756 passed, 0 failed, 11 opt-in tests skipped; subsequent
87
+ adapter-only addition separately passed (13 adapter tests).
88
+ - Independent Critic reruns confirmed the behavioral/native-secret tests and
89
+ adapter regressions. Final review is recorded separately from these observations.
90
+
91
+ The runtime harness called actual installer effects against owned resources.
92
+ It deliberately did not claim a complete public install: the isolated custom
93
+ store/socket cannot satisfy standard vendor boot-unit selection, and the public
94
+ preflight rejects that configuration. A harness backup label collision was
95
+ correctly rejected; separate per-domain labels were used afterward.
96
+
97
+ ## Reproduction and remaining release gates
98
+
99
+ From `packages/installer`, with installed package dependencies:
100
+
101
+ ```sh
102
+ umask 022
103
+ node --test
104
+ OURS_TEST_DOCKER=1 node --test test/gateway-docker.test.mjs
105
+ PODMAN_COMPOSE_PROVIDER=/absolute/path/to/docker-compose \
106
+ OURS_TEST_PODMAN=1 node --test test/podman-integration.test.mjs test/gateway-docker.test.mjs
107
+ ```
108
+
109
+ The gateway fixture covers root and nested base paths, discovery, service routing,
110
+ forged-host denial, Telegram authentication, WebSocket upgrade and backend IP
111
+ replacement without restarting nginx. Test projects and their resources are
112
+ uniquely named and cleaned up. The Podman tests require a functional matching API
113
+ socket; `OURS_PODMAN_SOCKET` can select an isolated harness socket.
114
+
115
+ Before advertising a supported engine/provider pair, run the full public installer
116
+ and update/rebuild/legacy-conversion matrix on a disposable native Linux amd64
117
+ host with SELinux Enforcing, standard vendor user units, logout/linger, reboot,
118
+ unexpected exit and explicit-stop behavior. Include Docker parity for the full
119
+ maintenance matrix. This shared host is not a safe reboot/SELinux test target.
120
+ No fallback to privileged containers, whole-volume service mounts, host chown,
121
+ keep-id, or globally disabled SELinux is permitted.
122
+
123
+ ### Disposable VM handoff
124
+
125
+ Provision a dedicated x86_64 VM (at least 4 vCPU, 8 GiB RAM, 40 GiB free disk)
126
+ whose distribution supplies Podman 6.1.2, compatible netavark/aardvark, subordinate
127
+ UID/GID support and vendor `podman-restart.service` with
128
+ `--filter should-start-on-boot=true`. Use an unprivileged test account, cgroup v2,
129
+ standard HOME/XDG paths, SELinux **Enforcing**, Node.js 22+, npm, Python 3.9+
130
+ with `os.pidfd_open`/`signal.pidfd_send_signal` support, and standalone
131
+ Docker Compose 5.5.1. The Podman pass must have no Docker CLI/daemon dependency.
132
+ The VM owner must explicitly authorize reboots and terminate all sessions for the
133
+ logout test. These commands are for that disposable VM, not the shared host.
134
+
135
+ After checking out this PR and preparing installer assets/dependencies, run as the
136
+ test user (administrator provisions subordinate ranges and linger beforehand):
137
+
138
+ ```sh
139
+ uname -m # x86_64
140
+ getenforce # Enforcing
141
+ podman version # native client 6.1.2
142
+ export PODMAN_COMPOSE_PROVIDER=/absolute/path/to/docker-compose
143
+ unset OURS_PODMAN_SOCKET DOCKER_HOST DOCKER_CONTEXT CONTAINER_HOST CONTAINER_CONNECTION
144
+ unset CONTAINERS_CONF CONTAINERS_STORAGE_CONF PODMAN_USERNS
145
+ systemctl --user enable --now podman.socket
146
+ systemctl --user enable podman-restart.service
147
+ systemctl --user show podman-restart.service --property=ExecStart
148
+ loginctl show-user "$(id -u)" --property=Linger # yes
149
+ export PODMAN_TEST_ROOT="$HOME/ours-podman-qualification"
150
+ node install.mjs server install --mode docker --container-engine podman \
151
+ --state-dir "$PODMAN_TEST_ROOT" --identity-name RootlessQualification
152
+ node install.mjs server status --state-dir "$PODMAN_TEST_ROOT"
153
+ node install.mjs server backup server before-rebuild --state-dir "$PODMAN_TEST_ROOT"
154
+ node install.mjs server rebuild --state-dir "$PODMAN_TEST_ROOT"
155
+ node install.mjs server restore server before-rebuild --state-dir "$PODMAN_TEST_ROOT"
156
+ ```
157
+
158
+ Record all five healthy services and authenticated gateway requests. From an
159
+ external controller, end **all** test-user sessions, wait, reconnect, and repeat
160
+ status/authenticated checks. Then reboot the VM and repeat the same checks before
161
+ running any explicit start command. Capture relevant user-unit journal and
162
+ SELinux AVC evidence; no denial should be bypassed by changing enforcement.
163
+
164
+ Next explicitly stop the installation, verify empty running status, reboot, and
165
+ verify it remains stopped. Only then start explicitly and verify all services.
166
+ For unexpected-exit recovery, select the exact project from its retained record
167
+ and terminate only its verified container-init host process through a PID handle,
168
+ then verify automatic recovery and retained identity/credentials. Do not use
169
+ `podman kill` for this case: an explicit engine stop suppresses restart policy.
170
+ The following refuses a PID whose cgroup does not identify the selected container:
171
+
172
+ ```sh
173
+ node install.mjs server stop --state-dir "$PODMAN_TEST_ROOT"
174
+ node install.mjs server status --state-dir "$PODMAN_TEST_ROOT" # no running services
175
+ # Disposable-VM controller reboots here; reconnect and repeat status before start.
176
+ node install.mjs server start --state-dir "$PODMAN_TEST_ROOT"
177
+ PODMAN_TEST_PROJECT=$(node -e 'console.log(require(process.argv[1]).project)' "$PODMAN_TEST_ROOT/installation.json")
178
+ podman inspect --format '{{.State.StartedAt}} {{.RestartCount}}' "${PODMAN_TEST_PROJECT}-daemon-1"
179
+ python3 - "$PODMAN_TEST_PROJECT" <<'PYTHON'
180
+ import json, subprocess, sys
181
+ project = sys.argv[1]
182
+ container = json.loads(subprocess.check_output(['podman', 'inspect', project+'-daemon-1']))[0]
183
+ assert container['Config']['Labels']['com.docker.compose.project'] == project
184
+ assert container['State']['Running'] and container['State']['Pid'] > 1
185
+ script = """
186
+ import os, signal, sys
187
+ pid, cid = int(sys.argv[1]), sys.argv[2]
188
+ fd = os.pidfd_open(pid)
189
+ try:
190
+ assert cid in open('/proc/'+str(pid)+'/cgroup').read(), 'Container identity mismatch'
191
+ assert os.stat('/proc/'+str(pid)).st_uid == 1000, 'Unexpected runtime owner'
192
+ signal.pidfd_send_signal(fd, signal.SIGKILL)
193
+ finally:
194
+ os.close(fd)
195
+ """
196
+ subprocess.run(['podman','unshare','python3','-c',script,str(container['State']['Pid']),container['Id']],check=True)
197
+ PYTHON
198
+ # Wait for recovery; assert changed StartedAt, increased RestartCount and health.
199
+ podman inspect --format '{{.State.StartedAt}} {{.RestartCount}} {{.State.Health.Status}}' "${PODMAN_TEST_PROJECT}-daemon-1"
200
+ # Verify retained identities and authenticated gateway as well.
201
+ node install.mjs server status --state-dir "$PODMAN_TEST_ROOT"
202
+ ```
203
+
204
+ Still required: repeat component backup/reset/restore for daemon, Telegram,
205
+ Cowork and messenger; source update and interrupted retry; fresh schema-1 legacy
206
+ conversion fixtures; foreign/malformed volume refusal; and the equivalent full
207
+ Docker maintenance matrix. These require owned fixture data and before/after
208
+ identity, credential, provenance and mount-boundary assertions; a command exit
209
+ code alone is insufficient. The existing unit conversion tests are not a claim
210
+ that this real-engine legacy fixture matrix has run.
package/assets/Dockerfile CHANGED
@@ -3,9 +3,11 @@ FROM node:24 AS toolchain
3
3
  RUN apt-get update && apt-get install -y --no-install-recommends build-essential python3 ca-certificates git && rm -rf /var/lib/apt/lists/*
4
4
  FROM toolchain AS build
5
5
  WORKDIR /opt/ours
6
- COPY sources.json /opt/ours/sources.json
7
- COPY scripts/build/*.mjs /build-scripts/
8
- COPY scripts/maintenance/build-context.mjs scripts/maintenance/provenance-compare.mjs scripts/maintenance/release-graph.mjs /maintenance/
6
+ # The host selection is private (0600); this non-secret image policy must be
7
+ # readable by arbitrary runtime UIDs while remaining root-owned and non-writable.
8
+ COPY --chmod=644 sources.json /opt/ours/sources.json
9
+ COPY --chmod=644 scripts/build/*.mjs /build-scripts/
10
+ COPY --chmod=644 scripts/maintenance/build-context.mjs scripts/maintenance/provenance-compare.mjs scripts/maintenance/release-graph.mjs /maintenance/
9
11
  RUN --mount=type=secret,id=github_token --mount=type=cache,id=ours-dist-2-npm,target=/root/.npm,sharing=locked node /build-scripts/build.mjs
10
12
  # Testing delivery: npm resolves transitive versions afresh; repeat builds may differ.
11
13
  # build.mjs creates package.json once from the sources.json selections.
@@ -16,20 +18,22 @@ RUN node /build-scripts/record-build.mjs
16
18
  # must be able to read it. Protected state/host copies retain private ownership.
17
19
  RUN chmod 755 /opt/ours /opt/ours/docker /opt/ours/docker/vendor && chmod 644 package.json package-lock.json dependency-tree.json build-context.json docker/vendor/*.tgz
18
20
  LABEL network.ours.build-context="1"
19
- COPY scripts/maintenance/build-context.mjs scripts/maintenance/provenance-compare.mjs scripts/maintenance/release-graph.mjs /opt/ours/maintenance/
20
- COPY scripts/runtime/runtime-common.mjs /opt/ours/docker/runtime-common.mjs
21
- COPY scripts/runtime/check-client.mjs /opt/ours/docker/check-client.mjs
22
- COPY scripts/runtime/check-start.mjs /opt/ours/docker/check-start.mjs
23
- COPY scripts/runtime/client-setup.mjs /opt/ours/docker/client-setup.mjs
24
- COPY scripts/runtime/legacy-import.mjs /opt/ours/docker/legacy-import.mjs
25
- COPY scripts/runtime/entrypoint.sh /opt/ours/docker/entrypoint.sh
26
- COPY scripts/runtime/healthcheck.mjs /opt/ours/docker/healthcheck.mjs
27
- COPY scripts/runtime/start-telegram.sh scripts/runtime/health-telegram.sh scripts/runtime/start-cowork.sh scripts/runtime/health-cowork.sh scripts/runtime/start-messenger.sh scripts/runtime/health-messenger.mjs /opt/ours/docker/
21
+ COPY --chmod=644 scripts/maintenance/build-context.mjs scripts/maintenance/provenance-compare.mjs scripts/maintenance/release-graph.mjs /opt/ours/maintenance/
22
+ COPY --chmod=644 scripts/runtime/runtime-common.mjs /opt/ours/docker/runtime-common.mjs
23
+ COPY --chmod=644 scripts/runtime/check-client.mjs /opt/ours/docker/check-client.mjs
24
+ COPY --chmod=644 scripts/runtime/check-start.mjs /opt/ours/docker/check-start.mjs
25
+ COPY --chmod=644 scripts/runtime/client-setup.mjs /opt/ours/docker/client-setup.mjs
26
+ COPY --chmod=644 scripts/runtime/diagnostics.mjs /opt/ours/docker/diagnostics.mjs
27
+ COPY --chmod=644 scripts/runtime/legacy-import.mjs /opt/ours/docker/legacy-import.mjs
28
+ COPY --chmod=644 scripts/runtime/entrypoint.sh /opt/ours/docker/entrypoint.sh
29
+ COPY --chmod=644 scripts/runtime/healthcheck.mjs /opt/ours/docker/healthcheck.mjs
30
+ COPY --chmod=644 scripts/runtime/start-telegram.sh scripts/runtime/health-telegram.sh scripts/runtime/start-cowork.sh scripts/runtime/health-cowork.sh scripts/runtime/start-messenger.sh scripts/runtime/health-messenger.mjs scripts/runtime/notifications-gateway.mjs scripts/runtime/supervise.mjs /opt/ours/docker/
31
+ RUN chmod 755 /opt/ours/maintenance /opt/ours/docker
28
32
  FROM node:24 AS maintenance
29
33
  LABEL network.ours.build-context="1"
30
34
  WORKDIR /opt/ours
31
35
  COPY --from=build /opt/ours /opt/ours
32
- COPY scripts/maintenance/*.mjs /opt/ours/docker/
36
+ COPY --chmod=644 scripts/maintenance/*.mjs /opt/ours/docker/
33
37
  COPY --chmod=644 scripts/maintenance/package.json /opt/ours/docker/package.json
34
38
  RUN --mount=type=cache,id=ours-dist-2-npm,target=/root/.npm,sharing=locked npm install --prefix /opt/ours/docker --omit=dev --ignore-scripts --no-audit --no-fund
35
39
  USER 1000:1000
@@ -0,0 +1,5 @@
1
+ FROM nginx:1.28-alpine@sha256:a8b39bd9cf0f83869a2162827a0caf6137ddf759d50a171451b335cecc87d236
2
+ COPY --chmod=644 nginx.conf /etc/nginx/ours.conf.template
3
+ COPY --chmod=644 gateway-entrypoint.sh /gateway-entrypoint.sh
4
+ USER 101:101
5
+ ENTRYPOINT ["/bin/sh", "/gateway-entrypoint.sh"]
@@ -164,8 +164,11 @@ services:
164
164
  OURS_MESSENGER_PORT: "8420"
165
165
  OURS_MESSENGER_PUBLIC_ORIGIN: "http://127.0.0.1:${OURS_MESSENGER_PORT:-8420}"
166
166
  OURS_MESSENGER_STATE_DIR: /var/lib/ours-messenger
167
+ # Started beside Messenger when the runtime ships @ours.network/notifications.
168
+ OURS_NOTIFICATIONS_CONFIG: /var/lib/ours-notifications/config.json
167
169
  volumes:
168
170
  - {type: volume, source: server-storage, target: /var/lib/ours-messenger, volume: {nocopy: true, subpath: state/messenger}}
171
+ - {type: volume, source: server-storage, target: /var/lib/ours-notifications, volume: {nocopy: true, subpath: state/notifications}}
169
172
  - {type: volume, source: server-storage, target: /credentials/messenger, read_only: true, volume: {nocopy: true, subpath: state/credentials/messenger}}
170
173
  ports:
171
174
  - {target: 8420, published: "${OURS_MESSENGER_PORT:-8420}", host_ip: 127.0.0.1}
@@ -215,7 +218,7 @@ services:
215
218
  OURS_STATE_ROOT: /storage
216
219
  OURS_LIVE_ROOT: "${OURS_LIVE_ROOT:-/storage/state}"
217
220
  OURS_BUILD_ROOT: /opt/ours
218
- OURS_CLI_PATH: /opt/ours/node_modules/.bin/ours
221
+ OURS_DAEMON_BIN_DIR: /opt/ours/node_modules/.bin
219
222
  OURS_DAEMON_CONFIG: /var/lib/ours/config.json
220
223
  OURS_COWORK_CLI_PATH: /opt/ours/node_modules/.bin/ours-cowork
221
224
  OURS_COWORK_CONFIG: /var/lib/ours-cowork/config.json
@@ -0,0 +1,48 @@
1
+ {
2
+ "subscriptions": [
3
+ "codex"
4
+ ],
5
+ "assignmentStrategy": "one-model",
6
+ "models": {
7
+ "development": {
8
+ "harness": "codex",
9
+ "session": "acp",
10
+ "model": "gpt-6-astra",
11
+ "efforts": [
12
+ "low",
13
+ "medium",
14
+ "high",
15
+ "xhigh",
16
+ "max",
17
+ "ultra"
18
+ ]
19
+ },
20
+ "review": {
21
+ "harness": "codex",
22
+ "session": "acp",
23
+ "model": "gpt-6-astra",
24
+ "efforts": [
25
+ "low",
26
+ "medium",
27
+ "high",
28
+ "xhigh",
29
+ "max",
30
+ "ultra"
31
+ ]
32
+ },
33
+ "coordination": {
34
+ "harness": "codex",
35
+ "session": "acp",
36
+ "model": "gpt-6-astra",
37
+ "efforts": [
38
+ "low",
39
+ "medium",
40
+ "high",
41
+ "xhigh",
42
+ "max",
43
+ "ultra"
44
+ ]
45
+ }
46
+ },
47
+ "reasoning": "balanced"
48
+ }
@@ -0,0 +1,37 @@
1
+ #!/bin/sh
2
+ set -eu
3
+ # Replace only our resolver marker. Never expand nginx's $variables.
4
+ resolvers=$(awk '
5
+ function ipv4(s, a,n,i) {
6
+ n=split(s,a,"."); if(n!=4)return 0;
7
+ for(i=1;i<=n;i++) if(a[i]!~/^[0-9]+$/ || length(a[i])>3 || a[i]+0>255 || (length(a[i])>1 && substr(a[i],1,1)=="0"))return 0;
8
+ return 1
9
+ }
10
+ function ipv6(s, a,n,i,count,copy,compressed) {
11
+ if(s!~/:/ || s~/[^0-9a-fA-F:.]/ || s~/:::/)return 0;
12
+ copy=s; compressed=gsub(/::/,"X",copy); if(compressed>1)return 0;
13
+ if(!compressed && (s~/^:/ || s~/:$/))return 0;
14
+ n=split(s,a,":"); count=0;
15
+ for(i=1;i<=n;i++) {
16
+ if(a[i]=="")continue;
17
+ if(a[i]~/\./) {if(i!=n || !ipv4(a[i]))return 0; count+=2}
18
+ else {if(a[i]!~/^[0-9a-fA-F]+$/ || length(a[i])>4)return 0; count++}
19
+ }
20
+ return compressed ? count<8 : count==8
21
+ }
22
+ $1=="nameserver" {
23
+ if(ipv4($2))address=$2;
24
+ else if(ipv6($2))address="[" $2 "]";
25
+ else next;
26
+ if(!seen[address]++) {printf "%s%s",sep,address; sep=" "}
27
+ }
28
+ ' "${OURS_RESOLV_CONF:-/etc/resolv.conf}")
29
+ if [ -z "$resolvers" ]; then echo 'Gateway: no valid runtime DNS nameserver' >&2; exit 1; fi
30
+ umask 077
31
+ config=$(mktemp /tmp/ours-nginx.XXXXXX)
32
+ trap 'rm -f "$config"' EXIT HUP INT TERM
33
+ sed "s/@@OURS_RESOLVERS@@/$resolvers/g" "${OURS_NGINX_TEMPLATE:-/etc/nginx/ours.conf.template}" > "$config"
34
+ nginx -t -c "$config"
35
+ # nginx opens the config before the shell exits; exec leaves this file in tmpfs.
36
+ trap - EXIT HUP INT TERM
37
+ exec nginx -c "$config" -g 'daemon off;'