Skip to content

macOS: daemon fails to start — secure local state (local_state.py) is Linux-only #7

Description

@Micaxes

The daemon can't start on macOS: the new secure-local-state preflight (src/tendwire/local_state.py, introduced with the 0.7.0 hardening) hard-fails with no opt-out. I've root-caused it, confirmed macOS has the pieces for a portable fix, and started one — but there's a second, deeper issue I'd like maintainer guidance on before completing, since this is security-critical code.

Repro

$ tendwire daemon --db-path <path>/tendwire.db
tendwire.local_state.LocalStateError: secure local state requires supported POSIX filesystem operations   # UNSUPPORTED_PLATFORM

Root cause (blocker 1 — fixable)

proc_fd_path and canonical_path_from_fd resolve an open directory fd back to a pathname via /proc/self/fd/<dir_fd>, gated on sys.platform.startswith("linux"). macOS has no procfs, so both raise UNSUPPORTED_PLATFORM (these feed the sqlite terminal preflight).

Notably, the capability preflight is fine on macOS — it already passes O_NOFOLLOW, O_DIRECTORY, dir_fd support for open/stat/mkdir/unlink/chmod/chown/link, and stat/chown follow_symlinks. The only Linux-specific dependency is the /proc fd→path resolution.

macOS/BSD have the exact portable equivalent: fcntl(fd, F_GETPATH) returns the descriptor's canonical pathname (verified round-trip on macOS 26.5: F_GETPATH(/tmp fd) -> /private/tmp, same inode).

Partial fix (offered)

Branch Micaxes/tendwire:macos-local-state (commit 28edb66, off current main) adds a _fd_directory_path() helper that uses F_GETPATH on non-Linux POSIX hosts, re-verified against the fd's inode exactly as the /proc path is. This clears the UNSUPPORTED_PLATFORM wall — the two functions resolve correctly and the daemon gets past that preflight.

Blocker 2 (needs your guidance)

With blocker 1 fixed, the daemon then fails ENTRY_CHANGED deeper in the strict path-walk. An isolated probe of proc_fd_path/canonical_path_from_fd on macOS passes the same_inode check, so the mismatch is elsewhere in the walk — I suspect an interaction between the O_NOFOLLOW component-by-component walk and F_GETPATH's canonicalization (possibly APFS firmlinks like /Users). Separately, tests/test_store.py has ~257 macOS failures, so there may be more Linux-specific assumptions in the secure-local-state model.

Since this is a security boundary, I didn't want to keep changing its guarantees blind. Happy to complete the macOS port (and the F_GETPATH change above) if you can point me at the intended invariants — especially whether the path-walk assumes Linux-only /proc-anchored resolution semantics that F_GETPATH can't fully reproduce.

Environment

  • macOS 26.5.1 (arm64) · Python 3.14.3 · tendwire v0.1.0rc5 (33fa90a)
  • Context: driving the Herdres→Telegram bridge on macOS (a downstream vendors this engine). Prior macOS gaps (turn adapter) were resolved upstream in herdres #138.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions