Jean logoJean

Web Access and Headless

Every Jean instance can expose the full UI over HTTP/WebSocket - desktop or headless server - with token auth and Tailscale-friendly binds.

Every Jean instance can run Web Access: an embedded HTTP + WebSocket server that serves the same UI in a browser.

That applies to both:

ModeHow you get Web Access
Native desktop (macOS / Windows / Linux)Preferences → Web Access - enable the HTTP server, set port/bind address, copy the token URL
Headless server (jean-server)Always on - the process is the Web Access endpoint

Use it to open Jean from another machine on your LAN, a phone or tablet browser, or a remote host. Token authentication is on by default; keep it enabled for any non-localhost bind.

What web access provides

The HTTP server serves the frontend and keeps app state connected over WebSocket.

In practice, that means you can:

  • open Jean in a browser on the same machine or across the network
  • keep using the same projects, worktrees, and sessions as the host instance
  • copy a tokenized URL for quick access
  • connect the native desktop app to a remote Jean instance (see Connect from the native app)

Desktop app (native)

  1. Open Preferences → Web Access
  2. Enable HTTP server (optionally turn on Auto-start)
  3. Set Port (default 3456) and Bind address
  4. Open the shown URL (includes ?token=...) in a browser, or share it with devices that can reach that host

Jean can list likely bind host options for the machine so you do not have to guess the address manually.

What you can configure

From Preferences → Web Access:

  • whether the server is running
  • port
  • bind host
  • auto-start on launch
  • whether a token is required
  • token regeneration

Default behavior

The safe baseline on desktop is:

  • HTTP server disabled
  • localhost-only binding
  • token required

That is the right place to start unless you have a clear reason to expose Jean more broadly.

Web Access binds a normal TCP port (default 3456). For access beyond the local machine, prefer a private mesh VPN rather than exposing the port to the public internet:

  • Tailscale (recommended) - bind Jean to the Tailscale IP (or use the installer's --host tailscale preset) and open the URL from any device on your tailnet
  • Other options: WireGuard, ZeroTier, SSH tunnel, or a reverse proxy with TLS in front of 127.0.0.1

Keep token auth enabled, use a long random token (openssl rand -base64 32), and avoid binding 0.0.0.0 on untrusted networks unless you also terminate TLS and restrict who can reach the port.

Headless server (jean-server)

jean-server is a standalone Linux binary: Tokio/Axum only - no Tauri, WebView, GTK, or display server. Linux amd64 and arm64 (glibc + OpenSSL 3; Ubuntu 22.04+ / Debian 12+ recommended).

jean-server --host 127.0.0.1 --port 3456
# or with an explicit token:
jean-server --host 127.0.0.1 --port 3456 --token "$JEAN_TOKEN"

When it starts, Jean prints the browser URL. If token auth is enabled, the URL includes the token as a query parameter.

Options and environment variables

CLI optionEnvironment variableDefault
--host <addr>JEAN_HOST127.0.0.1 (or installer choice)
--port <port>JEAN_PORT3456
--token <token>JEAN_TOKENsaved or generated token
--no-tokenJEAN_NO_TOKEN=1off
--allow-unsafe-no-tokenJEAN_ALLOW_UNSAFE_NO_TOKEN=1off
n/aJEAN_ALLOWED_ORIGINSsame-origin + native Jean clients

--host accepts a concrete address or presets such as localhost, all / 0.0.0.0, lan, and tailscale (auto-detect Tailscale IPv4). Jean refuses --no-token with a public bind unless you also pass --allow-unsafe-no-token. Prefer leaving token auth on.

Install (release binary + systemd)

Interactive install (prompts for bind interface + port when a TTY is available):

curl -fsSL https://raw.githubusercontent.com/coollabsio/jean/main/scripts/install-jean-server.sh | sudo bash

Non-interactive (defaults to 127.0.0.1:3456, or pass --host / --port):

curl -fsSL https://raw.githubusercontent.com/coollabsio/jean/main/scripts/install-jean-server.sh | sudo bash -s -- -y

Common options:

# Public bind with an explicit token
sudo ./scripts/install-jean-server.sh \
  --host 0.0.0.0 \
  --port 3456 \
  --token "$(openssl rand -base64 32)" \
  -y

# Tailscale-only bind (auto-detect Tailscale IPv4) - recommended for remote use
sudo ./scripts/install-jean-server.sh --host tailscale -y

# Current user only (user systemd unit)
./scripts/install-jean-server.sh --user-install --host 127.0.0.1 -y

The installer downloads the latest release, installs the binary, writes an env file (host/port/token), and registers a systemd service. Re-run to upgrade; existing tokens are preserved unless you pass --token.

Docker image

Jean publishes a server image:

ghcr.io/coollabsio/jean-server:<tag>

Bind to 0.0.0.0 inside the container, keep token auth enabled, and mount Jean's app-data directory so projects, preferences, and sessions persist.

docker run --rm \
  -e JEAN_HOST=0.0.0.0 \
  -e JEAN_PORT=3456 \
  -e JEAN_TOKEN=change-me-long-random-token \
  -p 127.0.0.1:3456:3456 \
  -v jean-data:/home/jean/.local/share/com.jean.desktop \
  ghcr.io/coollabsio/jean-server:latest

Keep the host-side port bind on 127.0.0.1 when you are putting Caddy, Nginx, Tailscale, or an SSH tunnel in front of Jean.

Health checks

Use these endpoints for process checks and deployment probes:

  • GET /healthz - process is alive
  • GET /readyz - HTTP server and WebSocket broadcaster are initialized

Authenticated API endpoints accept either ?token=... or an HTTP bearer token:

curl -H "Authorization: Bearer $JEAN_TOKEN" http://127.0.0.1:3456/api/auth
curl "http://127.0.0.1:3456/api/init?token=$JEAN_TOKEN"

Reverse proxy and Tailscale

Reverse proxies do not need to rewrite paths. The browser UI uses /api/init, /api/auth, and /ws from the same origin.

A minimal Caddy route looks like this:

jean.example.com {
  encode zstd gzip
  reverse_proxy 127.0.0.1:3456
}

For Tailscale-only access, bind directly to your Tailscale IP and keep token auth enabled:

jean-server --host 100.x.y.z --port 3456 --token "$JEAN_TOKEN"
# or:
sudo ./scripts/install-jean-server.sh --host tailscale -y

Connect from the native app

In the desktop app, click the server icon in the title bar, choose Add remote, and enter either the full Web Access URL (including ?token=...) or the server URL and token separately.

Selecting the remote switches the entire Jean backend while keeping the desktop app's shell (menus, native controls). Commands and events for the selected instance travel over HTTP/WebSocket. Select Local from the same dialog (or the command palette) to return to the desktop app's local backend.

Native Jean client origins are allowed automatically. HTTP and HTTPS server URLs are both supported; keep token authentication enabled on remote servers.

Server updates (bare-metal Linux)

jean-server can install a newer binary when you choose to from Web Access or a connected native client. Nothing is installed in the background - apply only runs after you confirm Update & restart.

PieceBehavior
Manifestserver-latest.json on the latest GitHub release
CheckWeb Access / remote client shows an update offer if newer
ApplyUser confirms → download, SHA-256 verify, restart service or re-exec
ContainersNot supported - update the Docker/GHCR image instead
Active sessionsApply is refused while chat sessions are running

Security recommendations

  • Prefer 127.0.0.1 behind Caddy/Nginx, SSH tunnel, or Tailscale
  • Keep token auth enabled for every non-localhost bind
  • Use a long random token, for example openssl rand -base64 32
  • Set JEAN_ALLOWED_ORIGINS=https://jean.example.com only when you need additional cross-origin browser access; native Jean client origins are allowed by default

Use this when

Web access and headless mode make sense when:

  • Jean runs on a dedicated development machine or server
  • you want browser access to long-running work
  • you need to check session state without opening the desktop window on that host
  • you want one powerful host and lighter clients (browser or native remote)

Next reads

On this page