Skip to content
Login

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

When you register — with a password or with SSO — HLE records how you arrived: the UTM source, medium and campaign, the referring site, the landing page, and your optional answer to the How did you hear about HLE? select. It is stored once against your account and used only in aggregate to understand which channels bring people here. The website reads these values from the URL; a 30-day first-party hle_attr cookie is set only if you accept analytics cookies, and never otherwise. Account deletion removes the record. See the Privacy Policy.

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

  • Format: hle_ prefix + 32 hex characters (36 chars total)
  • Agents: an agent’s credential uses the same key format, scoped to tunnels. It can only publish the endpoints declared for that agent (or a label nobody else uses), cannot take over another agent’s or a CLI tunnel, and is refused by firepuncher. Deleting the agent revokes its key.
  • Admin keys: admin API routes accept only keys with the admin scope; ordinary account keys get 403 there. An admin creates one in the dashboard (Create key → Admin key). Admin keys expire after 30 days by default (90 at most) and can be limited to an IP allowlist.
  • 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. Only traffic relayed through HLE counts, in both directions — LAN traffic never touches the meter.
  • Created in: Dashboard > Keys
  • Visible once: The raw key is only shown once on creation — save it immediately
  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>

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

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

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.

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)

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.

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 tunnel basic-auth set ha # prompts for username & password
hle tunnel basic-auth status ha # show whether active
hle tunnel basic-auth remove ha # remove credentials

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

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 tunnel create 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.

The CLI commands under hle tunnel 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 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

The OAuth handshake stores its CSRF state in a signed session cookie — SameSite=Lax, Secure in production, with a 14-day max_age. That is deliberately far longer than the seconds-to-minutes a Google or GitHub round-trip takes, so a slow or parked consent screen cannot expire the state before the callback lands.

When you sign in with SSO and the provider’s email matches an existing password account, the pending link recorded for the Link your account? consent page is only valid for 10 minutes. Cancel drops it immediately. An abandoned consent therefore cannot be completed later from the same browser. The same pending link is dropped when you log out or sign in with your password.

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

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