The fab-upload GC test stat-ed the .part immediately after observing the slot free, but expireIdleUpload unlinks AFTER releasing the mutex. Passed alone, failed in the full package once this release's render tests made web heavier. Not a production defect - a new upload mints a fresh random .part. The test now waits for the outcome it asserts on the same deadline; red-proofed by removing the unlink from production.
8.8 KiB
REPORT — v0.152.0 + felhom-samba 1.1.0: mDNS for macOS (S-3), and the connect card stops offering a dead form
Date: 2026-07-20 · Repo: felhom-controller (v0.151.0 → v0.152.0) + felhom-samba
1.0.0 → 1.1.0 · Trunk, pushed to main.
Origin: operator report — smb://FELHOM still failing from a Mac after v0.151.0; S-3 of
felhom.eu/documentation/audits/DIAG-sharing-2026-07-20.md.
Baselines
| Repo / artifact | start | end |
|---|---|---|
| felhom-controller | 5c105fb, v0.151.0 live on 9201 |
37e12c8 (+ this docs commit), v0.152.0 |
| felhom-samba | 1.0.0 |
1.1.0, digest sha256:1c17c094… |
| felhom.eu | a7d9837 |
1d1d60a |
The finding that redirected the fix
My earlier conclusion — "macOS no longer does NetBIOS" — was wrong, and a packet capture on the
box disproved it. On a bare smb://FELHOM from the operator's Mac (192.168.0.11):
11:18:30.222080 .11:52844 > 192.168.0.255:137 NBNS query, 50 B
name → "FELHOM" + 9 pad + suffix 0x20 (File Server Service — correct for SMB)
11:18:30.222220 .104:137 > .11:52844 NBNS response, 62 B, +140 µs
flags 0x8580 = response, AUTHORITATIVE, RCODE=0 · ANCOUNT 1
TTL 259200 · NB_FLAGS 0x0000 (unique, B-node) · RDATA 192.168.0.104
macOS asked correctly, was answered correctly in 140 microseconds, and never opened a TCP
connection. It retried once, was answered again, gave up. Sixteen seconds later the same Mac
queried FELHOM.local over mDNS and went straight to port 445.
So NetBIOS on macOS feeds legacy browsing, not smb:// URL resolution: the bare smb://<NÉV>
cannot be made to work from a Mac by any change on our side, and nmbd — which the R-6/S4b spike
was right to insist on — was never the broken part. It is precisely what serves Windows.
Investigated and dismissed: the box answers twice per broadcast (nmbd holds 0.0.0.0:137,
<ip>:137 and <bcast>:137; a broadcast lands on two). Standard Samba, and a duplicated correct
answer is still a correct answer that macOS declined to use.
Proven matrix
| Client | Working form | Served by |
|---|---|---|
| Windows | \\<NÉV> |
nmbd (+ wsdd for the Network view) |
| macOS | smb://<NÉV>.local |
avahi/mDNS — new in 1.1.0 |
| Any | smb://<IP> |
direct |
Method — spike before publish (operator's call, and it paid)
avahi was installed by hand into the running container and configured, then proven from a second
machine before any image was built. The operator confirmed smb://FELHOM.local connects. Only then
was 1.1.0 built. The spike also chose the design: a static avahi service file beats smbd's own
multicast dns register, because smb.conf is bind-mounted READ-ONLY and owned by the controller's
renderer, and a static file additionally publishes _device-info._tcp.
Changes
| File | Change |
|---|---|
controller/infra-images/samba/Dockerfile |
avahi + dbus added; rm of packaged service files; header documents the captured NBNS proof and that sidebar discovery is NOT claimed |
controller/infra-images/samba/entrypoint.sh |
templates avahi-daemon.conf + _smb._tcp/_device-info._tcp service file from FELHOM_SERVER_NAME; starts dbus + avahi, both non-fatal |
controller/internal/infra/infra.go |
SambaImage pin → 1.1.0 (Images() and the golden bake follow automatically) |
controller/internal/web/templates/sharing.html |
Mac line smb://<NÉV> → smb://<NÉV>.local; Windows flat name untouched |
controller/internal/web/sharing_connect_card_test.go |
assertions updated + new TestSharingConnectCard_MacLineIsDotLocalNotBareName |
controller/internal/infra/samba_test.go |
tag assertion derives from SambaImage; adds an explicit non-:latest assertion |
controller/internal/web/handler_export_upload_test.go |
async-race fix (below) |
CHANGELOG.md, controller/README.md, REPORT.md |
docs |
Tests
23/23 packages green, run twice with -count=1.
Red-proofs, both mutated → FAILED → restored:
| Mutation | Result |
|---|---|
revert the template's Mac line to the bare smb://{{.SMBServerName}} |
TestSharingConnectCard_MacLineIsDotLocalNotBareName FAILED on both the missing .local and the present bare form, for both configured names (FELHOM, OTTHON) |
delete os.Remove(job.partPath) from expireIdleUpload |
TestFabUpload_GCAndIdleTimeout FAILED — idle-expired .part must be deleted |
Two test bugs found, neither a production defect
TestRenderSambaComposepinned the literal tag1.0.0, so a routine image bump read as a renderer regression. Now derives fromSambaImage, plus a separate assertion for what actually matters — the tag is explicit and never:latest.TestFabUpload_GCAndIdleTimeoutraced.expireIdleUploadnils the slot, releases the mutex, and only then closes and unlinks the.part— so "the slot is free" does not yet mean "the file is gone", and the test stat-ed immediately. It passed in isolation and failed in the full package once this release's new render tests madewebheavier. Not caused by this change and not a production bug (a new upload mints a fresh random.part, so the gap is harmless); the test was asserting an async post-condition synchronously. It now waits on the same 3 s deadline with the assertion unchanged.
Design gates template_id_gate / emoji_gate / native_confirm_gate / offbox_rename_gate: OK.
Build, publish, deploy
./controller/scripts/build-samba-image.sh 1.1.0 --push → sha256:1c17c09422be…
smoke test (DooPlex, DEFAULT BRIDGE — never --network host on this host, it would bind 445/5353)
→ smbd · nmbd · wsdd · dbus-daemon · avahi-daemon: running [SMOKETEST.local] (all 5 up)
→ host-name=SMOKETEST, allow-interfaces=eth0, _smb._tcp/445 in the service file
anonymous pull from guest 9201 (no registry creds) → OK
./build.sh 0.152.0 --push → deployed to 9201
Live: felhom-controller:0.152.0 Up (healthy) · felhom-samba:1.1.0 Up — the controller's reconcile
recreated the samba container onto the real image, discarding the hand-patched spike.
Live validation
Method: on-the-wire from a second machine (felhom-pve) + endpoint-level for the page.
mDNS from felhom-pve, against the SHIPPED image:
FELHOM.local (A) -> A 192.168.0.104 (from 192.168.0.104)
_smb._tcp (browse) -> PTR FELHOM._smb._tcp.local (from 192.168.0.104)
daemons in felhom-samba:1.1.0: smbd · nmbd · wsdd · dbus-daemon · avahi-daemon: running [FELHOM.local]
GET /sharing -> smb://FELHOM.local smb://192.168.0.104 \\FELHOM
GET /sharing/status ×2 -> phase:"idle", running:true (v0.151.0 contract still holding)
Operator, from the Mac: smb://FELHOM.local connects and prompts for credentials.
Still open
- Finder-sidebar discovery — NOT shipped, NOT claimed. The
_smb._tcprecord is published and answers browse queries on the wire, but FELHOM did not appear in the operator's Finder sidebar. That window showed no Network/Bonjour section at all, which points at Finder Settings → Sidebar → Locations rather than at the box — unverified either way. Next: that setting, anddns-sd -B _smb._tcpon the Mac. Zero-typing discovery was the R-6 spike's original ambition and is still not demonstrated. - Windows was not retested this session.
\\FELHOMis served by nmbd, which this release does not touch, and the capture proves nmbd answers correctly — but no Windows client was exercised.
Observations — noticed, not acted on
- The username trap. macOS prefills the local account name (
Viktor.Nagyfenyvesi) in the SMB credential dialog; the household account isfelhom. The page states this in the password card, but not next to the connect addresses where the customer is looking at that moment. Worth a sentence in the connect card — deliberately not added mid-session without a design pass. - nmbd's duplicate answer is harmless here but would look alarming in any future capture. Noted in the DIAG so the next person does not re-investigate it.
- The spike left avahi installed by hand in the old container. It was discarded when the reconcile recreated the container on 1.1.0 — worth remembering that hand-patching a managed infra container is always temporary by construction, which is a safety property, not a limitation.
dns-sd/avahi-browsewere not available anywhere on the box side, so both the NBNS and mDNS verifications were done with small hand-written UDP probes run from felhom-pve. That turned out to be the more valuable method anyway: querying from a second machine is what distinguishes "the daemon answers itself" from "the daemon answers the network", and it is what proved the box innocent before any code changed.