Skip to content

Add ios tunnel service install/uninstall to run the tunnel agent as a managed OS service (cloudflared-style) #750

Description

@danielpaulus

Is your feature request related to a problem? Please describe.

Running the go-ios tunnel agent (ios tunnel start, especially the upcoming --cloud mode) persistently on a host today means hand-writing and maintaining an OS service. On Linux that's a systemd unit in /etc/systemd/system, placing the binary somewhere stable, capturing the right flags + env (ORCHESTRATOR_URL, --userspace, pair-record path, working dir), setting a restart policy, and enable-ing it at boot. This is manual, easy to get wrong (env not inherited, wrong working dir, missing restart), and has to be repeated on every host.

That cost scales badly for the cloud/device-farm use case, where the agent is a fleet agent running on many device hosts. Onboarding a host should be one command, not a unit-file checklist. (We hit exactly this friction bringing up a host by hand — the service didn't inherit the shell env, the working dir/pair-record path mattered, etc.)

Describe the solution you'd like

A service group under the tunnel subcommand, modeled on cloudflared service install (and ngrok service install, gitlab-runner install):

ios tunnel service install   [--cloud] [--userspace] [--user] [other tunnel flags]
ios tunnel service uninstall
ios tunnel service status        # optional thin wrapper
  • install registers go-ios as a managed OS service that runs ios tunnel start …, capturing the current invocation's flags and relevant env (ORCHESTRATOR_URL, pair-record path, etc.) into the service definition so it reproduces a known-good config, sets a restart policy, and enables it at boot. Idempotent (re-running overwrites + re-enables).
  • uninstall stops, disables, and removes it.

Onboarding a cloud host then becomes:

ios login
ios tunnel service install --cloud --userspace

…and the host joins the fleet and stays joined across reboots.

Describe alternatives you've considered

  • Document a systemd unit (status quo). Works, but manual and per-host; doesn't scale to a fleet and is the source of the friction above.
  • Hand-rolled, systemd-only installer. Lowest dependency cost (we already know the exact unit), covers the Linux device hosts. Good MVP, but doesn't match the cross-platform precedents.
  • github.com/kardianos/service (used by gitlab-runner, telegraf, etc.) abstracts systemd / launchd / Windows-SCM behind one API. Matches cloudflared/ngrok cross-platform from day one. Dependency lives in the main application package, not the ios library, so the library stays dep-clean.

Recommendation: take the cloudflared route via kardianos/service for cross-platform install/uninstall, or — if we want to stay minimal first — ship systemd-only and add launchd/Windows later.

Additional context

Precedents (the fleet-agent / tunnel-daemon category conventionally self-installs): cloudflared (cloudflared service install), ngrok (ngrok service install), gitlab-runner (gitlab-runner install). Server software (Caddy/Consul/Vault/Prometheus) instead documents a unit — go-ios cloud mode is the former category.

Things to decide during implementation:

  1. Privilege. A system service needs root/admin. A --userspace cloud agent doesn't need root to run, so offer --user (systemd --user / LaunchAgent) for the no-root case — noting that user services need lingering enabled to run headless without a login session. Default to the system service (robust for headless farm hosts), document the sudo requirement.
  2. Env capture. Bake ORCHESTRATOR_URL (and any other needed env) into the service definition (Environment= on systemd) — a service that doesn't inherit the operator's shell env silently runs with the wrong config.
  3. Consistency with ENABLE_GO_IOS_AGENT. go-ios already self-spawns a tunnel child process (RunAgent, "experimental daemon mode"). The service command should complement that managed-process story, not contradict it.
  4. No-systemd Linux (Alpine/OpenRC) — detect and fail with a clear message rather than writing a broken unit.
  5. go-ios does not currently install any OS service; selfIdentity.plist is the only thing the binary writes itself.

Acceptance criteria (MVP):

  • ios tunnel service install [--cloud] [--userspace] creates + enables + starts a service that survives reboot on Linux/systemd.
  • The service reproduces the operator's flags and ORCHESTRATOR_URL.
  • ios tunnel service uninstall fully removes it (stop, disable, delete).
  • Clear errors when privileges are insufficient or no supported service manager is present.
  • Docs note the sudo requirement and the --user alternative.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions