rustd-resolved 0.2.1

A compatibility-oriented reimplementation of systemd-resolved
# Certified replacement procedure

`rustd-resolved` must not replace the host resolver merely because it
builds or because ordinary CI is green. Replacement is permitted only when a
single immutable Git tree has a portable schema-3 certificate with the exact
24-gate contract set to `pass`.

The distribution's `systemd-resolved` package remains installed throughout the
procedure. It is the rollback payload and must not be removed.

## Certification gates

The certificate is fail-closed and binds all evidence to the same Git tree,
upstream systemd commit, daemon SHA-256, client SHA-256, and NSS module
SHA-256. It requires:

- a clean source tree and immutable pinned upstream baseline;
- complete pinned D-Bus, Varlink, configuration, and `resolvectl` surface
  inventory coverage;
- Rust formatting, Clippy, all-target tests, an exact Rust 1.74 toolchain, native
  C/Fortran checks, packaging checks, and NSS integration;
- byte-identical daemon, client, NSS, and normalized replacement packages from
  two independent exact-source release proofs, with the canonical artifacts
  built under Rust 1.74 and used by every binary-bearing proof;
- live UDP, TCP, D-Bus, Varlink, NSS, mDNS resolver, mDNS responder, simultaneous
  resolver/responder, and DNS-SD publication/reload tests;
- host shadow comparison against the installed resolver without changing
  `/etc/resolv.conf`;
- the pinned upstream `TEST-75-RESOLVED` suite with the candidate daemon,
  client, and NSS module, plus `TEST-89-RESOLVED-MDNS`, with bound proof that
  the candidate daemon actually ran;
- libFuzzer, AddressSanitizer, UndefinedBehaviorSanitizer, Miri strict
  provenance, ThreadSanitizer, and Valgrind evidence;
- two QEMU boots with the candidate healthy on both boots followed by a healthy
  rollback to the distribution resolver.

A skipped, stale, missing, extra, duplicate, malformed, or failed gate makes
`certified` false. Schema-2 and older certificates are rejected.

## Trust-root blocker

Schema 3 proves source and artifact hash consistency, but it does not
cryptographically authenticate who produced the certificate or whether the
reported gates actually ran. A user who can supply an arbitrary clean checkout
can also fabricate a self-consistent certificate, manifest, and passing gate
records.

Treat every bundle as unauthenticated until an administrator acquires it from
the exact repository, workflow, and run through an authenticated GitHub channel
and independently establishes its provenance (for example with a verified
artifact attestation), or repeats the complete certification locally. The
current tooling does not yet establish that trust root. Host replacement is
therefore blocked; do not run the transactional switch solely on the strength
of a schema-3 bundle.

## Produce one exact-SHA certificate

In GitHub Actions, run **Full replacement certification** against the current
`main` commit. Leave `source_sha` blank to select the dispatch-time commit, or
provide that same current `main` commit as a full 40-character ID. The present
provenance checks require each dispatched run's `head_sha` to equal the
requested source while dispatches use `ref: main`; historical commits therefore
fail closed. Keep `main` quiescent for the entire fleet run. If it advances
between dispatches, discard the partial evidence and restart certification for
the new head.

The orchestrator dispatches and waits for:

1. Reproducible replacement release proof
2. Replacement security gates
3. Replacement upstream TEST-75 proof
4. Replacement upstream TEST-89 mDNS proof
5. Replacement boot and rollback proof
6. Replacement security proof
7. Replacement readiness certificate

Download the final `replacement-readiness-<sha>` artifact. The workflow only
succeeds when `replacement-certification.json` says:

```json
{"certified": true}
```

The artifact also contains the verified daemon, client, NSS module, logs,
toolchain evidence, normalized package, reproducible release output, and all
files named by each of the five external proofs. The certificate binds portable
paths, sizes, and hashes for those proof files and evidence files. Portable
verification re-hashes the complete sets and reruns their semantic validators;
gate names with deleted proof material are not sufficient. The readiness
workflow rejects a TEST-75, TEST-89, boot, or reproducibility proof whose
recorded candidate hashes differ from these final artifacts.

## Verify the downloaded bundle

Check out the exact source commit named by the certificate, leave the checkout
clean, then verify the bundle before acquiring root privileges:

```sh
/usr/bin/python3 -I scripts/verify-readiness-bundle.py \
  --certificate /path/to/replacement-certification.json \
  --source-root "$(pwd)"
```

The verifier rejects stale or pre-schema-3 certificates, a
missing/extra/duplicate gate or external proof, an incorrect or dirty source
checkout, absolute or unsafe artifact paths, missing evidence, proof or
reproducibility-manifest drift, and daemon, client, or NSS module hash
mismatches. A certificate is accepted for at most 24 hours by default.

## Transactionally switch the host (currently blocked)

Install mode currently exits before acquiring root-managed state or changing
the host because authenticated artifact provenance verification has not yet
been implemented. The following is the intended future interface, not an
authorized command today:

```sh
sudo bash scripts/switch-resolved-transactionally-v2.sh \
  --certificate /path/to/replacement-certification.json \
  --external-name example.com
```

The operation:

- installs versioned candidate binaries without deleting the distribution
  package;
- snapshots the existing drop-in, mask/enable state, active state, guard unit,
  `/etc/resolv.conf`, and `/etc/nsswitch.conf`;
- changes only the `systemd-resolved.service` `ExecStart` drop-in;
- verifies the actual executable, UDP, TCP, D-Bus, Varlink, statistics,
  `_localdnsstub`, NSS, `resolv.conf`, and the optional external name;
- restores the prior resolver automatically if any immediate check fails;
- leaves a boot guard active until a successful reboot is confirmed.

The script prints a transaction identifier. Reboot once, then confirm only
after the boot guard and health checks pass:

```sh
sudo reboot
sudo /usr/lib/systemd/rustd-resolved-switch \
  --confirm TRANSACTION_ID
```

## Roll back

Rollback remains available before or after confirmation:

```sh
sudo /usr/lib/systemd/rustd-resolved-switch \
  --rollback TRANSACTION_ID
```

The rollback restores the exact previous service drop-in and service state,
then restarts the distribution resolver. Omitting the identifier selects the
active transaction:

```sh
sudo /usr/lib/systemd/rustd-resolved-switch --rollback
```

For a legacy `scripts/install-replace.sh` transaction, use its paired restore
tool instead of deleting files manually:

```sh
sudo bash scripts/uninstall-restore.sh
```

It restores the captured resolver paths, unit and socket files, enablement,
activity, and `/etc/resolv.conf`, then verifies the resulting state.

## Prohibited shortcut

Do not remove, purge, mask permanently, or overwrite the distribution
`systemd-resolved` package. Do not manually replace files under `/usr/lib` or
change `/etc/resolv.conf` as part of the switch. A failed certificate or an
uncertified source tree is a hard stop, not a warning.