Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
8fc5e7b
fix: replace broken backage badge with self-hosted pkgbadge
web-flow Mar 10, 2026
7576021
fix: NPM port URLs resolving to Sentinel domain for all containers
web-flow Mar 11, 2026
7750407
fix: skip wildcard domains in NPM port URL resolution
web-flow Mar 11, 2026
383d9a8
docs: add Docker Hub pulls badge to README
web-flow Mar 11, 2026
18f6bc9
docs: replace fake email with GitHub security advisory link
web-flow Mar 11, 2026
2ed1308
docs: fix phantom email in CODE_OF_CONDUCT, outdated version in DESIG…
web-flow Mar 11, 2026
d649a87
fix: add bottom border to filter bar for visual consistency (#61)
web-flow Mar 11, 2026
0f1d0f2
fix: Portainer connector hot-reload, stale credentials, and duplicate…
web-flow Mar 11, 2026
bf24b9b
fix: pending updates stat card count mismatch and remove tick icon
web-flow Mar 11, 2026
8617738
fix: container detail page for Portainer-managed containers
web-flow Mar 11, 2026
811cedc
docs: add Portainer detail page and stat card fixes to changelog
web-flow Mar 11, 2026
9e753af
fix: use scoped key for history/snapshot lookups on remote containers
web-flow Mar 11, 2026
77305f6
docs: add multi-instance Portainer design spec
web-flow Mar 11, 2026
d9d4e15
docs: add multi-instance Portainer implementation plan
web-flow Mar 11, 2026
c5b172e
feat: add portainer_instances BoltDB bucket with CRUD operations
web-flow Mar 11, 2026
2a64a2e
feat: add migration from old portainer settings to instance record
web-flow Mar 11, 2026
f6e10b4
feat: migrate queue/history HostID fields for portainer instance IDs
web-flow Mar 11, 2026
802113f
feat: multi-instance Portainer scanning with per-endpoint filtering
web-flow Mar 11, 2026
cbc7f31
feat: add IsLocalSocket detection for Portainer endpoints
web-flow Mar 11, 2026
4638856
feat: multi-instance Portainer API endpoints with local socket detection
web-flow Mar 11, 2026
13bcd06
feat: wire multi-instance Portainer adapters and migration on boot
web-flow Mar 11, 2026
854f0d3
feat: Portainer containers appear as dashboard host groups
web-flow Mar 11, 2026
7e5c8e1
feat: multi-instance Portainer connector UI with endpoint toggles
web-flow Mar 11, 2026
df74e23
docs: add multi-instance Portainer to changelog
web-flow Mar 11, 2026
9496794
fix: runtime scanner management, TLS skip, and CSRF token calls
web-flow Mar 11, 2026
47101c2
docs: add bug fixes from live testing to changelog
web-flow Mar 11, 2026
0a63af9
fix: auto-block local socket Portainer endpoints
web-flow Mar 11, 2026
229e0c5
docs: add local socket blocking fix to changelog
web-flow Mar 11, 2026
6da699b
fix: smart local socket blocking for multi-portainer
web-flow Mar 11, 2026
e3229bc
docs: update changelog with smart blocking and engine wiring fixes
web-flow Mar 11, 2026
e5fd7eb
feat: Portainer self-update via portainer-updater helper
web-flow Mar 12, 2026
e8c2c27
fix: NPM resolver auto-detects local IPs to prevent cross-host port s…
web-flow Mar 12, 2026
be014ae
fix: history page scan summary rows display correctly
web-flow Mar 12, 2026
24bf587
fix: align Summary badge and duration under correct column headers
web-flow Mar 12, 2026
55209dc
docs: add history page scan summary fix to changelog
web-flow Mar 12, 2026
2fa2e50
fix: record failed approvals in update history
web-flow Mar 12, 2026
9c31028
fix: use red badge for unused images on the images page
web-flow Mar 12, 2026
d531086
fix: align Size, Status, and Actions columns on the images page
web-flow Mar 12, 2026
8ad4509
docs: add images page alignment and unused badge fixes to changelog
web-flow Mar 12, 2026
3bae22d
docs: add 4 missing fixes to changelog (filter bar, scoped keys, NPM)
web-flow Mar 12, 2026
914a647
fix: include NewImage in failed approval history records
web-flow Mar 12, 2026
ca93028
fix: add 2s timeout to DNS lookup in isLocalPortainerInstance
web-flow Mar 12, 2026
7d3f463
fix: guard portainerInstances with RWMutex to prevent data race
web-flow Mar 12, 2026
d23efaf
fix: handle Docker Hub canonical prefixes in IsPortainerImage
web-flow Mar 12, 2026
0486e35
fix: use colon-based detection for already-migrated portainer keys
web-flow Mar 12, 2026
1c18dec
fix: exclude container-local IPs from NPM host filtering
web-flow Mar 12, 2026
21f05c9
fix: use per-request host IP for NPM lookup to prevent cross-host por…
web-flow Mar 12, 2026
979c635
fix: Portainer routing priority and digest comparison for update dete…
web-flow Mar 12, 2026
ae92d24
fix: Portainer updates via streaming pull + history recording
web-flow Mar 12, 2026
aa9e305
fix: add SENTINEL_CLUSTER_ADVERTISE for TLS cert SANs
web-flow Mar 12, 2026
6ff8610
chore: add .worktrees/ to gitignore for isolated development
web-flow Mar 13, 2026
dd75031
feat: source deduplication via Docker Engine ID fingerprinting
web-flow Mar 13, 2026
25926b8
fix: host-group dividers not spanning full table width (#62)
web-flow Mar 13, 2026
27891cd
fix: remove phantom 7th column from swarm service rows (#62)
web-flow Mar 13, 2026
f6b129c
docs: add issue #62 fixes to changelog
web-flow Mar 13, 2026
04e1972
fix: dashboard stuck on Updating after queue approval
web-flow Mar 13, 2026
b75db38
feat: actionable CA mismatch guidance for cluster agents
web-flow Mar 13, 2026
4a8f4e7
docs: changelog entries for CA mismatch guidance and cluster troubles…
web-flow Mar 13, 2026
58ba99c
fix: show stopped containers by default on dashboard (#63)
web-flow Mar 13, 2026
228d9d2
docs: add issue #63 fix to changelog
web-flow Mar 13, 2026
fa32a2a
fix: align swarm task row columns with table header (#64)
web-flow Mar 13, 2026
0afa779
docs: add issue #64 fix to changelog
web-flow Mar 13, 2026
6820f66
build: rebuild frontend bundle with issue #64 fix
web-flow Mar 13, 2026
cd1ba12
fix: correct colspan on scaled-to-0 placeholder row (#64)
web-flow Mar 13, 2026
57d5eb0
docs: update issue #64 changelog with colspan root cause
web-flow Mar 13, 2026
c5dc135
fix: cache swarm tasks so shutdown rows persist across page refresh (…
web-flow Mar 13, 2026
eb46abc
fix: data race on portainerInstances, adapter field loss, zero-value …
web-flow Mar 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ CLAUDE.md
sentinel
docs/wiki/
docs/reddit-post.md
.worktrees/
173 changes: 173 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,179 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- **Source deduplication.** When the same Docker host is reachable via multiple
sources (local socket, cluster agent, Portainer connector), Sentinel now
detects the overlap using Docker Engine IDs and auto-blocks the lower-priority
source. Priority order: local > cluster agent > Portainer. Blocked endpoints
show the overlap reason on the Connectors page with a "Force Enable" button
for user overrides. Cluster agents report their Engine ID in the StateReport
proto; Portainer endpoints are probed via the Docker info API on Test
Connection. An SSE `source_overlap` event notifies the dashboard in real time.
- **Multi-instance Portainer support.** Connect to multiple Portainer servers,
each with per-endpoint enable/disable toggles. Local Docker socket endpoints
are auto-detected and blocked to prevent duplicate monitoring.
- **Portainer containers on dashboard.** Portainer endpoint containers appear
as host groups on the dashboard, with full policy, queue, severity, and
maintenance support (same as cluster hosts).
- **Connectors page redesigned.** Portainer tab now shows instance cards with
add/remove/test/configure workflow, replacing the single URL+token form.
- **Portainer self-update via portainer-updater.** Portainer containers detected
by Sentinel can now be safely updated without crashing the Portainer API.
Uses the official `portainer/portainer-updater` helper container, which mounts
the Docker socket directly and survives the Portainer stop/recreate cycle.
Works for both queue approvals and manual "update to version" actions.

### Improved
- **Agent CA mismatch detection.** When the server's TLS CA has changed (volume
deleted, host migration), agents now detect the `x509` certificate error and
log a single clear ERROR message with fix steps: stop the agent, delete the
cluster data directory, and re-enroll with a fresh token. Subsequent reconnect
attempts log only at WARN level to avoid flooding.
- **Cluster page troubleshooting for all disconnected hosts.** The troubleshoot
section now appears for every disconnected host, not just those with a known
disconnect category. A new generic fallback covers the three common causes:
agent not running, network issue, and CA certificate mismatch after server
volume recreation.

### Fixed
- **Stopped swarm tasks vanish on page refresh (#65).** When a swarm service is
scaled to 0, Docker removes all tasks within seconds. On page refresh, the
server-side template received no task data and showed a generic "Service
scaled to 0" placeholder instead of the actual task rows with node names and
SHUTDOWN badges. Added an in-memory task cache to `swarmAdapter` that
preserves the last-seen running tasks per service. When a service has 0
desired replicas and Docker returns no tasks, the cache serves them with
state overridden to "shutdown". The JS-side `_svcTaskCache` still handles
the instant UI update during scale-down; the server cache handles persistence
across page refreshes.
- **Swarm service table shrinks when services stopped (#64).** The server-side
"Service scaled to 0" placeholder row had `colspan="6"` after an empty
checkbox cell, creating a 7-column total in a 6-column `table-layout:fixed`
table. This forced the browser to redistribute column widths, visibly
shrinking the table whenever any swarm service was at 0 replicas. Changed to
`colspan="5"` (1 + 5 = 6). Also fixed JS-created task rows that had the wrong
cell count and missing `col-*` classes for column alignment.
- **Stopped containers hidden by default on dashboard (#63).** Fresh databases
had no `show_stopped` setting in BoltDB, but `LoadSetting` returns an empty
string with no error for missing keys. The handler treated this as `false`,
hiding all non-running containers. Now only an explicit user preference
overrides the default (show all).
- **Portainer runtime scanner creation.** Adding a Portainer instance via the
UI now creates a live scanner immediately. Previously, instances added after
boot had no scanner until the next restart.
- **Portainer TLS verification.** Portainer connections now skip TLS certificate
verification, fixing failures with self-signed certs (standard in homelab and
private network setups).
- **Connectors page CSRF token.** Fixed `csrfToken` function reference being
passed as a header value instead of being called, which broke all fetch
requests on the connectors page.
- **Local socket endpoints not blocked.** `IsLocalSocket()` was defined but
never called. Now auto-blocks local Docker socket endpoints during Test
Connection and excludes them from scanning.
- **Smart local socket blocking.** `unix://` endpoints are only auto-blocked
when the Portainer instance runs on the same host as Sentinel. Previously
all `unix://` endpoints were blocked, which incorrectly disabled remote
Portainer instances whose Docker sockets are valid monitoring targets.
- **Runtime Portainer instances not scanned.** Portainer instances added via
the UI after boot were saved to the store and had scanners for API calls,
but were invisible to the scan engine. Now `ConnectInstance` registers
with both the web layer and the engine, so runtime-added instances are
scanned immediately. Endpoint config changes (enabled/blocked) are also
synced to the engine without requiring a restart.
- **Queue approval for Portainer containers.** Approving a Portainer-managed
container from the Pending Updates queue previously fell through to the
local Docker daemon (which can't reach remote containers). Now routes
through the Portainer API, with Portainer image detection for self-update.
- **NPM resolver cross-host port shadowing.** When multiple NPM proxy hosts
forwarded the same port to different IPs (e.g. port 8080 on both .57 and
.64), the resolver matched the lowest NPM ID regardless of which host the
container actually ran on. Replaced the single `SENTINEL_HOST` string match
with auto-detection of local network addresses via `net.InterfaceAddrs()`,
hostname, and `host.docker.internal` DNS. `SENTINEL_HOST` is still honoured
as an additive override for containerised deployments where bridge networking
hides the host's LAN IP.
- **Failed approvals missing from history.** When an approved update failed
(e.g. Portainer agent disconnected, network error), only a log line was
written. The update vanished from the queue with no history record. Now
records a "failed" entry with the error message and elapsed duration.
- **History page scan summary display.** Scan summary rows were rendered as
regular container rows, causing truncated text in the Container column and
broken `/container/history-0` URLs when clicked. Now render with colspan
spanning Container + Version columns, non-clickable, with proper text
wrapping and column alignment at all viewport widths.
- **Dashboard table squashed when Swarm Services present (#62).** Swarm
service rows had a phantom 7th `col-actions` cell that regular container
rows and the thead did not. With `table-layout: fixed`, this created an
invisible extra column that stole ~300px of width, pushing the entire table
left and preventing dividers from spanning the full UI width. Removed the
unused cells so all rows consistently have 6 columns.
- **Host-group dividers not spanning full width (#62).** The `tbody
tr:last-child { border-bottom: none }` rule removed the bottom border from
each host-group's last row, so consecutive host groups had no visible
separator. Added a more-specific `.host-group tr:last-child` override.
Also moved the Swarm Services section-divider border from the inner div
to the `<td>` so it spans the full table width in `border-collapse` mode.
- **Dashboard stuck on "Updating" after queue approval.** Approving updates
from the Pending Updates page and navigating to the Dashboard could leave
containers showing "Updating" indefinitely. The update completed and
cleared the maintenance flag, but the SSE event was published before the
Dashboard's EventSource connection was established -- a classic missed-event
race. Added a catch-up fetch on SSE connect: the Dashboard now scans for
any rows still showing "Updating" and re-fetches their current state from
the server, picking up the cleared maintenance flag.
- **Images page column alignment.** Size, Status, and Actions columns were
left-aligned while their content (numbers, badges, buttons) sat off-centre.
Size is now right-aligned, Status and Actions are centred. Unused badge
changed from grey to red for better visibility.
- **Portainer connector: hot-reload without restart.** Saving Portainer URL
and API token in the UI now takes effect immediately. Previously the test
button always returned "not configured" because the provider was only
created at startup. Uses the same factory pattern as the NPM connector.
- **Portainer connector: stale credentials after token change.** The connection
test now always recreates the provider from current DB settings, so changing
the URL or token and re-testing uses the new values instead of the ones
from startup.
- **Portainer duplicate queue entries.** When a Portainer endpoint pointed at
the same Docker socket Sentinel runs on, every container was scanned twice
(once locally, once via Portainer), creating duplicate queue entries. The
Portainer scan now collects local container IDs and skips any Portainer
container whose Docker ID matches a locally-monitored container.
- **Portainer API token help text.** Corrected the path from "Settings > Users >
Access tokens" to "My account > Access tokens".
- **Portainer container detail page 404.** Clicking a Portainer container in
the pending updates queue returned "Container not found" because the detail
handler only knew about local and cluster containers. Now resolves Portainer
containers via the Portainer API.
- **Dashboard stat card count mismatch.** The "Updates Pending" stat card only
counted local containers, while the nav badge counted all queue items
including Portainer. Both now use the full queue length. Removed the
redundant checkmark icon from the zero-state.
- **Filter bar missing bottom border.** The filter bar on multiple pages lacked
a visual divider separating it from the table content below.
- **Remote container history/snapshot key mismatch.** History and snapshot
lookups for cluster and Portainer containers used the plain container name
instead of the scoped `hostID::name` key, returning empty results.
- **NPM port URLs for all containers.** NPM URL resolution matched every
container against Sentinel's own domain when no `SENTINEL_HOST` was set,
causing all containers to show the same proxy URL.
- **NPM wildcard domain resolution.** Proxy hosts with wildcard domains
(e.g. `*.s3.garage.example.com`) produced broken URLs. Now skips wildcard
entries and picks the first non-wildcard domain.

### Changed
- **Queue/history key format.** Portainer HostIDs changed from `portainer:N`
to `portainer:instanceID:N`. Existing queue and history entries are
automatically migrated on first boot.
- **Portainer settings storage.** Old flat settings (`portainer_url`,
`portainer_token`, `portainer_enabled`) are migrated to a structured
`portainer_instances` BoltDB bucket on first boot. The migration is
idempotent and safe to re-run.
- **Portainer integration descriptions.** Updated the vague "view endpoints"
description to accurately explain that Sentinel scans Portainer endpoints
for updates, applies policies, and can redeploy stacks or update standalone
containers via Portainer's API.

## [2.11.1] - 2026-03-10

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Examples of unacceptable behaviour:
## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behaviour may be
reported by contacting the project team at **conduct@pphserv.uk**.
reported by [opening a discussion](https://github.com/Will-Luck/Docker-Sentinel/discussions) or contacting the maintainer via their [GitHub profile](https://github.com/Will-Luck).

All complaints will be reviewed and investigated promptly and fairly.

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
[![Go Report Card](https://goreportcard.com/badge/github.com/Will-Luck/Docker-Sentinel)](https://goreportcard.com/report/github.com/Will-Luck/Docker-Sentinel)
[![GHCR](https://img.shields.io/badge/ghcr.io-docker--sentinel-blue)](https://github.com/Will-Luck/Docker-Sentinel/pkgs/container/docker-sentinel)
[![GitHub Downloads](https://img.shields.io/github/downloads/Will-Luck/Docker-Sentinel/total)](https://github.com/Will-Luck/Docker-Sentinel/releases)
[![GHCR Pulls](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fipitio.github.io%2Fbackage%2FWill-Luck%2FDocker-Sentinel%2Fdocker-sentinel.json&query=%24.downloads&label=ghcr%20pulls)](https://github.com/Will-Luck/Docker-Sentinel/pkgs/container/docker-sentinel)
[![GHCR Pulls](https://img.shields.io/endpoint?url=https%3A%2F%2Fpkgbadge.pphserv.uk%2Fwill-luck%2Fdocker-sentinel%2Fpulls.json)](https://github.com/Will-Luck/Docker-Sentinel/pkgs/container/docker-sentinel)
[![Docker Pulls](https://img.shields.io/docker/pulls/willluck/docker-sentinel)](https://hub.docker.com/r/willluck/docker-sentinel)

A container update orchestrator with a web dashboard, written in Go. Replaces Watchtower with per-container update policies, pre-update snapshots, automatic rollback, and real-time notifications.

Expand Down
6 changes: 3 additions & 3 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@

| Version | Supported |
| -------- | ------------------ |
| v2.10.x | Yes |
| < v2.10 | No |
| Latest | Yes |
| Older | No |

Only the latest minor release receives security patches. Please upgrade to the latest version before reporting issues.

## Reporting a Vulnerability

**Do not open a public issue for security vulnerabilities.**

Email **security@pphserv.uk** with:
Instead, [open a private security advisory](https://github.com/Will-Luck/Docker-Sentinel/security/advisories/new) on GitHub. Include:

- A description of the vulnerability
- Steps to reproduce
Expand Down
Loading
Loading