PAM Tunnel Non-Interactive Modes
PAM Tunnel — Non-Interactive Modes
Non-interactive modes for pam tunnel start let you run PAM tunnels from scripts, CI/CD jobs, headless servers, and process supervisors such as systemd, without requiring an interactive terminal session. The feature shipped in PR #1848 (initial, 2026-03-06) and was finalized in PR #1993 (supersedes #1848, 2026-04-24).
Overview
Interactive tunnel workflows assume a user at a TTY. Automation scenarios—batch jobs, remote servers without a login shell, pipelines, and supervised services—need tunnels that start predictably, signal readiness, survive or detach according to policy, and tear down when work completes.
Commander adds optional flags to pam tunnel start so the same command can:
Run in the foreground until stopped or timed out.
Detach into a background child process while the parent exits immediately.
Wrap a single shell command: bring the tunnel up, run the command, tear the tunnel down, and exit with the wrapped command’s exit code.
A file-based tunnel registry supports cross-process visibility and shutdown: other Commander instances can list tunnels started elsewhere and stop them via signals. Inside keeper shell (interactive mode), --foreground and --background are gracefully skipped, because the tunnel already persists for that session.
Commander also auto-detects non-interactive environments (no TTY) when applying these behaviors.
Primary audiences
Headless servers where no interactive terminal owns the tunnel lifecycle.
CI/CD pipelines that must bring a tunnel up for a bounded step and tear it down reliably.
systemd services (and similar supervisors) that expect a main PID, PID files, and signal-driven shutdown.
Automation scripts that wrap operational commands against services exposed on localhost through the tunnel port.
Implementation note
PR #1993 rebased the feature cleanly onto the release branch and kept the final change set narrow (three files), superseding the earlier PR #1848 implementation while preserving the same operator-facing behavior described here.
New flags reference (pam tunnel start)
pam tunnel start)Syntax (conceptual)
The following illustrates only the new options documented on this page; other existing pam tunnel start arguments (such as --port) behave as they did previously:
--foreground
-fg
Boolean
false
Run the tunnel in the current process. Stays alive until Ctrl+C or --timeout (WebRTC wait).
--background
-bg
Boolean
false
Spawn a detached child; parent returns immediately. Readiness is determined by polling the file-based tunnel registry.
--run
-R
String
(none)
After the tunnel is up, exec the given shell command; on exit, tear down the tunnel and propagate the command’s exit code.
--timeout
—
Integer (seconds)
30
Maximum time to wait for the WebRTC connection when starting the tunnel.
--pid-file
—
Path string
(none)
On successful connect, write the process PID to this file; the file is removed when the tunnel stops.
All new flags are optional. --foreground, --background, and --run are mutually exclusive (see Mutual exclusivity rules).
--foreground (-fg)
--foreground (-fg)How it works
The tunnel runs in the same process as the keeper invocation. It remains active until the process receives SIGINT (e.g. Ctrl+C) or until the WebRTC connection wait exceeds --timeout.
In practical terms:
The tunnel shares fate with the
keeperprocess—no detached child owns the WebRTC session.Interactive interrupt (Ctrl+C) ends the process in the usual terminal-driven way (SIGINT).
If the tunnel cannot become ready within
--timeout, startup fails according to the same timeout semantics used by the other modes.
When to use
Use foreground mode when a supervisor or interactive operator should own the tunnel lifecycle—typical for terminals, containers with a single main process, or systemd units that track MAINPID.
Example — stay alive until killed
Example — foreground with PID file
Write the PID after connect so external tools can signal the process:
--background (-bg)
--background (-bg)How it works
Commander starts a detached child process that owns the tunnel. The parent exits immediately once startup proceeds. Readiness is observed by polling the file-based tunnel registry (JSON session files under <tmp>/keeper-tunnel-sessions/).
In practical terms:
Parent / child split: the child continues running the tunnel; the parent returns to your shell or CI runner immediately after initiating startup.
Readiness: rather than blocking interactively, Commander consults the registry files to determine when the tunnel registration reflects a live session.
Shutdown: you may stop the tunnel by signaling the PID recorded with
--pid-file, or by usingpam tunnel stop(including cross-process discovery—see below).
When to use
Use background mode when a script should return control immediately—e.g. start the tunnel, then run client commands in the same script, then stop via PID file or pam tunnel stop.
Example — daemonize, run client, stop by PID
--run (-R)
--run (-R)How it works
Commander starts the tunnel, then executes the supplied shell command. When that command exits, Commander stops the tunnel and the overall process exits with the same exit code as the wrapped command.
Lifecycle summary:
Start
Establish the tunnel and wait up to --timeout for WebRTC readiness (default 30 seconds).
Execute
Run "<command>" with normal shell semantics after the tunnel is usable.
Stop
Tear the tunnel down automatically when the wrapped command finishes—success or failure.
Exit code
Propagate the wrapped command’s exit code to the caller (CI systems, scripts, systemd).
When to use
Use --run for one-shot automation: database dumps, migrations, health checks, or any CLI that needs localhost access through the tunnel without managing start/stop manually.
Example — single command with tunnel lifecycle
Example — custom WebRTC wait before running the command
--timeout and --pid-file
--timeout and --pid-file--timeout <seconds>
--timeout <seconds>Controls how long Commander waits for the WebRTC connection when bringing the tunnel up. Default: 30 seconds. Increase this value on slow or high-latency networks when using --run or other modes.
The timeout applies to connection establishment, not to how long the tunnel remains up afterward. For long-running tunnels, prefer --foreground or --background and manage lifetime externally (signals, pam tunnel stop, or supervisor semantics).
--pid-file <path>
--pid-file <path>After the tunnel connects, Commander writes the tunnel process PID to the given path. The file is removed when the tunnel stops. This pairs naturally with --foreground (signal the main process) or --background (signal the detached owner).
Operational notes
The PID file is a stable integration point for
kill -SIGTERM $(cat /path/to/pid)style orchestration (as shown in the PR examples).Combined with
systemd, alignPIDFile=with--pid-fileso$MAINPIDandExecStoptarget the same process (see systemd service example).
Examples
Cross-process tunnel registry
The module keepercommander/commands/tunnel_registry.py implements a file-based registry so tunnels are visible across processes:
Session metadata is stored as atomic JSON writes under:
The sessions directory uses
0o700permissions on POSIX systems.Stale entries are cleaned up automatically.
Duplicate bind detection helps avoid conflicting tunnel endpoints.
Filename and layout
Each JSON file is keyed by <pid>.json, where pid identifies the owning tunnel process for that registry entry. The parent directory keeper-tunnel-sessions lives under the platform temporary directory (<tmp>), which resolves according to OS conventions for temp paths.
Why this exists
Without a cross-process view, only the originating Commander process could reason about active tunnels. The registry makes background startup observable and lets list/stop remain coherent when tunnels outlive the shell that started them—critical for scripts, services, and remote automation.
This registry is what enables pam tunnel list and pam tunnel stop to interoperate with tunnels started by other Commander processes.
pam tunnel list and pam tunnel stop (cross-process)
pam tunnel list and pam tunnel stop (cross-process)pam tunnel list
pam tunnel listThe list command now surfaces tunnels from other processes by reading the file registry, not only tunnels owned by the current session. Stale registry entries are auto-cleaned.
pam tunnel stop
pam tunnel stopStop falls back to the file registry to terminate cross-process tunnels using SIGTERM on POSIX or TerminateProcess on Windows. The --all option also stops cross-process tunnels discovered via the registry.
Operator cheat sheet
Stop using the PID file pattern from the examples
Read --pid-file and send SIGTERM (for example kill -SIGTERM $(cat /tmp/tunnel.pid)).
Stop cross-process tunnels via Commander
Run pam tunnel stop—it falls back to the file registry and signals remote owners (SIGTERM / TerminateProcess).
Stop everything
pam tunnel stop --all also stops cross-process tunnels found through the registry.
See tunnels from other processes
Run pam tunnel list—registry-backed sessions appear here; stale entries are auto-cleaned.
systemd service example
PR #1993 included the following commented illustration for [Service] wiring—expanded here into a complete unit file you can install under /etc/systemd/system/ (paths and keeper location may differ):
The following unit runs the tunnel in the foreground under systemd supervision, writes a PID file after connect, and stops cleanly via SIGTERM:
Adjust ExecStart paths, <UID>, --port, and PIDFile for your environment.
Mutual exclusivity rules
The following modes cannot be combined:
Foreground
--foreground / -fg
Background
--background / -bg
Run wrapper
--run / -R
Choose exactly one of these three for a given invocation.
Additional behavior
Commander auto-detects non-interactive use when there is no TTY.
Inside
keeper shell(interactive session),--foregroundand--backgroundare gracefully skipped—the tunnel already persists in that context.
Interaction summary
Context
--foreground / --background
Where Commander auto-detects non-interactive mode (no TTY)
Behaviors apply as documented in each section above.
Interactive keeper shell
--foreground and --background are gracefully skipped—the tunnel already persists for that session.
Backward compatibility
All new flags default to false, None, or 30 (for --timeout) as described in the reference table. If you omit them, pam tunnel start behaves the same as before these changes—existing scripts and muscle memory remain valid without modification.
Last updated
Was this helpful?

