diff --git a/docs/driver-dns.md b/docs/driver-dns.md
new file mode 100644
index 0000000..7fb7291
--- /dev/null
+++ b/docs/driver-dns.md
@@ -0,0 +1,55 @@
+# DNS resolver driver (`adapters/dns/resolver`) — WIP design contract
+
+Status: design only (lane S7C, ANTIFL0CK-OSS-COMPLETION-02). No Go code landed yet.
+Implements `agent/driver.Driver` (contract v1, PR #64) for protected resolver configuration.
+
+## Binding
+- One driver instance binds to exactly one target at construction:
+ - `resolvconf` backend: target == root-relative resolv path (default `/etc/resolv.conf`), resolved
+ under `Config.Root` (production `/`; tests always a temp dir so the host is never touched).
+ - `resolved` backend: target == link name (`eth0`), driven through a trusted `resolvectl`
+ (`/usr/bin/resolvectl`, root-owned, non-writable path, nftables.go:278-330 checks) via an
+ injected runner; fixed argument patterns `status`, `dns ...`, `domain ...`, `revert `.
+- Operation type is echoed from the scope (type routing is the planner's job); Simulate rejects a
+ type that differs from the captured scope with `AF-DRIVER-DNS-TYPE-MISMATCH`.
+- Protected config comes from `Config.Protected` (nameservers, search, options, per-link domains);
+ operation parameters may override `nameservers`, `searchDomains`, `options`, `domains`.
+
+## Probe keys / reason codes
+- `dns.resolver.observe`, `dns.resolver.enforce` (domain DNS).
+- `AF-DRIVER-DNS-MANAGED-BY-RESOLVED` (resolv path is the systemd-resolved stub symlink; never rewritten),
+ `AF-DRIVER-DNS-SYMLINK`, `AF-DRIVER-DNS-RECOVERY-LOSS`, `AF-DRIVER-DNS-CAPTIVE-PORTAL`,
+ `AF-DRIVER-DNS-TAMPERED`, `AF-DRIVER-DNS-RESOLVE-FAILED`, `AF-DRIVER-DNS-TARGET-UNBOUND`,
+ plus shared `AF-PROBE-*` (binary missing/untrusted, privilege missing, journal corrupt, platform unsupported on !linux).
+
+## Snapshot entries (resolvconf)
+`file.present`, `file.mode`, `file.owner` (uid:gid), `file.symlink`, `file.digest` (sha256 of bytes),
+`nameserver.N`, `search`, `options`. Resolved: `link..dns`, `link..domains`, `global.dns`.
+
+## Durable ownership
+Before journal BEGIN the driver persists an ownership record under `Config.StateDir/ownership/.json`
+holding the full original config (bytes + mode + owner, or per-link dns/domains), the applied content
+digest and state (applied|rolled|tampered). Rollback and Recover read only this record and the journal,
+never DNS. Recovery networks are literal IPs; DNS recovery = original resolver preserved in the record
+(and optionally appended as fallback via `Protected.KeepOriginalFallback`, which clears RECOVERY-LOSS).
+
+## Apply / Verify / Rollback / Recover
+- Apply: validate, journal health, capture, simulate (reject on RECOVERY-LOSS), persist ownership,
+ journal BEGIN, atomic write (dir opened O_NOFOLLOW|O_DIRECTORY, fstatat refuses a symlink target,
+ temp O_EXCL|O_NOFOLLOW, write, fsync, fchmod/fchown to original, renameat, fsync dir) or resolvectl
+ dns+domain, re-capture, journal FINISH COMMIT.
+- Verify: re-read digest must equal receipt; then optional `CaptivePortalDetector` (HTTP GET to probe
+ URL via injected client expecting exact status+body; mismatch => Verified=false, CAPTIVE-PORTAL);
+ then optional `Resolver` (default net.Resolver dialing the protected server, bounded timeout) must
+ resolve the configured probe name.
+- Rollback: restore journaled bytes + mode (+owner) / `resolvectl revert` then re-set original if it
+ differs; idempotent (`AF-DRIVER-ALREADY-ROLLED-BACK`).
+- Recover: journal-driven; host == applied content => restore; host == original => close; anything else
+ => `AF-DRIVER-DNS-TAMPERED`, journal closed, record kept, host NOT clobbered.
+
+## Tests planned
+Conformance (memory + file journal, temp root, fake runner), disk-full/rename/partial-write faults,
+symlinked target, mode/owner mismatch, resolved stub detection, captive-portal mismatch, split-DNS via
+fake runner, lost-process recovery, tamper, netns proof (`unshare -rn`, in-process UDP responder on
+127.0.0.1:, skip precisely when unavailable). Mutations: rollback ignores mode restoration;
+captive-portal mismatch still reports verified.