Portunus
CLI Reference

portunus-client

The edge binary that picks up rule pushes over TLS and binds listeners on the local host.

portunus-client is the edge-side binary. It dials a server endpoint specified in its bundle, validates the server certificate by SHA-256 fingerprint, and accepts rule pushes over a bidirectional gRPC stream.

Synopsis

# Daemon mode (no subcommand): connect, accept rule pushes, forward.
portunus-client [--bundle <PATH>] [--reconnect-initial-delay-ms <N>]
                [--reconnect-max-delay-secs <N>] [--shutdown-drain-timeout-secs <N>]
                [--stats-report-interval-secs <N>]

# Enroll: redeem a one-time URI and write a bundle, then exit.
portunus-client enroll <URI> [--out <PATH>]

Enroll

portunus-client enroll 'portunus://...' --out ./edge-01.bundle.json

Redeems a one-time enrollment URI (from portunus-server enroll-client), verifies the pinned server certificate, and writes the credential bundle with mode 0600. --out defaults to the normal client bundle location. On success the bundle path is printed to stdout; on failure the client exits 1.

Bundle resolution

When --bundle is omitted (since v0.8), the client searches:

  1. $PORTUNUS_CLIENT_BUNDLE
  2. $XDG_CONFIG_HOME/portunus/client.bundle.json
  3. $HOME/.config/portunus/client.bundle.json
  4. ./client.bundle.json

If none resolve, the client exits 1 listing every attempted path.

Flags

Daemon-mode flags (no subcommand):

FlagDefaultPurpose
--bundle <PATH>(resolution chain)Explicit bundle path
--reconnect-initial-delay-ms <N>500Initial reconnect delay; base of the full-jitter exponential backoff
--reconnect-max-delay-secs <N>30Reconnect backoff cap
--shutdown-drain-timeout-secs <N>30Drain budget on SIGTERM/SIGINT
--stats-report-interval-secs <N>5Stats reporting interval

Run portunus-client --help for the current authoritative surface.

Environment variables

VariablePurpose
PORTUNUS_CLIENT_BUNDLEBundle path (highest precedence in resolution chain)
RUST_LOGtracing-subscriber env-filter

Lifecycle

  1. Read bundle.
  2. Resolve server_endpoint.
  3. TLS dial; pin SHA-256 against server_cert_sha256.
  4. Send Hello with bearer; receive Welcome with server-side tunables.
  5. Open bidirectional RuleUpdateRuleStatus stream.
  6. For each RuleUpdate:
    • Allocate listener (TCP bind or UDP bind).
    • Activate the forwarder loop.
    • Reply Active (or Failed(<reason>)).
  7. Push StatsReport every --stats-report-interval-secs (default 5 s).

Drain on shutdown

SIGTERM / SIGINT triggers drain:

  • Listeners stop accepting immediately.
  • In-flight forwarded connections finish up to --shutdown-drain-timeout-secs (default 30 s).
  • The kernel reaps remaining sockets.

Match TimeoutStopSec= in the systemd unit to avoid SIGKILL mid-drain.

Logging

JSON lines by default:

{"event":"process.start"}
{"event":"control.connecting","endpoint":"…:7443"}
{"event":"control.tls_pinned","fingerprint_sha256":"f5e7c2a1..."}
{"event":"control.connected"}
{"event":"rule.received","rule_id":1,"listen_port":18080}
{"event":"rule.activated","rule_id":1,"listen_port":18080}

A pin mismatch:

{"event":"control.tls_pinned_mismatch","expected":"…","got":"…"}

…and the client exits non-zero.

On this page