# Go on 9front All verified on `lab.qcow2`, not inferred. ## Go 1.24.x is broken on plan9/amd64 Every binary dies before `main`: M structure uses sizeclass 1792/0x700 bytes; incompatible with mutex flag mask 0x3ff fatal error: runtime.m memory alignment too small for spinbit mutex runtime.lockVerifyMSize() lock_spinbit.go:97 The spinbit mutex landed in 1.24 and `runtime.m` falls in a sizeclass that cannot meet its alignment. Tested: **1.23.11 ok, 1.24.4 broken, 1.25.14 ok, 1.27.0 ok**. The host's installed Go is 1.24.4 -- precisely the broken one -- so `mk.sh` pins `GOTOOLCHAIN=go1.27.0`. ## TLS needs a CA bundle you supply Go's x509 looks only at `/sys/lib/tls/ca.pem` on plan9, and 9front ships none: SystemCertPool ERR: open /sys/lib/tls/ca.pem: file does not exist Copy any bundle there and HTTPS works -- verified TLS 1.3 with full cert verification. Already installed on `lab.qcow2`. ## Networking is /net, not a helper program Go opens `/net/tcp/clone`, writes `connect`, and resolves through `/net/cs` and `/net/dns` (`net/fd_plan9.go`, `net/ipsock_plan9.go`). No `exec.Command`, no webfs. It is what `dial(2)` does. ## No native graphics yet `9fans.net/go/draw` is a complete libdraw port and *builds* for `GOOS=plan9`, but at runtime it does cmd := exec.Command(devdraw, os.Args[0], "(devdraw)") which is plan9port's helper. 9front has no `devdraw`. A native transport means opening `/dev/draw` and reading `/dev/mouse`, `/dev/kbd` -- plain file I/O, no cgo, a few hundred lines under an already-complete library. Nobody has written it. ## Building Cross-compile from Linux; do not put a toolchain on the guest. A native plan9/amd64 toolchain builds fine via `bootstrap.bash` (needs a bootstrap Go >= 1.24.6), but it is 249MB unpacked -- `compile` alone is 27MB -- against a 3MB `ical/fs`. Getting binaries in, with an HTTP server on the host: hget http://10.0.2.2:8099/fs > /tmp/icalfs # qemu user-net host ## The guest clock is skewed by the host's timezone `run.sh` passes `-rtc base=localtime`, so the guest's idea of *UTC* equals the host's *local* time. With the host on EDT the guest is 4h behind real UTC. Anything time-sensitive must be generated in the guest's frame -- take `date -n` from the guest, not from the host.