SwiftOS Networking Guide
This guide explains how to run and verify networking on the current SwiftOS image. It is written for operators, application authors, and support engineers who need a working network profile, exact command examples, and a clear picture of the current limits.
SwiftOS networking today is real but intentionally small: a QEMU virt
virtio-net device, an in-kernel pure-Swift TCP/IP stack, capability-gated socket
syscalls, DHCPv4 lease acquisition with a QEMU/slirp static fallback, and a set
of native userland network tools. It is enough to serve static files, serve
local TinyStories completions, run TCP and UDP echo services, resolve DNS, and
exercise the TLS 1.3 client path. It also includes an SSH client transport
preflight against OpenSSH and an SSHD session/exec preflight that accepts a
normal OpenSSH client on guest TCP/22, loads development SSH key material from
the base image, authenticates a configured Ed25519 key, and runs one direct
remote command.
Use this guide with:
- Getting Started for the shortest first-boot path.
- Service Guide for readiness markers and service behavior.
- AI Hosting Guide for
/bin/llmdbundle operation. - Command Reference for exact command syntax.
- Observability Guide for serial logs and metrics.
- Troubleshooting for failure diagnosis.
- API Reference for socket syscall details.
Current Network Model
| Area | Current behavior |
|---|---|
| Hardware profile | QEMU virt with virtio-net-device over virtio-mmio |
| Link mode | QEMU user networking, also called slirp; cloud virtio-net DHCP is the first deployment target |
| Guest IPv4 | DHCPv4 lease when offered; fallback 10.0.2.15 for QEMU slirp |
| Host/gateway alias | DHCP router option when offered; fallback 10.0.2.2 |
| Default DNS | DHCP DNS option when offered; fallback QEMU slirp DNS at 10.0.2.3:53 |
| IPv4 routing | Outbound sockets ARP same-subnet peers directly and use the configured gateway for off-link or /32 destinations |
| DHCP | Minimal boot-time DHCPv4 DISCOVER/OFFER/REQUEST/ACK; no renewal or user command yet |
| Target-side status | /bin/netinfo reports link readiness, IPv4/gateway/DNS/mask, IPv6 address/prefix, and IPv6 gateway status |
| Socket authority | Processes need capNet; seeded root has it |
| Inbound host access | QEMU hostfwd from host ports to guest ports |
| Outbound guest access | Guest clients connect to 10.0.2.2 for host services |
| IPv6 | Link-local/NDP and IPv6 socket smoke paths exist; hostfwd varies by host |
| Runtime entropy | SYS_RANDOM is backed by virtio-rng when the VM attaches a virtio-rng-device; /bin/ssh and /bin/sshd use it for KEX |
| TLS | /bin/tlsget proves the TLS 1.3 runtime path; production certificate verification is not complete |
The current implementation lives in the trusted core. The virtio-net driver and the network stack are in the kernel today, while the roadmap moves toward restartable userland drivers and narrower network services.
Build Artifacts
Build the normal direct-boot artifacts before running any manual network session:
make build base-image build/virt.dtb
The resulting QEMU session needs three pieces:
- The kernel:
build/kernel.elf. - The immutable base filesystem:
build/base.img. - A virtio-net device attached to a slirp
-netdev.
make run does not attach a NIC by default, so manual network sessions use an
explicit QEMU command.
Choose A Network Workflow
Start from the traffic direction and service shape. Inbound services need
hostfwd; guest-initiated clients only need a slirp NIC. All socket commands
need a login context with capNet, so the examples below assume root.
| Goal | QEMU profile | Guest command | Host action | Proof |
|---|---|---|---|---|
| Serve static files | HTTP-or-LLM profile | /bin/httpd |
curl -fsS http://127.0.0.1:8080/ |
./tests/httpd_test.sh |
| Serve local AI completions | HTTP-or-LLM profile | /bin/llmd |
curl -fsS -X POST --data "Once upon a time" http://127.0.0.1:8080/completion |
./tests/llm_serve_test.sh |
| Test one TCP request | Echo profile | /bin/tcpecho |
`printf 'swos tcp\n' | nc -w8 127.0.0.1 5555` |
| Test one UDP datagram | Echo profile | /bin/udpecho |
`printf 'swos udp' | nc -u -w2 127.0.0.1 5555` |
| Connect from guest to host | Outbound-only profile | /bin/tcpget 10.0.2.2 5555 |
`printf 'srv-reply\n' | nc -l 5555` |
| Resolve DNS | Outbound-only profile | /bin/nslookup example.com |
None for default slirp DNS | ./tests/dns_test.sh |
| Inspect guest network status | Outbound-only profile | /bin/netinfo |
None | make netinfo-test |
| Exercise TLS runtime path | Outbound-only profile | /bin/tlsget 10.0.2.2 44310 localhost |
Start the host TLS 1.3 test server | ./tests/tls_test.sh |
| Exercise SSH client transport | Outbound-only profile | /bin/ssh 10.0.2.2 <port> |
Start a host OpenSSH sshd; attach virtio-rng for runtime entropy proof |
./tests/ssh_transport_test.sh, ./tests/ssh_runtime_entropy_test.sh |
| Exercise SSHD remote command | SSHD profile | Autostart from /etc/swos/services; manual /bin/sshd for custom ports |
ssh -i fixtures/ssh/sshd_hc5_ed25519 -p <host-port> root@127.0.0.1 /bin/id |
./tests/sshd_transport_test.sh |
| Exercise SSHD IPv6 listener | SSHD IPv6 profile | Custom /etc/swos/services containing sshd6; manual /bin/sshd -6 |
Host OpenSSH over ::1 when QEMU IPv6 hostfwd is available |
make sshd-ipv6-listener-test |
| Exercise SSHD deploy candidate | SSHD deploy profile | Temporary image with static IPv6, deploy SSHD seeds, deploy authorized_keys, sshd6, and virtio-rng |
Host OpenSSH over ::1 when QEMU IPv6 hostfwd is available; serial /bin/netinfo always runs |
make sshd-deploy-preflight-test |
| Exercise Hetzner deploy handoff bundle | SSHD deploy profile | Same temporary static-IPv6 SSHD image with evidence capture enabled | Verify manifest, hashes, serial log, public key material, and omitted private seeds | make hetzner-deploy-bundle-test |
| Exercise SSHD restart proof | SSHD supervised profile | Custom /etc/swos/services containing sshd-once |
Two host OpenSSH commands before and after restart | make sshd-supervision-test |
| Exercise SSHD IPv6 restart proof | SSHD IPv6 supervised profile | Custom /etc/swos/services containing sshd6-once |
Two host OpenSSH commands when IPv6 hostfwd is available; serial restart proof otherwise | make sshd-ipv6-supervision-test |
| Exercise IPv6 link-local/NDP | IPv6 smoke profile | Test harness driven | None beyond QEMU profile | ./tests/ipv6_smoke_test.sh |
Operator flow:
- Build
build/kernel.elf,build/base.img, andbuild/virt.dtb. - Boot the smallest QEMU profile that matches the workflow.
- Log in as
root. - Wait for the readiness marker before sending host traffic.
- Save the serial log and host command output when debugging.
- Run the focused proof before broadening to
make test.
Do not run /bin/httpd and /bin/llmd together in the same guest; both bind
TCP 8080. The echo programs are one-shot and exit after a single request.
QEMU Network Profiles
All-In-One Validation Profile
This profile exposes the HTTP and echo service ports and is the best starting point for manual validation:
qemu-system-aarch64 -M virt -cpu cortex-a72 -m 256M -nographic \
-global virtio-mmio.force-legacy=false \
-device loader,file=build/virt.dtb,addr=0x4FF00000,force-raw=on \
-drive file=build/base.img,format=raw,if=none,id=swosbase,readonly=on \
-device virtio-blk-device,drive=swosbase \
-netdev user,id=n0,hostfwd=tcp:127.0.0.1:8080-:8080,hostfwd=tcp:127.0.0.1:5555-:5555,hostfwd=udp:127.0.0.1:5555-:5555 \
-device virtio-net-device,netdev=n0 \
-kernel build/kernel.elf
Use this profile for:
/bin/httpdon guest TCP 8080./bin/llmdon guest TCP 8080./bin/tcpechoon guest TCP 5555./bin/udpechoon guest UDP 5555.
Run only one TCP 8080 service at a time. /bin/httpd and /bin/llmd both bind
guest TCP port 8080.
HTTP-Or-LLM Profile
Use this smaller profile when you only need a TCP 8080 service:
qemu-system-aarch64 -M virt -cpu cortex-a72 -m 256M -nographic \
-global virtio-mmio.force-legacy=false \
-device loader,file=build/virt.dtb,addr=0x4FF00000,force-raw=on \
-drive file=build/base.img,format=raw,if=none,id=swosbase,readonly=on \
-device virtio-blk-device,drive=swosbase \
-netdev user,id=n0,hostfwd=tcp:127.0.0.1:8080-:8080 \
-device virtio-net-device,netdev=n0 \
-kernel build/kernel.elf
Echo Profile
Use this when testing only the echo tools:
qemu-system-aarch64 -M virt -cpu cortex-a72 -m 256M -nographic \
-global virtio-mmio.force-legacy=false \
-device loader,file=build/virt.dtb,addr=0x4FF00000,force-raw=on \
-drive file=build/base.img,format=raw,if=none,id=swosbase,readonly=on \
-device virtio-blk-device,drive=swosbase \
-netdev user,id=n0,hostfwd=tcp:127.0.0.1:5555-:5555,hostfwd=udp:127.0.0.1:5555-:5555 \
-device virtio-net-device,netdev=n0 \
-kernel build/kernel.elf
Static Cloud IPv6
For cloud providers that assign IPv4 by DHCP but require a static Primary IPv6
address, stage /etc/swos/net-ipv6 at image build time:
cat >support/network/net-ipv6 <<'EOF'
address=2001:db8:0:3df1::1/64
gateway=fe80::1
EOF
make NET_IPV6_CONFIG_FILE=support/network/net-ipv6 base-image
The kernel accepts only an IPv6 /64 address and a link-local gateway. Missing
config keeps the default EUI-64 link-local behavior; invalid config is ignored
with a serial warning. A successful boot logs net-hc23 OK: static IPv6 ....
This config prepares the dual-stack route/neighbor path; SSHD-over-IPv6 remains
a separate acceptance step.
Proof:
make net-static-ipv6-test
SSHD Session Profile
Use this profile to validate host-to-guest SSH reachability through a minimal
authenticated remote command. The current /bin/sshd exchanges SSH
identification strings with an OpenSSH client, negotiates curve25519-sha256,
ssh-ed25519, OpenSSH strict KEX, and chacha20-poly1305@openssh.com,
loads its host-key seed from /etc/ssh/ssh_host_ed25519_seed, authenticates
root with a key from /etc/ssh/authorized_keys, opens a session channel,
executes a bounded direct /bin/<tool> or /usr/bin/<tool> command, and
forwards small remote stdin payloads into fd 0. It currently returns up to 4096
bytes of captured stdout/stderr per remote exec and logs when output is
truncated. TCP write-side backpressure now waits for ACK-driven send-buffer
space on blocking socket writes; larger streaming stdin/stdout remains a later
SSHD milestone.
qemu-system-aarch64 -M virt -cpu cortex-a72 -m 256M -nographic \
-global virtio-mmio.force-legacy=false \
-device loader,file=build/virt.dtb,addr=0x4FF00000,force-raw=on \
-drive file=build/base.img,format=raw,if=none,id=swosbase,readonly=on \
-device virtio-blk-device,drive=swosbase \
-netdev user,id=n0,hostfwd=tcp:127.0.0.1:2222-:22 \
-device virtio-net-device,netdev=n0 \
-kernel build/kernel.elf
Outbound-Only Profile
Guest-initiated clients such as /bin/tcpget, /bin/nslookup, /bin/tlsget,
and /bin/ssh do not need host forwarding. They only need a slirp NIC:
qemu-system-aarch64 -M virt -cpu cortex-a72 -m 256M -nographic \
-global virtio-mmio.force-legacy=false \
-device loader,file=build/virt.dtb,addr=0x4FF00000,force-raw=on \
-drive file=build/base.img,format=raw,if=none,id=swosbase,readonly=on \
-device virtio-blk-device,drive=swosbase \
-netdev user,id=n0 \
-device virtio-net-device,netdev=n0 \
-kernel build/kernel.elf
IPv6 Smoke Profile
IPv6 link-local and NDP setup is enabled with ipv6=on:
qemu-system-aarch64 -M virt -cpu cortex-a72 -m 256M -nographic \
-global virtio-mmio.force-legacy=false \
-device loader,file=build/virt.dtb,addr=0x4FF00000,force-raw=on \
-drive file=build/base.img,format=raw,if=none,id=swosbase,readonly=on \
-device virtio-blk-device,drive=swosbase \
-netdev user,id=n0,ipv6=on \
-device virtio-net-device,netdev=n0 \
-kernel build/kernel.elf
On Darwin, QEMU/slirp IPv6 host forwarding can be unavailable or inconsistent. The checked-in IPv6 tests fall back to the link-local/NDP smoke when the host cannot provide true IPv6 host forwarding.
Login And Capability
After boot, complete the interactive TTY smoke prompt and log in as root:
swift-os login: root
password: swordfish
The seeded root principal has capNet. The seeded user and guest
principals do not, which makes them useful for confinement checks:
id
/bin/nslookup example.com
When a process lacks capNet, socket and resolver operations fail before any
network traffic is sent.
Tool Catalog
| Tool | Direction | Guest port | Host requirement | Proof |
|---|---|---|---|---|
/bin/httpd |
Host to guest | TCP 8080 | TCP hostfwd to 8080 | ./tests/httpd_test.sh |
/bin/llmd |
Host to guest | TCP 8080 | TCP hostfwd to 8080 | ./tests/llm_serve_test.sh |
/bin/tcpecho |
Host to guest | TCP 5555 | TCP hostfwd to 5555 | ./tests/tcp_echo_test.sh |
/bin/udpecho |
Host to guest | UDP 5555 | UDP hostfwd to 5555 | ./tests/udp_echo_test.sh |
/bin/sshd |
Host to guest | TCP 22 | TCP hostfwd to 22 | ./tests/sshd_transport_test.sh |
/bin/netinfo |
Guest diagnostic | none | Slirp NIC | ./tests/netinfo_test.sh |
/bin/tcpget |
Guest to host | Client-chosen | Host TCP listener | ./tests/tcp_connect_test.sh |
/bin/nslookup |
Guest to DNS | UDP client | Slirp DNS or host responder | ./tests/dns_test.sh |
/bin/tlsget |
Guest to host | TCP client | Host TLS 1.3 server | ./tests/tls_test.sh |
/bin/tcpecho and /bin/udpecho are one-shot programs: they serve one request,
print their result, and exit. Start them again for another request.
Runbooks
Verify The NIC
Use the virtio-net acceptance test for the lowest-level network bring-up:
./tests/virtio_net_test.sh
Healthy serial output includes:
net-dhcp OK: lease 10.0.2.15 gateway 10.0.2.2 dns 10.0.2.3
net-a: virtio-net up, MAC
net-a: ARP reply, 10.0.2.2 is at
net-a OK: ICMP echo reply from 10.0.2.2
These markers prove that the driver attached, ARP resolved the slirp gateway, and the ICMP probe completed.
If a DHCP server does not answer, the boot path logs the fallback and keeps the old slirp constants so local QEMU validation remains usable.
Inspect Guest Network Status
Run /bin/netinfo after logging in with capNet to capture the in-guest
network state for deploy preflights:
/bin/netinfo
/bin/netinfo --check
Expected QEMU slirp output includes:
netinfo: ready yes
netinfo: ipv4 10.0.2.15/24 source dhcp
netinfo: gateway4 10.0.2.2
netinfo: dns4 10.0.2.3
netinfo: HC27 OK
netinfo: check ok
The IPv4 source may be fallback when DHCP is absent. IPv6 reports either the
default link-local address or the static /etc/swos/net-ipv6 address staged
with NET_IPV6_CONFIG_FILE. Use /bin/netinfo --check --require-static6 in a
static cloud IPv6 image to fail the deploy script unless staged IPv6 and the
link-local gateway are visible inside the guest.
Proof:
make netinfo-test
Serve Static Files
Boot with TCP 8080 forwarded, log in as root, and start:
/bin/httpd
Wait for:
httpd: listening on 8080
Then run host requests:
curl -fsS http://127.0.0.1:8080/
curl -fsS http://127.0.0.1:8080/hello.txt
curl -fsS http://127.0.0.1:8080/sub/
curl -i http://127.0.0.1:8080/nope
Expected behavior:
/maps to/www/index.html.- Static paths are resolved under
/www. - Common file suffixes receive MIME types.
- Directories without
index.htmlreturn a simple listing. - Missing paths return
404. - The server can multiplex multiple live TCP connections through
poll.
Proof:
./tests/httpd_test.sh
bash ./tests/net_zero_copy_throughput_test.sh
Serve Local AI Completions
Boot with TCP 8080 forwarded, log in as root, and start:
/bin/llmd
Healthy default startup verifies the signed bundle under /models/stories15M
and logs:
llmd: trust root loaded (/etc/swos/model-signing.pub)
llmd: generation 2 rejected (model size/sha256 mismatch)
llmd: bundle stories15M generation 1 verified (ed25519+sha256)
llmd: serving on 8080
Host requests:
curl http://127.0.0.1:8080/health
curl -X POST --data "Once upon a time" http://127.0.0.1:8080/completion
curl http://127.0.0.1:8080/metrics
For model-bundle layout, raw override behavior, health semantics, metrics, and support evidence, see AI_HOSTING_GUIDE.md.
Proof:
./tests/llm_serve_test.sh
Run TCP Echo
Boot with TCP 5555 forwarded, log in as root, and start:
/bin/tcpecho
Wait for:
tcpecho: listening on 5555
Then send from the host:
printf 'swos tcp\n' | nc -w8 127.0.0.1 5555
The guest prints a byte count and exits. Start /bin/tcpecho again for another
connection.
Proof:
./tests/tcp_echo_test.sh
Run UDP Echo
Boot with UDP 5555 forwarded, log in as root, and start:
/bin/udpecho
Wait for:
udpecho: listening on 5555
Then send from the host:
printf 'swos udp' | nc -u -w2 127.0.0.1 5555
The guest prints the sender and exits. Start /bin/udpecho again for another
datagram.
Proof:
./tests/udp_echo_test.sh
Exercise SSHD Remote Command
Boot with host TCP 2222 forwarded to guest TCP 22. The default base image starts
/bin/sshd through /bin/swos-init because /etc/swos/services contains
sshd. Custom images can stage a different service manifest with
SWOS_SERVICES_FILE=PATH; sshd-supervised restarts a normal SSHD child and
sshd-once is the restart acceptance token used by make sshd-supervision-test.
The sshd6 token starts /bin/sshd -6 as an AF_INET6 TCP/22 listener for
IPv6 cloud preflights; sshd6-supervised keeps that listener under the restart
loop, and sshd6-once is the deterministic IPv6 restart acceptance token used
by make sshd-ipv6-supervision-test. The default checked-in service manifest
stays IPv4-only so the existing local hostfwd path remains stable.
For a manual debug run or a custom port, log in as root and start:
/bin/sshd
# or
/bin/sshd -6
Wait for:
sshd: listening on 22 (session exec preflight)
sshd: listening on 22 (IPv6 session exec preflight)
Then connect from the host:
make sshkey
build/sshkey known-host \
--host '[127.0.0.1]:2222' \
--seed-file base/etc/ssh/ssh_host_ed25519_seed \
> build/swift-os-sshd-known-hosts
ssh -F /dev/null -vvv -p 2222 \
-i fixtures/ssh/sshd_hc5_ed25519 \
-o BatchMode=yes \
-o IdentitiesOnly=yes \
-o PasswordAuthentication=no \
-o PubkeyAuthentication=yes \
-o StrictHostKeyChecking=yes \
-o UserKnownHostsFile=build/swift-os-sshd-known-hosts \
-o KexAlgorithms=curve25519-sha256 \
-o HostKeyAlgorithms=ssh-ed25519 \
-o Ciphers=chacha20-poly1305@openssh.com \
-o MACs=hmac-sha2-256 \
root@127.0.0.1 /bin/id
Expected current behavior is a successful SSH command with remote software
version swift-os_sshd-session, KEX debug lines for curve25519-sha256,
ssh-ed25519, and chacha20-poly1305@openssh.com, publickey authentication,
stdout like principal=1(root) ..., and exit status 0. The checked test also
proves that the old HC4 key is rejected, the HC5 key is loaded from
/etc/ssh/authorized_keys, the host-key seed is loaded from
/etc/ssh/ssh_host_ed25519_seed, the host OpenSSH client pins the derived
SwiftOS host key through known_hosts, /bin/id runs as root, and /bin/echo HC6-OK receives an argv argument. It also runs a quoted /bin/echo command
that proves quote removal, backslash escaping, and empty argv preservation,
pipes a small host payload into remote /bin/cat and requires exact stdout,
then reads a capped long output from /bin/cat /models/tok512.bin and requires
the truncation marker. This proves TCP/22 reachability through SSH KEX,
host-key pinning, encrypted userauth, session channel setup, bounded direct
remote exec, bounded stdin forwarding, and bounded output capture; PTY, shell
command parsing, scp, sftp, runtime host-key rotation, larger
streaming, and broader authorized-key option enforcement are follow-up work.
For deploy-specific image-time keys, generate a host-key seed with
build/sshkey seed --out support/keys/ssh_host_ed25519_seed, create
support/keys/authorized_keys with the allowed login public keys, optionally
generate support/keys/ssh_kex_seed, then build with make base-image while
setting SSHD_HOST_SEED_FILE, SSHD_KEX_SEED_FILE, and
SSHD_AUTHORIZED_KEYS_FILE. Derive the host known_hosts line from the exact
staged host-key seed. The KEX seed is an image-time hardening input; it does
not replace the runtime entropy source provided by SYS_RANDOM on virtio-rng
VM profiles.
Proof:
./tests/sshd_transport_test.sh
make sshd-ipv6-listener-test
make sshd-deploy-preflight-test
make sshd-runtime-entropy-test
make sshd-kex-seed-test
make hetzner-deploy-bundle-test
Exercise The SSH Client
Start a host OpenSSH sshd on a high loopback port with an Ed25519 host key and
modern algorithms, boot SwiftOS with a slirp NIC, log in as root, and run:
/bin/ssh 10.0.2.2 2222
The current client preflight connects outbound, exchanges SSH identification
strings, completes curve25519-sha256, verifies the server's ssh-ed25519
host-key signature over the exchange hash, matches the host key against
/etc/ssh/known_hosts, handles OpenSSH strict KEX, derives
chacha20-poly1305@openssh.com keys, and completes one encrypted
ssh-userauth service request/accept. Its KEX cookie and Curve25519 client
ephemeral scalar use SYS_RANDOM when virtio-rng is attached. It does not yet
implement user authentication, session/exec channels, PTY, scp, or sftp.
Proof:
./tests/ssh_transport_test.sh
./tests/ssh_runtime_entropy_test.sh
Connect From Guest To Host
Start a host listener:
printf 'srv-reply\n' | nc -l 5555
Boot SwiftOS with a slirp NIC, log in as root, and run:
/bin/tcpget 10.0.2.2 5555
10.0.2.2 is the QEMU slirp alias for the host. /bin/tcpget sends
GET swos\n, prints connection status, and prints the reply.
Proof:
./tests/tcp_connect_test.sh
Resolve DNS
With QEMU user networking attached, query the default slirp resolver:
/bin/nslookup example.com
For hermetic tests or local DNS experiments, pass an explicit server and port:
/bin/nslookup test.swos 10.0.2.2 5354
/bin/nslookup example.com 10.0.2.3 53 AAAA
The final AAAA argument requests IPv6 records. Full eight-group IPv6 server
addresses are accepted by the command parser.
Proof:
./tests/dns_test.sh
Exercise The TLS Client
/bin/tlsget opens a TCP connection, performs the current TLS 1.3 client flow,
sends GET / HTTP/1.1, and prints decrypted response body bytes.
Run a host TLS server with TLS 1.3 and ChaCha20-Poly1305 support, then connect from the guest:
/bin/tlsget 10.0.2.2 44310 localhost
Certificate verification is deliberately incomplete in the current branch. Use
tlsget as a runtime smoke and interoperability path, not as a production trust
decision.
Proof:
./tests/tls_test.sh
Exercise IPv6 Paths
Use ipv6=on in the QEMU -netdev and run:
./tests/ipv6_smoke_test.sh
./tests/ipv6_tcp_echo_test.sh
./tests/ipv6_udp_echo_test.sh
The smoke test requires net: IPv6 link-local configured. The TCP and UDP echo
tests exercise the dual-stack path; on Darwin they may report a hostfwd skip
after the IPv6 smoke passes.
Verification Matrix
Run the narrowest proof for the path you changed:
| Area | Command |
|---|---|
| Host network protocol unit tests | make test builds and runs tests/net_test.swift |
| Virtio-net attach, ARP, ICMP | ./tests/virtio_net_test.sh |
| Static HTTP | ./tests/httpd_test.sh |
| HTTP zero-copy throughput smoke | bash ./tests/net_zero_copy_throughput_test.sh |
| TCP echo | ./tests/tcp_echo_test.sh |
| UDP echo | ./tests/udp_echo_test.sh |
| Static cloud IPv6 config | make net-static-ipv6-test |
| SSHD remote-command preflight | ./tests/sshd_transport_test.sh |
| SSHD IPv6 listener preflight | make sshd-ipv6-listener-test |
| SSHD IPv6 supervision preflight | make sshd-ipv6-supervision-test |
| SSHD static-IPv6 deploy preflight | make sshd-deploy-preflight-test |
| Hetzner static-IPv6 deploy evidence bundle | make hetzner-deploy-bundle-test |
| SSH client transport preflight | ./tests/ssh_transport_test.sh, ./tests/ssh_runtime_entropy_test.sh |
| Guest-to-host TCP | ./tests/tcp_connect_test.sh |
DNS resolver and nslookup |
./tests/dns_test.sh |
| TLS client smoke | ./tests/tls_test.sh |
| IPv6 link-local/NDP | ./tests/ipv6_smoke_test.sh |
| IPv6 TCP path | ./tests/ipv6_tcp_echo_test.sh |
| IPv6 UDP path | ./tests/ipv6_udp_echo_test.sh |
| LLM serving over HTTP | ./tests/llm_serve_test.sh |
| Full checked-in gate | make test |
The protocol-core unit coverage is in tests/net_test.swift. The user-visible end-to-end paths are the shell tests above.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
socket failed |
No virtio-net device, no capNet, or unsupported socket path |
Boot with -netdev user and -device virtio-net-device; log in as root |
bind failed |
Port already in use in the guest, or a stale service is still running | Run only one service per guest port; reboot or stop the previous QEMU session |
Host curl cannot connect |
Missing or mismatched hostfwd, service not ready, or host port conflict |
Wait for the service readiness marker; confirm the hostfwd rule; choose another host port |
| TCP or UDP echo works once then stops | Echo programs are one-shot | Start /bin/tcpecho or /bin/udpecho again |
/bin/tcpget cannot reach host |
Host listener not running or wrong port | Start the host listener first and connect to 10.0.2.2:<port> |
/bin/ssh fails before preauth |
Host sshd is not listening, algorithms are unsupported, /etc/ssh/known_hosts does not trust the host key, or the base image is stale |
Start an OpenSSH server with curve25519-sha256, ssh-ed25519, and chacha20-poly1305@openssh.com; rebuild base-image; check for ssh: host key matched /etc/ssh/known_hosts; run ./tests/ssh_transport_test.sh |
| DNS fails | Resolver not reachable or explicit test responder not running | Try /bin/nslookup example.com; for tests, start the responder and use 10.0.2.2 5354 |
| SSH exits before stdout | Missing private key, stale base image, missing /etc/ssh/authorized_keys, missing /etc/ssh/ssh_host_ed25519_seed, stale or wrong host known_hosts entry, unsupported command syntax, invalid /etc/ssh/ssh_kex_seed, or a protocol regression |
Use a private key whose .pub line was staged into the exact image, rebuild base-image, derive known_hosts from the exact image seed with build/sshkey, run a simple /bin/id, and check for swift-os_sshd-session, sshd: loaded host key seed /etc/ssh/ssh_host_ed25519_seed, optional sshd: loaded kex seed /etc/ssh/ssh_kex_seed, host-key match in OpenSSH debug output, sshd: authorized key matched /etc/ssh/authorized_keys, publickey auth, and sshd: session exec completed status 0 |
| TLS succeeds but certificate is untrusted | Expected current limit | Do not use tlsget for production trust decisions |
| IPv6 echo skipped on Darwin | QEMU/slirp hostfwd limitation | Treat a pass from ./tests/ipv6_smoke_test.sh as the current host proof |
For deeper diagnosis, see TROUBLESHOOTING.md and collect serial evidence with OBSERVABILITY_GUIDE.md.
Security And Product Limits
Current limits that matter when exposing a SwiftOS network service:
capNetis coarse. It grants the ability to create sockets generally, not a specific port, address, or protocol./bin/netinfoexposes a read-only status snapshot, but there is no target-side firewall command, routing table command, or network configuration command yet. DHCPv4 is boot-time only and does not renew leases./bin/sshdis a session/exec preflight, not a full login daemon. It uses a base-image host-key seed from/etc/ssh/ssh_host_ed25519_seed; the checked-in default seed and authorized key are development-only, and deploy builds should provideSSHD_HOST_SEED_FILE,SSHD_KEX_SEED_FILE, andSSHD_AUTHORIZED_KEYS_FILE. The KEX seed is mixed with a per-session counter, and SSHD usesSYS_RANDOMruntime entropy when virtio-rng is attached. It can bind IPv4 by default or AF_INET6 with-6/sshd6.make sshd-deploy-preflight-testproves the static-IPv6/deploy-key/sshd6image profile locally; provider-routed SSHD-over-IPv6 still needs a real cloud acceptance run. It supports simplessh-ed25519lines plus safe restriction options in/etc/ssh/authorized_keys, bounded stdout/stdin, and direct single-component/bin/<tool>or/usr/bin/<tool>remote exec with whitespace splitting, quote removal, and backslash escaping; PTY, shell command parsing, scp, sftp, runtime host-key rotation, larger streaming, and broader authorized-key option enforcement are still missing./bin/sshis a client transport preflight, not a full SSH client. It verifies the server's host-key signature for the current exchange and checks a minimal/etc/ssh/known_hoststrust store. It usesSYS_RANDOMruntime entropy for KEX when virtio-rng is attached, but has no user authentication and no session/exec or file-copy modes.- TLS certificate verification is not production-ready.
httpdis an HTTP static-file service, not a hardened Internet-facing web server./bin/llmdis a foreground service with no service manager or restart policy yet.- The writable filesystem is RAM-backed
/tmp; no network service state persists across reboot unless it is built into the base image or provided by a read-only package payload. - The virtio-net driver and TCP/IP stack are currently in the kernel. The hardening roadmap moves this authority toward restartable userland services.
Use host-only loopback forwarding, such as hostfwd=tcp:127.0.0.1:8080-:8080,
for local validation. Do not expose current services directly to an untrusted
network.
Source Map
| Area | Source |
|---|---|
| Socket constants and static slirp addresses | kernel/net/socket.swift |
| DHCPv4 codec | kernel/net/dhcp.swift |
| Virtio-net driver | kernel/drivers/virtio_net.swift |
| Socket syscall dispatch | kernel/syscall/syscall.swift |
| Native Swift socket bridge | userland/lib/swift_user.h |
| Static HTTP server | userland/httpd.swift |
| LLM HTTP server | userland/llmd.swift |
| SSH client transport preflight | userland/ssh.swift |
| SSHD session/exec preflight | userland/sshd.swift |
| TCP echo | userland/tcpecho.swift |
| UDP echo | userland/udpecho.swift |
| TCP client | userland/tcpget.swift |
| DNS client | userland/nslookup.swift |
| TLS client | userland/tlsget.swift |