zyginit Release Notes
0.2.6 — 2026-07-21
Bug-fix release. Fixes bug 003 — spurious exit=-1 service failures in
first-boot reports on Hammerhead. Root-cause analysis found exit=-1 was
never an exec-failure code (a real exec failure exits 127); it was the
"could not resolve an exit code" sentinel, produced by two distinct defects:
- Exec robustness / no silent failure. Under first-boot I/O contention
(devfsadm + ZFS pool-finalize) the child's exec of a valid start command
could transiently fail, and the child then exited silently with an empty
per-service log. The child now retriesprocess_exec(5×, 100 ms backoff)
and, on final failure, records the reason to both the per-service
<name>.log(fd 2, survives a privilege drop) and a new central
/var/log/zyginit/zyginit.log, then exits 127. - Reap-race reconciliation (the actual source of
-1). The
contract-empty event on the bundle fd can beat the child's exit-status
posting;handle_contract_eventused to commit the-1sentinel and tear
down tracking, after which the catch-all reaper discarded the real code. It
now defers when there is no hint, no resolvable code yet, and a child is
still tracked (rt.pid > 0), so the unconditionalreap_children
re-dispatches with the true code within ~1 s. This fixes both the 127→-1
masking and the independent false-positive where an exit-0 oneshot that
lost the race was reported FAILED.
New central operational log: /var/log/zyginit/zyginit.log (distinct from
the per-service logs). Adds integration Suite 9b (spawn-failure capture);
97/97 tests pass. The reap-race half rides the Hammerhead contract path
(not exercised by the Linux stubs) and awaits an hh-alpha first boot to
confirm in situ.
0.2.5 — 2026-07-17
Reef 0.7.6 migration; no init-engine behavior change. Migrated the suite
to Reef 0.7.6: Result/Option idioms, TOML inline arrays, and the
socket → ctlsocket module rename (avoids a clash with the stdlib
net.socket, which net.unix now imports). Internal/tooling release — no
source tarball was cut for 0.2.5.
0.2.4 — 2026-06-13
Service mutual exclusion. Adds the [dependencies].conflicts array:
mutually-exclusive services both fail if both are enabled, and a start is
refused if a conflicting peer is already running. Boot-time resolution fails
both sides and surfaces the reason in zygctl status. A one-sided
declaration is enforced symmetrically (both directions).
0.2.3 — 2026-06-02
Boot/shutdown progress gauge relocated into the header divider. The gauge
previously lived on the volatile last screen row, where interleaved
kernel/daemon writes would scroll it off. It now paints into the header's
horizontal rule. fmt_progress_bar and PROGRESS_WIDTH were removed and
fmt_divider_gauge added in src/ui.reef; two screen rows are reclaimed for
the rolling tape.
0.2.2 — 2026-05-21
Repo content reshape; no behavioral change. zyginit is now
strictly engine + examples + schema docs. The in-repo services/hammerhead/
tree (22 production service TOMLs) has been removed — distros own all
service-definition policy in their own overlay trees. The Hammerhead
team has absorbed the prior set into base/usr/src/zyginit/overrides/.
The examples/ directory is slimmed to four heavily-commented
canonical examples:
daemon-with-restart/— long-running daemon with[restart]policyoneshot-setup/— type="oneshot" with[dependencies].requirestask-with-condition/— type="task" with[condition].exists_filewith-stop-script/—[exec].stop+ companionstop.sh
scripts/install-to-be.sh was deleted; its "deploy our services to
a BE" purpose evaporates in an engine-only world. (Distros can ship
their own equivalent.)
docs/SMF_MIGRATION.md has been rewritten as a methodology guide
(how to convert an SMF manifest to a zyginit TOML) rather than a
catalog of our specific conversions.
Tarball: ~22 KB smaller than 0.2.1. No clang / reefc / API changes.
0.2.1 — 2026-05-15
Patch release with two operational improvements:
type = "transient"in 0.1.x-style TOMLs no longer silently demotes
todaemon. The parser now treats it astask(the renamed type) and
emits a deprecation warning. This prevents a service that should run
as a task from hanging in STATE_STARTING after an incomplete upgrade.- Missing
/etc/zyginit/services/directory at boot now produces a
distinct, actionable log message instead of silently entering the
idle loop. The operator is told to run the migration tool from a
recovery medium.
0.2.0 — 2026-05-15
Breaking change. The filesystem layout under /etc/zyginit/ is
restructured. Operators upgrading from 0.1.x MUST run the migration
tool before installing 0.2.0 binaries:
/path/to/scripts/migrate-layout.sh --dry-run # preview
/path/to/scripts/migrate-layout.sh --apply # apply
The tool is idempotent (safe to re-run) and refuses to proceed if
both old and new layouts coexist for the same service.
New: per-service directory layout
Each service is self-contained:
/etc/zyginit/services/<name>/
├── service.toml
├── start.sh (optional)
└── stop.sh (optional)
enabled.d/<name> symlinks target ../services/<name>/ (the
directory), not ../<name>.toml (the file).
New: type = "task" service type
For services that may run for a while but are expected to eventually
exit cleanly — platform probes, one-time setup work, etc. Replaces
the previously-unused type = "transient".
[service]
name = "acpihpd"
type = "task"
Semantics:
- Exit 0 → STATE_STOPPED (counts as online in the boot banner)
- Exit ≠ 0 → STATE_FAILED (counts as failed)
- Never restarts
- Skips the daemonize handshake (tasks don't double-fork)
New: [condition] block
Declarative pre-flight gating. Services whose conditions fail at
startup go into a new SKIPPED state — they don't fork, don't fail,
don't loop. Supported keys:
[condition]
exists_file = "/dev/acpihp"
exists_file_any = ["/dev/acpihp", "/dev/acpi"]
command = "/usr/sbin/acpi_probe" # 5-second timeout
All specified keys must pass (AND). Absent keys are no-ops. The
command form is bounded by a 5-second timeout.
New: SKIPPED state in zygctl status
Skipped services render with the · (dim) glyph and the skip reason
in the NOTES column. The boot banner and final card include a
"skipped" counter alongside online/failed.
Hammerhead vendoring
The Hammerhead team runs the migration tool against their override
tree in base/usr/src/zyginit/overrides/ as part of consuming this
source drop. Follow the same dry-run → apply pattern.
Other changes
- Renamed
SERVICE_TYPE_TRANSIENT→SERVICE_TYPE_TASKin the API. type = "transient"in TOML produces a deprecation warning and is treated astype = "task"(see 0.2.1).
0.1.8 — 2026-05-14
Late-failure race fix. A 1-second settle period after the last tier
catches daemons that fork successfully but die immediately (e.g.,
acpihpd on Hammerhead without ACPI hardware).
0.1.7 — 2026-05-14
Boot screen no longer scrolls the [Z] banner off the top of the
framebuffer. Drop the trailing newline that was advancing the cursor
past the last row.
0.1.6 — 2026-05-14
illumos build fix: <sys/termios.h> now explicitly included for
struct winsize and TIOCGWINSZ. Linux's <sys/ioctl.h> pulls
those in transitively; illumos doesn't.
0.1.5 — 2026-05-14
(Broken on illumos: missing <sys/termios.h> for TIOCGWINSZ — use
0.1.6+.) Spinner-in-tape redesign (option B): each tape row tracks
its own active flag, lifecycle procs update rows in place,
parallel-startup ready. TIOCGWINSZ for dynamic screen size. Oneshot
single-count fix. Dynamic SERVICE column in zygctl status.
0.1.4 — 2026-05-14
Framebuffer-first mode detection (PID 1 + isatty + no TERM defaults
to truecolor). Sets TERM=sun-color in zyginit env so children
inherit it. Plain-mode emit no longer mislabels event types.
0.1.3 — 2026-05-12
Initial visual pass: [Z] sigil, palette, rolling tape, progress
bar, spinner, final cards, mirrored shutdown UI, zygctl status
banner+table.