# SMF Migration — Handoff to Hammerhead

This document hands off the remaining SMF-removal work from the zyginit project to
the Hammerhead team. It covers source vendoring, build integration, the decision
process for converting each SMF manifest to a zyginit TOML service definition,
worked examples, anti-patterns drawn from the iter 1-20 development history,
per-category guidance, and a coarse catalog of all 128 manifests found on
`hh-prototest` as of 2026-05-02.

**See also:** `docs/DESIGN.md` for the zyginit architecture reference;
`docs/ROADMAP.md` for the forward-looking task checklist.

---

## 1. Handoff Brief

*This section orients the Hammerhead team on what zyginit has done, what is left,
and where to ask questions.*

### What zyginit has done (through rev 82)

zyginit is a working replacement for `svc.startd`, `svc.configd`, and the SMF
master restarter — a single Reef process that acts as PID 1, supervises services
defined in TOML files, and exposes an administrative socket for `zygctl`.

As of rev 82 (2026-05-02), zyginit boots Hammerhead to a fully-functional
multi-user state on `hh-prototest` (192.168.122.50) without any SMF
infrastructure running:

- **Multi-user boot** — all tier-0 through tier-7 services start cleanly.
- **Single-user mode** — boot arg `-s` or runtime `zygctl single` drops to
  `/sbin/sh` on `/dev/console`.
- **Runtime runlevel transitions** — `zygctl single` / `zygctl multi` (and the
  `init s` / `init 3` sysv wrappers) work bidirectionally.
- **Clean shutdown** — `zygctl halt` / `reboot` / `poweroff` perform a
  reverse-tier stop, ZFS sync, `umountall -l`, and `uadmin` from a forked
  non-PID-1 child.
- **utmpx integration** — `who -b`, `who -r`, and `uptime` report correctly.
- **18 services converted** — the current boot chain in `services/hammerhead/`
  covers the essential Hammerhead daemons. These TOMLs are the starting point
  for the Hammerhead source tree.

### What the Hammerhead team must do

1. **Vendor zyginit source** into `hammerhead-userland` (see Section 5 for the
   file-by-file layout and build instructions).
2. **Wire the build** to produce `/sbin/init` (zyginit), `/sbin/zygctl`, and
   `/sbin/sysv-wrapper` with its symlinks.
3. **Migrate the 18 existing TOMLs** from `services/hammerhead/` into the
   Hammerhead source tree.
4. **Validate** that the vendored source builds and boots identically to
   `hh-prototest` before starting new conversions.
5. **Convert the remaining ~110 SMF manifests** using the decision tree in
   Section 2, the worked examples in Section 3, the per-category guidance in
   Section 6, and the catalog in Section 7.
6. **Remove SMF infrastructure** — `svc.startd`, `svc.configd`, master restarter
   binaries, manifest infrastructure, and `libscf`-only consumers — from
   `hammerhead-userland` packaging.

### Done criteria for "SMF removal complete"

- Hammerhead boots to multi-user without `svc.startd` or `svc.configd` ever
  being invoked (verify with `pgrep -fl svc.startd`; expect zero results).
- `libscf` is not linked into any binary in the default boot path (acceptable to
  keep `libscf` installed for optional/zports consumers, but no required boot
  service calls into it).
- All 18 zyginit-managed services continue to work at least as well as the
  SMF-managed equivalents they replaced.
- Manifest XML files and `svc:/` repository infrastructure are removed from
  default packaging (they may be retained in a `smf-compat` optional package
  for administrators who need to run SMF tooling temporarily).
- `halt`, `reboot`, `poweroff`, and `init <N>` reach the hardware-level
  operation cleanly.

### Coordination

- **Questions about existing zyginit behavior** — open an issue against the
  zyginit repo. Include the service name, the symptom, and the hh-prototest log
  output.
- **TOML fields that don't fit a service's needs** — file an RFE against zyginit.
  Do not work around missing features in the service scripts; get the runtime
  feature first.
- **Hammerhead team's scope** — modifying `hammerhead-userland` is the
  Hammerhead team's job. The zyginit CLAUDE.md constraint "DO NOT modify
  Hammerhead source code" applies to the zyginit-project Claude, not to the
  Hammerhead Claude. The Hammerhead Claude should freely modify its own tree.

**See also:** Section 5 (Integration with Hammerhead), `docs/ROADMAP.md`
"SMF Removal from Hammerhead."

---

## 2. Decision Tree

*This section is the core reference for every manifest conversion. Walk each
manifest through the three steps in order.*

### Step 1 — Should this service exist on Hammerhead at all?

```
Does Hammerhead ship this functionality?
   |
   +-- No: deprecated, SPARC-only, SMF infrastructure, or NIS/iSCSI/etc.
   |     --> DROP. Remove from packaging; do not write a TOML.
   |         The catalog (Section 7) lists the obvious drops.
   |
   +-- Possibly, but as an optional zports add-on (Kerberos KDC, iSNS, etc.)
   |     --> DEFER. Out of scope for hammerhead-userland; leave for zports.
   |
   +-- Yes, ships in hammerhead-userland
         --> Continue to Step 2.
```

**Mandatory: do not skip Step 1.** Converting a service "because it exists" is
the single biggest source of wasted effort. The 128 manifests include services
Hammerhead does not and should never ship (SMF infrastructure, SPARC platform
code, NIS, iSCSI target, Trusted Extensions label daemon, etc.). These are
straightforward drops.

### Step 2 — Is the service coupled to SMF infrastructure at runtime?

```
Does the daemon call into svc.configd / libscf / the SMF repository door
at runtime (not just at startup)?
   |
   +-- Yes, fundamentally requires libscf or the repository for ongoing
   |   operation (e.g., svc.startd, svc.configd themselves)
   |     --> DROP (if it IS SMF) or INVESTIGATE (if it merely uses libscf).
   |         For investigate: look for a foreground/standalone mode flag.
   |         Coordinate with the daemon's upstream if none exists.
   |
   +-- Yes at startup, but the daemon has a non-SMF mode flag
   |   (-d, -f, -D, or similar) that skips the startup gate and the
   |   daemonize double-fork
   |     --> CONVERT using the foreground-mode pattern. See Example 1.
   |         Known flags on Hammerhead daemons:
   |           dlmgmtd   -d   (debug/foreground; bypasses SMF_FMRI gate)
   |           ipmgmtd   -f   (identical pattern)
   |           sshd      -D   (standard OpenSSH; no daemonize)
   |           syslogd   -d   (foreground; standard illumos)
   |           fmd       -f   (foreground; from fmd source)
   |         Check each daemon's man page and source before assuming
   |         SMF coupling is unavoidable.
   |
   +-- No SMF coupling, or coupling is purely cosmetic (e.g., SMF_FMRI
       env var used only for a cache-file name)
         --> Continue to Step 3.
```

**Getting Step 2 wrong was the entire iter 1-5 dead end.** When dlmgmtd was
started without its foreground flag, it failed silently — no error on stderr
(daemonize had already closed fd 2), and the network never came up. See
`memory/pid1_boot_iter11_15.md` for the full diagnosis.

### Step 3 — What runs the service today?

