Conversation
Add a launchd user agent (services.nas-mount, enabled on host k) that mounts Personal-Drive at ~/nas over SMB using the nas.home.lan DNS name set up earlier, with -N keychain-only auth and a StartInterval retry since the NAS/network may not be up yet at login. Discovered along the way that macOS refuses a second concurrent SMB mount of the same share reached via a different hostname (Bonjour vs DNS) — it dedupes by negotiated server identity, not the connect string.
writeShellScript put the script at the store path's top level, so its hash-prefixed basename is what macOS shows in System Settings > Login Items. writeShellScriptBin nests it under $out/bin/, so only the directory carries the hash and the displayed name is plain "nas-mount", matching how the other nix-built agents show up clean.
…manual path Root-run activation scripts can't reach the login keychain's private key (a real macOS security boundary, not a bug) — confirmed even with launchctl asuser reattaching the session. Rejected the alternative of sops-committing the exported Developer ID key for automated signing: permanent exposure of a real signing identity for a cosmetic label isn't worth it. nas-mount.nix now just copies the built script to a stable path (cmp -s guarded so a manual signature survives routine nrs runs); the actual signing is a manual, transient rcodesign + .p12 procedure the machine owner runs himself, documented in docs/unifi-dream-machine.md and knowledge/decisions/nas-mount-codesigning.md.
sign-launchd-agents.ts scans ~/Library/LaunchAgents, shows each agent's signature status (Developer ID / Apple-signed-no-team / ad-hoc / unsigned) with an fzf multi-select and live codesign preview, then signs a whole batch off one passphrase export instead of repeating the manual procedure per agent. Same transient, never-committed .p12 posture as the single-target script it generalizes. Adds rcodesign to the dev shell.
Matches the existing bin/nix-pkgs-versions and bin/opencode pattern so it runs as a bare command inside the dev shell.
cp preserves the nix store source's read-only mode, and chmod +x only ever adds execute bits, so the copied file was permanently r-xr-xr-x with no write bit -- rcodesign/codesign need to write here to sign it. Fixed to chmod u+w,+x and moved outside the cmp -s content-diff guard (permission bits don't affect a code signature) so it self-heals every activation instead of only being set once. Found by sign-launchd-agents correctly reporting the file read-only.
rcodesign errored "specified path is not of a recognized type" when signing the shell-script version -- it only handles Mach-O/bundle/ DMG/pkg. Kept rcodesign over plain codesign (which would work fine here) specifically because it never touches Keychain/session ACLs, which is what will let this same signing approach run in GitHub CI later with certs stored there securely (not implemented yet). nas-mount is now pkgs/nas-mount/ -- pure-std Rust, no crates, built via a bare rustc rather than rustPlatform.buildRustPackage's Cargo.lock machinery -- registered as a real package (packages.nix + overlays/nas-mount.nix) and referenced as pkgs.nas-mount instead of writeShellScriptBin. mountPoint/share now pass as CLI args via launchd's ProgramArguments. Bonus: rustc auto-ad-hoc-signs the output at build time, upgrading Login Items from unsigned to ad-hoc before any manual signing.
…tity security export -t identities has no per-item filter -- it bundled an unrelated "Apple Development" cert (Xcode) alongside "Developer ID Application", and rcodesign silently signed nas-mount with the wrong one instead of erroring on the ambiguity. Real (non-ad-hoc) signature, but Login Items still showed unidentified developer since Apple Development certs don't chain through the Developer ID authority BTM checks for. Both the script and the manual doc procedure now prompt for a .p12 path exported via Keychain Access.app (select exactly one identity) instead of calling security export programmatically.
Found while end-to-end verifying nas-mount through its launchd agent: lsof +D on an SMB mount hangs walking the tree and its own references then hold the mount busy, so the unmount being debugged keeps failing until the lsof processes are killed.
A correct Developer ID signature on the bare binary changed nothing -- BTM attributes launchd items only through an associated app bundle (AssociatedBundleIdentifiers, per Apple DTS). pkgs/nas-mount now also builds NasMount.app (bundle id net.kris.nas-mount, LSUIElement, icns rendered from an SF Symbol by the checked-in make-icon.swift), ad-hoc signed at build time via rcodesign in postFixup: an unsigned bundle is an invalid code object launchd refuses with EX_CONFIG, and signing in postInstall would be undone by fixupPhase's strip. The module deploys the app to ~/Applications (stamp-guarded copy so a manual Developer ID signature survives routine nrs runs) and runs lsregister -f on it (cp-installed apps are invisible to LaunchServices, which Login Items resolves the association through). The plist is written via environment.userLaunchAgents because launchd.user.agents' serviceConfig submodule is closed and lacks AssociatedBundleIdentifiers. BTM caches by plist path and only re-reads on remove+re-add; the module header documents the refresh dance. sign-launchd-agents now signs the enclosing .app when a plist's executable lives inside one. Verified live: Login Items shows NasMount with the custom icon and Developer Name "Kris Williams" (Team Y6VCVC728W); agent exit 0, mount intact. Notarization not required.
The codesigning knowledge from the nas-mount arc is general darwin/nix material, not UDM material: identity/cert-type pitfalls, the codesign-vs-rcodesign trust paths, the hard keychain boundaries, the sign-launchd-agents tool, and the full Login Items attribution recipe (.app bundle + AssociatedBundleIdentifiers + lsregister + BTM cache-bust, notarization not required). unifi-dream-machine.md keeps a pointer; knowledge-bundle references repointed.
Replace the manual sign-launchd-agents chore (for this one agent) with an activation-time rcodesign step: sops-nix installs the dedicated signing PEM at postActivation order 1500, the nas-mount block signs the ~/Applications bundle at 1600 in the same switch — triggered by a fresh stamp-guarded copy or a bundle whose codesign -dvv lacks Authority=Developer ID Application (Authority, never TeamIdentifier). Failures warn without failing activation. The identity is purpose-minted (openssl PKCS#8 key -> CSR -> Apple portal; the personal Keychain identity is never exported) and sops-encrypted as nas-signing-pem with owner k. Its declaration ships commented-out in the host file until the key exists in secrets.yaml — sops-nix fails the build otherwise (verified). Rotation = mint a new cert + replace the sops value. Decision narrative: knowledge/decisions/nas-mount-codesigning.md (2026-07-10 update); procedure: docs/darwin-codesigning.md. Claude-Session: https://claude.ai/code/session_01Si4x5wbNVNuHgh4TyU66D8
…wn PR (#35) The CI commits (ci.yml + update-flake-lock.yml + decision record) were carried on this branch but belong in their own PR; they now live on ci-builds (PR #35) with the FlakeHub cache integration on top. The 370592b commit had bundled this branch's codesigning log entry with the CI log entry — restore the codesigning half here. Transient: two dangling-link warnings (nas-mount-codesigning.md and nas-mount.md -> ci-github-actions.md) until this branch merges main after #35 lands.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a
services.nas-mountdarwin module that auto-mounts the UNAS Pro 4's Personal-Drive SMB share at~/nasvianas.home.lanat login, and takes the resulting launchd agent all the way to a clean System Settings > Login Items presentation: proper name (NasMount), custom icon, and real developer attribution (Kris Williams / Y6VCVC728W) instead of "Item from unidentified developer."What's in here
modules/darwin/nas-mount.nix— host-selective module (enabled onk): launchd user agent (RunAtLoad+ 5-min retry), deploysNasMount.appto~/Applicationswith a stamp-guarded copy (a manual Developer ID signature survives routinenrsruns) +lsregister -f, and writes its plist viaenvironment.userLaunchAgentsbecauselaunchd.user.agents' closed serviceConfig submodule can't expressAssociatedBundleIdentifiers.pkgs/nas-mount/— pure-std Rust mount helper (barerustc -O, no Cargo) shipping bothbin/nas-mountand an ad-hoc-signedNasMount.app(rcodesign inpostFixup; an unsigned bundle is an invalid code object launchd rejects withEX_CONFIG). Icon rendered from an SF Symbol by the checked-inmake-icon.swift.scripts/sign-launchd-agents.ts(+bin/symlink,rcodesignin the dev shell) — fzf batch signer for~/Library/LaunchAgents: shows per-agent signature status/authority/timestamp with a livecodesign -dvpreview, signs a whole selection off one user-supplied.p12(exported single-identity via Keychain Access —security exportcan't filter and grabbed the wrong cert), and signs the enclosing.appwhen the executable lives in a bundle.docs/unifi-dream-machine.md+ knowledge bundle — the full investigation trail: keychain private keys are unreachable from root activation scripts (even vialaunchctl asuser), sops-committing the signing key rejected as disproportionate exposure, Developer ID signing a bare executable does nothing for Login Items (bundle association is the only path), BTM caches by plist path and only refreshes on remove+re-add, and assorted gotchas (lsof +Dself-deadlocking SMB unmounts,sfltool dumpbtmauth-prompting per call).Verification
launchctl kickstart/bootstrap→ keychain-auth mount (-N) → share browsable; exit 0.codesign -vvalid; Login Items verified live showing NasMount + icon + developer identity (BTM record:Developer Name: Kris Williams,Team Identifier: Y6VCVC728W).nix flake checkgreen;okf validate168 files, 0 errors.https://claude.ai/code/session_017uKm5pJE599j11mjfaG551