signalk-container 1.8.1 → 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
@@ -117,60 +117,64 @@ See [doc/plugin-developer-guide.md](doc/plugin-developer-guide.md) for the full
117
117
 
118
118
  ## API
119
119
 
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? }` |
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. |
151
153
 
152
154
  ## REST Endpoints
153
155
 
154
156
  All mounted at `/plugins/signalk-container/api/`:
155
157
 
156
- | Method | Path | Description |
157
- | ------ | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
158
- | GET | `/runtime` | Detected runtime info |
159
- | GET | `/containers` | List managed containers |
160
- | GET | `/containers/:name/state` | Container state |
161
- | POST | `/containers/:name/start` | Start a stopped container |
162
- | POST | `/containers/:name/stop` | Stop a running container |
163
- | POST | `/containers/:name/remove` | Stop and remove a container |
164
- | 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 |
165
- | GET | `/containers/:name/logs/stream` | Server-Sent Events stream of live log lines. Closes when the container is removed or the client disconnects |
166
- | POST | `/prune` | Prune dangling images |
167
- | GET | `/updates` | List last update-check results |
168
- | GET | `/updates/:pluginId` | Last update-check result for one plugin |
169
- | POST | `/updates/:pluginId/check` | Force a fresh update check (HTTP 200 even when offline) |
170
- | GET | `/containers/:name/resources` | Effective resource limits + user override |
171
- | POST | `/containers/:name/resources` | Apply new resource limits (live or recreate). Body is a `ContainerResourceLimits` diff against the consumer plugin's default. |
172
- | DELETE | `/containers/:name/resources` | Clear any user override and restore the consumer plugin's pristine default limits to the running container. |
173
- | 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+). |
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`. |
174
178
 
175
179
  ## Configuration
176
180
 
@@ -448,45 +452,107 @@ this plugin needs access to the host's container runtime to manage other
448
452
  containers. The plugin auto-detects this scenario via `/.dockerenv` or
449
453
  `/run/.containerenv` and prefixes the status with `(in-container)`.
450
454
 
451
- For the plugin to work, you must expose the host's container runtime to
452
- the Signal K container:
455
+ For the plugin to work, two things must be true inside the Signal K
456
+ container:
453
457
 
454
- ### 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`:
455
507
 
456
508
  ```yaml
457
509
  services:
458
510
  signalk:
459
- 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
460
513
  volumes:
461
- - /var/run/docker.sock:/var/run/docker.sock
462
- - /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
463
517
  ```
464
518
 
465
- The Signal K image must include the `docker` CLI binary, OR you mount
466
- it from the host as shown above. Containers managed by the plugin
467
- become **siblings** of the Signal K container, not nested.
519
+ Your image's Dockerfile should include `podman` or `podman-remote`:
468
520
 
469
- > [!warning]
470
- > Mounting `/var/run/docker.sock` gives the container **root-equivalent
471
- > access to the host**. Anyone who compromises Signal K (including via
472
- > a malicious plugin) can take over the entire host. Only use this if
473
- > 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
+ ```
474
526
 
475
- ### Podman (rootless, safer)
527
+ ### Rootful Podman
476
528
 
477
529
  ```yaml
478
530
  services:
479
531
  signalk:
480
- image: signalk/signalk-server
532
+ image: your-signalk-image-with-podman
481
533
  volumes:
482
- - $XDG_RUNTIME_DIR/podman/podman.sock:/var/run/podman.sock
483
- - /usr/bin/podman:/usr/bin/podman:ro
534
+ - /run/podman/podman.sock:/run/podman/podman.sock
484
535
  environment:
485
- - 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`
486
549
  ```
487
550
 
488
- Rootless Podman runs as your user, not root, so the security exposure
489
- 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.
490
556
 
491
557
  ### Networking caveats
492
558
 
@@ -502,12 +568,6 @@ network namespace. This affects:
502
568
  to Docker containers automatically (Podman already provides it); set
503
569
  `ContainerConfig.extraHosts` to override it or to add other hostnames.
504
570
 
505
- ### Recommended setup
506
-
507
- For the simplest experience with managed containers, run **Signal K
508
- natively on the host** rather than in a container. The plugin and its
509
- ecosystem (signalk-questdb, signalk-grafana) are designed for this case.
510
-
511
571
  ## License
512
572
 
513
573
  MIT
