signalk-container 1.8.0 → 1.9.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.gitattributes ADDED
@@ -0,0 +1,5 @@
1
+ # Force LF line endings on checkout regardless of the host's
2
+ # core.autocrlf setting. Prettier/eslint enforce LF; without this,
3
+ # Windows CI runners check out CRLF and `prettier --check` reports
4
+ # every line as "Delete `␍`".
5
+ * text=auto eol=lf
package/AGENTS.md CHANGED
@@ -10,7 +10,7 @@ Key components:
10
10
  - **`src/resources.ts`** — cgroup-limit flag emission + live-update path via `podman/docker update`. The "Bug D" precedent for diff-on-already-running lives here.
11
11
  - **`src/runtime.ts`** — Runtime detection (`podman` vs `docker`), version probing, `execRuntime`/`execRuntimeLong` dispatch, `isContainerized()` self-detection.
12
12
  - **`src/updates/`** — Centralized image-update detection (digest drift for floating tags, version comparison for semver). Used by all consumer plugins via `containers.updates.register(...)`.
13
- - **`public/`** — React config panel served via Module Federation into the Signal K Admin UI.
13
+ - **`src/configpanel/`** — React config panel source. Built with Vite + `@module-federation/vite` (see `vite.config.ts`); build artifacts land in `public/`, served via Module Federation into the Signal K Admin UI.
14
14
 
15
15
  ## Code Quality Principles
16
16
 
@@ -39,7 +39,7 @@ Do not add error handling, fallbacks, or validation for scenarios that cannot ha
39
39
  - All new code requires tests. Test behavior at the function boundary, not internal control flow.
40
40
  - Inject `exec: ExecFn = execRuntime` rather than calling the runtime directly. Tests stub via `fakeExec`. See `src/test/getLiveResources.test.ts` for the canonical pattern: synthetic `{stdout, stderr, exitCode}`, no real podman invocations.
41
41
  - Container-integration tests (those that actually pull `alpine:3.19`) gate on `hasContainerRuntime()` which returns `null` on Windows. Do not add new tests that pull real Linux images without the same guard.
42
- - 333 tests today across 59 suites; expect them all green on every commit.
42
+ - All tests must pass on every commit. Run `npm test` after `npm run build` (or `npm run build:all` to do both); `node --test` runs the compiled `dist/test/**/*.test.js` glob, so a fresh build is required before testing.
43
43
 
44
44
  ## Runtime Invariants
45
45
 
@@ -137,11 +137,13 @@ This repo is maintained by Dirk Wahrheit. Workflow is deliberate; AI tools shoul
137
137
 
138
138
  Before pushing or opening a PR:
139
139
 
140
- 1. `npm run format` — prettier write + eslint --fix
141
- 2. `npm run build:all` — `clean && tsc && webpack && test`. All 333 tests must pass.
142
- 3. `npm run ci-lint` — `eslint && prettier --check` (the strict-no-write variant CI runs)
140
+ 1. `npm run format` — `prettier --write .` + `eslint --fix` (writes back fixes)
141
+ 2. `npm run build:all` — `build && test`, where `build` = `clean && build:server (tsc) && build:configpanel (vite build)`. All tests must pass.
142
+ 3. `npm run ci-lint` — `eslint && prettier --check .` — read-only verification of step 1's output; this is what CI runs and what catches uncommitted format/lint drift.
143
143
  4. `cr review --plain | tee /tmp/cr-review-<branch>.txt` — local CodeRabbit pass. `cr` only sees committed changes, so commit first, then review. The CLI is rate-limited (~50min cooldown); pipe to a file so reruns aren't needed.
144
144
 
145
+ For iterative server-side work, `npm run watch` runs `tsc --watch`. It does **not** rebuild the configpanel and does **not** clean stale `dist/test/` — run a full `npm run build` before invoking `npm test`.
146
+
145
147
  Only push after all four pass. **Never push without explicit approval.** `git push` always needs its own permission — commit/test/format/cr approval does not cover push.
