Skip to content

Releases: netresearch/ofelia

v0.23.1

23 Mar 22:55
v0.23.1
9b41451

Choose a tag to compare

What's Changed

Security

  • Migrate go-viper/mapstructure v1 to v2.4.0 — fixes GO-2025-3787 and GO-2025-3900 (sensitive information leak in logs when processing malformed data) (#544)

Fixed

  • Release pipeline migrated from slsa-github-generator to actions/attest-build-provenance via org-wide reusable workflow at netresearch/.github (#542). This fixes the v0.23.0 release which had no binaries due to SHA-pinning conflict.

Note: v0.23.0 was released without binaries due to the pipeline issue fixed in this release. Users should use v0.23.1 instead.

v0.23.0

22 Mar 16:13
v0.23.0
59b52a1

Choose a tag to compare

What's Changed

Environment File & Container Env Support

Load environment variables from external sources for all job types (#540, closes #314, #336, #351):

  • env-file: Load KEY=VALUE pairs from files, like Docker's --env-file. Supports multiple files, quoted values, export prefix, and special characters.
  • env-from: Copy environment variables from a running Docker container at job execution time.

Merge order (last wins): env-file < env-from < environment (explicit always wins).

Bug Fixes

  • Environment variable values containing # or ; are no longer truncated by INI comment parsing (#539, fixes #538)
  • Environment variable expansion now works in webhook config values, section names, and the log-level pre-parse path (#539)

Security

  • SHA-pin all GitHub Actions and add Dependabot for actions updates (#536)

v0.22.0

20 Mar 10:11
v0.22.0
793791c

Choose a tag to compare

What's Changed

Environment Variable Substitution in INI Config

Support ${VAR} and ${VAR:-default} syntax in INI configuration files (#532, closes #362).

Keep secrets out of version-controlled config files:

[global]
smtp-password = ${SMTP_PASS}

[job-run "backup"]
image = ${BACKUP_IMAGE:-postgres:15}
command = pg_dump ${DB_NAME:-mydb}
  • ${VAR} — replaced if defined and non-empty; kept literal if undefined (typos visible)
  • ${VAR:-default} — uses default when undefined or empty
  • $VAR (no braces) — not substituted, keeping cron expressions and shell commands safe

Thanks to @nut-neek for describing their use case.

Dependencies

  • Bump OpenTelemetry modules to v1.42.0 (#533)
  • Bump step-security/harden-runner to v2.16.0 (#533)
  • Bump aquasecurity/trivy-action to v0.35.0 (#532)
  • Bump google.golang.org/grpc to v1.79.3 (#531)

Full Changelog: v0.21.5...v0.22.0

Verification

All binaries include SLSA Level 3 provenance attestations.

Verify binary provenance

slsa-verifier verify-artifact ofelia-linux-amd64 \
  --provenance-path ofelia-linux-amd64.intoto.jsonl \
  --source-uri github.com/netresearch/ofelia

Verify checksums signature

cosign verify-blob \
  --certificate checksums.txt.pem \
  --signature checksums.txt.sig \
  --certificate-identity "https://github.com/netresearch/ofelia/.github/workflows/release-slsa.yml@refs/tags/v0.22.0" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  checksums.txt

Included in this release

View all PRs and Issues included in this release

v0.21.5

18 Mar 08:14
v0.21.5
76f8a19

Choose a tag to compare

What's Changed

Two new features for this release.

Thanks to @sethlinnkuleuven for reporting #527.

Added

  • ofelia version command (#528): Print version information via ofelia version or ofelia --version. Release builds show tag and commit; dev builds fall back to Go build info.
  • Volume support for job-service-run (#529, closes #527): Mount host directories and named volumes into swarm service containers using the volume config key. Same source:target[:ro|rw] format as job-run.
[job-service-run "backup"]
schedule = @daily
image = postgres:15
network = cluster
volume = /host/script.sh:/script.sh
volume = backups:/backups:rw
command = /script.sh

Full Changelog: v0.21.4...v0.21.5

Verification

All binaries include SLSA Level 3 provenance attestations.

Verify binary provenance

slsa-verifier verify-artifact ofelia-linux-amd64 \
  --provenance-path ofelia-linux-amd64.intoto.jsonl \
  --source-uri github.com/netresearch/ofelia

Verify checksums signature

cosign verify-blob \
  --certificate checksums.txt.pem \
  --signature checksums.txt.sig \
  --certificate-identity "https://github.com/netresearch/ofelia/.github/workflows/release-slsa.yml@refs/tags/v0.21.5" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  checksums.txt

Included in this release

View all PRs and Issues included in this release

v0.21.4

17 Mar 00:05
v0.21.4
2414bf2

Choose a tag to compare

What's Changed

Fix job-service-run network attachment being silently dropped — services now correctly join the specified network.

Thanks to @sethlinnkuleuven for reporting the issue.

Fixed

  • Service network not attached (#524): convertToSwarmSpec now reads networks from both ServiceSpec.Networks and TaskTemplate.Networks, fixing a mismatch introduced during the domain/adapter refactor
  • Service inspect missing fields: convertFromSwarmService now converts Mounts, RestartPolicy, Resources, Networks, Mode, Placement, LogDriver, and EndpointSpec — all were previously lost silently

Added

  • Swarm service adapter now converts Placement, LogDriver, and EndpointSpec in both directions — these domain types existed but had no conversion code
  • 13 round-trip tests verifying every service spec field survives the domain→swarm→domain conversion cycle

Full Changelog: v0.21.3...v0.21.4

Verification

All binaries include SLSA Level 3 provenance attestations.

Verify binary provenance

slsa-verifier verify-artifact ofelia-linux-amd64 \
  --provenance-path ofelia-linux-amd64.intoto.jsonl \
  --source-uri github.com/netresearch/ofelia

Verify checksums signature

cosign verify-blob \
  --certificate checksums.txt.pem \
  --signature checksums.txt.sig \
  --certificate-identity "https://github.com/netresearch/ofelia/.github/workflows/release-slsa.yml@refs/tags/v0.21.4" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  checksums.txt

Included in this release

View all PRs and Issues included in this release

v0.21.3

15 Mar 14:45
v0.21.3
7216aef

Choose a tag to compare

What's Changed

Wire missing container spec fields that the Docker API already supports but job types didn't expose, fixing environment not working for job-service-run (#519).

Thanks to @sethlinnkuleuven for reporting the issue.

Fixed

  • job-service-run: environment, hostname, and dir config keys now work — passed through to Docker Swarm ContainerSpec
  • job-run: working-dir config key now sets the container working directory; volumes-from was defined in the struct but never wired to Docker — now functional
  • job-exec: privileged config key now enables privileged exec mode
  • Corrected misleading documentation that claimed job-service-run inherits from RunJob (it embeds BareJob)
  • Removed non-existent swarm fields (replicas, placement-constraints, resource limits) from documentation examples

Full Changelog: v0.21.2...v0.21.3


Included in this release

View all PRs and Issues included in this release

v0.21.2

14 Mar 15:22
v0.21.2
f988960

Choose a tag to compare

Highlights

This security-focused release addresses 5 vulnerabilities and 6 stability issues discovered during a comprehensive code review.

Security hardening

  • Credential leak prevention: /api/config no longer exposes WebPasswordHash and WebSecretKey
  • CSRF bypass removed: The X-Requested-With header bypass has been eliminated
  • Rate limiter DoS fix: Stale entries are now cleaned up to prevent unbounded memory growth
  • IP spoofing prevention: X-Forwarded-For and X-Real-IP headers are only trusted from loopback or explicitly configured proxies
  • Configurable trusted proxies: New web-trusted-proxies option for deployments behind reverse proxies in non-loopback networks

Stability improvements

  • Context propagation to Docker API calls — scheduler shutdown, job removal, and max-runtime cancellation now reach Docker containers
  • Double-close panic on daemon done channel fixed with sync.Once
  • Concurrent map access crash in Config protected with mutex
  • Shutdown hooks execute in priority groups instead of all concurrently
  • Shutdown timeout now enforced even when hooks ignore context cancellation
  • Swarm services correctly return NonZeroExitError for non-zero exit codes

Changes

Security

  • fix(security): hide WebPasswordHash and WebSecretKey from /api/config (#511)
  • fix(security): remove CSRF bypass via X-Requested-With header (#511)
  • fix(security): implement rate limiter cleanup to prevent memory DoS (#511)
  • fix(security): only trust forwarded headers from trusted proxies (#511)
  • fix(security): make trusted proxies configurable (#511)
  • fix(security): also check X-Real-IP in rate limiter middleware (#511)

Bug Fixes

  • fix: propagate context to Docker API calls for cancellation support (#511)
  • fix: prevent double-close panic on daemon done channel (#511)
  • fix: add mutex to Config to prevent concurrent map access crash (#511)
  • fix: execute shutdown hooks in priority groups (#511)
  • fix: enforce shutdown timeout even when hooks ignore context (#511)
  • fix: return NonZeroExitError for non-zero Swarm service exit codes (#511)

Dependencies

  • chore(deps): bump golang.org/x/crypto from 0.48.0 to 0.49.0 (#512)
  • chore(deps): bump github.com/netresearch/go-cron from 0.13.0 to 0.13.1 (#514)
  • chore(deps): bump golang.org/x/time from 0.14.0 to 0.15.0 (#515)

Verification

All binaries include SLSA Level 3 provenance attestations.

Verify binary provenance

slsa-verifier verify-artifact ofelia-linux-amd64 \
  --provenance-path ofelia-linux-amd64.intoto.jsonl \
  --source-uri github.com/netresearch/ofelia

Verify checksums signature

cosign verify-blob \
  --certificate checksums.txt.pem \
  --signature checksums.txt.sig \
  --certificate-identity "https://github.com/netresearch/ofelia/.github/workflows/release-slsa.yml@refs/tags/v0.21.2" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  checksums.txt

Included in this release

View all PRs and Issues included in this release

v0.21.1

11 Mar 07:46
v0.21.1
7cfe95a

Choose a tag to compare

Highlights

Fix duration string parsing in configuration (#510)

Duration fields like max-runtime, notification-cooldown, and webhook timeout now correctly accept Go duration strings (1h, 30m, 55s, 1h30m) in INI config files and Docker labels. Previously, these values failed with strconv.ParseInt errors.

Before (broken):

[job-run "my-job"]
schedule = @hourly
image = myimage
max-runtime = 1h  # ❌ strconv.ParseInt: parsing "1h": invalid syntax

After (fixed):

[job-run "my-job"]
schedule = @hourly
image = myimage
max-runtime = 1h  # ✅ works as expected

All 10 time.Duration config fields are fixed: max-runtime, notification-cooldown, config-poll-interval, docker-poll-interval, polling-fallback, poll-interval, restore-history-max-age, webhook timeout, retry-delay, preset-cache-ttl.

Fixes #509. Thanks to @azlux for reporting the issue!

Changes

Bug Fixes

  • fix: add StringToTimeDurationHookFunc to mapstructure decoders (#510)

Verification

All binaries include SLSA Level 3 provenance attestations.

Verify binary provenance

slsa-verifier verify-artifact ofelia-linux-amd64 \
  --provenance-path ofelia-linux-amd64.intoto.jsonl \
  --source-uri github.com/netresearch/ofelia

Verify checksums signature

cosign verify-blob \
  --certificate checksums.txt.pem \
  --signature checksums.txt.sig \
  --certificate-identity "https://github.com/netresearch/ofelia/.github/workflows/release-slsa.yml@refs/tags/v0.21.1" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  checksums.txt

Included in this release

View all PRs and Issues included in this release

v0.21.0

07 Mar 20:30
v0.21.0
42cdbc9

Choose a tag to compare

Highlights

Native go-cron DAG workflow engine (#499)

Replaced the custom WorkflowOrchestrator with go-cron's native DAG engine. Job dependencies, on-success, and on-failure chains now execute through go-cron's built-in directed acyclic graph scheduler, removing significant custom code while improving reliability.

Native pause/resume for job disable/enable (#497)

Job enable/disable now uses go-cron's native pause/resume API instead of removing and re-adding cron entries. This preserves job state and is more efficient. Disabled (paused) jobs correctly skip NextRuns/PrevRuns in the API.

Schedule introspection API (#495)

New PrevN/NextN endpoints expose upcoming and past scheduled run times for any job via the web API, useful for dashboards and monitoring.

Workflow observability (#496)

Added workflow completion hooks and metrics — track DAG workflow execution, step durations, and outcomes through the metrics subsystem.

Rate limiting via go-cron middleware (#500)

Replaced the custom scheduler semaphore with go-cron's MaxConcurrentSkip middleware for cleaner concurrency control.

Go 1.26.1 security update (#506)

Updated to Go 1.26.1 fixing 5 stdlib vulnerabilities: crypto/x509 certificate handling, net/url IPv6 parsing, os root escape, and html/template XSS.

Test coverage 60% → 86% (#502, #503)

Comprehensive test coverage improvements across core, CLI, web, middlewares, and Docker adapter packages.

Changes

Features

  • feat: add PrevN/NextN schedule introspection to web API (#495)
  • feat: add workflow completion observability hooks and metrics (#496)
  • feat: use native go-cron pause/resume for job disable/enable (#497)
  • feat: use native go-cron @triggered schedule instead of manual handling (#498)
  • feat: replace custom WorkflowOrchestrator with go-cron native DAG engine (#499)
  • feat: replace scheduler semaphore with go-cron MaxConcurrentSkip middleware (#500)

Bug Fixes

  • fix: don't track internal labels as config params (#485)
  • fix: add allow-list for global config keys from Docker labels (#487)
  • fix: update Go to 1.26.1 to fix stdlib vulnerabilities (#506)
  • fix: eliminate data races detected by -race flag
  • fix(web): improve API error handling, status codes, auth panic, rate limiter leak
  • fix(core): nil safety, idempotent EnableJob, remove dead WaitGroup

Testing

  • test: comprehensive test coverage improvement 60% → 82.5% (#502)
  • test: increase coverage from 82.5% to 86.2% (#503)

Dependencies

  • chore(deps): bump go-cron from 0.11.0 → 0.13.0 (quoted TZ values, DAG engine, pause/resume)
  • chore(deps): bump docker/cli to 29.3.0
  • chore: add SPDX headers and DCO enforcement (#501)
  • chore: remove flaky CodSpeed benchmarks from CI (#507)

Full Changelog: v0.20.0...v0.21.0

Verification

All binaries include SLSA Level 3 provenance attestations.

Verify binary provenance

slsa-verifier verify-artifact ofelia-linux-amd64 \
  --provenance-path ofelia-linux-amd64.intoto.jsonl \
  --source-uri github.com/netresearch/ofelia

Verify checksums signature

cosign verify-blob \
  --certificate checksums.txt.pem \
  --signature checksums.txt.sig \
  --certificate-identity "https://github.com/netresearch/ofelia/.github/workflows/release-slsa.yml@refs/tags/v0.21.0" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  checksums.txt

Included in this release

View all PRs and Issues included in this release

v0.20.0

17 Feb 06:39
v0.20.0
69f6151

Choose a tag to compare

Highlights

Webhook notifications from Docker labels (#481)

Docker labels like ofelia.webhook.slack-alerts.preset=slack are now fully functional. Define named webhooks, global webhook settings, and per-job webhook assignments entirely via container labels — no INI file required. INI-defined webhooks take precedence over labels for security, and webhook configs are dynamically synced on container changes. Reported by @julian-heng in #467.

Allow job-run on stopped containers (#461)

The job-run type can now be defined on stopped containers via --docker-include-stopped. Running container definitions take precedence over stopped ones. Other job types (exec, local, service-run, compose) from stopped containers are ignored. Contributed by @nekrich.

Replace logrus with stdlib log/slog (#480)

Replaced the logrus dependency with Go's standard library log/slog package. This reduces external dependencies, improves structured logging, and aligns with the Go ecosystem's direction.

Go 1.26 modernization (#475, #477)

Upgraded to Go 1.26 with modern features: errors.AsType[T], reflect.Type.Fields() iterators, t.TempDir() in tests, and go-cron v0.11.0 scheduler enhancements (JobWithContext, WaitForJobByName, UpsertJob).

Mutation testing at 85%+ efficacy (#479)

Added mutation tests across all packages to validate test suite quality, reaching 85%+ test efficacy.

Changes

Features

  • feat: allow job-run on non-service and stopped containers (#461) — @nekrich
  • feat: modernize scheduler to leverage go-cron v0.11.0 features (#476)
  • feat(api): expose job running status via IsJobRunningByName
  • feat: implement JobWithContext on jobWrapper for per-entry context
  • feat: wait for running jobs before removal with WaitForJobByName
  • feat(hooks): add YAML and actionlint pre-commit checks (#474)

Bug Fixes

  • fix: parse webhook config from Docker labels (#481, #467) — reported by @julian-heng
  • fix: protect INI webhooks from label overwrite during sync
  • fix: propagate context.Context through RunJob and workflow APIs
  • fix(ci): update golangci-lint to v2.9.0 for Go 1.26 support
  • fix(ci): skip auto-approve for external contributors (#464)
  • fix(ci): fix dependency review YAML and increase smoke test timeout

Refactoring

  • refactor: replace logrus with stdlib log/slog (#480)
  • refactor: modernize dependencies and adopt newer API features (#478)
  • refactor: remove go-dockerclient dependency
  • refactor: adopt Go 1.26 features and clean up test temp files (#477)
  • refactor: use errors.AsType[T] from Go 1.26
  • refactor: replace hand-rolled cron validation with cron.ValidateSpec
  • refactor: use UpdateEntryJobByName for atomic job updates in web API
  • refactor: unify Docker label decoding with INI config path

Testing

  • test: add mutation tests to reach 85%+ test efficacy (#479)
  • test: add comprehensive webhook label tests and documentation (#482)
  • test: add IPv6 URL coverage for Go 1.26 stricter parsing

Performance

  • perf: add WithCapacity to cron scheduler initialization

CI/CD

  • chore(deps): upgrade Go to 1.26 and all dependencies (#475)
  • chore(ci): update CodeQL Action from v3 to v4
  • chore(ci): add codecov config, make project coverage informational (#465)
  • chore: limit the number of Docker events (#466) — @nekrich

Full Changelog: v0.19.2...v0.20.0

Contributors

Thanks to the contributors who made this release possible: