# synous.dev Synous gives a local service a temporary public HTTPS URL through OpenSSH. You don’t need to install a Synous client. - [Use Synous](https://synous.dev/docs) - [Read the privacy policy](https://synous.dev/docs/privacy) - [View the source](https://github.com/ahimsalabs/synous.dev) The homepage, usage guide, and privacy policy return Markdown when a request includes `Accept: text/markdown`. ## Create a public URL 1. Start an HTTP service on your computer. This example uses `localhost:8080`. 2. Run this command: ```sh ssh -T -R 80:localhost:8080 t.synous.dev ``` 3. If SSH prints a short-lived authorization link, give it to the user who owns the tunnel. An unknown offered key can be approved Once, for 24 hours, for 30 days, or Forever. If no public key was offered, the live connection can only be allowed once. Either kind can be denied. 4. Open the printed public URL. Keep SSH connected while you use the route. ```text Public: https://otter-lake.synous.dev Press Ctrl-C to stop. ``` ## Authentication - Every tunnel is associated with a GitHub account. - A previously approved SSH key authenticates as its associated account until its approval expires or is revoked. - An unknown key enters a restricted pending state. It cannot claim a public route until the user approves the browser authorization. - The Once choice applies only to the waiting SSH connection. The 24 hours, 30 days, and Forever choices save the key for later connections. - When no key is offered, Synous uses a zero-question keyboard-interactive browser approval during SSH authentication. No route exists before approval. The approval is bound to that live connection, cannot be saved or transferred, and a reconnect requires a new approval. - A key already registered to GitHub can authenticate without browser approval when the SSH username is the GitHub username: `GITHUB_USERNAME@t.synous.dev`. - Unattended keyless automation can use a single-use `syn_...` credential issued from the signed-in dashboard as the SSH username. This is a bearer secret. It expires after 10 minutes and is consumed when SSH connects. - Synous does not support password authentication. Keyboard-interactive authentication is limited to zero-question browser approval. - GitHub key lookup results can remain cached for up to five minutes. Removing a GitHub key doesn’t close an existing tunnel. To force the no-identity path: ```sh ssh -T -o PubkeyAuthentication=no \ -o PreferredAuthentications=keyboard-interactive \ -R 80:localhost:8080 t.synous.dev ``` To create an ephemeral Ed25519 identity, use it for one tunnel, and delete it when SSH exits: ```sh d=$(mktemp -d); trap 'rm -rf "$d"' EXIT; ssh-keygen -q -t ed25519 -N '' -f "$d/key"; ssh -T -i "$d/key" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new -R 80:localhost:8080 t.synous.dev ``` If OpenSSH offers the wrong key, select the key explicitly: ```sh ssh -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes -T \ -R 80:localhost:8080 GITHUB_USERNAME@t.synous.dev ``` Don’t use `-N`. Synous uses the SSH status session to print the URL, request lines, and service notices. When OpenSSH allocates a terminal, Synous shows an interactive view. Press `q` or Ctrl-C to disconnect. Without an allocated terminal, Synous uses line-oriented output. Pass options to the `connect` command, which runs by default: - `--name NAME` uses the stable hostname `NAME-.synous.dev`. `NAME` is 1 to 23 letters or digits. - `--new` draws a new hostname instead of resuming a held one. - `--private` allows only the route owner's GitHub account. - `--allow LOGIN,...` also allows the listed GitHub logins, implies `--private`, and is repeatable. - `--auth USER:PASSWORD` also allows one HTTP Basic credential and implies `--private`. - `--quiet` hides per-request lines from SSH output while retaining dashboard request records. ```sh ssh -T -R 80:localhost:8080 t.synous.dev connect --name api --private --quiet ``` Write `connect` before the options. The word stops the local ssh client from reading the options as its own, and `ssh -T -R 80:localhost:8080 t.synous.dev` alone still runs `connect`. Run `ssh t.synous.dev help` without a remote forward to list SSH session options. A client that opens no session, such as `ssh -N`, can’t pass options and receives a hostname that is visible only in the dashboard. The owner is always allowed on a private route. `--allow alice,bob` adds GitHub logins. `--auth demo:demo-password` adds one Basic username and password. The password appears in shell history and can appear in the process list. Use `--auth` for demos, not for secrets. ## Backend protocols Synous supports only remote-forward ports `80` and `443`. - Use `-R 80:localhost:8080` for a cleartext HTTP, WebSocket, or gRPC backend. - Use `-R 443:localhost:8443` for a TLS HTTP, secure WebSocket, or gRPC backend. Self-signed backend certificates work inside the authenticated SSH tunnel. Public clients use HTTPS. Synous supports HTTP/1.1, HTTP/2, streaming, server-sent events, WebSockets, protocol upgrades, and gRPC. ## Request data Synous forwards the public host and path to the local service. It sets standard `X-Forwarded-*` headers and these protected headers: - `X-Synous-Remote-IP` contains the public client IP address. - `X-Synous-Request-Start` contains the UTC request start time. - `X-Synous-Forwarded-Host` contains the public hostname. - `X-Synous-Forwarded-Proto` contains `https`. - `X-Synous-User` contains the GitHub login or Basic username for an authenticated private-route request. - `X-Synous-Auth` contains `github` or `basic` for an authenticated private-route request. Synous removes client-supplied `X-Synous-*` headers before it adds trusted values. ## Route lifetime and privacy - Each SSH connection gets one public route. - The route ends when SSH disconnects. Synous then holds the hostname for 10 minutes for the same GitHub account and SSH key fingerprint. - Reconnecting within 10 minutes resumes the hostname, and Synous prints `(resumed)` after the public URL. A connection without a key resumes only a hostname that was held without a key. - Use `--name` to give each tunnel a stable hostname if more than one hostname is held for the account. Without it, Synous draws a new hostname and lists the held ones. - `--new` draws a new hostname instead of resuming one. - `--name NAME` gives the stable hostname `NAME-.synous.dev`. No other account can receive it. A second connection with the same name is refused while the first one is active. - A hostname returns to the pool 10 minutes after its connection ends, and Synous might assign it to another account later. - Routes are public by default. A private route requires sign-in. - A browser that opens a private route sees a Sign in with GitHub page, then returns to the original route and path after sign-in. - A private-route session lasts for 12 hours or until the route ends. - Synous removes a Basic `Authorization` header that authenticates a request before proxying it. Other `Authorization` headers pass through unchanged. - Before proxying a public or private request, Synous removes every `__Host-synous-*` cookie. Other application cookies pass through unchanged. - For a valid backend `Set-Cookie` response whose `Domain` matches the public route, Synous removes `Domain`, making the cookie host-only to that exact route hostname. It drops malformed, unrelated-domain, and reserved-name cookies. - Cookie signatures prevent changes to signed session contents; they do not encrypt the contents or prevent replay of a copied, unexpired cookie. - Signing out of the dashboard does not sign the browser out of private routes. - HTTP response rewriting cannot intercept `document.cookie`. While routes share `synous.dev`, route applications must not set `Domain=synous.dev`. - Generated hostnames can be reassigned after their 10-minute hold, so retained application cookies or browser storage can reach a later tunnel on that exact hostname. - Synous reserves `/.synous-basic` and the `/.synous/` path prefix for sign-in on private routes. A public route receives these paths unchanged. - Synous keeps up to 100 completed request records for each active route. A record includes the method, URI including the query string, client IP address, status, duration, and byte counts. It doesn’t include headers or bodies. - Live route data clears when the route ends. ## Errors - `404` means that the route isn’t active. - `502` means that the local service didn’t accept the connection. - `503` means that Synous is temporarily at capacity. Retry after the interval in `Retry-After`. - `504` means that the local forwarding stream timed out. A route supports 16 active requests by default. A long-lived stream or WebSocket uses one slot until it closes.