Repository navigation
Description
Task
Unit 3 is a single-task unit and collapses per ADR 0028. This task parents directly to tracking issue #478.
Re-scoped 2026-06-23 — DNS mechanism realigned to spec B5 (host-side egress proxy), superseding the systemd-resolved narrowing of #485/#500/#546. The sandbox (hakoniwa) and microVM (libkrun) runtimes have no systemd, and the networking spec (
03-spec-networking/networking-with-diagrams.md) never required it: R5 "PTask hostnames in host DNS" → B5 "*.localhost+ host-side proxy (or one-time/etc/resolver)", tagged "Needs decision (DNS) — zero-root path exists". Resolution + routing are host-side; the runtimes never resolve anything. PR #546 is held (do-not-merge) pending this re-scope; salvage its in-memory registry, drop its systemd-resolved probe.
repo: gominimal/minimal
spec: docs/specs/03-spec-networking/03-spec-networking.md
requirements: R3.1, R3.2, R3.3, R3.5, R3.6 (R3.4 systemd-resolved probe is removed by this re-scope — replaced by an egress-proxy reachability check; see below)
files in scope:
- crates/minimald/src/net/dns.rs (salvage from feat(minimald): Unit 3 DNS — B5 host-side egress proxy + .min.internal hostname registry #546) — in-memory hostname registry;
register(session_name, host_id)/deregister(session_name)with structuredtracingevents (R3.5); hostname format<session-name>.<host-id>.min.internal(TLD decided 2026-06-23 — Mike's preference; under B5 the TLD is a free label since the proxy routes byHost:header, so.min.internalis semantically honest for internal PTask services and the.localhostloopback-fallback is not relied on); registerHostNetPTasks →127.0.0.1,OwnIpPTasks → the gvproxy switch IP (R3.6). Remove the resolver write path (systemd-resolved D-Bus /resolvectl) — no host resolver is written.OwnIpswitch-IP registration depends on the feat(minimald,sandbox2): wire minimald::net switch into the live OwnIp session-launch path (R1.5) #542 live-switch wiring (now merged via feat(minimald,sandbox2): wire net switch into live OwnIp launch path #547). - crates/minimald/src/net/proxy.rs (new) — the B5 host-side egress proxy. A forward/
CONNECTproxy that routes a request by itsHost:header (orCONNECTauthority) to the target PTask's address via the registry:HostNet→127.0.0.1:<port>,OwnIp→ the PTask switch IP through the gvproxy switch (reuse the feat(minvmd): implement DM1 gvproxy tap relay for OwnIp PTasks #540 relay path). The host resolver is never consulted —*.min.internalis an opaque label the proxy maps internally, so the no-systemd runtimes and the TLD choice are both irrelevant to correctness. Clients reach the proxy via proxy-env / PAC (HTTP(S)_PROXY), the zero-root cross-platform path the spec asserts ("zero-root path exists");/etc/resolveris the macOS root-write alternative, not implemented here. - crates/minimald/src/lib.rs (or main.rs) — replace the systemd-resolved startup probe with an egress-proxy reachability check: on daemon start, bind the proxy listener; on bind failure →
tracing::warn!(component = "dns-proxy", status = "unavailable", remedy = "<port in use / set HTTP_PROXY>"). (Supersedes R3.4's systemd-resolved warn.) - crates/minimald/src/session.rs — call
dns::registerat session launch (HostNet + OwnIp),dns::deregisteron exit. - (deferred)
crates/minimal2/src/client.rshostname/TCP connect (UC2c, R3.2) — UDS-only client; TCP/hostname transport belongs with the DM5 network-listener work in Unit 4. - docs/specs/03-spec-networking/03-spec-networking.md — amend R3.1 hostname format to
<session-name>.<host-id>.min.internal; close Open Question 1 with the B5 host-side-egress-proxy decision (proxy routes byHost:/authority; resolution stays host-side; systemd not required), explicitly superseding spike spike: DNS hostname registration works rootlessly on target Linux distributions #485's systemd-resolved finding.
proof artifacts:
- Test: registry/proxy routing contract. Register a
HostNetPTask"myservice"/host-id"dev"; drive a request withHost: myservice.dev.min.internalthrough the proxy and assert it routes to the registered target. Afterderegister, assert the proxy returns a not-found (no stale route). Nogetaddrinfo/host-resolver dependency — asserts the proxy contract directly. Fails on base (no registry/proxy). - Test: OwnIp routing — a registered
OwnIpPTask routes through the gvproxy switch relay to its switch IP (may reuse / extend the feat(minvmd): implement DM1 gvproxy tap relay for OwnIp PTasks #540 relay test; the privileged netns leg defers toci-netns.yml). Fails on base. - Test: proxy-listener bind-failure emits the
component = "dns-proxy"tracing::warn!(supersedes the R3.4 systemd-resolved probe test). Fails on base.
verification:
- cargo build --locked
- cargo test -p minimald --locked
- cargo fmt --all -- --check
- cargo clippy -p minimald --all-targets -- -D warnings
depends on:
- builds on feat(minimald): gvproxy DM2 child lifecycle, OwnIp switch attachment via SCM_RIGHTS, and integration tests #496 (registry primitives) and the feat(minimald,sandbox2): wire minimald::net switch into the live OwnIp session-launch path (R1.5) #542 live-switch wiring (feat(minimald,sandbox2): wire net switch into live OwnIp launch path #547, merged) for
OwnIpswitch-IP routing - feat(minimald,minimal2): HTTPS reverse proxy with mTLS auth and minimal ssh-forward CLI #502 overlap — RESOLVED (2026-06-23): same component. This B5 egress proxy and feat(minimald,minimal2): HTTPS reverse proxy with mTLS auth and minimal ssh-forward CLI #502's B8 minimald HTTPS reverse proxy (mTLS) are one shared component. Factor
net/proxy.rsas the routing core (Host-header/authority → registry → target) so feat(minimald,minimal2): HTTPS reverse proxy with mTLS auth and minimal ssh-forward CLI #502 extends it by adding TLS/mTLS termination in front — do not duplicate. feat(minimald,minimal2): HTTPS reverse proxy with mTLS auth and minimal ssh-forward CLI #502 depends on this task landingnet/proxy.rs. - client→proxy mechanism: proxy-env / PAC (
HTTP(S)_PROXY→ gvproxy egress webproxy), the zero-root cross-platform default;/etc/resolvernot implemented.
Related to #478
Generated by sdd-triage-materialize for issue #478 · ◷
Activity
gominimal-aw-bot commentedon Jun 22, 2026
/execute
norrietaylor commentedon Jun 23, 2026
/execute
gominimal-aw-bot commentedon Jun 23, 2026
/execute
gominimal-aw-bot commentedon Jun 23, 2026
Blocked — needs a maintainer decision on three load-bearing points before I can implement at confidence.
I read the task scope, the
*.localhostdecision from spike #485 (merged as PR #487), the existingcrates/minimald/src/net/(from #496),crates/minimald/src/session.rs, andcrates/minimal2/src/client.rs. The in-memory registry (R3.5) and the systemd-resolved startup probe (R3.4, = proof artifact 2) are straightforward. But three parts of the task can't be implemented faithfully as written:1. Proof artifact 1 contradicts the
*.localhostmechanism the task adopts.It asserts
getaddrinfo("myservice.dev.localhost")returns127.0.0.1while anOwnIpPTask runs, then NXDOMAIN after it exits. But spike #485's finding (which this task adopts) is*.localhost+ host-side proxy: systemd-resolved synthesizes every*.localhostname to127.0.0.1statically, independent of any PTask lifecycle. So:minimaldinvolvement, i.e. a health check, not a proof (empty-PR rule); and*.localhostis never NXDOMAIN.This artifact assumes
minimaldserves a dynamic zone whose records appear/withdraw with the PTask — the*.min.localdesign the task is explicitly moving away from. The two are mutually exclusive.Decision: (a) keep
*.localhost+ host-side proxy and rewrite proof artifact 1 to assert the registry/proxy contract directly (register → entry present + proxy routes by Host header; deregister → entry gone), or (b) keep a dynamic getaddrinfo-lifecycle proof and revert to aminimald-served zone (not*.localhost). I won't pick this silently — it determines the whole DNS mechanism.2.
OwnIpregistration "to the gvproxy switch IP at session launch" has no switch IP to register against inminimald.GvproxySwitch::attach()(the R1.5 attach that yields a PTask's100.64.x.y) is never called from the native-Linuxminimaldsession path —session.rs/server.rsonly forwardnetwork_modeinto the sandbox; theOwnIpswitch wiring from #496/#522 lives inminvmd(the VM path). So atsession.rslaunch there is no per-PTask switch IP to pass todns::registerfor anOwnIpPTask.HostNet → 127.0.0.1I can do today.Decision: scope
session.rsregistration toHostNetonly for now (leavingOwnIpregistration to follow the switch-into-minimald-session wiring), or registerOwnIpat127.0.0.1too per R3.3's published-port model (which contradicts the task's explicit "switch IP" wording), or is the switch-into-session wiring expected here?3.
minimal2client "accept a.localhosthostname in addition to an IP" — the client has no IP/TCP path.Client::connectandresolve_socket_pathare UDS-only (tokio::net::UnixStream). There is no IP-based connect to add a hostname alternative to; UC2c/R3.2 (a network-accessible DM5minimald) presupposes a TCP transport this crate doesn't have yet.Decision: is a TCP/hostname client transport in scope for #500, or does R3.2/UC2c belong with the DM5 network-listener work (Unit 4)?
I have not opened a PR (a declared proof artifact that can't pass is a hand-off, not a partial PR). The clearly-sound parts — the in-memory
dns.rsregistry with R3.5 structured tracing, the R3.4 systemd-resolved startup probe (matches proof artifact 2 exactly), and the spec amendment closing Open Question 1 with the #485 finding — I can land immediately once the three decisions above are made. Clearneeds-humanwith guidance and I'll resume.Side note (independent of this task): the spike finding doc
docs/spikes/2026-06-20-dns-hostname-mechanism.mdthat PR #487 merged is absent from the currentmainworking tree — worth a look.1 remaining item
norrietaylor commentedon Jun 23, 2026
Maintainer decisions (re-scope approved) — resume on this:
*.localhost+ host-side proxy (spike spike: DNS hostname registration works rootlessly on target Linux distributions #485). Proof artifact 1 is rewritten to assert the registry/proxy contract (register → entry present + proxy routes byHost:header; deregister → gone), not agetaddrinfolifecycle, which*.localhost's static synthesis makes unobservable.session.rsregistration toHostNetonly for now.OwnIpregistration to the switch IP is deferred to feat(minimald,sandbox2): wire minimald::net switch into the live OwnIp session-launch path (R1.5) #542 (the switch-into-minimald-session wiring); resume that part once feat(minimald,sandbox2): wire minimald::net switch into the live OwnIp session-launch path (R1.5) #542 lands.Land now: the in-memory
dns.rsregistry (R3.5), the systemd-resolved startup probe (R3.4), theHostNetregistration path, and the spec amendment (R3.1*.localhost, close Open Question 1). The body is updated to match. Separately, I'm recovering the missingdocs/spikes/2026-06-20-dns-hostname-mechanism.md.gominimal-aw-bot commentedon Jun 23, 2026
The
sdd-specagent run failed before it could post a spec, a comment, or any lifecycle update — the failure was not classified by the conclusion job (e.g. the agent step itself crashed). Applyingneeds-humanso this is visible and recoverable. A human should investigate the failed run and, once ready, clear theneeds-humanlabel to re-run the spec agent on this tracking issue. Failed run: https://github.com/gominimal/minimal/actions/runs/28032382361gominimal-aw-bot commentedon Jun 23, 2026
This is
sdd-spec. Issue #500 is a task sub-issue parented directly to tracking issue #478 and currently carriessdd:in-progress— this issdd-execute's domain, notsdd-spec's. The previous bot comment's guidance (cleargominimal-aw-bot commentedon Jun 23, 2026
Pull request created: #546
fix: drop stale-base revert of #528 sessions split from #500 PR
feat(minimald,sandbox2): wire net switch into live OwnIp launch path
feat(minimald): re-scope Unit 3 DNS to B5 host-side egress proxy
feat(minimald,sandbox2): wire net switch into live OwnIp launch path (#…
fix: drop stale-base revert of #528 sessions split from #500 PR
feat(minimald): re-scope Unit 3 DNS to B5 host-side egress proxy
feat(minimald): Unit 3 DNS — B5 host-side egress proxy + .min.interna…