Skip to content

feat: minimum image age gate for auto-updates#641

Draft
mgabor3141 wants to merge 2 commits intoFinsys:mainfrom
mgabor3141:feature/minimum-image-age
Draft

feat: minimum image age gate for auto-updates#641
mgabor3141 wants to merge 2 commits intoFinsys:mainfrom
mgabor3141:feature/minimum-image-age

Conversation

@mgabor3141
Copy link

@mgabor3141 mgabor3141 commented Feb 23, 2026

Summary

Add a configurable minimum image age requirement that defers auto-updates until images have been published for a specified number of days. This reduces risk from regressions, supply chain attacks, and quickly-retracted releases — similar to Renovate's minimumReleaseAge feature but adapted for Docker's mutable tag model.

Features

Minimum image age (days)

  • Configurable at environment level (Settings → Environments → Updates tab) and per-container (container auto-update settings)
  • Per-container setting overrides environment default; null = inherit, 0 = disabled
  • Checks the image creation timestamp from the registry (manifest → config blob → created field)
  • If the image is younger than the threshold, the update is deferred with a log message
  • Dates before 2013 (Docker's creation year) or in the future are treated as suspicious and deferred

Security fix bypass

  • Optional toggle: "Bypass age gate for security fixes"
  • When enabled and an update is deferred, Dockhand scans both the current and new images
  • If the new image has strictly fewer critical+high CVEs than the current image, the age gate is bypassed
  • Requires vulnerability scanning to be configured in the environment
  • Tag safety guaranteed via finally blocks that restore original tags on error paths

Per-container exclusion from environment auto-updates

  • New toggle: "Exclude from environment auto-updates"
  • Skips the container during the apply phase of environment-level scheduled updates
  • Update checks still run and report availability (shown in UI, pending updates, and notifications)
  • Independent of per-container auto-update scheduling

Implementation

New function: getImageCreatedDate(imageName) in docker.ts

Fetches the image creation timestamp from the registry via:

  1. GET manifest (resolves manifest list → platform-specific manifest matching host arch via process.arch)
  2. GET config blob → parse created field

Reuses existing getRegistryBearerToken + parseImageReference auth flow. Tested against Docker Hub and GHCR with both OCI and Docker manifest formats.

Age gate insertion points

  • env-update-check.ts: After checkImageUpdateAvailable() confirms an update, before the safe-pull flow. Uses imageAlreadyPulled flag to skip redundant pull/scan when bypass succeeds.
  • container-update.ts: Same pattern, between registry check and pull/scan phases.

New utilities in update-utils.ts

  • shouldDeferUpdate(createdDate, minAgeDays) — age comparison with safe handling of unparseable/suspicious dates (epoch zero, pre-2013, future)
  • shouldBypassAgeForSecurity(currentScan, newScan) — compares critical+high CVE counts

Data model

  • EnvUpdateCheckSettings (JSON in settings table): minimumImageAgeDays, bypassAgeForSecurityFixes
  • auto_update_settings table: 3 new nullable columns — minimum_image_age_days, bypass_age_for_security_fixes, excluded_from_env_update
  • Per-container row is preserved when auto-update is disabled if overrides are set (previously hard-deleted)
  • Server-side validation on both API routes: rejects negative/non-finite values, clamps 0–365, coerces types

UI

  • Environment Updates tab: Number input for minimum age, bypass toggle (shown when age > 0 and scanning enabled)
  • Per-container settings: Age override input (empty = inherit), bypass ToggleGroup (Inherit/Yes/No), exclusion toggle (always visible, independent of auto-update enabled state)
  • Change detection includes new fields so saves aren't silently skipped

Files changed (20 files)

Area Files
Core logic docker.ts, update-utils.ts, env-update-check.ts, container-update.ts
Data model db.ts, schema/index.ts, schema/pg-schema.ts
Migrations drizzle/0004_*, drizzle-pg/0004_*, journal + snapshot files
API routes auto-update/[containerName]/+server.ts, environments/[id]/update-check/+server.ts
UI UpdatesTab.svelte, AutoUpdateSettings.svelte, ContainerSettingsTab.svelte, EditContainerModal.svelte, EnvironmentModal.svelte

@CLAassistant
Copy link

CLAassistant commented Feb 23, 2026

CLA assistant check
All committers have signed the CLA.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a configurable “minimum image age” gate to defer auto-updates until an image has aged past a threshold, with an optional security-fix bypass based on vulnerability scan deltas, plus per-container exclusion from environment-level scheduled updates.

Changes:

  • Add environment + per-container configuration for minimum image age and (optionally) bypassing the age gate when the new image reduces critical/high CVEs.
  • Implement registry lookup for image created timestamp (getImageCreatedDate) and insert age-gate logic into env- and container-level update flows.
  • Extend auto-update settings schema/API/UI, including a per-container “exclude from environment auto-updates” toggle.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/routes/settings/environments/tabs/UpdatesTab.svelte Adds environment-level controls for minimum image age + security bypass toggle.
src/routes/settings/environments/EnvironmentModal.svelte Loads/saves new environment update-check settings fields.
src/routes/containers/EditContainerModal.svelte Loads/saves new per-container override fields for auto-update settings.
src/routes/containers/ContainerSettingsTab.svelte Wires new per-container auto-update props through settings UI.
src/routes/containers/AutoUpdateSettings.svelte Adds per-container minimum age override UI, bypass toggle, and env-update exclusion toggle.
src/routes/api/environments/[id]/update-check/+server.ts Extends env update-check settings API payload with new fields.
src/routes/api/auto-update/[containerName]/+server.ts Extends per-container auto-update settings API + preserves rows when overrides exist.
src/lib/server/scheduler/tasks/update-utils.ts Adds shouldDeferUpdate + shouldBypassAgeForSecurity helpers.
src/lib/server/scheduler/tasks/env-update-check.ts Applies exclusion check and introduces min-age gate + security bypass into env auto-update job.
src/lib/server/scheduler/tasks/container-update.ts Introduces min-age gate + security bypass into container auto-update task.
src/lib/server/docker.ts Implements registry created timestamp lookup via manifest + config blob fetch.
src/lib/server/db/schema/index.ts Adds new SQLite auto_update_settings columns to schema model.
src/lib/server/db/schema/pg-schema.ts Adds new Postgres auto_update_settings columns to schema model.
src/lib/server/db.ts Extends types and upsert logic for new auto-update/env update-check settings fields.
drizzle/meta/_journal.json Adds migration journal entry for new settings columns (SQLite).
drizzle/0004_add_image_age_settings.sql Adds SQLite migration for new auto_update_settings columns.
drizzle-pg/meta/_journal.json Adds migration journal entry for new settings columns (Postgres).
drizzle-pg/0004_add_image_age_settings.sql Adds Postgres migration for new auto_update_settings columns.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@mgabor3141 mgabor3141 force-pushed the feature/minimum-image-age branch 2 times, most recently from ced1746 to 1d2aab8 Compare February 23, 2026 15:55
Add a configurable minimum image age requirement that defers auto-updates
until images have been published for a specified number of days. This
reduces risk from regressions, supply chain attacks, and quickly-retracted
releases.

Features:
- Minimum image age (days) setting at environment and per-container level
- Per-container settings override environment defaults (null = inherit)
- Security fix bypass: when enabled, scans both current and new images;
  if the new image fixes critical/high CVEs, the age gate is bypassed
- Per-container exclusion from environment-level auto-updates (update
  checks still report availability, but auto-update is skipped)

Implementation:
- New getImageCreatedDate() in docker.ts: fetches image creation timestamp
  from registry via manifest + config blob (handles multi-arch images)
- shouldDeferUpdate() and shouldBypassAgeForSecurity() utilities
- Age gate inserted in both env-update-check.ts and container-update.ts,
  between registry check and pull/scan phases
- DB migration adds minimum_image_age_days, bypass_age_for_security_fixes,
  and excluded_from_env_update columns to auto_update_settings table
- EnvUpdateCheckSettings extended with minimumImageAgeDays and
  bypassAgeForSecurityFixes (stored as JSON in settings table)

UI:
- Environment Settings > Updates tab: number input for minimum age,
  toggle for security fix bypass (shown when age > 0 and scanning enabled)
- Per-container auto-update settings: age override (empty = inherit),
  bypass toggle, and exclusion toggle (always visible)
@mgabor3141 mgabor3141 force-pushed the feature/minimum-image-age branch from 1d2aab8 to d523a8c Compare February 23, 2026 19:34
@mgabor3141
Copy link
Author

mgabor3141 commented Feb 23, 2026

Addressing review feedback

Fixed (comments 1, 3, 4, 5, 6, 8, 9)

Comment 1 — Excluded containers skip update checks too early:
Moved the excludedFromEnvUpdate check from the container discovery loop to the apply phase. Excluded containers now still get update availability checks (and appear in pending updates / notifications), but are skipped when applying updates.

Comment 3 — Bypass toggle visibility when env age is 0:
Changed condition from (minimumImageAgeDays ?? 1) > 0 to minimumImageAgeDays !== 0. Now the bypass toggle hides only when the per-container age is explicitly set to 0 (disabled). When null (inherit), it remains visible since the environment default may be >0.

Comment 4 — Missing input validation on per-container API:
Added server-side validation: rejects negative/non-finite values for minimumImageAgeDays, clamps to 0–365, coerces bypassAgeForSecurityFixes to boolean | null, and excludedFromEnvUpdate to boolean.

Comment 5 — Hardcoded amd64 for multi-arch manifest resolution:
Now uses process.arch to match the host platform (arm64, arm, or amd64) instead of hardcoding amd64.

Comments 6 & 9 — Tag safety in security bypass flow:
Added finally blocks in both container-update.ts and env-update-check.ts that restore the original image tag whenever a pull was performed but the bypass didn't succeed. This prevents the local tag from being left pointing at the new image on error paths.

Comment 8 — Missing validation on env update-check API:
Added validation: rejects negative/non-finite minimumImageAgeDays, clamps to 0–365, coerces bypassAgeForSecurityFixes to boolean.

Acknowledged but deferred (comments 2, 7)

Comment 2 — N+1 queries for getAutoUpdateSetting:
Valid optimization concern. With the exclusion check moved to the apply loop (comment 1 fix), getAutoUpdateSetting is now called once per update (not per container), which is a much smaller set. A batch prefetch could be added as a follow-up but isn't a correctness issue.

Comment 7 — Duplicated pull/tag/scan flow:
Agreed that extracting a shared helper would reduce duplication. However, this would significantly increase the diff size and touch the existing safe-pull flow. I'd prefer to do this as a separate refactoring PR to keep this one focused on the feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants