Repository navigation
Releases: otfabric/dnp3ctl
Release list
v0.6.6
Date: 2026-10-21
Previous release: v0.6.5
Summary
Built on go-dnp3 v1.4.1. The simulated outstation (server start) no longer crashes on a malformed request, and it answers as the DNP Users Group's IED certification procedures require; go-dnp3 now runs those in CI.
Security
server startno longer crashes on a malformed READ: go-dnp3 v1.3.2 and earlier took the whole outstation process down on a READ of g70v5 (file transport) whose header carries indices but no objects. Any master that can reach aserver startcould send it. Fixed in go-dnp3 v1.4.0: such a request is answered with IIN2.2.
Changed
The simulated outstation follows go-dnp3 v1.4.x:
- An OPERATE after the select timeout is answered TIMEOUT, not NO_SELECT. A control it does not support (
NOT_SUPPORTED) also sets IIN2.2. A control whose request carries a status is refused with FORMAT_ERROR. - A read of a point type the model has no points of is answered with IIN2.1.
- Relative-time events carry an unsynchronized CTO (g51v2) until the time is synchronized.
- A variation without flags reports a point with abnormal flags in the variation with flags.
- The response that reports a broadcast to
confirmoroptionalasks for confirmation. - A READ sent while an unsolicited response waits for its confirmation is answered after it.
client against an outstation that answers NOT_SUPPORTED with IIN2.2 (go-dnp3 v1.4, opendnp3, Step Function I/O's dnp3) reports the echoed statuses, as for any refused control: exit code 6, or 7 when other controls of a direct operate were executed. With go-dnp3 v1.4.0 it would have reported only the IIN error.
Docs
- FAQ: go-dnp3 is tested against dnp3-interop v0.5.1 and with the IED certification procedures (dnp3c).
- SECURITY: use 0.6.6 or later.
Upgrade notes
- No breaking changes in the command line or the JSON output.
v0.6.5
Date: 2026-10-21
Previous release: v0.6.4
Summary
A misspelt subcommand of client or server gets the "Did you mean this?" suggestion with flags and without, for a typo as well as an abbreviation.
Fixed
- Suggestions for subcommands:
dnp3ctl client polsaid onlyunknown command "pol" for "dnp3ctl client"; it now adds "Did you mean this? poll", the same message as with flags. Both paths also suggest by edit distance, not only by prefix:dnp3ctl server statsuggestsstart(Cobra sets that distance only for the root's own commands). One function builds the message for both paths.
Upgrade notes
- No breaking changes.
v0.6.4
Date: 2026-10-21
Previous release: v0.6.3
Summary
An unknown subcommand of client or server given together with a flag is reported as the unknown command it is, with suggestions, instead of as an unknown flag.
Fixed
- Unknown subcommand with a flag:
dnp3ctl client nope --format json(or any flag after an unknown subcommand) reportedunknown flag: --format: Cobra stops at theclientgroup, which has none of its subcommands' flags. It now reportsunknown command "nope" for "dnp3ctl client", with "Did you mean this?" suggestions for a misspelt one (client pol→poll), as text or as the JSON error object. Without flags the message was already right. Exit code 2 as before.
Upgrade notes
- No breaking changes.
v0.6.3
Date: 2026-10-21
Previous release: v0.6.2
Summary
Four behaviours of the command line and of the simulated outstation follow what the documentation promises: usage errors honour --format json, a binary built with go install reports its version, a connection style on the command line wins over another one in the environment, and over UDP the event log says connected once a master is known. Commands, flags and the JSON shapes are unchanged.
Changed
- Usage errors and
--format json: an unknown command or flag, a missing required flag or a value a flag cannot hold is now the JSON error object{"error": {"kind": "usage", "code": "invalid_input", "message": ...}}on stdout when the invocation asks for JSON, by--format json(also--format=json, wherever it stands on the command line) or byDNP3CTL_OUTPUT_FORMAT=jsonfor a command that has--format. It was always the texterror:line on stderr. The exit code stays 2, and text stays as it was.server generate-config --formatis the format of the model file it writes, not of a result, so its errors stay text. - Connection styles, environment and command line: a flag of one connection style on the command line now wins over the environment variables of the others, as for every other flag:
client … --ipworks withDNP3CTL_URLset (likewise--url,--listenwithDNP3CTL_IP,DNP3CTL_MASTER_LISTEN), andserver start --dialwithDNP3CTL_PORTorDNP3CTL_UDPset (likewise--serial, and--listen,--port,--udp,--udp-peer,--allowwithDNP3CTL_DIALorDNP3CTL_SERIAL). Two styles both on the command line, or both in the environment, are still refused as mutually exclusive. - go-dnp3 v1.3.2, which runs its interop suite against otfabric/dnp3-interop v0.4.3 (the FAQ says so); no change of the library.
- Docs: README.md, docs/json-output.md, docs/exitcodes.md and docs/server-config.md describe the new behaviour, and say why
server generate-config --formattakesyamlorjson.
Fixed
- Version of
go installbinaries: a binary built without the ldflags of the release (go install …@v0.6.3,go build) reported versiondev. It now reports the version the go toolchain records, such asv0.6.3(withgo buildin a clone the one it derives from the git tags, such asv0.6.3+dirty), and the git commit it records, when there is one;devremains when nothing is recorded, as in a copy without git. - UDP
connected: over UDP (server start --udp, with one model or several) the event log saidconnectedwith an emptyremoteas soon as the socket was served, before any master had sent anything. It now saysconnectedwhen the first datagram arrives, naming its sender asremote(at once, naming the master, with--udp-peer); a socket no master sent to logs neitherconnectednordisconnected. server start --dialwith--duration: the goroutine that dials the master read the context that--durationreplaces, a data race the race detector reports; it now gets its context before it starts.
Upgrade notes
- No breaking changes. A script that read usage errors from stderr while passing
--format jsonnow finds them on stdout, as every other error.
v0.6.2
Date: 2026-10-20
Previous release: v0.6.1
Summary
dnp3ctl moves to go-dnp3 v1.3.1, a documentation-only release, and its documentation has been reviewed claim by claim against the code before the repository goes public. Two behaviours of the simulated outstation are fixed: simulated counters follow ramp, sine and random, and with several outstations over UDP the event log names the master. Commands, flags and JSON output are unchanged.
Changed
- go-dnp3 v1.3.1 (documentation only, no API change).
- Docs, reviewed for going public:
- README.md: the install commands unpack into an emptied folder and install the binary by its exact name, so that an update cannot install an older binary left behind, and stop on an unsupported architecture (also on macOS); building from source names the tools
make buildneeds and the plaingo build; the required flags of every command, the connection styles staying exclusive across environment and flags, the defaults per command in the environment table, the command aliases and the undocumented flags (client objects --group/--events, the file commands'--permissions,--block-size,--file-password-file,file get --force/--output,server generate-config --force/--format,server start --format); the BCD and unsigned integer read example (--object g101v1,g102v1), a FREEZE_AT_TIME example in the future, the commands that change an outstation, the comparison table worded as typical per category, the interop stacks (with dnp3py) and the project structure. - docs/server-config.md: the main example model validates (it lacked binary output 0, and its
events.csvmust exist); what reads of unsupported objects and of point types without points answer; which attribute writes are refused; the Secure Authentication statistics and reply timeout flags;--max-sessions; double-bit values in YAML need no quotes. - docs/json-output.md, docs/exitcodes.md: usage errors (exit code 2) are always a text
error:line on stderr, also with--format json, with codeinvalid_input; the IIN order (NEED_TIME|DEVICE_RESTART);CHATTER_FILTERfor double-bit inputs andBIT5-BIT7for unnamed flags;controlsamong the arrays. - SECURITY.md: supported versions (0.6.x); the security statistic flags; DSC Systems' go-dnp3 linked. CONTRIBUTING.md: the release notes rule and the full list of client commands. RELEASE.md: the linux/armv7 compile error of v0.5.0 never shipped, and is listed under Quality.
- README.md: the install commands unpack into an emptied folder and install the binary by its exact name, so that an update cannot install an older binary left behind, and stop on an unsupported architecture (also on macOS); building from source names the tools
Fixed
- Simulated counters: a
counterorfrozen-counterwithsimulatemoderamp,sineorrandomcounted up bysteplikecount; it now follows its mode, rounded to a whole number the counter can hold (0 … 4 294 967 295). - Several outstations over UDP: with more than one
--configand--udp, the event log named the server's own socket asremoteof every event; it now names the master the last datagram came from, as with one outstation.
Upgrade notes
- No breaking changes. A model that simulates a counter with
ramp,sineorrandomnow gets values betweenminandmaxinstead of a count.
v0.6.1
Date: 2026-10-20
Previous release: v0.6.0
Summary
dnp3ctl moves to go-dnp3 v1.3.0, which generates its object table and the objects it reports as measurements from one catalog. Commands, flags and output are unchanged, except for the size client objects gives g86v2.
Changed
client objects: g86v2 (Data Set Descriptor - Characteristics) has size 1, the one octet IEEE 1815-2012 defines; it was listed as free-format (size 0). dnp3ctl does not read or write data sets, so nothing else changes.- go-dnp3 v1.3.0.
Upgrade notes
- No breaking changes.
v0.6.0
Date: 2026-10-20
Previous release: v0.5.0
Summary
dnp3ctl moves to go-dnp3 v1.2.0: server start serves several outstations behind one port, serial line or UDP socket, and the client commands report BCD (g101) and unsigned integer (g102) points. Nothing that worked before changes.
Highlights
- Several outstations:
server start --config a.yaml --config b.yaml(or comma-separated files, orDNP3CTL_CONFIG) serves every model as an outstation of its own, behind the same port, serial line or UDP socket, as on a multi-drop line. Each answers at the link address of its model with its own points, event buffers and controls; a broadcast reaches all of them. Two models with the same link address are refused;--dialand--udp-peertake a single model. - The event log names the outstation when several are served:
[11]after the event in text,outstationin JSON, an OUTSTATION column in the table.listeninglists every outstation with its link address. - BCD and unsigned integers:
client poll,readandmonitorreport g101 (BCD,typebcd, the decimal number) and g102 (typeunsigned-integer), which go-dnp3 now decodes.
Changed
- dnp3ctl reads times and intervals (g50v4) through go-dnp3's own measurement list, which now carries them, instead of its own decoding; the output is the same.
Fixed
- go-dnp3 v1.2.0 fixes a panic of the outstation when a response fragment had almost no room left, which could stop the simulated outstation while it answered a large poll.
Upgrade notes
- No breaking changes to commands, flags or JSON output. Server events gain
outstationwhen several outstations are served, and the table output ofserver startgains an OUTSTATION column (empty with one outstation).
v0.5.0
Date: 2026-10-20
Previous release: v0.4.0
Summary
dnp3ctl exposes what go-dnp3 v1.1.0 added for dnp3-interop's contract 1.3 (broadcast, scheduled freezes, device attribute sets, file append, deadbands) and the rest of go-dnp3's API that a command line can use: several controls in one request, the management functions, clearing the restart indication, outstation indications, go-dnp3's log under --debug and the Secure Authentication options of the outstation. New flags and commands; nothing that worked before changes.
Highlights
--broadcast no-confirm|confirm|optionalonclient operate(with--mode directordirect-no-ack) andclient freeze: the request goes to every outstation on the channel at a broadcast address. Nobody answers; each outstation sets IIN1.0 BROADCAST in its next response. Refused with secure authentication: outstations running SAv5 accept a critical broadcast only with an aggressive mode MAC over an earlier challenge of the same connection, which a dnp3ctl command cannot have.client freeze --at TIME [--interval DURATION]: FREEZE_AT_TIME, a freeze at a time to come, repeated at the interval.- Device attribute sets:
client attributes --set Nreads a set other than the standard set 0. client write-attribute(new): writes a device attribute of any set, with--valueof--data-type vstr|uint|int|float.client file put --append: adds to the end of a file (OPEN_FILE in APPEND mode).client read --object g34: the deadbands of the analog inputs, which outstations now answer;client write-deadband(new) writes them.- Several controls in one request:
client operate --index 0,1,4, with--valueonce or per index for set-points. The result lists a status per control (controlsin JSON); a direct operate refused for some controls reports the others as executed, with exit code 7. client manage(new): INITIALIZE_DATA, INITIALIZE_APPL, START_APPL, STOP_APPL and SAVE_CONFIG.client clear-restart(new): clears DEVICE_RESTART (IIN1.7), which the one-shot commands leave set.--debugalso shows go-dnp3's own log entries (connections, reconnects, warnings, authentication), next to the frame trace.server start --sa-reply-timeout,--sa-statistics-class,--sa-statistics-threshold: the challenge reply timeout and the security statistic events of the outstation.- Simulated outstation:
attribute_setsin the model, with writable variations; set 0 accepts writes of the user-assigned attributes the model gives (owner, location, user-id, device-name) and refuses the others; it answers broadcasts, scheduled freezes, reads and writes of deadbands and the management functions (refused_functionsin the model refuses some), and reports theindicationsof the model (local-control, device-trouble, config-corrupt).
Changed
- The simulated outstation refuses writes of device attributes of set 0 other than the user-assigned ones the model gives; it accepted every write before.
- Docs name the DSC Systems library "DSC go-dnp3", as dnp3-interop does, and the interop note refers to dnp3-interop v0.4.1 (contract 1.3).
Fixed
- The linux/armv7 build: a range check of the outstation model compared an
intwith 4294967295, which does not compile whereinthas 32 bits; whole numbers that do not fit aninton such platforms are now accepted as well.make checknow typechecks for 32-bit targets (make cross).
Upgrade notes
- No breaking changes to commands, flags or JSON output; the JSON gains
broadcast,interval,set,attribute,append,functionandvaluefields where they apply, andclient operategainscontrols(anderrorfor a partial result). --indexofclient operateis now a list; a single index works as before.
Quality
- Every changed line is covered by tests, end to end against the simulated outstation: broadcast controls and freezes, scheduled freezes (also the refusal of a time that has passed), attribute sets read and written (writable and not), set-0 writes, append, deadbands read and written, several controls (all succeeded, partly refused, refused at select), every management function and a refused one, indications and clearing the restart indication, the debug log of both roles.
v0.4.0
Date: 2026-10-20
Previous release: v0.3.2
Summary
dnp3ctl exposes what go-dnp3 v1.0.0 added: the dual end point, where the outstation connects to its master, in both roles; Secure Authentication aggressive mode and security statistics; and command events and times and intervals in the simulated outstation. New flags only: nothing that worked before changes.
Highlights
- Dual end point (IEEE 1815-2012 annex A.3), for outstations behind a firewall or on a dial-out link:
client … --listen host[:port](DNP3CTL_MASTER_LISTEN) makes every client command wait for the outstation to connect instead of connecting to it, within--timeout;client monitorwaits for it again after a lost connection. With--tls-certand--tls-keydnp3ctl is the TLS server of that connection, and--tls-camakes the outstation present a certificate of that CA. The result'stargetis the address the outstation connected from. Mutually exclusive with--urland--ip.server start --dial tcp://host[:port]ortls://host[:port](DNP3CTL_DIAL) makes the simulated outstation connect to its master, and again whenever the connection ends (after 1 s, doubling up to 1 min). Over TLS it is the TLS client: the new--tls-ca(DNP3CTL_TLS_CA) verifies the master,--tls-certand--tls-keyare presented to it. The event log starts with adialingevent.
- Secure Authentication aggressive mode:
--sa-aggressive(DNP3CTL_SA_AGGRESSIVE) sends critical requests with their MAC once the outstation has challenged one, saving a round trip; inclient operate --mode sbothe OPERATE goes in aggressive mode after the challenged SELECT. It needs a key. The simulated outstation accepts it. - Security statistics: the simulated outstation keeps the 18 statistics of IEEE 1815-2012 clause 7.6 per master when secure authentication is on, and reports g122 events in class 1.
client read --object g121v1shows them with their name (statisticin JSON:"session key changes","authentication failures", …). - Command events in the outstation model:
command_event_class(andcommand_event_variation) on abinary-outputoranalog-outputreports every executed control as a g13 or g43 event with the commanded value and the status. The demo outstation has them on binary output 0 and analog output 0. - Times and intervals (g50v4): the point type
time-and-intervalwith an RFC 3339value,intervalandinterval_unit, read withclient read --object g50v4, which shows the time and the interval (intervalandinterval_unitin JSON, typetime-and-interval). Like in the reference outstations it is not in class 0. The demo outstation has one.
Changed
clientresults: a measurement of a time and interval has the new fieldsintervalandinterval_unit, a security statistic the new fieldstatistic; text and table output show them after the value. g50v4 objects used to be left out of results.- The client's
--tls-ca,--tls-certand--tls-keyalso apply to--listen, andserver starthas a--tls-cafor--dial;--tls-cawithout atls://--dialis refused. - An outstation that does not connect to
--listenin time, and a--listenaddress that cannot be opened, exit with code 4 (transport_connect_failed,listen_failed). - docs: SECURITY.md on aggressive mode, the security statistics and the dual end point; the interoperability note now names DSC Systems' go-dnp3, against which go-dnp3 verifies Secure Authentication.
Upgrade notes
- No breaking changes. Scripts that read
--format jsonsee three new optional fields (interval,interval_unit,statistic) and thedialingevent. DNP3CTL_MASTER_LISTENis the client's variable for--listen;server start --listenkeepsDNP3CTL_LISTEN.
v0.3.2
Date: 2026-10-20
Previous release: v0.3.1
Summary
dnp3ctl now builds on go-dnp3 v1.0.0, its first production release, whose API follows semantic versioning from here on. No change to commands, flags or output.
Changed
- go-dnp3 v1.0.0. Of its behaviour changes, none affects dnp3ctl: a client whose startup sequence fails is now closed by go-dnp3 as well, which dnp3ctl already did; the new point types (security statistics, command events, time and interval) are not served by the simulated outstation's model; aggressive mode is not used.
Upgrade notes
- No breaking changes.