Documentation
¶
Overview ¶
Package packaging renders service-manager unit files (launchd plist on darwin, systemd user unit on linux) and installs them where the user's service manager expects to find them. Called from `bridge init` so a single operator command leaves behind a running, auto-restarting service.
Index ¶
- Constants
- Variables
- func CmdEscape(s string) string
- func DefaultConfigDir() (string, error)
- func DefaultLogPath() (string, error)
- func Install(p Params) (unitPath string, err error)
- func InstallStartup(p Params) (unitPath string, err error)
- func InstallWindowsService(_ Params) (string, error)
- func IsAdmin() bool
- func IsInitialized() (cfgPath string, ok bool)
- func IsInstalled() bool
- func IsListening(host string, port int) bool
- func IsRoot() bool
- func Restart() error
- func SpawnDetached(binary, configPath, logPath string) error
- func Start() error
- func Stop() error
- func Uninstall() (string, error)
- func UninstallWindowsService() error
- func WaitForListen(host string, port int, timeout time.Duration) bool
- type Params
- type ServiceKind
Constants ¶
const ServiceLabel = "com.acoseac.1-bit-bridge"
ServiceLabel is the launchd Label / systemd unit name. Chosen to match the iOS bundle id ("com.acoseac.onebit") so `launchctl list` groups it sensibly alongside the app's user defaults.
Variables ¶
var ErrServiceInstallUnsupported = errors.New("Windows Service install only supported on windows")
ErrServiceInstallUnsupported is returned when InstallWindowsService is called on a non-Windows host. Callers check the error and fall back to launchd/systemd (unix) or the Startup-folder launcher (windows).
var ErrSystemInstallNeedsRoot = errors.New("system-level install detected; re-run as root or convert to a user-context install")
ErrSystemInstallNeedsRoot is returned by Stop / Restart when the detected install is a system-level launchd LaunchDaemon or systemd system unit and the current process can't drive it. The menu surfaces this as a friendly "Re-launch as root or convert to a user-context install" hint rather than calling user-domain commands that would silently no-op against the wrong namespace.
Functions ¶
func CmdEscape ¶ added in v0.1.1
CmdEscape prepares a string for use inside a double-quoted argument on a Windows `cmd.exe` command line. The only thing that can tear a double-quoted argument is a literal `"` — cmd.exe's escape for a quote inside quotes is `""`, NOT backslash-anything. Backslashes are fine inside quoted arguments; they're literal path separators. CR / LF would end the line, which would break the script. NUL can't appear in a Windows path, but stripping it is cheap insurance.
Shared by the `cmdEscape` template func (startup.cmd.tmpl) and the runtime `SpawnDetached` helper so the init-time spawn and the logon-time Startup launcher produce byte-identical command lines.
func DefaultConfigDir ¶
DefaultConfigDir returns the standard config location for the current OS. macOS uses ~/Library/Application Support/1-bit-bridge so it's backed up by Time Machine and survives reinstalls; Linux follows XDG; Windows uses %LOCALAPPDATA%\1-bit-bridge (roaming is overkill for a per-machine SQLite DB + TLS cert).
func DefaultLogPath ¶
DefaultLogPath returns a per-OS log file location.
func Install ¶
Install writes the service unit for the current OS and asks the service manager to load it. Returns the path of the unit file that was written. Non-darwin / non-linux: returns an empty path and (nil) error — the caller should advise the user to run `bridge serve` manually.
**Windows two-tier semantics**: tries the SCM service first; falls through to the Startup-folder launcher when SCM access is denied. Callers that need to *force* the Startup-folder path (because the operator explicitly chose option 1 in the wizard, even though they might be running elevated) MUST use `InstallStartup` instead — otherwise an Administrator-shell wizard run silently auto-elevates to SCM regardless of the operator's pick.
func InstallStartup ¶ added in v0.1.1
InstallStartup installs the Startup-folder launcher only on Windows; never touches SCM. Used by the wizard when the operator explicitly picks "Launch when I log in" (option 1) — without this strict path, `Install`'s SCM-first auto-elevation would silently install as a Windows Service when the wizard was run from an Administrator shell, and the post-install status / restart behaviour would mismatch the operator's stated choice.
On non-Windows: returns ("", nil). The launchd / systemd paths have only one mode each, so the operator never sees this distinction outside Windows.
func InstallWindowsService ¶
InstallWindowsService is a non-Windows stub. Always returns ErrServiceInstallUnsupported. The signature matches the windows build so callers don't need build-constrained dispatch code.
func IsAdmin ¶ added in v0.1.1
func IsAdmin() bool
IsAdmin is the Windows-only elevation probe; on POSIX it returns true so callers that gate UX on admin rights don't need build-tagged dispatch — the gate is irrelevant when the relevant install paths are user-context (launchd user agent, systemd --user).
func IsInitialized ¶ added in v0.1.1
IsInitialized reports whether bridge.yaml exists at the platform-default config path. Returns the resolved config-file path either way so the caller can re-use it (e.g. as the --config arg for a manual launch).
func IsInstalled ¶ added in v0.1.1
func IsInstalled() bool
IsInstalled is a convenience wrapper around InstalledKind. Returns true when any non-None kind is detected; swallows the error so callers that only need the boolean can stay terse.
func IsListening ¶ added in v0.1.1
IsListening returns true if something accepts TCP on host:port within a short dial timeout. Used before `SpawnDetached` so a re-run of `bridge init` while an SCM-installed service (or a previously-spawned launcher) is already bound doesn't produce a port-bind error that ends up in the log as a permanent-looking failure.
func IsRoot ¶ added in v0.1.1
func IsRoot() bool
IsRoot reports whether the current process is running with euid 0. Used to warn against `sudo bridge` on POSIX, where running as root resolves $HOME to /root and silently breaks the bridge config dir resolution at next user-context launch.
func Restart ¶ added in v0.1.1
func Restart() error
Restart stops then re-starts the installed service. Implemented per platform because the launchd/systemd/SCM APIs differ — the dispatcher here just keeps the public surface tidy. Returns nil when nothing is installed; same system-install gate as Stop.
func SpawnDetached ¶ added in v0.1.1
SpawnDetached is Windows-only. On macOS/Linux, init hands off to launchd / systemctl which start the daemon as part of the unit install — the shell doesn't need a detached child. This stub exists only so init.go compiles without a build-tag dance around the call site.
func Start ¶ added in v0.1.1
func Start() error
Start asks the service manager to boot the installed bridge service if it isn't already running. No-op when nothing is installed (returns nil so menu / CLI callers don't have to gate on `IsInstalled()` first). Same system-install + admin gates as Stop / Restart.
Distinct from Restart in semantics: Start is idempotent "service should be up". Restart unconditionally bounces. The CLI surface added in this PR exposes both to operators.
func Stop ¶ added in v0.1.1
func Stop() error
Stop asks the service manager to stop the running bridge service (launchd / systemd / SCM) but leaves the install in place. A follow-up Start (via the OS itself or via Restart below) brings it back. No-op when nothing is installed — returns nil so menu callers can call this without a prior IsInstalled() check.
On Windows, calling Stop without admin returns the platform's access-denied error wrapped — the menu surfaces this as a "Re-launch as Administrator" hint rather than a stack trace.
On POSIX, calling Stop against a system-level install returns ErrSystemInstallNeedsRoot — see the comment on that sentinel.
func Uninstall ¶
Uninstall reverses Install: stops the service and removes the unit file. Missing files are not an error so `bridge init` can rerun idempotently. Returns (unitPath, err).
func UninstallWindowsService ¶
func UninstallWindowsService() error
UninstallWindowsService is a non-Windows stub. Always returns nil so a best-effort uninstall doesn't fail across platforms.
func WaitForListen ¶ added in v0.1.1
WaitForListen polls host:port until something accepts, or the timeout expires. Used after `SpawnDetached` so `openInBrowser` fires only after the admin server is ready to answer. Returns true if a connection landed within the budget.
Types ¶
type Params ¶
type Params struct {
Label string // e.g. com.acoseac.1-bit-bridge
BinaryPath string // absolute path to the bridge binary
ConfigPath string // absolute path to bridge.yaml
WorkingDir string // absolute path; where stdout/stderr filenames resolve
LogPath string // absolute path to a log file
}
Params bundles the values the templates expand. All paths must be absolute — launchd won't expand $HOME inside a plist, and systemd user units on most distros inherit a minimal PATH.
type ServiceKind ¶ added in v0.1.1
type ServiceKind int
ServiceKind identifies which service-manager artifact is installed for the bridge on the current host. The user/system split matters because the install paths differ — a user agent lives under the user's home dir and runs at logon, a system daemon lives under shared OS dirs and runs at boot under root / LocalSystem.
const ( // KindNone — no service-manager artifact found. KindNone ServiceKind = iota // KindLaunchdUser — ~/Library/LaunchAgents/<label>.plist. // The supported macOS install path; runs at logon as the user. KindLaunchdUser // KindLaunchdSystem — /Library/LaunchDaemons/<label>.plist. // Rare and only present from a sudo install, which the menu // warns against because the bridge config dir lives under // the user's home and won't resolve correctly under root. KindLaunchdSystem // KindSystemdUser — ~/.config/systemd/user/<label>.service. // The supported Linux install path. KindSystemdUser // KindSystemdSystem — /etc/systemd/system/<label>.service. // Rare and operator-installed; same caveat as KindLaunchdSystem. KindSystemdSystem // KindWindowsSCM — registered with Windows SCM. // Requires admin to install/uninstall; survives logout. KindWindowsSCM // KindWindowsStartup — Startup-folder .cmd launcher. // User-context install; runs at logon while user is logged in. KindWindowsStartup )
func InstalledKind ¶ added in v0.1.1
func InstalledKind() (ServiceKind, error)
InstalledKind reports which service-manager artifact (if any) is installed on the current host. Best-effort: file-existence probes on darwin/linux, SCM-then-Startup probe on windows. Returns KindNone with nil error when nothing is found. Errors are surfaced only when a probe failed in a way that prevents detection (e.g. $HOME unresolvable) — a genuine "no service" state is not an error.
On Windows, SCM is probed first; if SCM access is denied (no admin) the Startup-folder file probe still runs. Both can coexist on the same machine across reinstalls; SCM wins precedence in this report.
func (ServiceKind) Description ¶ added in v0.1.1
func (k ServiceKind) Description() string
Description returns a short, human-readable label for the service kind, suitable for a CLI status line. Distinct strings per kind so a UI never collapses two install kinds into the same display.