```
What does the SMF exec_method actually invoke?
   |
   +-- Direct daemon binary (no method script)
   |     --> CONVERT with a simple TOML. exec.start = the binary + args.
   |         Most daemons with exec=':kill' for stop use the contract
   |         signal by default — omit [stop] unless you need custom signal.
   |
   +-- /lib/svc/method/<name> shell script
   |     |
   |     +-- Script is trivial (rm a FIFO, mkdir, then exec the daemon)
   |     |     --> INLINE the setup into a wrapper start.sh. See Example 2.
   |     |         Services that take this path: cron (rm FIFO + exec),
   |     |         syslogd (touch /var/adm/messages + exec), console-login
   |     |         (write utmpx entry + exec ttymon).
   |     |
   |     +-- Script sources /lib/svc/share/smf_include.sh AND calls
   |     |   smf_present() or smf_clear_method_context()
   |     |     --> Check if smf_present() branch is the failure path only.
   |     |         If so, the script works fine without configd (smf_present
   |     |         returns false; the relevant branch is skipped). Test
   |     |         carefully. Many scripts are safe even with smf_include.sh.
   |     |
   |     +-- Script does substantial, maintained setup work
   |           --> KEEP the method script as exec.start. Add SMF_FMRI to
   |               [exec] environment if the script requires it for
   |               non-configd purposes (e.g., FIFO name, log prefix).
   |               See Example 2 alternate form.
   |
   +-- Oneshot configuration command (mountall, swapadd, dumpadm, coreadm)
   |     --> CONVERT as type = "oneshot". See services/hammerhead/
   |         root-fs.toml, swap.toml, filesystem.toml for patterns.
   |         Onshots use restart.on = "never".
   |
   +-- inetd-managed service (not a standalone daemon)
         --> DROP unless there is a strong reason to keep inetd.
             Hammerhead does not run inetd. If the underlying
             functionality is needed (e.g., legacy RPC), the service
             needs its own TOML as a standalone daemon.
```

### Field-by-field translation table