146
148
 
147
149
  ### Release flow
package/README.md CHANGED
@@ -20,7 +20,10 @@ Instead of each plugin implementing its own container orchestration, they delega
20
20
  - **SELinux support** -- `:Z` volume flags for Podman bind mounts on Fedora/RHEL; named volumes are handled correctly (`:Z` is not applied)
21
21
  - **Per-volume host-source policy** -- volumes accept `{ source, ifMissing: "skip" | "abort" }` for user-managed (USB drives, NFS) or deployment-required (TLS certs) mounts. Plugins subscribe to `onVolumeIssue` events for `'skipped'`, `'aborted'`, and `'recovered'` actions; signalk-container auto-recreates the container when a previously-missing source reappears. See the [developer guide](doc/plugin-developer-guide.md#optional-and-required-volumes).
22
22
  - **Container log streaming** -- click **Logs** on any managed-container card to open a live-streaming popup of the container's stdout+stderr (combined, the same shape `podman logs <name>` produces). Plugin authors can also wire `onContainerLog` in `ensureRunning` options to forward the same stream into their plugin's `app.debug` channel — visible in the Signal K server log when debug is enabled. Multiple subscribers share a single underlying tail process. See the [developer guide](doc/plugin-developer-guide.md#streaming-container-logs-into-your-plugins-debug-channel).
23
+ - **Host-UID ownership alignment** -- managed containers run by default under the Signal K host user's UID/GID (via `--user host:host` on Docker/rootful Podman, `--userns=keep-id` on rootless Podman). Files created on bind mounts are owned by the same identity that runs Signal K, with no `chmod` sweeps. Override per container via `ContainerConfig.user` for images with a non-root `USER` directive, or `user: false` to opt out. See the [developer guide](doc/plugin-developer-guide.md#host-uid-ownership).
24
+ - **Image compliance probes** -- `containers.doctor.imageRunsAsUser(image, user?)` runs the image under the live UID mapping and verifies it can write `/tmp` as the host caller. Surfaces UID-compatibility problems _before_ a container wedges in a restart loop. See the [developer guide](doc/plugin-developer-guide.md#containersdoctorimagerunsasuserimage-user-promiseimageproberesult).
23
25
  - **Podman image qualification** -- automatically prefixes `docker.io/` for short image names
26
+ - **Docker `host.containers.internal` parity** -- signalk-container adds the `host-gateway` mapping for Docker automatically (Podman has it natively). User-supplied `extraHosts` overrides are respected.
24
27
  - **Cross-plugin API** -- other plugins use `globalThis.__signalk_containerManager`
25
28
 
26
29
  ## Config Panel
@@ -114,58 +117,64 @@ See [doc/plugin-developer-guide.md](doc/plugin-developer-guide.md) for the full
114
117
 
115
118
  ## API
116
119
 
117
- | Method | Description |
118
- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
119
- | `getRuntime()` | Returns `{ runtime, version, isPodmanDockerShim }` or `null` |
120
- | `whenReady()` | Resolves once runtime detection settles (success OR failure). Replaces the polling-loop pattern; check `getRuntime()` after the await |
121
- | `pullImage(image, onProgress?)` | Pull a container image (auto-qualifies for Podman) |
122
- | `imageExists(image)` | Check if image exists locally |
123
- | `getImageDigest(imageOrContainer)` | Local image ID (sha256) for an image:tag or container |
124
- | `ensureRunning(name, config, options?)` | Create and start container if not running; auto-recreates on config drift across `image`, `tag`, `command`, `networkMode`, `env`, `volumes`, `ports` |
125
- | `start(name)` | Start a stopped container |
126
- | `stop(name)` | Stop a running container |
127
- | `remove(name)` | Stop and remove a container |
128
- | `getState(name)` | Returns `running`, `stopped`, `missing`, or `no-runtime` |
129
- | `runJob(config)` | Execute a one-shot container job |
130
- | `getLogs(name, options?)` | One-shot fetch of the last N lines of a container's combined stdout+stderr log. `tail` defaults to 200, max 10000; `since` is unix-epoch seconds |
131
- | `prune()` | Remove dangling images |
132
- | `listContainers()` | List all `sk-` prefixed containers |
133
- | `execInContainer(name, command)` | Run a command inside a running container |
134
- | `ensureNetwork(name)` | Create a Podman/Docker network if it doesn't exist |
135
- | `removeNetwork(name)` | Remove a network |
136
- | `connectToNetwork(container, network)` | Add a container to a network (bridge mode only) |
137
- | `disconnectFromNetwork(container, net)` | Remove a container from a network |
138
- | `updates.register(reg)` | Register a container for update detection |
139
- | `updates.unregister(pluginId)` | Stop tracking updates for a plugin |
140
- | `updates.checkOne(pluginId)` | Force a fresh update check (or coalesce with in-flight) |
141
- | `updates.getLastResult(pluginId)` | Cached last result, no network |
142
- | `updateResources(name, limits)` | Apply new resource limits live, fall back to recreate |
143
- | `getResources(name)` | Currently effective limits (plugin defaults ⊕ user override) |
144
- | `resolveSignalkDataMount()` | Resolve the volume name or host path that backs `app.getDataDirPath()` in the current deployment; returns `null` if the runtime is not yet initialised |
145
- | `resolveHostPath(absPath)` | Translate an arbitrary absolute path into the `{ source, subPath }` pair the runtime needs to mount it; handles bare-metal, bind, and named-volume topologies |
146
- | `resolveContainerAddress(name, port)` | Return the `host:port` string to reach `port` on a managed container from the SignalK process; call after `ensureRunning()` with `signalkAccessiblePorts` set |
120
+ | Method | Description |
121
+ | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
122
+ | `getRuntime()` | Returns `{ runtime, version, isPodmanDockerShim }` or `null` |
123
+ | `whenReady()` | Resolves once runtime detection settles (success OR failure). Replaces the polling-loop pattern; check `getRuntime()` after the await |
124
+ | `pullImage(image, onProgress?)` | Pull a container image (auto-qualifies for Podman) |
125
+ | `imageExists(image)` | Check if image exists locally |
126
+ | `getImageDigest(imageOrContainer)` | Local image ID (sha256) for an image:tag or container |
127
+ | `ensureRunning(name, config, options?)` | Create and start container if not running; auto-recreates on config drift across `image`, `tag`, `command`, `networkMode`, `env`, `volumes`, `ports` |
128
+ | `start(name)` | Start a stopped container |
129
+ | `stop(name)` | Stop a running container |
130
+ | `remove(name)` | Stop and remove a container |
131
+ | `getState(name)` | Returns `running`, `stopped`, `missing`, or `no-runtime` |
132
+ | `runJob(config)` | Execute a one-shot container job |
133
+ | `getLogs(name, options?)` | One-shot fetch of the last N lines of a container's combined stdout+stderr log. `tail` defaults to 200, max 10000; `since` is unix-epoch seconds |
134
+ | `prune()` | Remove dangling images |
135
+ | `listContainers()` | List all `sk-` prefixed containers |
136
+ | `execInContainer(name, command)` | Run a command inside a running container |
137
+ | `ensureNetwork(name)` | Create a Podman/Docker network if it doesn't exist |
138
+ | `removeNetwork(name)` | Remove a network |
139
+ | `connectToNetwork(container, network)` | Add a container to a network (bridge mode only) |
140
+ | `disconnectFromNetwork(container, net)` | Remove a container from a network |
141
+ | `updates.register(reg)` | Register a container for update detection |
142
+ | `updates.unregister(pluginId)` | Stop tracking updates for a plugin |
143
+ | `updates.checkOne(pluginId)` | Force a fresh update check (or coalesce with in-flight) |
144
+ | `updates.getLastResult(pluginId)` | Cached last result, no network |
145
+ | `updateResources(name, limits)` | Apply new resource limits live, fall back to recreate |
146
+ | `getResources(name)` | Currently effective limits (plugin defaults ⊕ user override) |
147
+ | `resolveSignalkDataMount()` | Resolve the volume name or host path that backs `app.getDataDirPath()` in the current deployment; returns `null` if the runtime is not yet initialised |
148
+ | `resolveHostPath(absPath)` | Translate an arbitrary absolute path into the `{ source, subPath }` pair the runtime needs to mount it; handles bare-metal, bind, and named-volume topologies |
149
+ | `resolveContainerAddress(name, port)` | Return the `host:port` string to reach `port` on a managed container from the SignalK process; call after `ensureRunning()` with `signalkAccessiblePorts` set |
150
+ | `doctor.imageRunsAsUser(image, user?)` | Probe whether `image` runs cleanly under the host-UID mapping signalk-container will emit (1.8.0+). Never throws — returns `{ ok, output, error? }` |
151
+ | `doctor.selfDeployment()` | Diagnose the Signal K deployment itself: binary discovery, daemon reachability, rootless/rootful detection, and (when containerized) self-container ID. Returns `{ status, remediation, ... }` — see `SelfDeploymentResult` in `src/types.ts` |
152
+ | `doctor.generateSetupSnippet(format?, result?)` | Generate a ready-to-paste compose fragment (`format: "compose"`, default) or `podman/docker run` command (`format: "run"`) tailored to the detected runtime. Pure templating over `SelfDeploymentResult`; bundles a minimal Dockerfile sidecar and operator notes. |
147
153
 
148
154
  ## REST Endpoints
149
155
 
150
156
  All mounted at `/plugins/signalk-container/api/`:
151
157
 
152
- | Method | Path | Description |
153
- | ------ | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
154
- | GET | `/runtime` | Detected runtime info |
155
- | GET | `/containers` | List managed containers |
156
- | GET | `/containers/:name/state` | Container state |
157
- | POST | `/containers/:name/start` | Start a stopped container |
158
- | POST | `/containers/:name/stop` | Stop a running container |
159
- | POST | `/containers/:name/remove` | Stop and remove a container |
160
- | GET | `/containers/:name/logs?tail=N&since=ts` | Last N lines of the container's combined stdout+stderr log (one-shot). `tail` defaults 200, max 10000 |
161
- | GET | `/containers/:name/logs/stream` | Server-Sent Events stream of live log lines. Closes when the container is removed or the client disconnects |
162
- | POST | `/prune` | Prune dangling images |
163
- | GET | `/updates` | List last update-check results |
164
- | GET | `/updates/:pluginId` | Last update-check result for one plugin |
165
- | POST | `/updates/:pluginId/check` | Force a fresh update check (HTTP 200 even when offline) |
166
- | GET | `/containers/:name/resources` | Effective resource limits + user override |
167
- | POST | `/containers/:name/resources` | Apply new resource limits (live or recreate). Body is a `ContainerResourceLimits` diff against the consumer plugin's default. |
168
- | DELETE | `/containers/:name/resources` | Clear any user override and restore the consumer plugin's pristine default limits to the running container. |
158
+ | Method | Path | Description |
159
+ | ------ | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
160
+ | GET | `/runtime` | Detected runtime info |
161
+ | GET | `/containers` | List managed containers |
162
+ | GET | `/containers/:name/state` | Container state |
163
+ | POST | `/containers/:name/start` | Start a stopped container |
164
+ | POST | `/containers/:name/stop` | Stop a running container |
165
+ | POST | `/containers/:name/remove` | Stop and remove a container |
166
+ | GET | `/containers/:name/logs?tail=N&since=ts` | Last N lines of the container's combined stdout+stderr log (one-shot). `tail` defaults 200, max 10000 |
167
+ | GET | `/containers/:name/logs/stream` | Server-Sent Events stream of live log lines. Closes when the container is removed or the client disconnects |
168
+ | POST | `/prune` | Prune dangling images |
169
+ | GET | `/updates` | List last update-check results |
170
+ | GET | `/updates/:pluginId` | Last update-check result for one plugin |
171
+ | POST | `/updates/:pluginId/check` | Force a fresh update check (HTTP 200 even when offline) |
172
+ | GET | `/containers/:name/resources` | Effective resource limits + user override |
173
+ | POST | `/containers/:name/resources` | Apply new resource limits (live or recreate). Body is a `ContainerResourceLimits` diff against the consumer plugin's default. |
174
+ | DELETE | `/containers/:name/resources` | Clear any user override and restore the consumer plugin's pristine default limits to the running container. |
175
+ | POST | `/doctor/image` | Probe whether an image runs cleanly under the live host-UID mapping. Body: `{ image, tag?, user? }`. Never 5xx for a failed probe — `{ ok: false, error }` is a successful response (1.8.0+). |
176
+ | GET | `/doctor/deployment` | Diagnose this Signal K deployment: binary discovery, daemon reachability, rootless/rootful detection, self-container ID cascade. Returns a `SelfDeploymentResult` with `status` and copy-pasteable `remediation` lines. |
177
+ | GET | `/doctor/snippet?format=compose\|run` | Generate a ready-to-paste compose fragment or shell command for setting up Signal K with this runtime. `text/plain` by default; pass `Accept: application/json` for the structured `SetupSnippetResult`. |
169
178
 
170
179
  ## Configuration
171
180
 
@@ -443,45 +452,107 @@ this plugin needs access to the host's container runtime to manage other
443
452
  containers. The plugin auto-detects this scenario via `/.dockerenv` or
444
453
  `/run/.containerenv` and prefixes the status with `(in-container)`.
445
454
 
446
- For the plugin to work, you must expose the host's container runtime to
447
- the Signal K container:
455
+ For the plugin to work, two things must be true inside the Signal K
456
+ container:
448
457
 
449
- ### Docker (with security caveats)
458
+ 1. **The runtime CLI is installed.** Bake `podman` (or `podman-remote`)
459
+ into your Signal K image — this is the recommended path. As a quick
460
+ alternative you can bind-mount the host binary read-only, but that
461
+ couples the container to the host's exact runtime version.
462
+ 2. **The runtime socket is bind-mounted** from the host (rootless or
463
+ rootful, podman or docker — see examples below).
464
+
465
+ ### Quick check: `/api/doctor/deployment`
466
+
467
+ The plugin ships a self-diagnostic. After starting, hit:
468
+
469
+ ```bash
470
+ curl http://<signalk-host>:3000/plugins/signalk-container/api/doctor/deployment
471
+ ```
472
+
473
+ The response includes a `status` field (`ok` / `no-runtime` /
474
+ `socket-unreachable` / `permission-denied` / `self-id-unresolved`) and a
475
+ `remediation` array of copy-pasteable lines for whichever failure mode
476
+ applies. When startup detection fails, the same remediation is also
477
+ logged to the Signal K server log.
478
+
479
+ ### Generate a starter snippet
480
+
481
+ To bootstrap a new deployment, ask the plugin for a ready-to-paste
482
+ compose fragment or shell command tailored to the detected runtime:
483
+
484
+ ```bash
485
+ curl 'http://<signalk-host>:3000/plugins/signalk-container/api/doctor/snippet?format=compose' > docker-compose.yml
486
+ curl 'http://<signalk-host>:3000/plugins/signalk-container/api/doctor/snippet?format=run' > run-signalk.sh
487
+ ```
488
+
489
+ The endpoint returns plain text by default; pass
490
+ `Accept: application/json` to get the structured
491
+ `SetupSnippetResult` (snippet + Dockerfile sidecar + operator notes)
492
+ for programmatic consumers.
493
+
494
+ ### Rootless Podman (recommended)
495
+
496
+ The cleanest setup. Runs as your user, not root, so the security
497
+ exposure is limited to your user account rather than the entire host —
498
+ and matches signalk-container's default behaviour.
499
+
500
+ On the host, ensure the user-scoped podman socket is enabled:
501
+
502
+ ```bash
503
+ systemctl --user enable --now podman.socket
504
+ ```
505
+
506
+ Then in your compose / `podman run`:
450
507
 
451
508
  ```yaml
452
509
  services:
453
510
  signalk:
454
- image: signalk/signalk-server
511
+ image: your-signalk-image-with-podman-remote
512
+ user: "${UID}:${GID}" # match the uid that owns the host's podman socket
455
513
  volumes:
456
- - /var/run/docker.sock:/var/run/docker.sock
457
- - /usr/bin/docker:/usr/bin/docker:ro
514
+ - /run/user/${UID}/podman/podman.sock:/run/user/${UID}/podman/podman.sock
515
+ environment:
516
+ - CONTAINER_HOST=unix:///run/user/${UID}/podman/podman.sock
458
517
  ```
459
518
 
460
- The Signal K image must include the `docker` CLI binary, OR you mount
461
- it from the host as shown above. Containers managed by the plugin
462
- become **siblings** of the Signal K container, not nested.
519
+ Your image's Dockerfile should include `podman` or `podman-remote`:
463
520
 
464
- > [!warning]
465
- > Mounting `/var/run/docker.sock` gives the container **root-equivalent
466
- > access to the host**. Anyone who compromises Signal K (including via
467
- > a malicious plugin) can take over the entire host. Only use this if
468
- > you understand and accept the security implications.
521
+ ```dockerfile
522
+ RUN apt-get update && apt-get install -y podman # Debian/Ubuntu
523
+ # or:
524
+ RUN dnf install -y podman-remote # Fedora/RHEL
525
+ ```
469
526
 
470
- ### Podman (rootless, safer)
527
+ ### Rootful Podman
471
528
 
472
529
  ```yaml
473
530
  services:
474
531
  signalk:
475
- image: signalk/signalk-server
532
+ image: your-signalk-image-with-podman
476
533
  volumes:
477
- - $XDG_RUNTIME_DIR/podman/podman.sock:/var/run/podman.sock
478
- - /usr/bin/podman:/usr/bin/podman:ro
534
+ - /run/podman/podman.sock:/run/podman/podman.sock
479
535
  environment:
480
- - DOCKER_HOST=unix:///var/run/podman.sock
536
+ - CONTAINER_HOST=unix:///run/podman/podman.sock
537
+ ```
538
+
539
+ ### Docker
540
+
541
+ ```yaml
542
+ services:
543
+ signalk:
544
+ image: your-signalk-image-with-docker-cli
545
+ volumes:
546
+ - /var/run/docker.sock:/var/run/docker.sock
547
+ group_add:
548
+ - "<docker-gid-from-host>" # `getent group docker | cut -d: -f3`
481
549
  ```
482
550
 
483
- Rootless Podman runs as your user, not root, so the security exposure
484
- is limited to your user account rather than the entire host.
551
+ > [!warning]
552
+ > Mounting `/var/run/docker.sock` gives the container **root-equivalent
553
+ > access to the host**. Anyone who compromises Signal K (including via
554
+ > a malicious plugin) can take over the entire host. Prefer rootless
555
+ > Podman for production.
485
556
 
486
557
  ### Networking caveats
487
558
 
@@ -493,13 +564,9 @@ network namespace. This affects:
493
564
  (add it externally or via the same compose file)
494
565
  - `host.containers.internal` from spawned containers points to the host
495
566
  itself, not the Signal K container — use Signal K's container name
496
- for direct communication
497
-
498
- ### Recommended setup
499
-
500
- For the simplest experience with managed containers, run **Signal K
501
- natively on the host** rather than in a container. The plugin and its
502
- ecosystem (signalk-questdb, signalk-grafana) are designed for this case.
567
+ for direct communication. signalk-container 1.8.0+ adds this hostname
568
+ to Docker containers automatically (Podman already provides it); set
569
+ `ContainerConfig.extraHosts` to override it or to add other hostnames.
503
570
 
504
571
  ## License
505
572
 
@@ -363,6 +363,13 @@ export declare function getLiveContainerConfig(runtime: ContainerRuntimeInfo, na
363
363
  export declare function diffContainerConfig(requested: ContainerConfig, live: LiveContainerConfig, runtime: ContainerRuntimeInfo, prior?: ContainerConfig): {
364
364
  drifted: string[];
365
365
  };
366
+ /**
367
+ * Pull function injected into `ensureRunning` for testability. Production
368
+ * uses the module-level `pullImage` (which shells out via `execRuntimeLong`,
369
+ * not the injectable `ExecFn`). Tests pass a stub to assert call counts
370
+ * and simulate offline failures without touching the network.
371
+ */
372
+ type PullFn = (runtime: ContainerRuntimeInfo, image: string, onProgress?: (msg: string) => void) => Promise<void>;
366
373
  export declare function ensureRunning(runtime: ContainerRuntimeInfo, name: string, config: ContainerConfig, debug: (msg: string) => void, options?: HealthCheckOptions, exec?: ExecFn,
367
374
  /**
368
375
  * Prior `ContainerConfig` from the previous `ensureRunning` call within
@@ -372,7 +379,7 @@ export declare function ensureRunning(runtime: ContainerRuntimeInfo, name: strin
372
379
  * overwriting it; on the first call (or after a Signal K restart) this
373
380
  * will be undefined and only positive drift is detected.
374
381
  */
375
- prior?: ContainerConfig, _postRecreate?: boolean): Promise<void>;
382
+ prior?: ContainerConfig, _postRecreate?: boolean, _pull?: PullFn): Promise<void>;
376
383
  export declare function startContainer(runtime: ContainerRuntimeInfo, name: string): Promise<void>;
377
384
  export declare function stopContainer(runtime: ContainerRuntimeInfo, name: string): Promise<void>;
378
385
  export declare function removeContainer(runtime: ContainerRuntimeInfo, name: string, exec?: ExecFn): Promise<void>;
@@ -593,4 +600,5 @@ export declare function findAvailablePort(preferred: number): Promise<number>;
593
600
  */
594
601
  export declare function resolveSignalkNetworks(runtime: ContainerRuntimeInfo, debug?: (msg: string) => void): Promise<string[] | null>;
595
602
  export declare function waitForReady(url: string, timeoutMs?: number, intervalMs?: number): Promise<void>;
603
+ export {};
596
604
  //# sourceMappingURL=containers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"containers.d.ts","sourceRoot":"","sources":["../src/containers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"containers.d.ts","sourceRoot":"","sources":["../src/containers.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,8 @@ import * as net from "node:net";
2
2
  import { existsSync, readFileSync } from "node:fs";
3
3
  import { execRuntime, execRuntimeLong, isContainerized, spawnRuntimeStreaming, userMappingFlags, } from "./runtime.js";
4
4
  import { resourceFlagsForRun } from "./resources.js";
5
+ import { classifyTag } from "./updates/tagClassifier.js";
6
+ import { isOfflineError } from "./updates/offline.js";
5
7
  const CONTAINER_PREFIX = "sk-";
6
8
  function prefixedName(name) {
7
9
  return name.startsWith(CONTAINER_PREFIX)
@@ -1147,7 +1149,7 @@ export async function ensureRunning(runtime, name, config, debug, options, exec
1147
1149
  * overwriting it; on the first call (or after a Signal K restart) this
1148
1150
  * will be undefined and only positive drift is detected.
1149
1151
  */
1150
- prior, _postRecreate = false) {
1152
+ prior, _postRecreate = false, _pull = pullImage) {
1151
1153
  const state = await getContainerState(runtime, name, exec);
1152
1154
  const fullName = prefixedName(name);
1153
1155
  const imageRef = qualifyImage(config.digest
@@ -1167,7 +1169,48 @@ prior, _postRecreate = false) {
1167
1169
  return false;
1168
1170
  debug(`Container ${fullName} config drift detected (${drifted.join(", ")}); recreating`);
1169
1171
  await removeContainer(runtime, name, exec);
1170
- await ensureRunning(runtime, name, config, debug, options, exec, prior, true);
1172
+ await ensureRunning(runtime, name, config, debug, options, exec, prior, true, _pull);
1173
+ return true;
1174
+ };
1175
+ // Floating-tag digest drift: pull the tag, compare the registry-fresh
1176
+ // image-id to the running container's image-id, treat a mismatch as drift.
1177
+ // Skipped silently on offline or any pull/inspect error — update probing
1178
+ // must never block startup. `config.digest` set means the caller already
1179
+ // pins to a digest; nothing to probe.
1180
+ const checkAndRecreateOnDigestDrift = async () => {
1181
+ if (!config.autoUpdateOnFloatingTag)
1182
+ return false;
1183
+ if (config.digest)
1184
+ return false;
1185
+ if (classifyTag(config.tag) !== "floating")
1186
+ return false;
1187
+ const fullImage = `${config.image}:${config.tag}`;
1188
+ try {
1189
+ await _pull(runtime, qualifyImage(fullImage, runtime), debug);
1190
+ }
1191
+ catch (err) {
1192
+ if (isOfflineError(err)) {
1193
+ debug(`Container ${fullName} floating-tag digest check skipped (offline)`);
1194
+ }
1195
+ else {
1196
+ debug(`Container ${fullName} floating-tag digest check skipped: ${err instanceof Error ? err.message : String(err)}`);
1197
+ }
1198
+ return false;
1199
+ }
1200
+ // Compare image-ids (local content-store hash), not manifest digests.
1201
+ // `getImageDigest` returns the local `.Id` for both an `image:tag` ref
1202
+ // and a container name (via `.Image` on the container) — like-for-like.
1203
+ // Mixing in a `RepoDigest`-based identity here would always show drift
1204
+ // because RepoDigest and image-id are different namespaces. The same
1205
+ // image-id-vs-image-id comparison is what updates/service.ts uses.
1206
+ const remoteId = await getImageDigest(runtime, qualifyImage(fullImage, runtime), exec);
1207
+ const liveId = await getImageDigest(runtime, prefixedName(name), exec);
1208
+ if (!remoteId || !liveId || remoteId === liveId) {
1209
+ return false;
1210
+ }
1211
+ debug(`Container ${fullName} floating-tag digest drift detected (${liveId.slice(0, 19)}… → ${remoteId.slice(0, 19)}…); recreating`);
1212
+ await removeContainer(runtime, name, exec);
1213
+ await ensureRunning(runtime, name, config, debug, options, exec, prior, true, _pull);
1171
1214
  return true;
1172
1215
  };
1173
1216
  switch (state) {
@@ -1180,6 +1223,8 @@ prior, _postRecreate = false) {
1180
1223
  }
1181
1224
  if (await checkAndRecreateOnDrift("already running"))
1182
1225
  return;
1226
+ if (await checkAndRecreateOnDigestDrift())
1227
+ return;
1183
1228
  debug(`Container ${fullName} already running`);
1184
1229
  return;
1185
1230
  }
@@ -1196,6 +1241,8 @@ prior, _postRecreate = false) {
1196
1241
  }
1197
1242
  if (await checkAndRecreateOnDrift("stopped"))
1198
1243
  return;
1244
+ if (await checkAndRecreateOnDigestDrift())
1245
+ return;
1199
1246
  debug(`Starting stopped container ${fullName}`);
1200
1247
  const startResult = await exec(runtime, ["start", fullName]);
1201
1248
  if (startResult.exitCode !== 0) {