🐛 fix(compat): ship tri-tool fixes and v1.6 roadmap - #154
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
biggest-littlest
left a comment
There was a problem hiding this comment.
Reviewed the fresh-volume root cause, Portwing registry correction, compatibility boundary, v1.5.1 metadata, and v1.6 issue scope.
📝 WalkthroughWalkthroughThe PR initializes the Docker runtime socket volume with explicit ownership and permissions and adds CI startup validation using a fresh named volume. Compose and Docker Run guidance now uses Possibly related PRs
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ALARGECOMPANY
left a comment
There was a problem hiding this comment.
Reviewed the competitor claims, Docker API deprecation guidance, regression coverage, and release preparation.
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ci-verify.yml:
- Around line 528-558: Update the “Verify fresh socket-volume startup” step to
validate the socket itself, not just container liveness: after starting the
runtime container, use a helper container with the same socket volume to assert
that /var/run/sockguard/sockguard.sock exists and is usable, or send a request
through it. Preserve the existing startup failure check and cleanup behavior.
In `@Dockerfile`:
- Line 23: Replace the Alpine-based builder stage containing the runtime
directory setup with a pinned Chainguard/Wolfi Go builder image. Preserve the
existing build commands and /runtime/sockguard initialization, changing only the
builder base image and its required compatible setup.
- Line 35: Update the Dockerfile COPY command for /var/run/sockguard/ to avoid
owner-only 0700 permissions on the shared socket volume. Use group-accessible
ownership and permissions compatible with the documented your-app consumer, or
explicitly enforce UID/GID 65532 for that consumer and verify the shared-mount
flow.
In `@examples/compose/tri-tool/docker-compose.yml`:
- Line 53: Pin the Portwing image in the docker-compose service and the adjacent
drydock service to the audited Portwing 0.8.1 and drydock 1.5.2 versions in
examples/compose/tri-tool/docker-compose.yml:53-53. Update the Edge
compatibility claim in examples/compose/tri-tool/README.md:7-7 to explicitly
qualify it against those pinned versions.
In `@website/src/app/data/comparison-rows.ts`:
- Around line 176-195: Update the comparison-row ordering in the data definition
so “Podman native libpod API” and “Multiple main listeners” appear before the
existing terminal “Hot-reload + admin API” row, preserving the test’s expected
final feature. Do not change the test unless the new ordering is explicitly
intended.
In `@website/src/app/page-data.test.mjs`:
- Around line 93-112: Update the comment immediately before the v150 lookup to
describe v1.5.0 as the previous stable milestone, while leaving the surrounding
assertions and roadmap logic unchanged.
In `@website/src/components/compare-matrix.tsx`:
- Line 20: Add the multipleListeners feature to the FEATURES definition
alongside podmanNative, then add a corresponding multipleListeners entry to
every comparison row’s cells map in the matrix data. Use the published “Multiple
frontend listeners” feature key and preserve each row’s appropriate support
value so overview and detail pages expose the same feature set.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: ab9baf11-c373-43f4-ace5-0ec3d21e1511
⛔ Files ignored due to path filters (2)
CHANGELOG.mdis excluded by!CHANGELOG.mdwebsite/public/llms.txtis excluded by!website/public/**
📒 Files selected for processing (22)
.github/workflows/ci-verify.ymlDockerfileREADME.mdchart/sockguard/Chart.yamldocs/content/docs/getting-started.mdxdocs/content/docs/index.mdxdocs/content/docs/meta.jsondocs/content/docs/presets.mdxdocs/content/docs/roadmap.mdxexamples/compose/portwing/docker-compose.ymlexamples/compose/tri-tool/README.mdexamples/compose/tri-tool/docker-compose.ymlwebsite/src/app/data/comparison-rows.tswebsite/src/app/page-data.test.mjswebsite/src/components/compare-matrix.tsxwebsite/src/lib/comparison-route-data/11notes.tsxwebsite/src/lib/comparison-route-data/cetusguard.tsxwebsite/src/lib/comparison-route-data/linuxserver.tsxwebsite/src/lib/comparison-route-data/tecnativa.tsxwebsite/src/lib/comparison-route-data/wollomatic.tsxwebsite/src/lib/site-config.tswebsite/src/lib/site-content.ts
| - name: Verify fresh socket-volume startup | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| runtime_volume="sockguard-ci-runtime-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" | ||
| runtime_container="sockguard-ci-runtime-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" | ||
| socket_gid="$(stat -c '%g' /var/run/docker.sock)" | ||
|
|
||
| cleanup() { | ||
| docker rm -f "${runtime_container}" >/dev/null 2>&1 || true | ||
| docker volume rm "${runtime_volume}" >/dev/null 2>&1 || true | ||
| } | ||
| trap cleanup EXIT | ||
|
|
||
| docker volume create "${runtime_volume}" >/dev/null | ||
| docker run --detach \ | ||
| --name "${runtime_container}" \ | ||
| --group-add "${socket_gid}" \ | ||
| --volume /var/run/docker.sock:/var/run/docker.sock:ro \ | ||
| --volume "${runtime_volume}:/var/run/sockguard" \ | ||
| --env SOCKGUARD_LISTEN_SOCKET=/var/run/sockguard/sockguard.sock \ | ||
| sockguard:ci >/dev/null | ||
|
|
||
| if timeout 2 docker wait "${runtime_container}" >/dev/null; then | ||
| docker logs "${runtime_container}" | ||
| echo "::error::Sockguard exited while initializing a fresh named socket volume" | ||
| exit 1 | ||
| fi | ||
|
|
||
| test "$(docker inspect --format '{{.State.Running}}' "${runtime_container}")" = "true" |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Probe the socket, not only liveness.
The check only proves the process survives two seconds. A broken initialization can leave the container running without creating a usable socket, so CI can pass the regression. Mount the volume into a helper container and assert the socket exists, or perform a request through it.
Based on the PR objective, this step is the fresh socket-volume startup regression gate.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/ci-verify.yml around lines 528 - 558, Update the “Verify
fresh socket-volume startup” step to validate the socket itself, not just
container liveness: after starting the runtime container, use a helper container
with the same socket volume to assert that /var/run/sockguard/sockguard.sock
exists and is usable, or send a request through it. Preserve the existing
startup failure check and cleanup behavior.
| -X github.com/codeswhat/sockguard/internal/version.BuildDate=${BUILD_DATE}" \ | ||
| -trimpath \ | ||
| -o /sockguard ./cmd/sockguard/ | ||
| RUN install -d -m 0700 /runtime/sockguard && touch /runtime/sockguard/.volume-init |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Use a Wolfi/Chainguard builder.
Line 23 is added under the Alpine-based builder, but this repository requires the image to be built on Wolfi/Chainguard. Replace the builder stage with a pinned Chainguard/Wolfi Go image.
As per coding guidelines, Dockerfiles must build on Wolfi (Chainguard) rather than a different base image.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Dockerfile` at line 23, Replace the Alpine-based builder stage containing the
runtime directory setup with a pinned Chainguard/Wolfi Go builder image.
Preserve the existing build commands and /runtime/sockguard initialization,
changing only the builder base image and its required compatible setup.
Source: Coding guidelines
|
|
||
| COPY --from=builder /sockguard /sockguard | ||
| COPY app/configs/ /etc/sockguard/ | ||
| COPY --from=builder --chown=65532:65532 --chmod=0700 /runtime/sockguard/ /var/run/sockguard/ |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
Do not make the shared socket volume owner-only.
0700 prevents arbitrary non-root consumers from traversing /var/run/sockguard; the documented your-app container mounts this volume but has no required UID/GID contract. Use a shared group with matching permissions, or explicitly require consumers to run as UID/GID 65532 and test that flow.
Docker copies pre-existing target contents into an empty named volume by default, so these permissions become part of the first-start contract. (docs.docker.com)
Based on the supplied Quick Start, the socket volume is shared with your-app.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Dockerfile` at line 35, Update the Dockerfile COPY command for
/var/run/sockguard/ to avoid owner-only 0700 permissions on the shared socket
volume. Use group-accessible ownership and permissions compatible with the
documented your-app consumer, or explicitly enforce UID/GID 65532 for that
consumer and verify the shared-mount flow.
Source: MCP tools
|
|
||
| portwing: | ||
| image: codeswhat/portwing:latest | ||
| image: ghcr.io/codeswhat/portwing:latest |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Make the compatibility claim reproducible.
The bundle uses mutable latest tags while documenting behavior differences between drydock 1.5 and 1.6 and auditing Portwing 0.8.1/drydock 1.5.2.
examples/compose/tri-tool/docker-compose.yml#L53-L53: pin Portwing and the adjacent drydock service to the audited versions.examples/compose/tri-tool/README.md#L7-L7: qualify the Edge claim against those pinned versions.
Based on the supplied PR objective, the compatibility audit is version-specific.
📍 Affects 2 files
examples/compose/tri-tool/docker-compose.yml#L53-L53(this comment)examples/compose/tri-tool/README.md#L7-L7
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/compose/tri-tool/docker-compose.yml` at line 53, Pin the Portwing
image in the docker-compose service and the adjacent drydock service to the
audited Portwing 0.8.1 and drydock 1.5.2 versions in
examples/compose/tri-tool/docker-compose.yml:53-53. Update the Edge
compatibility claim in examples/compose/tri-tool/README.md:7-7 to explicitly
qualify it against those pinned versions.
| { | ||
| feature: "Podman native libpod API", | ||
| tecnativa: "No", | ||
| linuxserver: "Yes", | ||
| wollomatic: "Via manual regex", | ||
| elevenNotes: "No", | ||
| cetusguard: "Yes", | ||
| sockguard: "Planned v1.6", | ||
| planned: true, | ||
| }, | ||
| { | ||
| feature: "Multiple main listeners", | ||
| tecnativa: "No", | ||
| linuxserver: "No", | ||
| wollomatic: "No", | ||
| elevenNotes: "Yes (Unix + TCP)", | ||
| cetusguard: "Yes", | ||
| sockguard: "Planned v1.6", | ||
| planned: true, | ||
| }, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Keep comparison-row ordering consistent with the test.
These rows are appended after the existing final row, so website/src/app/page-data.test.mjs Line 87 now fails: the final feature is Multiple main listeners, not Hot-reload + admin API. Move these rows before the terminal row, or update the assertion if the new ordering is intentional.
Alternative test update
- assert.equal(comparisonRows.at(-1)?.feature, "Hot-reload + admin API");
+ assert.equal(comparisonRows.at(-1)?.feature, "Multiple main listeners");🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@website/src/app/data/comparison-rows.ts` around lines 176 - 195, Update the
comparison-row ordering in the data definition so “Podman native libpod API” and
“Multiple main listeners” appear before the existing terminal “Hot-reload +
admin API” row, preserving the test’s expected final feature. Do not change the
test unless the new ordering is explicitly intended.
| // Runtime and published-example fix release. | ||
| const releasedMilestones = roadmap.filter((m) => m.status === "released"); | ||
| assert.ok(releasedMilestones.length > 0, "must have at least one released milestone"); | ||
| const latestReleased = releasedMilestones[releasedMilestones.length - 1]; | ||
| assert.equal(latestReleased.version, "v1.5.0", "latest released milestone must be v1.5.0"); | ||
| assert.equal(latestReleased.version, "v1.5.1", "latest released milestone must be v1.5.1"); | ||
| assert.equal(latestReleased.status, "released"); | ||
|
|
||
| // Must reference the current stable milestone v1.5.0. | ||
| const v150 = roadmap.find((m) => m.version === "v1.5.0"); | ||
| assert.ok(v150, "roadmap must include a v1.5.0 milestone"); | ||
| assert.equal(v150.status, "released", "v1.5.0 must be released"); | ||
|
|
||
| const v151 = roadmap.find((m) => m.version === "v1.5.1"); | ||
| assert.ok(v151, "roadmap must include a v1.5.1 milestone"); | ||
| assert.equal(v151.status, "released", "v1.5.1 must be released"); | ||
|
|
||
| const nextMilestones = roadmap.filter((m) => m.status === "next"); | ||
| assert.equal(nextMilestones.length, 1, "roadmap must have exactly one next milestone"); | ||
| assert.equal(nextMilestones[0].version, "v1.6.0", "v1.6.0 must be the next milestone"); | ||
|
|
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Fix the stale “current stable” comment.
Line 100 still describes v1.5.0 as current stable, while this test now requires v1.5.1 as the latest release. Update the comment to describe v1.5.0 as the previous stable milestone.
Proposed fix
- // Must reference the current stable milestone v1.5.0.
+ // Must retain the previous stable milestone v1.5.0.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Runtime and published-example fix release. | |
| const releasedMilestones = roadmap.filter((m) => m.status === "released"); | |
| assert.ok(releasedMilestones.length > 0, "must have at least one released milestone"); | |
| const latestReleased = releasedMilestones[releasedMilestones.length - 1]; | |
| assert.equal(latestReleased.version, "v1.5.0", "latest released milestone must be v1.5.0"); | |
| assert.equal(latestReleased.version, "v1.5.1", "latest released milestone must be v1.5.1"); | |
| assert.equal(latestReleased.status, "released"); | |
| // Must reference the current stable milestone v1.5.0. | |
| const v150 = roadmap.find((m) => m.version === "v1.5.0"); | |
| assert.ok(v150, "roadmap must include a v1.5.0 milestone"); | |
| assert.equal(v150.status, "released", "v1.5.0 must be released"); | |
| const v151 = roadmap.find((m) => m.version === "v1.5.1"); | |
| assert.ok(v151, "roadmap must include a v1.5.1 milestone"); | |
| assert.equal(v151.status, "released", "v1.5.1 must be released"); | |
| const nextMilestones = roadmap.filter((m) => m.status === "next"); | |
| assert.equal(nextMilestones.length, 1, "roadmap must have exactly one next milestone"); | |
| assert.equal(nextMilestones[0].version, "v1.6.0", "v1.6.0 must be the next milestone"); | |
| // Runtime and published-example fix release. | |
| const releasedMilestones = roadmap.filter((m) => m.status === "released"); | |
| assert.ok(releasedMilestones.length > 0, "must have at least one released milestone"); | |
| const latestReleased = releasedMilestones[releasedMilestones.length - 1]; | |
| assert.equal(latestReleased.version, "v1.5.1", "latest released milestone must be v1.5.1"); | |
| assert.equal(latestReleased.status, "released"); | |
| // Must retain the previous stable milestone v1.5.0. | |
| const v150 = roadmap.find((m) => m.version === "v1.5.0"); | |
| assert.ok(v150, "roadmap must include a v1.5.0 milestone"); | |
| assert.equal(v150.status, "released", "v1.5.0 must be released"); | |
| const v151 = roadmap.find((m) => m.version === "v1.5.1"); | |
| assert.ok(v151, "roadmap must include a v1.5.1 milestone"); | |
| assert.equal(v151.status, "released", "v1.5.1 must be released"); | |
| const nextMilestones = roadmap.filter((m) => m.status === "next"); | |
| assert.equal(nextMilestones.length, 1, "roadmap must have exactly one next milestone"); | |
| assert.equal(nextMilestones[0].version, "v1.6.0", "v1.6.0 must be the next milestone"); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@website/src/app/page-data.test.mjs` around lines 93 - 112, Update the comment
immediately before the v150 lookup to describe v1.5.0 as the previous stable
milestone, while leaving the surrounding assertions and roadmap logic unchanged.
| { key: "signedBundles", label: "Signed bundles" }, | ||
| { key: "imageTrust", label: "Image trust" }, | ||
| { key: "metrics", label: "Metrics" }, | ||
| { key: "podmanNative", label: "Podman native" }, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Add the missing listener column.
website/src/lib/comparison-route-data/cetusguard.tsx:11 now publishes “Multiple frontend listeners,” but this overview matrix only adds podmanNative. Add multipleListeners to FEATURES and populate every cells map; otherwise the overview and detail comparison pages expose different feature sets.
Proposed fix
const FEATURES = [
{ key: "metrics", label: "Metrics" },
+ { key: "multipleListeners", label: "Multiple listeners" },
{ key: "podmanNative", label: "Podman native" },🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@website/src/components/compare-matrix.tsx` at line 20, Add the
multipleListeners feature to the FEATURES definition alongside podmanNative,
then add a corresponding multipleListeners entry to every comparison row’s cells
map in the matrix data. Use the published “Multiple frontend listeners” feature
key and preserve each row’s appropriate support value so overview and detail
pages expose the same feature set.
Outcome\n\nShips the v1.5.1 integration/packaging patch and turns the three-tool plus competitor audit into tested public contracts and an actionable v1.6 roadmap.\n\n## Fixed\n\n- Pre-owns /var/run/sockguard in the non-root image so a fresh named volume can accept sockguard.sock without manual chown.\n- Adds a Docker CI regression that boots the image against a genuinely fresh named volume.\n- Corrects Portwing examples to ghcr.io/codeswhat/portwing:latest.\n- Uses a portable OpenSSL token-generation command.\n- Loopback-binds the tri-tool demo ports and explicitly configures drydock anonymous auth for local-only use.\n- Adds the Docker socket group directly to README/docs quick starts.\n\n## Compatibility audit\n\nVerified Sockguard, Portwing 0.8.1, and drydock 1.5.2 with published peer images against Docker Engine API 1.53:\n\n- Fresh-volume startup succeeds as UID/GID 65532.\n- Portwing and drydock are healthy.\n- Drydock completes a 14-container Standard Mode handshake.\n- Versioned Docker routes normalize and pass through the Portwing preset.\n- Remote trigger is accurately documented as unsupported and returns HTTP 501.\n- Portwing Edge source/tests cover Docker tunneling, exec, logs, events, reconnect, and backpressure, but watcher/update trigger completion is not implemented.\n\nCross-repo implementation: CodesWhat/portwing#76 and CodesWhat/drydock#632.\n\n## Competitor and roadmap audit\n\nRechecked Tecnativa, LinuxServer, wollomatic, 11notes, CetusGuard, mikesir87, cerede2000, and Swarmgate from their primary repositories. Corrects inflated/incorrect comparison claims and commits v1.6 work for:\n\n- native Podman/libpod policy\n- multiple independently scoped main listeners\n- fail-closed declarative admission mutation\n- published three-tool conformance\n- Docker API 1.55 and current Compose/BuildKit transport\n- resource-policy parity on container updates and Swarm services\n\nTracked in milestone v1.6.0, issues #148-#153.\n\n## Verification\n\n- go test -race ./...\n- Portwing: go test -race ./...\n- drydock: 268 focused Portwing/Edge tests\n- npm test (92 root/script tests plus website tests)\n- npx biome check .\n- npm run build\n- docker compose config for both affected examples\n- local fresh-volume Docker regression\n- live three-container published-image integration\n- full pre-push gate: GoReleaser snapshot, golangci-lint, race tests, govulncheck, fuzz smoke, dedupe, knip, Biome, TypeScript tests/build, zizmor, clean tree\n\n## Release\n\nRelease metadata, Helm chart, README, website, and changelog are prepared for v1.5.1.
Changelog
✨ Added
DOCKER_SOCK_GID.🔧 Changed
🐛 Fixed
🔒 Security
Concerns
2026-07-28) is intentional.