Skip to content

Per-drive SMB share name suffix - #189

Merged
bbangert merged 2 commits into
mainfrom
feat/share-name-suffix
Aug 30, 2026
Merged

Per-drive SMB share name suffix#189
bbangert merged 2 commits into
mainfrom
feat/share-name-suffix

Conversation

@bbangert

Copy link
Copy Markdown
Owner

Rationale

The USB backup SMB share was always named the global usb_backup, so only one drive at a time could ever have a share (or, worse, two different sticks in sequence would collide on the same share name/credentials at the OS/mDNS level Home Assistant sees). This mirrors the sendspin-client naming work (#172, sendspin_dual_role_mdns_collision) by giving the share a stable per-drive suffix instead.

Derivation rules

Share name = usb_backup_<suffix>, mirroring Storage.Server's drive-key stability semantics:

  • Serial present: last 6 characters of the USB serial, lowercased, then sanitized by dropping anything outside [a-z0-9].
  • Sanitized serial too short (< 2 characters left) or no serial at all: falls back to the drive's vendor_id/product_id as lowercase 4-digit hex, concatenated (e.g. 09306545).
  • Result always matches ~r/^[a-z0-9]{2,16}$/ — a conservative, SMB-safe charset.
  • Same physical medium ⇒ same share name across replugs and ports (only vendor/product id and serial feed the derivation, never the USB slot path). Only one share ever runs at a time, so there are no live name collisions to worry about.

What changed

  • UniversalProxy.Storage.Smbd.share_name/1 / share_suffix/1 — the new pure derivation, with doctests.
  • Smbd.config/1 takes an optional :share_name param (default stays bare "usb_backup" for backward compatibility); the […] section header uses it.
  • Storage.Server derives the mounted drive's share name every convergence pass (mirroring how share_folder is refreshed), threads it into prepare_runtime/2's params, and includes share_name in the broadcast state payload.
  • The Overview drawer's "Share" copy-row now renders the live share_name from the payload, falling back to plain usb_backup text if it's ever nil.
  • UniversalProxy.ESPHome.EntityProvider doesn't reference the share name anywhere, so it's untouched.

HA-entry migration note

Any existing Home Assistant backup-location entry pointing at \\<host>\usb_backup will need its share field updated to the new per-drive name (visible in the drawer, or derivable from the drive's serial/vid/pid) after this ships — the old bare name no longer exists once a drive is re-provisioned.

Gate results

  • mix format — clean
  • mix compile --warnings-as-errors — clean
  • Targeted (test/universal_proxy/storage/, storage_test.exs, overview_live_test.exs): 393 passed (6 doctests, 387 tests)
  • Full mise run test: 1634 passed (8 doctests, 1626 tests), 3 excluded
  • mise exec -- sh -c 'MIX_TARGET=host MIX_ENV=dev mix dialyzer' — passed successfully, 0 errors

🤖 Generated with Claude Code

The share is now usb_backup_<suffix> (serial tail, or vid+pid when
the stick has no serial; SMB-safe [a-z0-9]) mirroring the sendspin
client naming, stable per medium across replugs. The drawer shows the
live name; existing HA entries need their share field updated once.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Mount adoption can associate the running share with a different drive’s derived name.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds stable per-drive SMB share names derived from USB identity and exposes them in storage state and the UI.

Changes:

  • Derives and configures per-drive share names.
  • Propagates share names through server state and broadcasts.
  • Updates UI display and test coverage.
File summaries
File Description
lib/universal_proxy/storage/smbd.ex Implements share-name derivation and configuration.
lib/universal_proxy/storage/server.ex Integrates names into storage lifecycle and state.
lib/universal_proxy/storage.ex Extends default storage state.
lib/universal_proxy_web/components/storage.ex Displays the live share name.
test/support/storage_stub.ex Updates stub state shape.
test/universal_proxy/storage/smbd_test.exs Tests derivation and configuration.
test/universal_proxy/storage/server_test.exs Tests server integration.
test/universal_proxy/storage_test.exs Updates default-state assertions.
test/universal_proxy_web/live/overview_live_test.exs Tests UI rendering and fallback.
Review details
  • Files reviewed: 9/9 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread lib/universal_proxy/storage/server.ex
Naming used the pre-adoption list head, so a mount adopted mid-pass
could start the share under another drive's name; resolution now goes
through the mounted_ref/owns_device chain like share_folder does.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The implementation matches the documented derivation rules, covers the full state flow, and resolves the prior drive-selection issue.

Review details
  • Files reviewed: 9/9 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@bbangert
bbangert merged commit 0494220 into main Aug 30, 2026
7 checks passed
@bbangert
bbangert deleted the feat/share-name-suffix branch August 30, 2026 22:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants