1.0.0-alpha.2 (2026-08-24)
⚠ BREAKING CHANGES
- LockResponse now carries its lock fields under a nested LockState message, and DetailsRequest.cmd is renamed to command.
- the -force flag is removed; use "dutctl unlock force" to break a lock held by another user.
- the version header is renamed from X-Dutctl-Version to Dutctl-Version and moved from internal/rpc to the new public pkg/headers. External clients must send and read the new name.
- The serial module's invocation changed. It no longer accepts a single optional expect regex; callers must pass an explicit step sequence. For example,
serial "(?i)hello"becomesserial -- expect "(?i)hello". Existing configs/scripts using the old form must be updated. - The
mainYAML config key on modules is replaced bypassthrough. Existing config files must be updated. - The dutagent configuration changed. Existing configurations must be updated in the following way: From commands: - name: example modules: - name: flash options: file: firmware.bin To commands: - name: example uses: - name: flash with: file: firmware.bin
- Flash module removed default tool. Users must now explicitly configure the tool option in their flash module configuration.
- The -v flag now enabled verbose output, while version information is printed with the 'dutctl version' command.
- The client does not add an extra newline to received Print messages anymore.
Features
- add --log level and keep diagnostics off stdout (93df26a)
- add command-level argument templating (711c8d2)
- add dpcmd support (13a4ace)
- add emulate module for em100 support (268bb80)
- add file module for bidirectional file transfers (ded8a2c)
- add flashprog support to flash module (7a9c309)
- add further output options to dutctl (5c027cc)
- add Gude PDU support via pluggable vendor backends (15fdd24)
- add lock and unlock client commands (f2b5ea8)
- add Lock and Unlock RPCs with agent handlers (99ccc6b)
- add per-device Locker engine (c825745)
- add structured logging to dutagent and dutserver (311b765)
- add systemd service (377e47e)
- add wifi socket module (329161d)
- agent and client verify dutctl versions (20e5d70)
- annotate YAML config errors with device, command, and line (71a0968)
- apply an agent default lock duration and nudge cooperative use (0749886)
- bound client RPCs with a signal-aware per-call deadline (f760ca3)
- consolidate reserved keywords into internal/keyword (c614f79)
- default to list when dutctl is run without arguments (352a464)
- drain in-flight RPCs on signal instead of cutting them (3a22eb5)
- dutctl verbose output flag (abb8d67)
- embed release version constant (f21b45d)
- enforce per-device locking on Run (9c33e80)
- establish reusable test doubles (62a5c99)
- extend module.Session interface with fmt-style print functions (3432675)
- honor context cancellation in modules, startup, and relay (56c1774)
- log lock expiry and auto-lock lifecycle at the locker (1740942)
- reject reserved command names in device config (81ee7f6)
- serial expect/send automation with monitor mode (9f57498)
- show device lock state in list output (0d56523)
Bug Fixes
- add missing newline to gpio-switch state output (a0fbd64)
- add plugdev group to dutagent (e934e4c)
- allow command templating without a passthrough module (282989f)
- broker does not treat io.EOF as error (1588898)
- carry the YAML line in reserved-command config errors (d3ec27d)
- clear serial expect pattern between runs (#315) (b55455e)
- default nil formatter writers to os.Stdout/os.Stderr (ed0c767)
- defer port opening to runtime in serial module (fd417af)
- distinguish in-use from locked in oneline output (d1a279c)
- don't cancel run context on stdin EOF in dutctl client (cf7de77)
- don't dial DUT in Init (dadd3ae)
- flush stale RX buffer on connect (040d45c)
- guard session currentFile with a mutex (e7ad90a)
- harden error handling and connect status-code mapping (3772d17)
- harden module Init/Deinit lifecycle (7a6518f)
- initialize gpio-switch state to off on startup (5dfd776)
- ipmi survives an unreachable or dropped BMC (c59eac7)
- ipmi-example-cfg.yml (2d61fc9)
- make force an unlock keyword and reject malformed lock/unlock args (7f550d9)
- make GPIO Deinit safe to call without prior Init (36c69c6)
- prevent infinite recursion in Device.UnmarshalYAML (af34a6e)
- publish the rpc header contract as pkg/headers (0c9bc44)
- relay flash tool stdout/stderr to client (78a623c)
- release the Run auto-lock on panic and error paths (8ebd06a)
- remove exec paths for dpcmd and scripts execs (80ee28a)
- remove extra newline for print messages (afe31fe)
- require a named caller to lock or release a device (6ec58af)
- stop ChanReader.Read dropping bytes across buffer and channel (1ee4c9b)
- stream bytes without waiting for newline (6a7205c)
- surface auto-locks in List so a busy device is not shown as free (52603db)
- unblock module session I/O when its workers exit first (c379dad)
Documentation
- add missing modules to README and fix IPMI link (b79eff2)
- add README and example config for dummy module (6e1673f)
- add release plan and tier public interface contract (dd89933)
- align doc comments with the Go style guide (af25611)
- explain module.Session interface (3e9612e)
- fix several linting issues in markdown files (1db2cfa)
- fix typos in stream and test fakes (52d7e32)
- fix typos, spelling and tables (c1dbf9b)
- pin go install commands to the released version (557eeb9)
- sanitize modules example configuration (0479c66)
- tidy example configs into canonical comment-free YAML (1b7352d)
Other Work
- add Devlist.AllModules iterator and Find accessor (ea584e0)
- add editorconfig (0fafe92)
- add EM100_HOME env var into systemd service (f8f28f0)
- add goreleaser config (f156a48)
- assert gpio switch state after Init and Run output format (437fe4e)
- broker worker lifecycle and error channel handling (1da20f9)
- broker.Start returns session and error channel (b970bac)
- bump actions/checkout from 4 to 5 (ab84c81)
- bump actions/checkout from 5 to 6 (3bb8551)
- bump actions/checkout from 6 to 7 (083a322)
- bump actions/checkout from 6 to 7 (1c601b4)
- bump actions/checkout from 6 to 7 (d49dfe0)
- bump actions/setup-go from 5 to 6 (77b3beb)
- bump actions/setup-go from 6 to 7 (3ff8ae3)
- bump actions/setup-node from 4 to 6 (bd4e5f7)
- bump actions/setup-node from 6 to 7 (2fa7f60)
- bump connectrpc.com/connect from 1.18.1 to 1.19.1 (323b013)
- bump connectrpc.com/connect from 1.19.1 to 1.19.2 (0fa7ce2)
- bump connectrpc.com/connect from 1.19.2 to 1.20.0 (710bbcd)
- bump github.com/bougou/go-ipmi from 0.7.7 to 0.7.8 (d561eab)
- bump github.com/bougou/go-ipmi from 0.7.8 to 0.8.2 (2a7da65)
- bump github.com/go-playground/validator/v10 (4674844)
- bump github.com/go-playground/validator/v10 (47e4c1f)
- bump github.com/go-playground/validator/v10 (cabf73d)
- bump github.com/go-playground/validator/v10 (3f39f64)
- bump github.com/go-playground/validator/v10 (2293e9d)
- bump go.bug.st/serial from 1.7.1 to 1.8.0 (c5b2fbf)
- bump golang.org/x/crypto (933701c)
- bump golang.org/x/crypto from 0.45.0 to 0.46.0 (4f262d8)
- bump golang.org/x/crypto from 0.46.0 to 0.47.0 (e75f000)
- bump golang.org/x/crypto from 0.49.0 to 0.50.0 (073285c)
- bump golang.org/x/crypto from 0.50.0 to 0.51.0 (18d6d9d)
- bump golang.org/x/crypto from 0.51.0 to 0.53.0 (c1ba946)
- bump golang.org/x/crypto from 0.53.0 to 0.54.0 (1ff7688)
- bump golang.org/x/crypto from 0.54.0 to 0.55.0 (53a48e3)
- bump golang.org/x/mod from 0.36.0 to 0.38.0 (285b4dc)
- bump golang.org/x/mod from 0.38.0 to 0.40.0 (929d349)
- bump golang.org/x/net from 0.42.0 to 0.46.0 (f11049e)
- bump golang.org/x/net from 0.46.0 to 0.47.0 (da28691)
- bump golang.org/x/net from 0.47.0 to 0.48.0 (bc399bd)
- bump golang.org/x/net from 0.48.0 to 0.49.0 (6e3aaa0)
- bump golang.org/x/net from 0.49.0 to 0.50.0 (2dd0cbb)
- bump golang.org/x/net from 0.50.0 to 0.52.0 (f7bc82f)
- bump golang.org/x/net from 0.52.0 to 0.53.0 (00f4600)
- bump golangci/golangci-lint-action from 8 to 9 (244cb31)
- bump google.golang.org/protobuf from 1.36.10 to 1.36.11 (4739d6b)
- bump google.golang.org/protobuf from 1.36.11 to 1.36.12 (1cc2f04)
- bump google.golang.org/protobuf from 1.36.6 to 1.36.10 (e49cc65)
- bump googleapis/release-please-action from 4 to 5 (39a9a60)
- centralise command query logic into Command methods (8175951)
- change naming of dutagent config items (f795b4c)
- clean up the ipmi module (fcaf167)
- copy locker.StatusAll entries as whole structs (504ab3a)
- cover dutagent/states (b7ea4c4)
- defer moduleErrCh channel init to FSM execution state (4c01fd0)
- drop the vestigial reserved module-ID check (7faff4a)
- dutctl output formatter (923f673)
- early ctx cancellation in Run RPC (7638b3b)
- embed LockState in LockResponse and align proto naming (e18f6b5)
- extract compat policy and rpc wrapper from buildinfo (6b12ae9)
- fix flaky TestWaitModules by resolving race condition (b98a7e3)
- fix typo and improve logging in rpc handler (53cac3a)
- group dutagent FSM states in own file (f1014a4)
- harden broker tests for the race detector (64a2363)
- ignore top-level taskfile.yml (fad001d)
- improve default output of dutctl (46817ce)
- make main module optional for commands (2af85e7)
- model the locker around Holds with a Kind, not slots (f14fb9b)
- modules use session.Print/f/ln functions (a5acb8f)
- nFPM packaging (9be7f4d)
- remove check if filepath on dutctl matches with args (9f625a4)
- remove retired Go Report Card badge from README (c5bd585)
- rename "main module" to "passthrough" (cdfeb52)
- replace deprecated h2c package with net/http Protocols (a85e0da)
- resolve busy waiting in waitModules loop (13b2ef7)
- resolve caller identity through an internal/auth seam (127adb5)
- run the suite under -race and fix the race it surfaced (50680e0)
- send/receive synchronization in dutclient (6f900f2)
- set prerelease versioning strategy (4fac7a7)
- split internal/dutagent into locker and session packages (0cda104)
- stop Dependabot applying ecosystem labels (37aebae)
- thread context through module Init/Deinit lifecycle (bab394a)
- tune .gitignore (4e9c943)
- unify broker and module channels to error-only pattern (84e39d1)
- unify immediate writer selection across output formatters (65464ba)
- update signoff signature in release-please-config (bae6d59)
- use constants for flag usage strings (ceb0bf6)
- use dutagent.Stream as project wide RPC stream abstraction (7c0fa17)
- use string for config version field (e8d9c0d)