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/RelayDriverinterface — seedocs/architecture.md. - P2P with relay fallback is frp's own
xtcp+fallbackTofeature: 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.mdfor what that does and doesn't cover. - UX goal: after setup,
ssh <name>andhttp://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
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)
| File | Size | Uploaded | |
|---|---|---|---|
| frp_jump-0.1.1.tar.gz | 36.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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