Skip to content
Login

Authentication

Dashboard authentication

Users log into hle.world/dashboard using one of these methods:

  • Email & password: Register a new account with email and password
  • GitHub SSO: OAuth 2.0 via GitHub
  • Google SSO: OIDC via Google

API keys

API keys authenticate the client tunnel to the relay server. Key details:

  • Format: hle_ prefix + 32 hex characters (36 chars total)
  • Stored as: SHA-256 hash in the database (plaintext never stored)
  • Per-key tunnel limit: Each key has a max_tunnels setting (unlimited for all tiers). All users receive 2 GB of bandwidth free per calendar month; PAYG users pay from their credit balance beyond that.
  • Created in: Dashboard > Keys
  • Visible once: The raw key is only shown once on creation — save it immediately

Tunnel authentication flow

  1. Client sends TUNNEL_REGISTER message with api_key
  2. Server hashes the key and looks up ApiKeyRecord
  3. Server validates:
    • Key exists and is active
    • Tunnel count does not exceed max_tunnels
  4. Server derives user_code from key owner’s UserRecord (3-char alphanumeric)
  5. Tunnel is registered with format: <service_label>-<user_code>.<base_domain>

Subdomain routing

Each tunnel gets a unique public URL:

<service_label>-<user_code>.hle.world

Example: If your user code is x7k and label is ha, the URL is https://ha-x7k.hle.world

  • user_code: Server-allocated 3-char alphanumeric per user, derived from API key owner
  • service_label: User-chosen name (e.g., “ha”, “jellyfin”, “app1”), auto-generated if omitted

Tunnel access control

The gate evaluates rules in a fixed order and the first match decides. That order is shown in the dashboard rather than being editable, because it comes from the relay:

  1. Public override (--auth none) — nothing else is checked
  2. Basic Auth, if set — checked before any cookie, with the address allow-list able to skip it
  3. A valid gate cookie — share link, allow-listed email, or PIN
  4. The address allow-list
  5. Otherwise: the gate page, or a refusal

Refuse unmatched visitors

By default, a visitor who matches no rule is refused. This is an account-level setting on the Access Control tab.

Turning it off restores the relay’s older behaviour, in which an endpoint with no sign-in rule and no PIN was reachable by anyone who had the address — including one whose owner had set up an address allow-list and nothing else, because step 3 above returned before the address was ever consulted.

Webhook endpoints are exempt: they cannot use the sign-in gate, so refusing would leave them permanently unreachable.

Email allow-list

For each tunnel, the owner can create an email allow-list with provider selection from the tunnel’s Access Control tab in the dashboard. Provider options:

  • any: Allow any auth method (native email/password, GitHub, Google)
  • github: Require GitHub SSO
  • google: Require Google SSO
  • hle: Require native HLE email/password login

When a user accesses the tunnel gate page, only the allowed auth methods are displayed:

  • provider: "github" — Only GitHub button shown
  • provider: "any" — All buttons (email form, GitHub, Google)

PIN protection

In addition to the email allow-list, any tunnel can be protected with a PIN. Visitors who pass the SSO gate are then prompted for the PIN before being granted access. PIN protection is available on all plans and is configured from the tunnel’s Access Control tab in the dashboard.

HTTP Basic Auth

Any tunnel can be protected with standard HTTP Basic Auth — a username and password enforced by the relay before the request ever reaches your local service. This works natively with browsers (which show a credentials dialog), curl, and any HTTP client that understands Authorization: Basic.

Configure it from the tunnel’s Access Control tab in the dashboard, or via the CLI:

Terminal window
hle config basic-auth set ha # prompts for username & password
hle config basic-auth status ha # show whether active
hle config basic-auth remove ha # remove credentials

Credentials are stored as PBKDF2-SHA256 hashes. The password is never returned in plaintext.

Upstream Basic Auth

Separately, you can inject Basic Auth credentials into every request forwarded to your local service. This is useful when the local service itself requires authentication (e.g., Home Assistant, Grafana, or Jupyter):

Terminal window
hle expose --service http://localhost:8123 --upstream-basic-auth admin:yourpassword

This injects an Authorization: Basic header on every proxied request — independent of the tunnel-level Basic Auth above.

Share links let you grant temporary access to a tunnel without adding someone to the permanent allow-list. Each share link embeds a signed token that is validated at the gate.

They are not a paid feature — the same limits apply on every plan:

  • Any number of active links per endpoint
  • Any duration up to 7 days, set in hours when you create the link
  • Optionally limited to a maximum number of uses

The 7-day ceiling is a security bound rather than a plan boundary: anyone holding the URL gets in, so a link that never expires becomes a credential nobody remembers issuing.

Share links appear as a rule in the tunnel’s Access Control list, alongside sign-in rules, the PIN and the address list, with the link itself on the row and its own access log.

CLI authentication

The CLI commands under hle config authenticate against the REST API using your API key sent as an Authorization: Bearer header. The key is resolved in this order:

  1. --api-key command-line flag
  2. HLE_API_KEY environment variable
  3. ~/.config/hle/config.toml config file

The same API key used for tunnel connections also authenticates REST API calls for managing access rules and listing tunnels. The dashboard uses JWT cookies for the same endpoints — both auth methods are accepted.

JWT tokens

JWT tokens are issued by the server and stored in HTTP-only, SameSite=Lax cookies. The secure flag is set automatically — cookies are marked secure in production (HTTPS) and plain in local development (HTTP).

  • Dashboard JWT (hle_access_token): 24h expiry, used for all /api/* dashboard endpoints
  • Tunnel access JWT (hle_tunnel_token): 24h expiry, issued after a visitor passes the SSO gate page, scoped to the specific tunnel subdomain

Password security

Passwords are hashed using PBKDF2-SHA256 with 600,000 iterations. This is OWASP-recommended and resistant to modern GPU attacks.

Rate limiting

Authentication endpoints are rate-limited to prevent brute force attacks. The limit is applied per IP address across the hle.world hosted service.