| SMF manifest element | TOML field | Notes |
|---|---|---|
| `<service name='X'>` | `[service] name` | Strip the category prefix (e.g., `network/ssh` → `"sshd"`) |
| `<template><common_name>` | `[service] description` | Plain string; omit XML whitespace |
| SMF behavior class | `[service] type` | Map: continuous daemon → `"daemon"`; fire-and-forget → `"oneshot"`; transient SMF → `"transient"` |
| `<exec_method name='start' exec='...'>` | `[exec] start` | Replace with binary + foreground flag if Step 2 says foreground-mode; or use method script path if Step 3 says keep |
| `<exec_method name='stop' exec=':kill'>` | (omit — default) | `:kill` in SMF = send contract signal; zyginit does this by default via `[stop] method = "contract"` |
| `<exec_method name='stop' exec='some-cmd'>` | `[stop] method = "exec"` + `[exec] stop = "some-cmd"` | For custom stop scripts |
| `<dependency type='service' ...><service_fmri value='svc:/X'>` | `[dependencies] requires = ["X"]` | Translate FMRI basename; drop all SMF-internal dependencies (milestone/*, system/svc/*, restarter) |
| `<dependency type='path' ...>` | `[dependencies] requires = ["filesystem"]` | Path deps mean "filesystem is up"; map to the filesystem service |
| `<property_group name='startd'><propval name='duration' value='transient'>` | `[service] type = "oneshot"` | SMF transient duration = zyginit oneshot |
| `<property_group name='general'><propval name='enabled'>` | (not in TOML) | Enabled state is set by the operator via symlink in `/etc/zyginit/enabled.d/` |
| Restart policy | `[restart] on = "always\|failure\|never"` | SMF `ignore_error` → `on = "failure"`; SMF restart contract → `on = "always"` |
| `<property_group name='startd'><propval name='ignore_error' value='core,signal'>` | `[restart] on = "failure"` | Core + signal ignores = only restart on clean failure |
| `<method_context><method_credential user='X' group='Y'>` | `[exec] user = "X"` and `[exec] group = "Y"` | Drop privileges before exec |
| `<method_context working_directory='/'>` | `[exec] working_directory = "/"` | Inherited if omitted; specify if non-root |
| Contract params | `[contract] param = [...]` | Defaults (`["inherit", "noorphan"]`) are correct for most services |

**See also:** `docs/man/man5/zyginit.5` for the authoritative field-by-field
TOML format reference; this table covers patterns and translation, not syntax.

---

## 3. Worked Examples

*This section walks three representative manifests through the full
conversion process.*

---

### Example 1 — Foreground-mode conversion: dlmgmtd

**Manifest:** `/lib/svc/manifest/network/dlmgmt.xml`
**Result:** `services/hammerhead/dlmgmtd.toml`
**Pattern:** foreground-mode (Decision Tree Step 2, middle branch)

#### Original manifest (key lines)

```xml
<service name='network/datalink-management' type='service' version='1'>
    <dependent name='dlmgmt-device-system' grouping='require_all'
        restart_on='none'>
        <service_fmri value='svc:/system/device/local' />
    </dependent>
    <exec_method type='method' name='start'
        exec='/lib/svc/method/svc-dlmgmtd' timeout_seconds='60' />
    <exec_method type='method' name='stop' exec=':kill' timeout_seconds='3' />
```

`/lib/svc/method/svc-dlmgmtd` checks for `SMF_FMRI` and calls `dlmgmtd`
with no additional flags — dlmgmtd daemonizes on its own.

#### Decision-tree walk

- **Step 1:** Yes, Hammerhead ships dlmgmtd. It is required for `ifconfig plumb`
  and `dladm` to work at all on illumos; libdladm resolves link names through
  dlmgmtd's door.
- **Step 2:** Yes, dlmgmtd checks `getenv("SMF_FMRI")` at startup to derive a
  cache-file name, then calls `daemonize()`. Without the foreground flag, both
  checks happen even when started outside SMF. But `dlmgmtd -d` skips both:
  the debug-mode branch in `dlmgmt_main.c` bypasses the `SMF_FMRI` check and
  the `dlmgmt_daemonize()` call entirely. The door is still created at the same
  path. This is the foreground-mode pattern — use `-d`.
- **Step 3:** not reached (Step 2 resolved it).

#### Resulting TOML

The existing `services/hammerhead/dlmgmtd.toml` is the production result.
Its comment block explains each decision:

```toml
# dlmgmtd — Datalink management daemon
#
# Runs in `-d` (debug) mode, which on dlmgmtd means "single foreground
# process, no double-fork daemonize." Three reasons we use it:
#   1. Skips the SMF_FMRI startup gate (debug mode bypasses the check).
#   2. Keeps stderr connected to our log; without it dlmgmtd's daemonize
#      runs fdwalk(closefunc), closing fd 2 — any startup error then
#      goes to syslog, which isn't running yet at this tier.
#   3. zyginit's contract tracks a single PID instead of dealing with
#      the parent-exits-0-after-handshake daemonize pattern.
# Cosmetic effect: the cache file is named "dlmgmtd.debug.cache" instead
# of "network-datalink-management:default.cache". Nothing external reads
# this file — only dlmgmtd itself, for fast restart state recovery.

[service]
name = "dlmgmtd"
description = "Datalink management daemon (foreground)"
type = "daemon"

[exec]
start = "/sbin/dlmgmtd -d"

[dependencies]
requires = ["filesystem"]

[restart]
on = "failure"
delay = 5
max_retries = 3

[contract]
param = ["inherit", "noorphan"]
fatal = ["hwerr"]
```

#### Key field mapping decisions

- `name` — FMRI basename `network/datalink-management` collapsed to `dlmgmtd`.
- `exec.start` — direct binary + flag; method script path dropped entirely.
- `dependencies.requires = ["filesystem"]` — the SMF `<dependent>` blocks on
  `network/physical` are dropped (SMF-internal ordering, not real deps).
- `stop` — omitted; SMF's `:kill` = zyginit's default contract signal.
- `contract.fatal = ["hwerr"]` not `["hwerr", "empty"]` — the daemonize
  handshake transiently empties the contract; zyginit handles that correctly.

Verify after boot: `pgrep -fl dlmgmtd` shows `dlmgmtd -d`; `ls /etc/svc/volatile/dladm/dlmgmt_door` shows the door; `dladm show-phys` returns the NIC list.

**See also:** `memory/pid1_boot_iter11_15.md` for the diagnostic history;
`services/hammerhead/ipmgmtd.toml` for the identical pattern applied to ipmgmtd.

---

### Example 2 — Method-script wrap: cron

**Manifest:** `/lib/svc/manifest/system/cron.xml`
**Result:** `services/hammerhead/cron.toml` + `services/hammerhead/cron/start.sh`
**Pattern:** trivial method-script inlined (Decision Tree Step 3, inline branch)

#### Original manifest (key lines)

```xml
<service name='system/cron' type='service' version='1'>
    <dependency name='usr' type='service' grouping='require_all' restart_on='none'>
        <service_fmri value='svc:/system/filesystem/local' />
    </dependency>
    <dependency name='ns' type='service' grouping='require_all' restart_on='none'>
        <service_fmri value='svc:/milestone/name-services' />
    </dependency>
    <exec_method type='method' name='start' exec='/lib/svc/method/svc-cron'
        timeout_seconds='60'>
        <method_context><method_credential user='root' group='root' /></method_context>
    </exec_method>
    <exec_method type='method' name='stop' exec=':kill' timeout_seconds='60' />
```

`/lib/svc/method/svc-cron` sources `smf_include.sh` (for `$SMF_EXIT_*`
constants and `smf_zonename` — no configd call), removes `/etc/cron.d/FIFO` if
stale, and runs `/usr/sbin/cron &`.

#### Decision-tree walk

- **Step 1:** Yes, Hammerhead ships cron. Required for log rotation and
  scheduled tasks.
- **Step 2:** No SMF coupling. `svc-cron` sources `smf_include.sh` only for
  `$SMF_EXIT_*` constants and `smf_zonename` (which calls `/sbin/zonename` — no
  configd). The exit codes are not observed by zyginit's contract model anyway.
  No configd calls anywhere.
- **Step 3:** Method script. Assessment: trivial. The script does two things
  before starting cron: check for a stale FIFO and remove it. The `pgrep` guard
  is irrelevant under zyginit (supervisor doesn't double-start). The `cron &`
  backgrounding is replaced by `exec /usr/sbin/cron` in the wrapper.

#### Two valid TOML forms

**Form A — Inline the setup in a wrapper start.sh (production choice):**

```toml
[service]
name = "cron"
description = "cron daemon"
type = "daemon"

[exec]
start = "/etc/zyginit/cron/start.sh"

[dependencies]
requires = ["syslogd"]

[restart]
on = "failure"
delay = 5
max_retries = 3

[contract]
param = ["inherit", "noorphan"]
fatal = ["hwerr"]
```

With `services/hammerhead/cron/start.sh`:

```sh
#!/bin/zsh
# Clean up stale FIFO before starting cron.
# /usr/sbin/cron creates /etc/cron.d/FIFO at startup and refuses to
# start if the file already exists — mirrors svc-cron's behavior.
set -e
/bin/rm -f /etc/cron.d/FIFO
exec /usr/sbin/cron
```

**Form B — Use method script directly (transitional):**

```toml
[exec]
start = "/lib/svc/method/svc-cron"
environment = ["SMF_FMRI=svc:/system/cron:default"]
```

Choose Form A when the setup is short and you want to decouple from SMF method
script files (the preferred end state — `/lib/svc/method/svc-cron` will be
removed). Choose Form B as a transitional step when the method script does
substantial correct setup and does not call `smf_present()` in any required
code path.

#### Key field mapping decisions

- `dependencies.requires = ["syslogd"]` — SMF's milestone deps (`filesystem/local`,
  `name-services`) are replaced with direct deps on zyginit services. Drop all
  milestone FMRI references; they don't exist in zyginit.
- `type = "daemon"` — continuous daemon; SMF's `<single_instance/>` is implicit.
- `stop` omitted; `working_directory` and `user/group = root` omitted (defaults).

**See also:** `services/hammerhead/syslogd.toml` and
`services/hammerhead/syslogd/start.sh` for the same pattern (touch log file +
exec daemon); `services/hammerhead/console-login/` for a more involved wrapper
(write utmpx entry + exec ttymon).

---

### Example 3 — Drop: svc.startd / system/svc/restarter

**Manifest:** `/lib/svc/manifest/system/svc/restarter.xml`
**Result:** DROP — no TOML.
**Pattern:** Decision Tree Step 1, no branch.

#### Original manifest (key lines)

```xml
<service name='system/svc/restarter' type='restarter' version='1'>
    <create_default_instance enabled='true' />
    <exec_method type='method' name='start' exec=':true' timeout_seconds='0' />
    <exec_method type='method' name='stop'  exec=':true' timeout_seconds='0' />
```

FMRI: `svc:/system/svc/restarter:default`. The exec methods are `:true` because
the kernel never invokes them — `init` starts svc.startd directly, and this
manifest exists only so svc.startd can store its own properties in the
SMF repository.

#### Decision-tree walk

- **Step 1:** No. This manifest IS SMF. zyginit replaces the functionality of
  `svc.startd`, `svc.configd`, and the master restarter framework entirely.
  There is nothing to convert. DROP.

#### The cascade

Every other SMF manifest on the system has at least one `<dependency>` block
referencing SMF-internal FMRIs. When you convert those services, drop all such
references. They are not real ordering constraints between daemons — they are
SMF's internal "this-service-needs-the-SMF-infrastructure" boilerplate.

Common SMF-internal FMRIs to drop from `requires` when converting:

```text
svc:/system/svc/restarter:default     (the restarter framework itself)
svc:/milestone/single-user:default    (SMF single-user milestone)
svc:/milestone/multi-user:default     (SMF multi-user milestone)
svc:/milestone/name-services:default  (SMF name-services milestone)
svc:/milestone/network:default        (SMF network milestone)
svc:/milestone/sysconfig:default      (SMF sysconfig milestone)
svc:/system/manifest-import:default   (SMF manifest import oneshot)
svc:/system/early-manifest-import:default
```

Replace the real ordering constraints (filesystem, syslogd, dlmgmtd, etc.)
with bare service names pointing at the corresponding zyginit-managed services.

#### Companion drops

The following manifests are all SMF infrastructure and must be dropped along
with the restarter:

| Manifest | FMRI | Reason |
|---|---|---|
| `system/svc/restarter.xml` | `svc:/system/svc/restarter:default` | Is svc.startd |
| `system/svc/global.xml` | `svc:/system/svc/global:default` | SMF global property store |
| `system/early-manifest-import.xml` | `svc:/system/early-manifest-import:default` | SMF import at boot |
| `system/manifest-import.xml` | `svc:/system/manifest-import:default` | SMF import daemon |

The SMF binaries to remove from `hammerhead-userland` packaging:
`/lib/svc/bin/svc.startd`, `/lib/svc/bin/svc.configd`,
`/usr/sbin/svcadm`, `/usr/sbin/svccfg`, `/usr/sbin/svcs`,
`/usr/sbin/svcprop`. The `/lib/svc/share/smf_include.sh` helper library
can remain if any method scripts continue to be used during the transition
(Form B of method-wrap); remove it when all method scripts are replaced.

**See also:** Section 6 "Per-Category Guidance" for the full `system/svc/`
category drop rule.

---

## 4. Anti-patterns and Gotchas

*This section documents known failure modes from the iter 1-20 development
history. Each entry gives the symptom, root cause, and fix.*

### 1. configd-coupled daemons degrade silently

**Symptom:** Service starts (fork succeeds, contract created), but functionality
is absent — `dladm show-phys` returns empty, `ifconfig plumb` returns
"no such interface."

**Cause:** Daemon checks `getenv("SMF_FMRI")` or calls into `libscf` at startup.
Without `svc.configd` running, the check fails or the call blocks/returns
error, and the daemon silently skips its initialization. No log output because
the daemon's `daemonize()` call has already closed stderr (fd 2) before the
error is reached.

**Fix:** Use the daemon's foreground/debug flag (see Decision Tree Step 2).
In foreground mode the SMF_FMRI check is bypassed AND stderr stays connected
to zyginit's log, so startup errors are visible. **Never start a configd-coupled
daemon without its foreground flag.**

Reference: `memory/pid1_boot_iter11_15.md` (dlmgmtd and ipmgmtd diagnosis).

### 2. Foreground-flag names vary by daemon

**Symptom:** You know a foreground mode exists but can't find the right flag.

**Cause:** There is no standard flag name. Each daemon chose its own.

**Fix:** Check the daemon's man page, its `main()` usage string, and
(for open-source daemons) its source. Known Hammerhead flags:

| Daemon | Flag | Effect |
|---|---|---|
| `dlmgmtd` | `-d` | foreground, no daemonize, bypass SMF_FMRI gate |
| `ipmgmtd` | `-f` | identical pattern |
| `sshd` | `-D` | foreground, no daemonize (standard OpenSSH) |
| `syslogd` | `-d` | foreground (standard illumos) |
| `fmd` | `-f` | foreground (Fault Manager Daemon) |
| `inetd` | n/a | SMF_FMRI env var required instead |

For daemons where no foreground flag exists, consider Form B method-wrap
(keep the SMF method script) as a transitional option.

### 3. soconfig must run before any network sockets

**Symptom:** `socket(AF_INET, ...)` returns `EAFNOSUPPORT` even though the
network interface is up.

**Cause:** illumos sockfs's protocol map is not populated until `soconfig -d /etc/sock2path.d` runs. SMF's `system/device/local` did this; without it `AF_INET` is unknown to the kernel.

**Fix:** `services/hammerhead/devfs/start.sh` runs `soconfig` in the devfs tier.
All services that need networking depend (directly or transitively) on `devfs`.
Reference: `memory/pid1_boot_iter11_15.md`, iter 11.

### 4. Daemonize handshake: supervisor sees "stopped (exit 0)"

**Symptom:** `zygctl status` shows a daemon as `STOPPED` right after start even though it is running.

**Cause:** Double-fork daemonize — the wrapper parent exits 0 while the daemon child is alive in the contract.

**Fix:** zyginit (rev 78+) detects the handshake: `type = "daemon"` + parent exits 0 + contract still has processes → clear `rt.pid` but keep the contract. Ensure TOML says `type = "daemon"`, not `"oneshot"`.
Reference: `memory/pid1_boot_iter16.md`.

### 5. /var/run is not tmpfs in the current zyginit BE

**Symptom:** Stale socket, PID, or lock files survive reboots.

**Cause:** Standard illumos mounts `/var/run` as tmpfs via `fs-minimal`. zyginit's `filesystem/start.sh` does not yet do this.

**Fix (short-term):** Add `rm -f /var/run/<svc>.pid` in affected `start.sh` wrappers. Long-term: add `mount -F tmpfs swap /var/run` to `filesystem/start.sh`. `socket.reef` already handles stale socket recovery via unlink-before-bind.

### 6. utmpx record positioning for who(1) -r

**Symptom:** `who -r` reports the wrong runlevel after a `zygctl single` / `zygctl multi` transition.

**Cause:** `pututxline(3C)` appends a new `RUN_LVL` record unless `getutxid` positions the cursor on an existing one first. `who -r` reads only the first matching record.

**Fix:** Call `getutxid` before `pututxline`. The `zyginit_write_runlvl_utmpx` helper in `src/helpers.c` does this correctly; use it.
Reference: `memory/pid1_boot_iter18.md`.

### 7. Do not set ZFS readonly=on during shutdown

**Symptom:** After `zygctl reboot`, the next boot sees pre-shutdown state — stale socket, daemons appear wrong.

**Cause:** `zfs set readonly=on` on the live root mid-shutdown makes all subsequent writes (including socket unlink and log writes) silently fail. ZFS records the pre-readonly txg; next boot sees it.

**Fix:** Remove `readonly=on` from any stop script. ZFS is transactionally consistent; `zpool sync` + `umountall -l` + kernel's `vfs_syncall` (in `mdboot`) is sufficient. `root-fs/stop.sh` was corrected in rev 82.
Reference: `memory/pid1_shutdown_fix.md`, bug #2.

### 8. uadmin must not be called from PID 1

**Symptom:** `zygctl reboot` completes the shutdown log but does not reset the hardware. The kernel re-execs `/sbin/init`; uptime does not reset; orphaned daemons collide with the new instance.

**Cause:** `uadmin` called from PID 1 hits the `restart_init` path — the kernel re-execs init before `mdboot()` fires the hardware reset.

**Fix:** Fork a child; call `uadmin` from the child (non-PID-1). Parent enters infinite sleep. This mirrors `cmd/svc/startd/graph.c:do_uadmin`. See `src/main.reef` shutdown sequence.
Reference: `memory/pid1_shutdown_fix.md`, bug #1.

### 9. /etc/hosts: localhost only, no hostname entry

**Symptom:** SSH login takes ~20 seconds even with the network up.

**Cause:** The bare Hammerhead BE ships `/etc/hosts` with only `localhost`. `sshd` does a reverse DNS lookup on the client address; without a local entry or working resolver, it times out.

**Fix (operator):** Add the system's IP and hostname to `/etc/hosts`.
**Fix (upstream):** File as a Hammerhead base-system bug; the install or `identity` service should populate `/etc/hosts`. Not a zyginit issue.

---

## 5. Integration with Hammerhead

*This section specifies how to vendor zyginit into `hammerhead-userland` and
wire it into the Hammerhead build.*

### 5.1 Source layout

Recommended target layout in the Hammerhead source tree (BSD/illumos convention
for `cmd/` userland utilities):

```
usr/src/cmd/init/
    src/                  <- zyginit Reef source files
    src/helpers.c         <- required C helper (non-optional)
    services/             <- service TOML definitions and start/stop scripts
    reef.toml             <- reefc project config

usr/src/cmd/zygctl/
    src/                  <- zygctl Reef source files
    src/symlink_wrapper.c <- required C helper for zygctl

usr/src/cmd/sysv-wrapper/
    wrapper.c
    Makefile
    <- builds /sbin/sysv-wrapper; Makefile install creates symlinks

usr/src/man/man5/
    zyginit.5

usr/src/man/man8/
    zyginit.8
    zygctl.8
```

The sync mechanism (git submodule, source snapshot, or fetch-and-extract)
is the Hammerhead team's call. A simple approach: tag zyginit releases in its
repo, fetch a source archive at a known tag, unpack under `usr/src/cmd/init/`.

### 5.2 File-by-file source mapping

| zyginit repo path | Hammerhead target | Notes |
|---|---|---|
| `src/*.reef` | `usr/src/cmd/init/src/` | All Reef source files |
| `src/helpers.c` | `usr/src/cmd/init/src/` | Required for link |
| `src/contract_linux_stubs.c` | DROP | Linux-only stubs; not for Hammerhead |
| `services/hammerhead/*.toml` | `usr/src/cmd/init/services/` | 18 converted service definitions |
| `services/hammerhead/*/` | `usr/src/cmd/init/services/<svc>/` | Per-service start.sh / stop.sh subdirs |
| `reef.toml` | `usr/src/cmd/init/reef.toml` | reefc project config |
| `tools/zygctl/src/` | `usr/src/cmd/zygctl/src/` | zygctl Reef source |
| `tools/zygctl/src/symlink_wrapper.c` | `usr/src/cmd/zygctl/src/` | Required for zygctl link |
| `tools/zygctl/reef.toml` | `usr/src/cmd/zygctl/reef.toml` | zygctl reefc config |
| `tools/sysv-wrapper/wrapper.c` | `usr/src/cmd/sysv-wrapper/` | sysv-init compat wrapper |
| `tools/sysv-wrapper/Makefile` | `usr/src/cmd/sysv-wrapper/` | Builds + installs symlinks |
| `docs/man/man5/zyginit.5` | `usr/src/man/man5/` | |
| `docs/man/man8/zyginit.8` | `usr/src/man/man8/` | |
| `docs/man/man8/zygctl.8` | `usr/src/man/man8/` | |

### 5.3 Build invocation

The Hammerhead master build must invoke these steps for `usr/src/cmd/init/`:

```sh
# Build helpers.o
gcc -c src/helpers.c -o build/helpers.o

# Build zyginit daemon (produces build/zyginit)
reefc build -l contract --obj build/helpers.o

# Install
cp build/zyginit /sbin/init
```

For `usr/src/cmd/zygctl/`:

```sh
gcc -c src/symlink_wrapper.c -o build/symlink_wrapper.o
reefc build --obj build/symlink_wrapper.o
cp build/zygctl /sbin/zygctl
```

For `usr/src/cmd/sysv-wrapper/`:

```sh
make
make install
# Creates /sbin/sysv-wrapper and symlinks:
#   /sbin/halt -> /sbin/sysv-wrapper
#   /sbin/reboot -> /sbin/sysv-wrapper
#   /sbin/poweroff -> /sbin/sysv-wrapper
#   /sbin/telinit -> /sbin/sysv-wrapper
```

Service files must be installed to `/etc/zyginit/`:

```sh
# Install TOML service definitions
cp services/<svc>.toml /etc/zyginit/

# Install per-service script directories
cp -r services/<svc>/ /etc/zyginit/

# Enable services by symlinking into enabled.d/
ln -s /etc/zyginit/<svc>.toml /etc/zyginit/enabled.d/<tier>-<svc>.toml
```

The `enabled.d/` naming convention is `<tier>-<svcname>.toml` where tier is a
two-digit number (e.g., `00-root-fs.toml`, `07-sshd.toml`). See
`services/hammerhead/enabled.d/` on hh-prototest for the current tier
assignments.

### 5.4 Reef compiler dependency

Hammerhead's build requires `reefc`. Options:

1. **Bootstrap from Reef repo** — clone `~/repos/reef-lang/` (or the Reef
   upstream repo) and build `reefc` as part of the Hammerhead build setup.
   Required version: Reef 0.4.0+.
2. **Pre-built reefc** — package a pre-built `reefc` binary for the Hammerhead
   build host (the `hammerhead-build` VM at 192.168.122.66 already has
   a working `reefc`). Simpler short-term; breaks if the build host is
   reprovisioned.

`clang` is not available on Hammerhead (standard Hammerhead build host uses
`gcc`); use `gcc` for `helpers.c` and `symlink_wrapper.c`.

### 5.5 First migration step (concrete deliverable)

Before starting any new manifest conversions, complete this validation
sequence:

1. **Vendor zyginit source** at a known rev into `usr/src/cmd/init/` and
   `usr/src/cmd/zygctl/` and `usr/src/cmd/sysv-wrapper/`.
2. **Wire the build** to produce `/sbin/init`, `/sbin/zygctl`, and
   `/sbin/sysv-wrapper`.
3. **Vendor the 18 existing TOMLs** from `services/hammerhead/` into
   `usr/src/cmd/init/services/`; install them to `/etc/zyginit/` and
   symlink them in `enabled.d/`.
4. **Build a Hammerhead BE** with the new binaries and service definitions.
5. **Boot hh-prototest** from the new BE; verify all 18 services reach
   `RUNNING` in `zygctl status`; verify SSH is reachable; verify console
   login works.
6. **Only then** begin the per-manifest conversion work using the decision
   tree and catalog.

Starting new conversions before step 5 is validated means any new issue
could be caused by the vendoring/build change rather than the conversion.

### 5.6 SMF removal step

Removing SMF infrastructure is a separate Hammerhead task after all needed
services have been converted. The sequence:

1. Remove `svc.startd` and `svc.configd` binaries from the build and packaging.
2. Remove SMF manifest XML files from `/lib/svc/manifest/` packaging.
3. Remove `libscf` linkage from any binary that links it but can be updated
   (check with `ldd -r /sbin/init` and any other boot-path binary).
4. Remove `svcadm`, `svccfg`, `svcs`, `svcprop` from the default packaging
   (optionally retain in a `smf-compat` package for administrators).
5. Verify boot still works end-to-end.

### 5.7 Upstream sync workflow

When zyginit ships new features (new TOML fields, runtime improvements):

1. Fetch the zyginit repo at a new tag or commit hash.
2. Refresh the vendored source under `usr/src/cmd/init/`.
3. Resolve any local Hammerhead patches that conflict.
4. Rebuild and test on `hh-prototest`.
5. Record the sync point (commit hash / rev number) in Hammerhead's
   changelog entry.

**See also:** Section 2 (Decision Tree), `docs/man/man5/zyginit.5`.

---

## 6. Per-Category Guidance

*This section gives top-down guidance for each SMF manifest tree category.
Read once for orientation, then drop into the catalog appendix (Section 7)
for per-manifest decisions.*

### `milestone/`

**Default: DROP all.** The six milestone manifests (`single-user`, `multi-user`,
`multi-user-server`, `network`, `name-services`, `sysconfig`) are SMF's
internal synchronization fences. zyginit replaces them with tier-ordered
service startup. When converting other manifests, drop all `<dependency>`
references to milestone FMRIs; replace with direct deps on the zyginit-managed
services that actually matter for ordering.

### `platform/`

**Default: keep amd64; drop all others.** There is only one platform manifest
on `hh-prototest`: `platform/amd64/acpihpd.xml` (ACPI hotplug daemon).
Hammerhead is amd64-only; no sun4u/sun4v entries exist on this system.
`acpihpd` is an amd64-platform-specific daemon for hotplug events —
investigate whether Hammerhead ships the hardware that needs it;
`needs-investigation` until confirmed.

### `network/`

**Selective.** Core networking (dlmgmtd, ipmgmtd — already converted, sshd)
are kept. The decision landscape:

- **Already converted:** dlmgmtd, ipmgmtd, sshd.
- **Consolidated into `network.toml`:** loopback, physical, initial, netmask,
  service, routing-setup — all collapsed into the single `network` oneshot.
- **Needs investigation:** NFS (does Hammerhead ship NFS client? server?),
  DNS resolver (likely needed; investigate whether to use a standalone
  `resolv.conf` or run `nscd`), ipfilter (does Hammerhead use it?),
  IPsec (policy and algs may be needed).
- **Drop:** NIS (`/network/nis/` — deprecated everywhere), iSCSI target/initiator
  (storage-specific; defer to zports), iSNS server, SMB client/server (defer),
  Kerberos KDC components (server-role only; defer to zports), LDAP client
  (environment-specific), loadbalancer (ilbd), Trusted Networking (tnctl/tnd
  — Trusted Extensions, which Hammerhead does not ship).
- **Drop inetd:** Hammerhead does not run inetd. The `inetd.xml` and
  `inetd-upgrade.xml` manifests are dropped. If any inetd-managed service is
  needed, write a standalone TOML for it.

### `system/`

The largest category; mixed disposition:

- **Already converted:** cron, cryptosvc (as `crypto`), devfs (devices-local),
  filesystem (local-fs + minimal-fs + root-fs), fmd, identity, pfexecd,
  syseventd, system-log (as `syslogd`), utmp (as `utmpd`), zones is pending.
- **Keep and convert:** coreadm, dumpadm, hotplug (if hardware warrants it),
  intrd (interrupt balancer daemon), keymap, power (powerd), rbac (config
  oneshot), scheduler, vtdaemon (VT multiplexer).
- **Needs investigation:** auditd (does Hammerhead ship BSM auditing?), idmap
  (identity mapping — needed if SMB/NFS Kerberos are used), name-service-cache
  (nscd), pools (resource pools — does Hammerhead use them?), rcap (resource
  capping), zonestat (zone statistics — relevant once zones are enabled).
- **Drop:** All of `system/svc/` (SMF infrastructure itself), `manifest-import`,
  `early-manifest-import`, `logadm-upgrade` (upgrade artifact), `boot-archive`
  and `boot-archive-update` (SMF-managed tasks; zyginit doesn't use
  boot-archive service), `update-man-index` (defer), `extended-accounting`
  (rarely used), `fcoe_initiator` / `fcoe_target` (storage-specific), IBSRP
  target (InfiniBand SRP — hardware-specific), `i2csimd` (platform-specific
  sensor daemon from a test tree), `labeld` (Trusted Extensions label daemon —
  Hammerhead does not ship TX), `tsol-zones` (Trusted Extensions zones), `ndmp`
  (backup protocol — defer to zports), `stmf` (SCSI Target Mode Framework —
  storage specific), `vscan` (virus scan framework — defer), `ipagent` (from
  a specific Nexenta/Triton context — likely irrelevant), `hostid` (sets
  hardware hostid; verify if Hammerhead needs it), `process-security` (noop
  service — just drop).

### `system/filesystem/`

**Default: all converted or redundant.** All four filesystem-related manifests
(`minimal-fs`, `local-fs`, `root-fs`, `usr-fs`) are collapsed into zyginit's
`filesystem.toml` and `root-fs.toml` onshots. `autofs` should be dropped
unless Hammerhead actively uses automounting. `vscan` is dropped.

### `system/fm/`

`fmd.xml` is already converted. `notify-params.xml` and `smtp-notify.xml` are
SMF notification hooks — drop or defer; zyginit has no equivalent mechanism.

### `system/device/`

`devices-local.xml` is the basis for `services/hammerhead/devfs.toml` (the
devfsadm + soconfig oneshot). `devices-audio.xml` can be method-wrapped or
dropped depending on whether Hammerhead ships audio support. `mpxio-upgrade.xml`
is an upgrade artifact; drop.

**See also:** Section 7 (Catalog Appendix) for per-manifest classification.

---

## 7. Catalog Appendix

*This appendix is a snapshot of all 128 SMF manifests found in
`/lib/svc/manifest/` and `/var/svc/manifest/` on `hh-prototest` as of
2026-05-02. It is a starting-point reference, not a living index — as
Hammerhead evolves, this catalog will drift from the actual source tree.*

*Classifications: `already-converted` (TOML exists in `services/hammerhead/`),
`drop` (should not be in Hammerhead), `foreground` (use daemon's foreground
flag), `method-wrap` (keep the SMF method script as exec.start), or
`needs-investigation` (Hammerhead-team policy decision required).*

| Manifest path | Category | Classification | Note |
|---|---|---|---|
| `/lib/svc/manifest/milestone/multi-user-server.xml` | milestone | drop | SMF milestone — no zyginit equivalent; referenced `<dependency>` blocks must be dropped from other conversions |
| `/lib/svc/manifest/milestone/multi-user.xml` | milestone | drop | SMF milestone — tier ordering replaces it |
| `/lib/svc/manifest/milestone/name-services.xml` | milestone | drop | SMF milestone — no zyginit equivalent |
| `/lib/svc/manifest/milestone/network.xml` | milestone | drop | SMF milestone — `network.toml` replaces it |
| `/lib/svc/manifest/milestone/single-user.xml` | milestone | drop | SMF milestone — `single-user-shell.toml` and runlevel mode replace it |
| `/lib/svc/manifest/milestone/sysconfig.xml` | milestone | drop | SMF milestone — exec=:true; purely a dependency fence |
| `/lib/svc/manifest/platform/amd64/acpihpd.xml` | platform | needs-investigation | ACPI hotplug daemon; amd64-specific; method script `/lib/svc/method/svc-acpihpd`; determine if Hammerhead hardware needs hotplug events |
| `/lib/svc/manifest/network/bridge.xml` | network | needs-investigation | Bridge networking daemon; determine if Hammerhead uses software bridging |
| `/lib/svc/manifest/network/dlmgmt.xml` | network | already-converted | `services/hammerhead/dlmgmtd.toml`; foreground flag `-d` |
| `/lib/svc/manifest/network/dns/client.xml` | network | needs-investigation | DNS client configuration service (exec=:true — just stores resolver config in SMF repo); in zyginit world resolv.conf is written directly by the installer; determine if a service is needed |
| `/lib/svc/manifest/network/dns/install.xml` | network | drop | Install-time DNS setup artifact |
| `/lib/svc/manifest/network/dns/multicast.xml` | network | needs-investigation | mDNS daemon (`/usr/lib/inet/mdnsd`); determine if Hammerhead needs mDNS |
| `/lib/svc/manifest/network/forwarding.xml` | network | needs-investigation | IPv4/IPv6 forwarding toggle; determine if Hammerhead acts as a router |
| `/lib/svc/manifest/network/ibd-post-upgrade.xml` | network | drop | InfiniBand IPoIB upgrade artifact |
| `/lib/svc/manifest/network/inetd-upgrade.xml` | network | drop | inetd upgrade artifact; Hammerhead does not run inetd |
| `/lib/svc/manifest/network/inetd.xml` | network | drop | Hammerhead does not run inetd; existing `inetd.toml` in repo is an experiment, not production |
| `/lib/svc/manifest/network/ipfilter.xml` | network | needs-investigation | IP packet filter daemon; determine if Hammerhead uses ipfilter for firewall |
| `/lib/svc/manifest/network/ipsec/ipsecalgs.xml` | network | needs-investigation | IPsec algorithm loader (`ipsecalgs -s`); needed if Hammerhead uses IPsec; simple oneshot |
| `/lib/svc/manifest/network/ipsec/manual-key.xml` | network | needs-investigation | IPsec manual key management; needs-investigation if IPsec is used |
| `/lib/svc/manifest/network/ipsec/policy.xml` | network | needs-investigation | IPsec policy loader; simple oneshot if used |
| `/lib/svc/manifest/network/ipsec/tcpkey.xml` | network | needs-investigation | TCP MD5 key loading; obscure; likely drop |
| `/lib/svc/manifest/network/iscsi/iscsi-initiator.xml` | network | drop | iSCSI initiator; storage-specific; defer to zports |
| `/lib/svc/manifest/network/iscsi/iscsi-target.xml` | network | drop | iSCSI target; storage-specific; defer to zports |
| `/lib/svc/manifest/network/isns_server.xml` | network | drop | iSNS server; storage-specific; defer to zports |
| `/lib/svc/manifest/network/ldap/client.xml` | network | drop | LDAP client; enterprise-specific; defer to zports or site customization |
| `/lib/svc/manifest/network/loadbalancer/ilbd.xml` | network | drop | illumos load balancer daemon; Hammerhead does not ship this use case |
| `/lib/svc/manifest/network/network-initial.xml` | network | drop | Consolidated into `network.toml` |
| `/lib/svc/manifest/network/network-install.xml` | network | drop | Install-time artifact |
| `/lib/svc/manifest/network/network-ipmgmt.xml` | network | already-converted | `services/hammerhead/ipmgmtd.toml`; foreground flag `-f` |
| `/lib/svc/manifest/network/network-ipmp.xml` | network | needs-investigation | IP Multipathing daemon (`in.mpathd`); needed if Hammerhead uses IPMP for NIC bonding |
| `/lib/svc/manifest/network/network-ipqos.xml` | network | needs-investigation | IP Quality of Service configuration; determine if Hammerhead uses IPQoS |
| `/lib/svc/manifest/network/network-iptun.xml` | network | needs-investigation | IP tunnel interfaces (GRE, etc.); determine if Hammerhead uses IP tunnels |
| `/lib/svc/manifest/network/network-location.xml` | network | drop | Network location/profile management (NWAM); Hammerhead uses static config |
| `/lib/svc/manifest/network/network-loopback.xml` | network | drop | Consolidated into `network.toml` |
| `/lib/svc/manifest/network/network-netmask.xml` | network | drop | Consolidated into `network.toml`; netmask config read directly |
| `/lib/svc/manifest/network/network-physical.xml` | network | drop | Consolidated into `network.toml` |
| `/lib/svc/manifest/network/network-routing-setup.xml` | network | drop | Consolidated into `network.toml`; static routing via `/etc/defaultrouter` |
| `/lib/svc/manifest/network/network-service.xml` | network | drop | Consolidated into `network.toml` |
| `/lib/svc/manifest/network/nfs/cbd.xml` | network | needs-investigation | NFS v4 callback daemon; needed only if NFS client is active |
| `/lib/svc/manifest/network/nfs/client.xml` | network | needs-investigation | NFS client; determine if Hammerhead ships NFS client support |
| `/lib/svc/manifest/network/nfs/mapid.xml` | network | needs-investigation | NFS v4 user/group ID mapping; needed with NFS v4 |
| `/lib/svc/manifest/network/nfs/nfslogd.xml` | network | needs-investigation | NFS logging daemon; server-side; drop unless NFS server is shipped |
| `/lib/svc/manifest/network/nfs/nlockmgr.xml` | network | needs-investigation | NFS lock manager; needed with NFS client or server |
| `/lib/svc/manifest/network/nfs/rquota.xml` | network | needs-investigation | Remote quota daemon; server-side; likely drop |
| `/lib/svc/manifest/network/nfs/server.xml` | network | needs-investigation | NFS server; determine if Hammerhead ships NFS server |
| `/lib/svc/manifest/network/nfs/status.xml` | network | needs-investigation | NFS status monitor; needed with NFS client or server |
| `/lib/svc/manifest/network/routing/legacy-routing.xml` | network | drop | Legacy in.routed/in.rdisc; Hammerhead uses static routing |
| `/lib/svc/manifest/network/routing/ndp.xml` | network | needs-investigation | IPv6 NDP daemon (`in.ndpd`); needed for IPv6 operation |
| `/lib/svc/manifest/network/routing/ripng.xml` | network | drop | RIPng routing daemon; dynamic routing; not needed for base system |
| `/lib/svc/manifest/network/rpc/bind.xml` | network | already-converted | `services/hammerhead/rpcbind.toml`; direct exec `/usr/sbin/rpcbind` |
| `/lib/svc/manifest/network/rpc/gss.xml` | network | drop | GSSAPI daemon; needed only with Kerberos/NFS |
| `/lib/svc/manifest/network/rpc/smserver.xml` | network | drop | Removable media RPC service; obsolete |
| `/lib/svc/manifest/network/security/kadmin.xml` | network | drop | Kerberos admin server; server-role; defer to zports |
| `/lib/svc/manifest/network/security/krb5_prop.xml` | network | drop | Kerberos slave propagation; server-role |
| `/lib/svc/manifest/network/security/krb5kdc.xml` | network | drop | Kerberos KDC; server-role; defer to zports |
| `/lib/svc/manifest/network/security/ktkt_warn.xml` | network | drop | Kerberos ticket warning daemon; drop unless Kerberos client is used |
| `/lib/svc/manifest/network/shares/group.xml` | network | drop | Share group management (NFS/SMB shares); defer to zports |
| `/lib/svc/manifest/network/shares/reparsed.xml` | network | drop | Reparse point daemon (CIFS/SMB); drop |
| `/lib/svc/manifest/network/smb/client.xml` | network | drop | SMB client (smbfs); defer to zports |
| `/lib/svc/manifest/network/smb/server.xml` | network | drop | SMB server (smbd); defer to zports |
| `/lib/svc/manifest/network/ssh.xml` | network | already-converted | `services/hammerhead/sshd.toml`; foreground flag `-D` |
| `/lib/svc/manifest/network/tnctl.xml` | network | drop | Trusted Extensions network control; TX not shipped in Hammerhead |
| `/lib/svc/manifest/network/tnd.xml` | network | drop | Trusted Extensions network daemon; TX not shipped in Hammerhead |
| `/lib/svc/manifest/network/varpd.xml` | network | needs-investigation | Virtual ARP daemon for overlay networking (VXLAN); determine if Hammerhead ships overlay networking |
| `/lib/svc/manifest/network/wpa.xml` | network | needs-investigation | WPA supplicant daemon; determine if Hammerhead supports Wi-Fi |
| `/lib/svc/manifest/system/auditd.xml` | system | needs-investigation | BSM audit daemon; determine if Hammerhead ships BSM auditing; method-wrap `/lib/svc/method/svc-auditd` if kept |
| `/lib/svc/manifest/system/auditset.xml` | system | needs-investigation | Audit policy oneshot; companion to auditd; same decision |
| `/lib/svc/manifest/system/boot-archive-update.xml` | system | drop | Updates boot archive after upgrade; SMF-infrastructure concern |
| `/lib/svc/manifest/system/boot-archive.xml` | system | drop | Checks boot archive consistency; zyginit does not manage boot archive |
| `/lib/svc/manifest/system/boot-config.xml` | system | drop | SMF boot config property store; no zyginit equivalent needed |
| `/lib/svc/manifest/system/consadm.xml` | system | needs-investigation | Console administration (alternate console); determine if needed |
| `/lib/svc/manifest/system/console-login.xml` | system | already-converted | `services/hammerhead/console-login.toml`; complex wrapper writes utmpx entry before exec of ttymon |
| `/lib/svc/manifest/system/coreadm.xml` | system | method-wrap | `coreadm -U` to apply coreadm configuration; simple oneshot; inline `coreadm -U` directly in a TOML oneshot |
| `/lib/svc/manifest/system/cron.xml` | system | already-converted | `services/hammerhead/cron.toml`; start.sh removes stale FIFO |
| `/lib/svc/manifest/system/cryptosvc.xml` | system | already-converted | `services/hammerhead/crypto.toml`; `cryptoadm` oneshot |
| `/lib/svc/manifest/system/device/devices-audio.xml` | system | needs-investigation | Audio device setup; determine if Hammerhead ships audio support |
| `/lib/svc/manifest/system/device/devices-local.xml` | system | already-converted | `services/hammerhead/devfs.toml`; start.sh runs devfsadm + soconfig |
| `/lib/svc/manifest/system/device/mpxio-upgrade.xml` | system | drop | MPXIO upgrade artifact |
| `/lib/svc/manifest/system/dumpadm.xml` | system | method-wrap | `svc-dumpadm` applies crash dump configuration; oneshot; wrap or inline `/usr/sbin/dumpadm -r /` |
| `/lib/svc/manifest/system/early-manifest-import.xml` | system | drop | SMF manifest import; exec=:true; no-op without SMF repo |
| `/lib/svc/manifest/system/extended-accounting.xml` | system | needs-investigation | Process/task/flow accounting via `acctadm`; rare use case |
| `/lib/svc/manifest/system/fcoe_initiator.xml` | system | drop | FCoE initiator; storage-specific; defer to zports |
| `/lib/svc/manifest/system/fcoe_target.xml` | system | drop | FCoE target; storage-specific; defer to zports |
| `/lib/svc/manifest/system/filesystem/autofs.xml` | system | drop | Automounter; not needed unless Hammerhead actively uses autofs |
| `/lib/svc/manifest/system/filesystem/local-fs.xml` | system | already-converted | Consolidated into `services/hammerhead/filesystem.toml` |
| `/lib/svc/manifest/system/filesystem/minimal-fs.xml` | system | already-converted | Consolidated into `services/hammerhead/filesystem.toml`; `/var/run` tmpfs mount is a follow-up |
| `/lib/svc/manifest/system/filesystem/root-fs.xml` | system | already-converted | `services/hammerhead/root-fs.toml`; ZFS readonly assertion |
| `/lib/svc/manifest/system/filesystem/usr-fs.xml` | system | already-converted | Consolidated into `services/hammerhead/filesystem.toml` |
| `/lib/svc/manifest/system/filesystem/vscan.xml` | system | drop | Virus scan framework; defer to zports |
| `/lib/svc/manifest/system/fm/notify-params.xml` | system | drop | SMF notification hooks (exec=:true); no zyginit equivalent |
| `/lib/svc/manifest/system/fm/smtp-notify.xml` | system | needs-investigation | SMTP fault notification daemon; determine if Hammerhead ships this; foreground check: try `smtp-notify` without args |
| `/lib/svc/manifest/system/fmd.xml` | system | already-converted | `services/hammerhead/fmd.toml`; foreground flag `-f` |
| `/lib/svc/manifest/system/hostid.xml` | system | needs-investigation | Sets hardware hostid via `svc-hostid` script; verify if Hammerhead needs a hostid set at boot |
| `/lib/svc/manifest/system/hotplug.xml` | system | needs-investigation | Hotplug daemon (`hotplugd` via `svc-hotplug`); determine if Hammerhead hardware needs it |
| `/lib/svc/manifest/system/i2csimd.xml` | system | drop | I2C sensor daemon from a test harness (`/opt/i2c-tests/`); not part of base system |
| `/lib/svc/manifest/system/ibsrp/target.xml` | system | drop | InfiniBand SRP target; hardware-specific; defer to zports |
| `/lib/svc/manifest/system/identity.xml` | system | already-converted | `services/hammerhead/identity.toml`; sets hostname/nodename |
| `/lib/svc/manifest/system/idmap.xml` | system | needs-investigation | Identity mapping daemon (`idmapd`); needed with SMB or Kerberos |
| `/lib/svc/manifest/system/intrd.xml` | system | foreground | Interrupt distribution daemon; `svc-intrd` calls `/usr/lib/intrd`; check `intrd` source for `-f` or `-d` flag; method-wrap if no flag |
| `/lib/svc/manifest/system/ipagent.xml` | system | drop | Triton/SmartOS IP agent; not part of base Hammerhead |
| `/lib/svc/manifest/system/keymap.xml` | system | method-wrap | Keyboard map configuration; `keymap` script applies console keymap; simple oneshot via method-wrap or inline |
| `/lib/svc/manifest/system/labeld.xml` | system | drop | Trusted Extensions label daemon; TX not shipped in Hammerhead |
| `/lib/svc/manifest/system/logadm-upgrade.xml` | system | drop | Log rotation config upgrade artifact |
| `/lib/svc/manifest/system/manifest-import.xml` | system | drop | SMF manifest import daemon |
| `/lib/svc/manifest/system/name-service-cache.xml` | system | needs-investigation | nscd name service cache daemon; determine if Hammerhead needs nscd |
| `/lib/svc/manifest/system/ndmp.xml` | system | drop | NDMP backup protocol; defer to zports |
| `/lib/svc/manifest/system/pfexecd.xml` | system | already-converted | `services/hammerhead/pfexecd.toml`; direct exec `/usr/lib/pfexecd` |
| `/lib/svc/manifest/system/picl.xml` | system | drop | PICL platform information daemon; Sun hardware-specific; not relevant for x86 virtual hardware |
| `/lib/svc/manifest/system/pools.xml` | system | needs-investigation | Resource pools (FSSv2); determine if Hammerhead uses resource pools |
| `/lib/svc/manifest/system/power.xml` | system | already-converted | `services/hammerhead/powerd.toml`; direct exec or method-wrap `svc-power` |
| `/lib/svc/manifest/system/process-security.xml` | system | drop | Noop service (exec=:true); no functionality |
| `/lib/svc/manifest/system/rbac.xml` | system | method-wrap | RBAC database initialization; `svc-rbac start` oneshot; method-wrap or inline `/usr/sbin/svcadm` alternative — check what `svc-rbac` actually does |
| `/lib/svc/manifest/system/rcap.xml` | system | needs-investigation | Resource capping daemon (`rcapd`); determine if Hammerhead uses resource capping |
| `/lib/svc/manifest/system/resource-mgmt.xml` | system | needs-investigation | Resource management configuration; companion to pools and rcap |
| `/lib/svc/manifest/system/rmtmpfiles.xml` | system | method-wrap | Removes tmp files at boot (`rmtmpfiles` oneshot); simple method-wrap or inline `/bin/rm -rf /tmp/*` equivalent |
| `/lib/svc/manifest/system/sar.xml` | system | needs-investigation | System activity reporter (sar/sa); determine if Hammerhead ships `sar` |
| `/lib/svc/manifest/system/scheduler.xml` | system | method-wrap | Sets default scheduler class (`svc-scheduler`); oneshot; inspect `svc-scheduler` and inline if trivial |
| `/lib/svc/manifest/system/stmf.xml` | system | drop | SCSI Target Mode Framework; storage-specific; defer to zports |
| `/lib/svc/manifest/system/svc/global.xml` | system | drop | SMF global property store |
| `/lib/svc/manifest/system/svc/restarter.xml` | system | drop | IS svc.startd; zyginit replaces it |
| `/lib/svc/manifest/system/sysevent.xml` | system | already-converted | `services/hammerhead/syseventd.toml`; method-wrap `svc-syseventd` |
| `/lib/svc/manifest/system/system-log.xml` | system | already-converted | `services/hammerhead/syslogd.toml`; foreground flag `-d`; start.sh touches `/var/adm/messages` first |
| `/lib/svc/manifest/system/tsol-zones.xml` | system | drop | Trusted Extensions zones; TX not shipped in Hammerhead |
| `/lib/svc/manifest/system/update-man-index.xml` | system | drop | Man page index update; not needed at every boot |
| `/lib/svc/manifest/system/utmp.xml` | system | already-converted | `services/hammerhead/utmpd.toml`; method-wrap `svc-utmpd` |
| `/lib/svc/manifest/system/vtdaemon.xml` | system | method-wrap | Virtual terminal multiplexer; `vtdaemon` script; determine if Hammerhead uses VTs; method-wrap if kept |
| `/lib/svc/manifest/system/zones.xml` | system | needs-investigation | Zones boot/halt service; zyginit will need a `zones` oneshot for `autoboot=true` zones; see `docs/DESIGN.md` Zones section |
| `/lib/svc/manifest/system/zonestat.xml` | system | needs-investigation | Zone statistics daemon (`zonestatd`); needed once zones are enabled |

**Catalog summary:**

| Classification | Count |
|---|---|
| `already-converted` | 19 |
| `drop` | 61 |
| `foreground` | 1 |
| `method-wrap` | 7 |
| `needs-investigation` | 40 |
| **Total** | **128** |

---

## 8. Format Reference Pointer

*This section points to the authoritative TOML format documentation.*

The zyginit service definition format — all valid fields, their types,
defaults, and semantics — is documented in `docs/man/man5/zyginit.5`.

The decision tree (Section 2) and catalog appendix (Section 7) use
field names from that reference. If a field name here appears to conflict
with what `man zyginit.5` says, the man page is authoritative.

For runtime behavior, signal handling, and socket protocol, see
`docs/man/man8/zyginit.8` and `docs/man/man8/zygctl.8`.

---

## 9. Glossary

*Definitions of SMF concepts that appear in this document for readers
without an illumos background.*

**BE (Boot Environment):** A ZFS dataset snapshot that represents a complete,
bootable OS image. Hammerhead supports multiple BEs; `beadm` (or direct
`zpool set bootfs`) selects which one the kernel boots.

**Contract (process contract):** A Hammerhead kernel mechanism that groups
related processes. When all processes in a contract exit, the kernel fires a
contract event. zyginit uses contracts to track service processes and detect
daemon exit without polling. See `docs/DESIGN.md` Contracts section.

**FMRI (Fault Management Resource Identifier):** SMF's naming scheme for
services, e.g., `svc:/network/ssh:default`. zyginit uses bare names (e.g.,
`sshd`) in TOML files instead.

**libcontract:** The Hammerhead userland library for managing process contracts.
zyginit links against it (`-l contract`). Not available on Linux.

**libscf (Service Configuration Facility):** The Hammerhead userland library
for reading and writing the SMF repository. Calls into `svc.configd` via a
door. A daemon that calls `scf_handle_create()` at startup requires configd
to be running.

**Manifest:** An XML file that describes an SMF service — its name, method
(start/stop command), dependencies, and properties. Stored in
`/lib/svc/manifest/`. zyginit replaces manifests with TOML files in
`/etc/zyginit/`.

**Method (SMF method):** The start, stop, or refresh command for an SMF
service, optionally implemented as a shell script in `/lib/svc/method/`.
The equivalent in zyginit is `[exec] start` and `[exec] stop`.

**Profile (SMF profile):** An XML file that selects which services are enabled
by default. zyginit replaces profiles with symlinks in `/etc/zyginit/enabled.d/`.

**Repository:** The SMF configuration database managed by `svc.configd`.
Stored in `/etc/svc/repository.db` (SQLite). zyginit has no database — service
state is in-memory only; configuration is in TOML files.

**Restarter:** In SMF, a service that manages other services. The master
restarter is `svc.startd`. zyginit is a single-process init that plays the
role of the restarter for all services.

**soconfig:** A Hammerhead utility (`/sbin/soconfig`) that loads the sockfs
protocol-to-module mapping from `/etc/sock2path.d/`. Must run before any
`socket(AF_INET, ...)` call succeeds. See Anti-patterns section 3.

**uadmin:** The Hammerhead system call for administrative operations including
halt, reboot, and poweroff. Must be called from a non-PID-1 process to avoid
the `restart_init` race. See Anti-patterns section 8.