@@ -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":"AAEA,OAAO,EACL,eAAe,EACf,aAAa,EACb,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,UAAU,EACX,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,sBAAsB,EAItB,qBAAqB,EAEtB,MAAM,cAAc,CAAC;AAWtB;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,oBAAoB,EAC7B,QAAQ,GAAE,OAAe,GACxB,MAAM,CAOR;AAcD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,GAAG,SAAS,EACxD,KAAK,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAoB,GAC5C;IACD,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,OAAO,EAAE,KAAK,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1D,OAAO,EAAE,KAAK,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC3D,CAuCA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EACD;IACE,OAAO,EAAE,KAAK,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1D,OAAO,EAAE,KAAK,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC3D,GACD,SAAS,EACb,cAAc,EAAE,KAAK,CAAC;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EAChE,cAAc,EAAE,KAAK,CAAC;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EAChE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3B,KAAK,CAAC;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAmBlD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,EACnE,KAAK,EAAE,WAAW,EAClB,WAAW,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,GAClC,IAAI,CAUN;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,EAC7D,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,GAClC,IAAI,CAON;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EAC9B,OAAO,CAAC,EAAE;IACR,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACvC,KAAK,CAAC,EAAE,OAAO,qBAAqB,CAAC;CACtC,GACA,sBAAsB,CAgBxB;AAED,kEAAkE;AAClE,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAe9B;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,MAAM,GACZ;IAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAqB/C;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAC3C,IAAI,GAAE,MAAoB,GACzB,OAAO,CAAC,MAAM,EAAE,CAAC,CA2BnB;AAUD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,oBAAoB,GAC5B,MAAM,CAwBR;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,oBAAoB,EAC7B,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,MAAoB,GACzB,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,oBAAoB,EAC7B,gBAAgB,EAAE,MAAM,EACxB,IAAI,GAAE,MAAoB,GACzB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA0BxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,EAC7B,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,MAAoB,GACzB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkBxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,oBAAoB,EAC7B,aAAa,EAAE,MAAM,EACrB,IAAI,GAAE,MAAoB,GACzB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAcxB;AAED,wBAAsB,SAAS,CAC7B,OAAO,EAAE,oBAAoB,EAC7B,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GACjC,OAAO,CAAC,IAAI,CAAC,CAUf;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,MAAoB,GACzB,OAAO,CAAC,cAAc,CAAC,CAoCzB;AAED;;;GAGG;AACH,MAAM,MAAM,MAAM,GAAG,CACnB,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,MAAM,EAAE,KACX,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,MAAoB,GACzB,OAAO,CAAC,OAAO,YAAY,EAAE,uBAAuB,CAAC,CA2EvD;AAmBD;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAkC1E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,MAAoB,GACzB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,CAUrC;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;OAKG;IACH,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;IACzC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC;;;;;;;;;;OAUG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,MAAoB,GACzB,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CA0JrC;AAqGD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,eAAe,EAC1B,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,oBAAoB,EAC7B,KAAK,CAAC,EAAE,eAAe,GACtB;IAAE,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAyJvB;AAmFD,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,EAE5B,OAAO,CAAC,EAAE,kBAAkB,EAC5B,IAAI,GAAE,MAAoB;AAC1B;;;;;;;GAOG;AACH,KAAK,CAAC,EAAE,eAAe,EACvB,aAAa,GAAE,OAAe,GAC7B,OAAO,CAAC,IAAI,CAAC,CA+Ff;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAMf;AA4BD,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAUf;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,MAAoB,GACzB,OAAO,CAAC,IAAI,CAAC,CAQf;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,aAAa,EAAE,CAAC,CA6B1B;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAC,CAY5D;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAG/D;AAED,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAQf;AAED,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,oBAAoB,EAC7B,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,oBAAoB,EAC7B,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW1E;AAED;;;;;;;;GAQG;AACH,wBAAgB,mCAAmC,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAW7E;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,8BAA8B,IAAI,MAAM,EAAE,CAQzD;AAgCD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,oBAAoB,EAC7B,KAAK,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAe,GACtC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA0BxB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,oBAAoB,EAC7B,KAAK,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAe,GACtC,OAAO,CAAC,MAAM,CAAC,CA2EjB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,cAAc,EAAE,GACvB,wBAAwB,GAAG,IAAI,CAgCjC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,oBAAoB,EAC7B,KAAK,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAe,GACtC,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC,CA6C1C;AAcD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEtD;AAeD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAS1E;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,oBAAoB,EAC7B,KAAK,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAe,GACtC,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAsC1B;AAED,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,SAAS,GAAE,MAAc,EACzB,UAAU,GAAE,MAAY,GACvB,OAAO,CAAC,IAAI,CAAC,CAYf"}
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) {