fd40b29119
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.
154 lines
8.8 KiB
Markdown
154 lines
8.8 KiB
Markdown
# 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
|
||
|
||
1. **`TestRenderSambaCompose` pinned the literal tag `1.0.0`**, so a routine image bump read as a
|
||
renderer regression. Now derives from `SambaImage`, plus a separate assertion for what actually
|
||
matters — the tag is explicit and never `:latest`.
|
||
2. **`TestFabUpload_GCAndIdleTimeout` raced.** `expireIdleUpload` nils 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 made `web` heavier. **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._tcp` record 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, and
|
||
`dns-sd -B _smb._tcp` on the Mac. Zero-typing discovery was the R-6 spike's original ambition and
|
||
is still not demonstrated.
|
||
- **Windows was not retested** this session. `\\FELHOM` is 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 is `felhom`. 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-browse` were 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.
|