From 82c9efd82192fbbd32d4c91fec2067d67e7b47f8 Mon Sep 17 00:00:00 2001
From: scttbnsn <80784472+scttbnsn@users.noreply.github.com>
Date: Thu, 8 Oct 2026 15:14:32 -0400
Subject: [PATCH 1/6] docs: state the measured compressed image size, not the
binary size
The ghcr.io/codeswhat/portwing:0.9.23 manifests sum to 44.8 MB (amd64),
40.5 MB (arm64) and 33.4 MB (arm/v7) of compressed layers. The ~10 MB
figure is the unpacked amd64 binary (9.4 MB), and ~47 MB matched
neither. The Watchtower comparison cell is now containrrr/watchtower:latest
at 5.6 MB compressed (amd64) instead of an unsourced ~50 MB.
---
README.md | 2 +-
docs/content/docs/migrating-from-watchtower.mdx | 4 ++--
docs/migrating-from-watchtower.md | 2 +-
website/src/components/github-badges.tsx | 4 ++--
4 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md
index a29de7b5..651a8cac 100644
--- a/README.md
+++ b/README.md
@@ -368,7 +368,7 @@ flowchart LR
| đĄ | **SSE Compatibility** | Drop-in replacement for existing Drydock agents, including `dd:watcher-snapshot` full inventory on connect. |
| âī¸ | **Signed Supply Chain** | Cosign keyless signatures, per-archive CycloneDX SBOMs, an image SBOM attestation, and SLSA Build L2 provenance on every release. Verifiable without managing signing keys. |
| đĄī¸ | **Two-Layer Defense** | Pair with [sockguard](https://github.com/codeswhat/sockguard) so the agent never touches the raw Docker socket directly. |
-| đĒļ | **Minimal Footprint** | Static Go binary, ~47 MB Wolfi (Chainguard) container image. CGO disabled, stripped, no external runtime dependencies. |
+| đĒļ | **Minimal Footprint** | Static Go binary (~10 MB). Compressed container image: ~45 MB amd64 and ~41 MB arm64 (Wolfi, Chainguard), ~33 MB arm/v7 (Alpine). CGO disabled, stripped, no external runtime dependencies. |
| đ§Š | **Standalone Mode** | `ADAPTER=generic` provides a clean REST + SSE API on `/api/v1/*` backed by the local Docker daemon â no Drydock account required. |
diff --git a/docs/content/docs/migrating-from-watchtower.mdx b/docs/content/docs/migrating-from-watchtower.mdx
index ccdee13d..6846d9e3 100644
--- a/docs/content/docs/migrating-from-watchtower.mdx
+++ b/docs/content/docs/migrating-from-watchtower.mdx
@@ -29,7 +29,7 @@ Drydock + Portwing invert the model. Portwing runs on each host and exposes the
| Container exec / terminal | No | Yes (WebSocket) |
| Container filtering | Label `com.centurylinklabs.watchtower.enable` | Label `dd.watch=true` |
| Self-hosted server required | No | Yes (Drydock) |
-| Image size | ~50 MB | ~10 MB (Portwing) + Drydock server |
+| Image size (compressed) | ~6 MB | ~45 MB (Portwing, ~10 MB binary) + Drydock server |
Portwing does **not** automatically pull and recreate containers on a schedule. That decision is made by the Drydock controller through its UI and notification pipeline â not by the agent. If you relied on Watchtower's fully autonomous update loop, you will need to configure update policies in Drydock.
@@ -211,7 +211,7 @@ For production deployments, upgrade to Ed25519 per-request signing after the ini
| Compose support | Recreate only | Full lifecycle (`up`/`down`/`pull`/`ps`/`logs`) |
| Exec / terminal | No | Yes (WebSocket) |
| Self-hosted required | No | Yes (Drydock server) |
-| Resource footprint | ~50 MB image | ~10 MB Portwing + Drydock server |
+| Resource footprint | ~6 MB compressed image | ~45 MB compressed Portwing image (~10 MB binary) + Drydock server |
See [Configuration](/configuration) for the full environment-variable reference and [Security Model](/security-model) for Portwing's default-deny socket filtering and rate-limiting details.
diff --git a/docs/migrating-from-watchtower.md b/docs/migrating-from-watchtower.md
index 51a894b8..830e3b2a 100644
--- a/docs/migrating-from-watchtower.md
+++ b/docs/migrating-from-watchtower.md
@@ -197,7 +197,7 @@ checked for updates by Drydock.
| Compose support | Recreate only | Full lifecycle (`up`/`down`/`pull`/`ps`/`logs`) |
| Exec / terminal | No | Yes (WebSocket) |
| Self-hosted required | No (just Docker) | Yes (Drydock server) |
-| Resource footprint | ~50 MB image | ~10 MB Portwing image + Drydock server |
+| Resource footprint | ~6 MB compressed image | ~45 MB compressed Portwing image (~10 MB binary) + Drydock server |
---
diff --git a/website/src/components/github-badges.tsx b/website/src/components/github-badges.tsx
index 620d05a6..8f734be3 100644
--- a/website/src/components/github-badges.tsx
+++ b/website/src/components/github-badges.tsx
@@ -42,8 +42,8 @@ const distribution: Badge[] = [
},
{
href: GHCR_PACKAGE_URL,
- src: "https://img.shields.io/badge/image%20size-~10%20MB-informational?logo=docker&logoColor=white",
- alt: "Image size ~10 MB",
+ src: "https://img.shields.io/badge/image%20size-~45%20MB%20compressed-informational?logo=docker&logoColor=white",
+ alt: "Compressed image size ~45 MB (amd64)",
},
{
href: `${GITHUB_URL}/stargazers`,
From ee398861157c3391b18766e6ab6e978cc96b388e Mon Sep 17 00:00:00 2001
From: scttbnsn <80784472+scttbnsn@users.noreply.github.com>
Date: Thu, 8 Oct 2026 15:14:39 -0400
Subject: [PATCH 2/6] docs(roadmap): drop the BuildKit Sockguard profile, it
already shipped
Sockguard's portwing-with-mediated-build.yaml preset (in sockguard since
v1.7.0) allows a stock BuildKit docker compose build, and COMPATIBILITY.md
already documents it. Remove it from the post-v1 list in ROADMAP.md and the
README roadmap table.
---
README.md | 2 +-
ROADMAP.md | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 651a8cac..767bb016 100644
--- a/README.md
+++ b/README.md
@@ -464,7 +464,7 @@ High-level themes only; see [ROADMAP.md](ROADMAP.md) for direction and non-goals
| **v0.8.x** â
| Operations & Distribution | Mode-aware `/health` and `/ready`, cursor-based NDJSON audit export, runnable Compose and Kubernetes observability examples, continuous edge logs, Homebrew cask and signed `deb`/`rpm` packages, published stability policy, edge mode production supported |
| **v0.9.x** â
| Controller-Owned Updates | Controller-owned Drydock watcher and update execution (Drydock `v1.6.0-rc.11+`), edge audit export, loopback default for the edge operations listener, MCP revision 2026-07-28, `ALLOWED_ORIGINS` and `ALLOWED_HOSTS` browser origin and Host checks (v0.9.23) |
| **v1.0.0** | Binding Stability | `STABILITY.md` guarantees become binding semver commitments, final re-verify of the competitive review against primary sources, decision on a versioned docs archive. Gated on verifiable items, not a date |
-| **Post-v1** | Demand-Driven | Controller-managed Portwing upgrade and rollback waves, optional client-certificate authentication, polling/intermittent edge transport, controller-assisted two-key rotation, BuildKit-aware Sockguard profile |
+| **Post-v1** | Demand-Driven | Controller-managed Portwing upgrade and rollback waves, optional client-certificate authentication, polling/intermittent edge transport, controller-assisted two-key rotation |
SLSA Build L3 isn't tied to a version. It follows an org-shared reusable release workflow landing.
diff --git a/ROADMAP.md b/ROADMAP.md
index 703f2944..16e8abd8 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -104,9 +104,9 @@ calendar date:
Post-v1 candidates are intentionally demand-driven: controller-managed
Portwing upgrade/rollback waves, optional client-certificate authentication,
-polling/intermittent edge transport, controller-assisted two-key rotation, and
-a BuildKit-aware Sockguard profile. None should expand the base socket policy
-or add an unaudited self-update path merely for feature-table parity.
+polling/intermittent edge transport, and controller-assisted two-key rotation.
+None should expand the base socket policy or add an unaudited
+self-update path merely for feature-table parity.
## Non-goals
From 88363bfa6c4cce29b424881f17dbfb9eea50f2f7 Mon Sep 17 00:00:00 2001
From: scttbnsn <80784472+scttbnsn@users.noreply.github.com>
Date: Thu, 8 Oct 2026 15:15:36 -0400
Subject: [PATCH 3/6] fix(website): both Watchtower and Diun document
Prometheus metrics
Watchtower documents an opt-in, experimental /v1/metrics endpoint behind
its HTTP API token (containrrr.dev/watchtower/metrics and /arguments).
Diun documents an opt-in metrics server on :9090/metrics
(crazymax.dev/diun/config/metrics). The Watchtower "No" was wrong, so the
row is a tie and the matrix cell is yes.
---
website/src/components/compare-matrix.tsx | 2 +-
website/src/lib/comparison-route-data/diun.tsx | 2 +-
website/src/lib/comparison-route-data/watchtower.tsx | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/website/src/components/compare-matrix.tsx b/website/src/components/compare-matrix.tsx
index 6441b74d..f9fdc676 100644
--- a/website/src/components/compare-matrix.tsx
+++ b/website/src/components/compare-matrix.tsx
@@ -111,7 +111,7 @@ const TOOLS: Tool[] = [
leastPrivilege: "no",
agentAudit: "no",
transparentApi: "no",
- metrics: "no",
+ metrics: "yes",
mcpServer: "no",
},
},
diff --git a/website/src/lib/comparison-route-data/diun.tsx b/website/src/lib/comparison-route-data/diun.tsx
index 274657f7..f387cac8 100644
--- a/website/src/lib/comparison-route-data/diun.tsx
+++ b/website/src/lib/comparison-route-data/diun.tsx
@@ -10,7 +10,7 @@ Auth for remote access|No (local only)|Yes (Ed25519 per-request signing)|self
Structured audit log|No|Yes (JSON, built-in)|self
Release artifact verification|Not evaluated|Cosign signatures + CycloneDX SBOM + SLSA provenance|self
Default-deny socket filter|No|Yes (with sockguard)|self
-Prometheus metrics|Yes|Yes|tie
+Prometheus metrics|Yes (opt-in)|Yes|tie
MCP server (AI-native, read-only)|No|Yes|self
Edge / NAT outbound tunnel|No|Yes (Drydock v1.6.0-rc.11+)|self
Single lightweight Go binary|Yes|Yes (~10 MB)|tie
diff --git a/website/src/lib/comparison-route-data/watchtower.tsx b/website/src/lib/comparison-route-data/watchtower.tsx
index e0efbd42..6dd5341c 100644
--- a/website/src/lib/comparison-route-data/watchtower.tsx
+++ b/website/src/lib/comparison-route-data/watchtower.tsx
@@ -11,7 +11,7 @@ Structured audit log|No|Yes (JSON, built-in)|self
Runtime image signature verification|Basic cosign support (newer builds)|No (controller or admission-control responsibility)|competitor
Published release evidence|Archived upstream project|Cosign signatures + CycloneDX SBOM + SLSA provenance|self
Default-deny socket filter|No|Yes (with sockguard)|self
-Prometheus metrics|No|Yes|self
+Prometheus metrics|Yes (opt-in, experimental)|Yes|tie
MCP server (AI-native, read-only)|No|Yes|self
Edge / NAT outbound tunnel|No|Yes (Drydock v1.6.0-rc.11+)|self
Single lightweight Go binary|Yes|Yes (~10 MB)|tie
@@ -22,7 +22,7 @@ key|Remote Auth (Ed25519)|Watchtower runs locally and has no remote access model
shield|Default-Deny Socket Filter|Portwing pairs with sockguard to filter Docker API calls at the socket level. Even if Portwing is compromised, the sockguard allowlist constrains what can be called. Watchtower mounts the socket unfiltered.
filetext|Structured Audit Log|Portwing logs every Docker API call it proxies as structured JSON for export to immutable storage. Watchtower has no audit trail.
packagecheck|Maintained Signed Releases|Watchtower's upstream repository is archived. Portwing remains actively maintained and every release ships per-archive CycloneDX SBOMs, an image SBOM attestation, cosign image signatures, and SLSA build provenance. This verifies Portwing itself; it is not workload image-signature enforcement.
-activity|Prometheus Metrics|Portwing exposes agent health, request counts, and latency histograms. Watchtower has no metrics endpoint.
+activity|Prometheus Metrics|Both expose Prometheus metrics. Watchtower's is an opt-in, experimental /v1/metrics endpoint behind its HTTP API token. Portwing's covers agent health, request counts, and latency histograms.
bot|MCP Server (AI-Native)|Portwing ships five read-only MCP tools for container and host inspection. Watchtower has no documented MCP support.
`,
highlightIconMap: {
From 547155a00f9e571cb2c3818b07d975d4b330c1b8 Mon Sep 17 00:00:00 2001
From: scttbnsn <80784472+scttbnsn@users.noreply.github.com>
Date: Thu, 8 Oct 2026 15:15:41 -0400
Subject: [PATCH 4/6] fix(website): show "not documented" instead of "no" in
the comparison matrix
The route pages already say competitor MCP and Portainer Prometheus
support is not documented, but the matrix and the home-page teaser
rendered a flat "no". Add an undocumented cell value to
ComparisonCellIcon (question-mark icon, "Not documented" for screen
readers), use it wherever the route data says not documented, and add it
to both legends. The Watchtower and Diun route-table MCP cells now match
their prose.
---
website/src/components/compare-matrix.tsx | 24 +++++++++++--------
website/src/components/compare-section.tsx | 16 ++++++++-----
.../src/components/comparison-cell-icon.tsx | 15 ++++++++++--
.../src/lib/comparison-route-data/diun.tsx | 2 +-
.../lib/comparison-route-data/watchtower.tsx | 2 +-
5 files changed, 39 insertions(+), 20 deletions(-)
diff --git a/website/src/components/compare-matrix.tsx b/website/src/components/compare-matrix.tsx
index f9fdc676..aea91eea 100644
--- a/website/src/components/compare-matrix.tsx
+++ b/website/src/components/compare-matrix.tsx
@@ -1,13 +1,13 @@
-import { ArrowUpRight, Check, Minus, X } from "lucide-react";
+import { ArrowUpRight, Check, CircleQuestionMark, Minus, X } from "lucide-react";
import Link from "next/link";
-import { ComparisonCellIcon } from "@/components/comparison-cell-icon";
+import { type CellValue, ComparisonCellIcon } from "@/components/comparison-cell-icon";
import { SITE_CONFIG } from "@/lib/site-config";
// Full comparison matrix for /compare. Each competitor row links to its
// dedicated deep-dive page. Cell values are derived from comparison-rows.ts
// and the per-competitor data in lib/comparison-route-data/.
-type Cell = "yes" | "partial" | "no";
+type Cell = CellValue;
const FEATURES = [
{ key: "remoteControl", label: "Remote control" },
@@ -55,8 +55,8 @@ const TOOLS: Tool[] = [
leastPrivilege: "no",
agentAudit: "partial",
transparentApi: "yes",
- metrics: "no",
- mcpServer: "no",
+ metrics: "undocumented",
+ mcpServer: "undocumented",
},
},
{
@@ -70,7 +70,7 @@ const TOOLS: Tool[] = [
agentAudit: "partial",
transparentApi: "no",
metrics: "partial",
- mcpServer: "no",
+ mcpServer: "undocumented",
},
},
{
@@ -84,7 +84,7 @@ const TOOLS: Tool[] = [
agentAudit: "partial",
transparentApi: "no",
metrics: "partial",
- mcpServer: "no",
+ mcpServer: "undocumented",
},
},
{
@@ -98,7 +98,7 @@ const TOOLS: Tool[] = [
agentAudit: "partial",
transparentApi: "yes",
metrics: "partial",
- mcpServer: "no",
+ mcpServer: "undocumented",
},
},
{
@@ -112,7 +112,7 @@ const TOOLS: Tool[] = [
agentAudit: "no",
transparentApi: "no",
metrics: "yes",
- mcpServer: "no",
+ mcpServer: "undocumented",
},
},
{
@@ -126,7 +126,7 @@ const TOOLS: Tool[] = [
agentAudit: "no",
transparentApi: "no",
metrics: "yes",
- mcpServer: "no",
+ mcpServer: "undocumented",
},
},
];
@@ -196,6 +196,10 @@ export function CompareMatrix() {
No
+
+ Not
+ documented
+
¡ Click a tool for the full breakdown
diff --git a/website/src/components/compare-section.tsx b/website/src/components/compare-section.tsx
index ce177cc6..c3e0f0f8 100644
--- a/website/src/components/compare-section.tsx
+++ b/website/src/components/compare-section.tsx
@@ -1,4 +1,4 @@
-import { ArrowRight, Check, Minus, X } from "lucide-react";
+import { ArrowRight, Check, CircleQuestionMark, Minus, X } from "lucide-react";
import Link from "next/link";
import { type CellValue, ComparisonCellIcon } from "@/components/comparison-cell-icon";
import { SectionHeading } from "@/components/section-heading";
@@ -59,10 +59,10 @@ const featureRows: FeatureRow[] = [
{
label: "Read-only MCP server",
portwing: "yes",
- portainer: "no",
- komodo: "no",
- arcane: "no",
- hawser: "no",
+ portainer: "undocumented",
+ komodo: "undocumented",
+ arcane: "undocumented",
+ hawser: "undocumented",
},
];
@@ -162,9 +162,13 @@ export function CompareSection() {
Partial
-
+
No
+
+ Not
+ documented
+
diff --git a/website/src/components/comparison-cell-icon.tsx b/website/src/components/comparison-cell-icon.tsx
index 5375f144..e62fd26f 100644
--- a/website/src/components/comparison-cell-icon.tsx
+++ b/website/src/components/comparison-cell-icon.tsx
@@ -1,6 +1,6 @@
-import { Check, Minus, X } from "lucide-react";
+import { Check, CircleQuestionMark, Minus, X } from "lucide-react";
-export type CellValue = "yes" | "partial" | "no";
+export type CellValue = "yes" | "partial" | "no" | "undocumented";
export function ComparisonCellIcon({ value }: { value: CellValue }) {
if (value === "yes") {
@@ -19,6 +19,17 @@ export function ComparisonCellIcon({ value }: { value: CellValue }) {
>
);
}
+ if (value === "undocumented") {
+ return (
+ <>
+
+ Not documented
+ >
+ );
+ }
return (
<>
diff --git a/website/src/lib/comparison-route-data/diun.tsx b/website/src/lib/comparison-route-data/diun.tsx
index f387cac8..6e45112c 100644
--- a/website/src/lib/comparison-route-data/diun.tsx
+++ b/website/src/lib/comparison-route-data/diun.tsx
@@ -11,7 +11,7 @@ Structured audit log|No|Yes (JSON, built-in)|self
Release artifact verification|Not evaluated|Cosign signatures + CycloneDX SBOM + SLSA provenance|self
Default-deny socket filter|No|Yes (with sockguard)|self
Prometheus metrics|Yes (opt-in)|Yes|tie
-MCP server (AI-native, read-only)|No|Yes|self
+MCP server (AI-native, read-only)|Not documented|Yes|self
Edge / NAT outbound tunnel|No|Yes (Drydock v1.6.0-rc.11+)|self
Single lightweight Go binary|Yes|Yes (~10 MB)|tie
License|MIT|AGPL-3.0|tie
diff --git a/website/src/lib/comparison-route-data/watchtower.tsx b/website/src/lib/comparison-route-data/watchtower.tsx
index 6dd5341c..ded8197f 100644
--- a/website/src/lib/comparison-route-data/watchtower.tsx
+++ b/website/src/lib/comparison-route-data/watchtower.tsx
@@ -12,7 +12,7 @@ Runtime image signature verification|Basic cosign support (newer builds)|No (con
Published release evidence|Archived upstream project|Cosign signatures + CycloneDX SBOM + SLSA provenance|self
Default-deny socket filter|No|Yes (with sockguard)|self
Prometheus metrics|Yes (opt-in, experimental)|Yes|tie
-MCP server (AI-native, read-only)|No|Yes|self
+MCP server (AI-native, read-only)|Not documented|Yes|self
Edge / NAT outbound tunnel|No|Yes (Drydock v1.6.0-rc.11+)|self
Single lightweight Go binary|Yes|Yes (~10 MB)|tie
License|Apache-2.0|AGPL-3.0|tie
From cac26ce39bc4e07cefca8674a4fc4405b0a469dd Mon Sep 17 00:00:00 2001
From: scttbnsn <80784472+scttbnsn@users.noreply.github.com>
Date: Thu, 8 Oct 2026 15:16:00 -0400
Subject: [PATCH 5/6] docs: record Portainer and Komodo supply-chain evidence
from their releases
The comparison pages said "Not published" while the landscape said "not
evaluated". Checked 2026-10-08: Portainer 2.39.5 ships checksums, and its
agent image carries SPDX SBOM and SLSA v0.2 provenance attestations.
Komodo v2.3.2 ships bare binaries, and its Periphery image carries SLSA v1
provenance. Neither has a cosign signature tag. The landscape also now
states the "not documented, not guessed absent" rule and cites the
Watchtower and Diun metrics docs.
---
docs/content/docs/competitive-landscape.mdx | 8 ++++++--
website/src/lib/comparison-route-data/komodo.tsx | 2 +-
website/src/lib/comparison-route-data/portainer.tsx | 2 +-
3 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/docs/content/docs/competitive-landscape.mdx b/docs/content/docs/competitive-landscape.mdx
index 4e876fcb..1f20dbd2 100644
--- a/docs/content/docs/competitive-landscape.mdx
+++ b/docs/content/docs/competitive-landscape.mdx
@@ -4,7 +4,7 @@ description: An evidence-backed comparison of Portwing with current remote Docke
---
-This review is a snapshot from **August 29, 2026**, with Arcane re-checked on **September 2, 2026**. Competitor behavior changes independently of Portwing. This page is the canonical matrix and decision record, with the primary sources listed under Primary evidence.
+This review is a snapshot from **August 29, 2026**, with Arcane re-checked on **September 2, 2026** and the Portainer and Komodo supply-chain evidence checked on **October 8, 2026**. Competitor behavior changes independently of Portwing. This page is the canonical matrix and decision record, with the primary sources listed under Primary evidence. Behavior a competitor's own docs or repo don't cover is recorded as "not documented", never guessed absent.
Portwing is the host agent in a three-layer system:
@@ -46,7 +46,7 @@ products rather than direct general-purpose agent peers.
| Controller-managed agent upgrades | Not shipped | Portainer and Arcane provide fleet upgrade workflows. This belongs in Drydock plus Portwing packaging, not in an unaudited self-update endpoint. |
| Builds behind the hardened socket profile | Denied by default, opt-in via preset | The base and compose presets deny builds; `portwing-with-build.yaml` allows classic `POST /build`, and `portwing-with-mediated-build.yaml` allows a stock BuildKit `docker compose build` by inspecting the `/session` and `/grpc` traffic in Sockguard. |
| Host shell, file browser, and Swarm | Not supported | Intentional agent non-goals even though broader competitors provide them. |
-| Verifiable release artifacts | Cosign signatures, CycloneDX SBOM, and SLSA Build L2 provenance | Arcane ships Cosign-verifiable artifacts and images. Portainer and Komodo were not evaluated for this. Hawser publishes checksummed releases with no equivalent set documented. |
+| Verifiable release artifacts | Cosign signatures, CycloneDX SBOM, and SLSA Build L2 provenance | Arcane ships Cosign-verifiable artifacts and images. Portainer 2.39.5 publishes checksum files with its release archives and its images carry BuildKit SPDX SBOM and SLSA v0.2 provenance attestations, but no cosign signature was found. Komodo v2.3.2 publishes bare binaries, and its Periphery image carries a BuildKit SLSA v1 provenance attestation with no SBOM or cosign signature found. The cosign check looked only for the legacy `.sig` tag, and only the first platform's attestation was read. Hawser publishes checksummed releases with no equivalent set documented. |
| Credential rotation | Multiple keys; file update plus SIGHUP; manual operational flow | Portainer has a revolving Edge password and Edge key lifecycle. Komodo automatically rotates Periphery keys. Arcane automatically renews certificates and can regenerate its environment token. Hawser requires manual token replacement. |
## What changed since the older comparison
@@ -129,4 +129,8 @@ parity. Those capabilities belong in Drydock or a specialized service.
- Arcane [remote environments](https://getarcane.app/docs/features/environments), [edge mTLS](https://getarcane.app/docs/security/edge-mtls), and [socket proxy](https://getarcane.app/docs/setup/socket-proxy)
- Arcane ML-DSA-87 migration: [v2.10.0 release notes](https://github.com/getarcaneapp/arcane/releases/tag/v2.10.0), [PR #3785](https://github.com/getarcaneapp/arcane/pull/3785), and [v2.10.1 release notes](https://github.com/getarcaneapp/arcane/releases/tag/v2.10.1)
- [Hawser repository](https://github.com/Finsys/hawser) and [edge-mode bind-address issue](https://github.com/Finsys/hawser/issues/71)
+- Portainer [2.39.5 release assets](https://github.com/portainer/portainer/releases/tag/2.39.5) and the `portainer/agent:2.39.5` image manifest and attestations, checked October 8, 2026
+- Komodo [v2.3.2 release assets](https://github.com/moghtech/komodo/releases/tag/v2.3.2) and the `ghcr.io/moghtech/komodo-periphery:2.3.2` image manifest and attestations, checked October 8, 2026
- Komodo outbound-leg issues: [proxy env vars ignored](https://github.com/moghtech/komodo/issues/1473) and [hardcoded handshake timeout](https://github.com/moghtech/komodo/issues/1518)
+- Watchtower [metrics](https://containrrr.dev/watchtower/metrics/) and [arguments](https://containrrr.dev/watchtower/arguments/) (opt-in, experimental `/v1/metrics`), checked October 8, 2026
+- Diun [metrics configuration](https://crazymax.dev/diun/config/metrics/) (opt-in metrics server, `:9090/metrics`), checked October 8, 2026
diff --git a/website/src/lib/comparison-route-data/komodo.tsx b/website/src/lib/comparison-route-data/komodo.tsx
index 6f56f91e..70bae1b8 100644
--- a/website/src/lib/comparison-route-data/komodo.tsx
+++ b/website/src/lib/comparison-route-data/komodo.tsx
@@ -12,7 +12,7 @@ Transparent Docker API|No (Komodo resource/command API)|Yes|self
Structured audit|Full controller audit trail|Agent-level API/auth/Compose/exec audit with cursor export|tie
Host metrics|Collected for dashboards and alerts|Prometheus scrape endpoint plus edge metrics|tie
Docker socket policy|Documented deployment mounts the raw socket|Recommended Sockguard path-and-method policy|self
-Supply-chain evidence|Not published|Cosign signatures + archive/image CycloneDX SBOMs + SLSA provenance|self
+Supply-chain evidence|v2.3.2 release has binaries only; Periphery image carries a BuildKit SLSA v1 provenance attestation; no SBOM or cosign signature found|Cosign signatures + archive/image CycloneDX SBOMs + SLSA provenance|self
Host shell, builds, automation, and Swarm|Yes|Host shell and Swarm are non-goals; Drydock owns fleet workflows|competitor
MCP server (read-only)|Not documented|Yes|self
License|GPL-3.0|AGPL-3.0|tie
diff --git a/website/src/lib/comparison-route-data/portainer.tsx b/website/src/lib/comparison-route-data/portainer.tsx
index 5da1bf43..12a052e4 100644
--- a/website/src/lib/comparison-route-data/portainer.tsx
+++ b/website/src/lib/comparison-route-data/portainer.tsx
@@ -10,7 +10,7 @@ Connection modes|Classic inbound; outbound Edge; Async Edge|Standard inbound; pe
Agent authentication|Claim key exchange; optional shared secret; Edge key and optional Business mTLS|Ed25519 per-request HTTP signatures; signed edge hello; token fallback in standard mode|tie
Docker socket policy|Documented agent mounts Docker socket and host paths|Recommended Sockguard path-and-method policy|self
Structured audit|Controller activity log in Business Edition|Agent-level structured audit and cursor export in every build|self
-Supply-chain evidence|Not published|Cosign signatures + archive/image CycloneDX SBOMs + SLSA provenance|self
+Supply-chain evidence|2.39.5 release has checksums only; agent image carries BuildKit SPDX SBOM and SLSA v0.2 provenance attestations; no cosign signature found|Cosign signatures + archive/image CycloneDX SBOMs + SLSA provenance|self
Fleet agent upgrades and policies|Yes|Not shipped; belongs in Drydock plus packaging|competitor
Host file APIs and Swarm aggregation|Yes|Intentional non-goals|competitor
Prometheus agent scrape endpoint|Not documented|Yes|self
From 3b38addb256862a6cec1662d3aeea11fbf066ef0 Mon Sep 17 00:00:00 2001
From: scttbnsn <80784472+scttbnsn@users.noreply.github.com>
Date: Thu, 8 Oct 2026 16:52:02 -0400
Subject: [PATCH 6/6] fix(website): say what competitors document, and source
the Watchtower image size
The differentiators heading claimed competitors don't cover things the data only shows as undocumented. The README's release-evidence cells for Portainer and Komodo now match the evidence checked today. The Watchtower image figure names its version and platform and has an evidence entry.
---
README.md | 4 ++--
docs/content/docs/competitive-landscape.mdx | 2 +-
docs/content/docs/migrating-from-watchtower.mdx | 2 +-
website/src/components/comparison-page.tsx | 2 +-
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index 767bb016..6b8708d3 100644
--- a/README.md
+++ b/README.md
@@ -403,7 +403,7 @@ flowchart LR
| Agent-level structured audit log | â
| â ī¸ | â ī¸ | â ī¸ | â ī¸ |
| Prometheus scrape endpoint on the agent | â
| ? | ? | ? | ? |
| Read-only MCP server | â
| ? | ? | ? | ? |
-| Signed release evidence (cosign, SBOM, provenance) | â
| ? | ? | â ī¸ | â ī¸ |
+| Signed release evidence (cosign, SBOM, provenance) | â
| â ī¸ | â ī¸ | â ī¸ | â ī¸ |
| Fleet UI and controller workflows | â | â
| â
| â
| ? |
| License | AGPL-3.0 | Zlib (agent) / proprietary Business features | GPL-3.0 | BSD-3-Clause | MIT |
@@ -437,7 +437,7 @@ flowchart LR
> Watchtower's upstream project is archived. Diun and Watchtower do update detection; Portwing is the access agent and leaves update decisions to Drydock.
>
-> The remote-agent table is compiled from the published [competitive landscape](https://portwing.codeswhat.com/docs/competitive-landscape), which lists its primary sources and records unknown competitor behavior as "not documented" rather than guessing it absent. Compared versions: Portainer 2.39.5, Komodo Periphery v2.3.2, Arcane Agent v2.10.1, Hawser v0.2.46. Reviewed 2026-08-29; Arcane re-checked 2026-09-02. The update-tools table follows the Diun and Watchtower comparison pages, which pin no version or review date.
+> The remote-agent table is compiled from the published [competitive landscape](https://portwing.codeswhat.com/docs/competitive-landscape), which lists its primary sources and records unknown competitor behavior as "not documented" rather than guessing it absent. Compared versions: Portainer 2.39.5, Komodo Periphery v2.3.2, Arcane Agent v2.10.1, Hawser v0.2.46. Reviewed 2026-08-29; Arcane re-checked 2026-09-02. Portainer and Komodo release evidence checked 2026-10-08. The update-tools table follows the Diun and Watchtower comparison pages, which pin no version or review date.
> Contributions welcome if any information is inaccurate.
diff --git a/docs/content/docs/competitive-landscape.mdx b/docs/content/docs/competitive-landscape.mdx
index 1f20dbd2..d6e6ea56 100644
--- a/docs/content/docs/competitive-landscape.mdx
+++ b/docs/content/docs/competitive-landscape.mdx
@@ -132,5 +132,5 @@ parity. Those capabilities belong in Drydock or a specialized service.
- Portainer [2.39.5 release assets](https://github.com/portainer/portainer/releases/tag/2.39.5) and the `portainer/agent:2.39.5` image manifest and attestations, checked October 8, 2026
- Komodo [v2.3.2 release assets](https://github.com/moghtech/komodo/releases/tag/v2.3.2) and the `ghcr.io/moghtech/komodo-periphery:2.3.2` image manifest and attestations, checked October 8, 2026
- Komodo outbound-leg issues: [proxy env vars ignored](https://github.com/moghtech/komodo/issues/1473) and [hardcoded handshake timeout](https://github.com/moghtech/komodo/issues/1518)
-- Watchtower [metrics](https://containrrr.dev/watchtower/metrics/) and [arguments](https://containrrr.dev/watchtower/arguments/) (opt-in, experimental `/v1/metrics`), checked October 8, 2026
+- Watchtower [metrics](https://containrrr.dev/watchtower/metrics/) and [arguments](https://containrrr.dev/watchtower/arguments/) (opt-in, experimental `/v1/metrics`), checked October 8, 2026; `containrrr/watchtower:1.7.1` linux/amd64 image manifest (`sha256:f9086bfda061âĻ`, 5.63 MB of compressed layers), measured October 8, 2026
- Diun [metrics configuration](https://crazymax.dev/diun/config/metrics/) (opt-in metrics server, `:9090/metrics`), checked October 8, 2026
diff --git a/docs/content/docs/migrating-from-watchtower.mdx b/docs/content/docs/migrating-from-watchtower.mdx
index 6846d9e3..65d73b83 100644
--- a/docs/content/docs/migrating-from-watchtower.mdx
+++ b/docs/content/docs/migrating-from-watchtower.mdx
@@ -29,7 +29,7 @@ Drydock + Portwing invert the model. Portwing runs on each host and exposes the
| Container exec / terminal | No | Yes (WebSocket) |
| Container filtering | Label `com.centurylinklabs.watchtower.enable` | Label `dd.watch=true` |
| Self-hosted server required | No | Yes (Drydock) |
-| Image size (compressed) | ~6 MB | ~45 MB (Portwing, ~10 MB binary) + Drydock server |
+| Image size (compressed) | ~6 MB (v1.7.1, linux/amd64) | ~45 MB (Portwing, ~10 MB binary) + Drydock server |
Portwing does **not** automatically pull and recreate containers on a schedule. That decision is made by the Drydock controller through its UI and notification pipeline â not by the agent. If you relied on Watchtower's fully autonomous update loop, you will need to configure update policies in Drydock.
diff --git a/website/src/components/comparison-page.tsx b/website/src/components/comparison-page.tsx
index 5775c17a..dba4a486 100644
--- a/website/src/components/comparison-page.tsx
+++ b/website/src/components/comparison-page.tsx
@@ -201,7 +201,7 @@ export function ComparisonPage({
Key Differentiators
- {`What we built that ${competitorName} doesn't cover.`}
+ {`Where Portwing goes beyond what ${competitorName} documents.`}