Documentation for Jetty

Installation

The fastest way to install Jetty is the global install script. It installs the jetty CLI so it works from any directory. You need a server name and token from Bridge (see Getting started in the app for copy-paste lines for this deployment).

Ingress (tunnel entry on your network) runs as a separate process — jetty-edge (Go binary from cli-v* GitHub Release assets jetty-cli-*, or build from this repo / Docker), not the same as the PHP jetty developer command. Operators deploy it behind TLS.

After install, configure the client, then share a local site—see Sharing local sites for port vs --site (local upstream) examples.


Run the install script. It downloads the PHAR and places it on your PATH (PHP 8.2+, curl, zlib):

curl -fsSL "https://usejetty.online/install/jetty.sh" | bash

The PHAR is installed as jetty under ~/.local/bin (or /usr/local/bin with --sudo). The script can append the right bin directory to your ~/.zshrc or ~/.bashrc. Then reload your shell so PATH is picked up:

exec "$SHELL" -l

Prefer this path. The PHAR is self-contained — it needs no Composer, and jetty update replaces the binary in place. If you already run Composer, see Install with cpx below.

Configure and share:

jetty config set server your-server-name
jetty config set token your-token-here
jetty share 8000

The client registers tunnels, connects the WebSocket agent to ingress (JETTY_EDGE_WS_URL on Bridge), and forwards HTTP to your local port. Use --skip-edge only for registration + heartbeats without forwarding.


Install with cpx

If you already work in PHP, cpx runs the CLI straight from Packagist without installing it into any project — the Composer equivalent of npx. Install cpx once:

composer global require cpx/cpx

Then run Jetty through it:

cpx jetty/client share 8000

jetty/client ships a single binary, so you can omit the command name — cpx jetty/client and cpx jetty/client share 8000 both resolve to jetty. Every argument after the package name passes straight through, so cpx jetty/client login, cpx jetty/client config set server your-server-name, and cpx jetty/client share 8000 --subdomain=demo all behave exactly like the PHAR.

Pin a version with a Composer constraint when you need reproducibility:

cpx jetty/client:^0.1 share 8000

Requirements and trade-offs versus the PHAR:

install.sh (PHAR) cpx
PHP 8.2+ 8.3+ (cpx's own floor)
Composer not needed required, installed globally
Updates jetty update, in place cpx re-resolves the package
Best for any machine, CI, non-PHP projects PHP developers already running Composer

Note the PHP floor: the Jetty client itself supports 8.2, but cpx requires 8.3, so that is the effective minimum on this path.

Because cpx runs the CLI outside a PHAR, jetty update does not apply — cpx owns the version. Use cpx's own resolution (or a version constraint) to move between releases, and run jetty doctor if you end up with both a PHAR and a cpx copy on your PATH.

Not the same as composer require jetty/client. Requiring the package adds a library to one project. cpx installs nothing into your project and gives you the CLI globally. For the library case, see the next section.


Embedding jetty/client in a Laravel app (optional, project-local)

If you want the package available in your app (e.g. to call the API client from PHP), install it as a dev dependency. This is not a way to install the CLI — it just adds the library:

composer require --dev jetty/client

The jetty install-client command does the same from inside a project. Use the system jetty binary (PHAR) for jetty share, jetty update, etc. — not the project-local vendor/bin/jetty.


Project configuration (jetty.config.json)

Pin per-project settings so jetty share doesn't need flags every time. Drop a jetty.config.json at the project root (the CLI walks up from cwd):

{
  "share": {
    "hostname": "tunnel.example.com",
    "local_host": "127.0.0.1",
    "local_port": 3000,
    "health_path": "/healthz",
    "routing_rules": [
      {"path": "/api", "target": "8080"},
      {"path": "/static", "target": "static-server:9000"}
    ]
  }
}
Key What it sets CLI override
share.hostname Custom domain (verified under Domains). Wins over share.subdomain if both are set. --hostname=
share.subdomain Reserved label on the shared tunnel host. --subdomain=
share.local_host Upstream host (default 127.0.0.1). --site=, --host=
share.local_port Upstream port (default 8000 / auto-detect). positional port arg
share.health_path Health-check path the agent probes. --health-path=
share.print_url_only Print the tunnel URL and exit. --print-url-only
share.routing_rules Path-prefix → upstream rules. Same shape as --route. --route= (replaces)

CLI flags always win. Environment variables (e.g. JETTY_SHARE_DELETE_ON_EXIT=1) also take precedence over project config.


Ingress binary (jetty-edge)

For operator-managed edge hosts, use the jetty-cli-* archives from your team’s GitHub cli-v* releases (contains jetty-edge only), build from the repository, or use cli/Dockerfile.

cd cli && go build -trimpath -ldflags="-s -w" -o jetty-edge ./cmd/jetty-edge

Use scripts/install-jetty-edge.sh (repo root) for systemd on a server (downloads from releases by default, or --bin). Run it with environment aligned to Bridge (JETTY_EDGE_SECRET, JETTY_LARAVEL_URL, JETTY_TUNNEL_HOST, listener address). On Debian/Ubuntu it installs Certbot and one apt plugin per JETTY_CERTBOT_DNS_PROVIDER in the env (nginx default; cloudflare, digitalocean, or all) when scripts/certbot-deps.inc.sh is present (same idea as scripts/jetty-edge-ssl.sh ensure-deps). It does not replace the PHP jetty CLI on developer machines — it terminates public HTTP and WebSockets from jetty share agents.


Docker (optional)

The repo includes cli/Dockerfile for building an image that runs jetty-edge (ingress). For developer machines, install the jetty CLI via the script above (PHAR).

docker build -f cli/Dockerfile -t jetty-edge ./cli

To reach a service on your host from inside the container, pass host.docker.internal with jetty share as --site=host.docker.internal (Docker Desktop). On Linux add --add-host=host.docker.internal:host-gateway.


Updates

jetty update (alias self-update) replaces the PHAR at ~/.local/bin/jetty in place. Override the path with JETTY_PHAR_PATH if you installed somewhere else. Set JETTY_CLI_GITHUB_REPO=owner/repo and optionally JETTY_PHAR_GITHUB_TOKEN for private repos.

If you have a Composer-installed copy somewhere (vendor/bin/jetty, ~/.composer/vendor/bin/jetty), that's a project library install — jetty update will not touch it. Reinstall via the install script to get a current PHAR.

Running through cpx? jetty update does not apply there either — cpx resolves the package version itself. Pin or move versions with a constraint (cpx jetty/client:^0.1) instead.


PHAR Checksum Verification

Every PHAR release includes a SHA256 checksum file (jetty.phar.sha256) published alongside the binary in GitHub Releases.

Automatic verification: When you run jetty update, the CLI downloads the .sha256 file and verifies the PHAR integrity automatically. If the checksum does not match, the update is aborted.

Manual verification: If you download the PHAR directly (e.g., from a release page or mirror), verify it yourself:

# Download both files
curl -fsSLO https://github.com/your-org/jetty/releases/latest/download/jetty.phar
curl -fsSLO https://github.com/your-org/jetty/releases/latest/download/jetty.phar.sha256

# Verify the checksum
sha256sum -c jetty.phar.sha256

A passing check prints jetty.phar: OK. If the output shows FAILED, do not use the binary -- re-download or contact your team.


Next steps

  1. Open Bridge and go to Getting started for platform-specific downloads, env snippets, and jetty share examples wired to this deployment.
  2. Read Sharing local sites for --site, reserved labels, and local HTTPS (Valet) notes.

Send feedback

Found an issue or have a suggestion? Let us know.