Skip to main content

frp-jump

Connect Linux boxes (including Wiren Board controllers) to each other over the internet using the SSH/HTTP/TCP clients you already have — no manual port juggling, and no caring whether the link ended up peer-to-peer or relayed through your server.

  • Tunneling is frp (frpc/frps), driven through an abstract TunnelDriver/RelayDriver interface — see docs/architecture.md.
  • P2P with relay fallback is frp's own xtcp + fallbackTo feature: a device pair first tries a direct (hole-punched) connection, and transparently falls back to relaying through your server if that doesn't complete within a timeout. Not something this project implements itself.
  • Security: a private CA (run by your server) issues an mTLS cert to every enrolled device, so nothing unenrolled can reach the relay at all; a per-pair secret means even enrolled devices can't reach each other's services without an explicit grant; the WebUI is behind passwordless magic-link auth (no passwords, no SMTP — links are generated by the server and you hand-deliver them yourself); only an admin can add devices, wire up services/grants, or revoke them — an invited non-admin user can view the dashboard but not change it. Revoking a device or grant takes effect on its next sync, not instantly — see docs/architecture.md for what that does and doesn't cover.
  • UX goal: after setup, ssh <name> and http://127.0.0.1:<port> just work with stock clients, whether the path underneath is p2p or relayed.

Installing the CLI

Not published to PyPI — install on every machine that will run either frp-jump server ... or frp-jump client ... (the server box, and each device you connect), Python 3.12+ required (already present on any recent Debian/Ubuntu, including Wiren Board controllers). Two ways:

From a tagged release (built by .github/workflows/release.yml, shows up under this repo's Releases tab):

python3 -m venv .venv    # needs the venv module: on Debian/Ubuntu that's
                          # a separate package, `apt install python3-venv`
gh release download v0.1.0 --repo aadegtyarev/frp-jump --pattern '*.whl' -O frp_jump.whl
.venv/bin/pip install frp_jump.whl

Needs the gh CLI authenticated (gh auth login) since the repo is private — fine on your own machine, probably not worth installing just for a headless device (see below).

Directly from a git tag (no release/wheel involved, works anywhere you can already git clone the repo — e.g. via the same read-only deploy key pattern used for node1 in docs/architecture.md; gh repo deploy-key add to mint one per device):

python3 -m venv .venv
.venv/bin/pip install "git+ssh://git@github.com/aadegtyarev/frp-jump.git@v0.1.0"

Either way you end up with frp-jump in that venv — put .venv/bin on PATH, or call .venv/bin/frp-jump directly. The rest of this README just says frp-jump.

To cut a new release: git tag vX.Y.Z && git push origin vX.Y.Z (bump version in pyproject.toml first) — the workflow builds and publishes it automatically.

Quick start

On the server (a box with a public IP/domain):

export FRP_JUMP_RELAY_PUBLIC_ADDR=tunnel.example.com   # or a bare IP
frp-jump server init --admin-email you@example.com
# -> prints an admin login link: open it in a browser to sign in
frp-jump server run

Lost that link, or need to sign in from somewhere else? It's not your only way in — mint a fresh one any time:

frp-jump server login-link you@example.com             # new login link
frp-jump server login-link friend@example.com --invite # invite someone else

(Under the systemd deployment below, server login-link needs the same FRP_JUMP_* env vars as server run, which it won't pick up on its own outside of systemd's EnvironmentFile — see packaging/scripts/frp-jump-login-link for a copy-pasteable wrapper.)

In the WebUI: Add device for each box you want to connect — this is also where the enroll token comes from, there's no other source for it. It prints a one-time frp-jump client enroll <url> <token> command; run that on the device itself (not on the server). Then define a service on the exposing device (e.g. wb01-ssh, protocol ssh, local port 22) and a grant wiring a consuming device to it.

On each device (after installing the CLI there too):

frp-jump client enroll https://tunnel.example.com <token-from-webui>
frp-jump client run          # foreground; wrap with systemd for real use

Once the consuming device has synced (its agent polls every agent_poll_interval_seconds, default 30s):

ssh wb01-ssh                 # just works — see `frp-jump client status` for
                              # the local address of a non-ssh grant

Configuration

Everything configurable lives in one place: src/frp_jump/common/settings.py — set via FRP_JUMP_<FIELD> environment variables, or a TOML file ($FRP_JUMP_CONFIG_FILE, else the first of ./frp-jump.toml, ~/.config/frp-jump/config.toml, /etc/frp-jump/config.toml that exists). Env vars win over the file. Nothing else in the codebase hardcodes a port, TTL, or version pin.

The only setting with no sane default is relay_public_addr — the address other devices dial to reach your relay; server init/server run refuse to start without it.

systemd

See packaging/systemd/. The client unit's comments explain a real gotcha: a device that consumes an SSH grant needs the agent running as the actual human (so it can maintain their real ~/.ssh/config) — a --user unit, not a system one, unless you point FRP_JUMP_SSH_CONFIG_PATH at that user's config explicitly. A device that only exposes services (e.g. a Wiren Board controller) is fine as a system service.

On the server, also install packaging/scripts/frp-jump-login-link to /usr/local/bin/ (chmod 755, root:root) — a one-line wrapper around server login-link that loads the systemd unit's env file for you, so minting a fresh login/invite link doesn't mean hand-assembling env $(sudo cat /etc/frp-jump/server.env | xargs) sudo -u frp-jump ... every time.

Development

uv sync
uv run pytest tests/unit -q        # fast, no network
uv run ruff check .
uv run pytest tests/integration -m integration -q   # downloads real frp binaries; loopback only

The integration test proves the whole chain works over loopback (real frp binaries, real mTLS, real xtcp-timeout-then-stcp-fallback), but it can't prove real NAT hole-punching across two separate networks — that's the one thing to manually check on your own machines after this lands.

Layout

src/frp_jump/
  common/     PKI (private CA), opaque tokens, settings, DB models
  driver/     TunnelDriver/RelayDriver abstraction; driver/frp/ = the frp
              implementation (config rendering, binary download+checksum,
              process supervision)
  server/     control-plane: registry (CRUD), auth (magic links), the
              agent-facing API, the WebUI, bootstrap (`server init`)
  agent/      runs on every device: enroll, the sync loop, ssh_config
              management
  cli/        `frp-jump server ...` / `frp-jump client ...`

Contributing

See CONTRIBUTING.md. Changes are tracked in CHANGELOG.md.

License

MIT

Release files for frp-jump 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for frp-jump 0.1.1
File Size Uploaded
frp_jump-0.1.1.tar.gz 36.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for frp-jump 0.1.1
File Interpreter ABI Platform
frp_jump-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 84.9 kB

Release files / frp_jump-0.1.1.tar.gz

Download URL frp_jump-0.1.1.tar.gz
Size 36.2 kB
Tags Source
SHA-256 checksum
How to use checksums
597663300ec7670ea4bda35d4ee0183c4c2c82207c24ff89f1cad7f80e70bdb8
BLAKE2b-256 checksum
How to use checksums
9d43df86cdb73808e65f6c8f68c761ad0c8b6a9b7c416f1634f2f937a1db4cd7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / frp_jump-0.1.1-py3-none-any.whl

Download URL frp_jump-0.1.1-py3-none-any.whl
Size 48.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a08c6d8c5a5acd7bd3a8760f593156b969183872df22f99567a481bc20f1b8c7
BLAKE2b-256 checksum
How to use checksums
ce02bd52ee5a58f07d7781378f91cdf2018b7f18c42723b3e3c0edfa31de4bfb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.0

2 release files

This release

0.1.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page