Docs
Start a tunnel in three steps, run it with your own frpc, or drive everything from the API or an AI assistant.
How a request flows
rhopens an encrypted tunnel to remotehost, outbound on port 443. Nothing at home is opened.- A visitor opens
https://myapp.t.onio.in. - The gate checks: is the tunnel online, within its plan, and is this visitor allowed?
- The request goes down the tunnel to
rh. rhhands it to your app onlocalhost:3000.- Your app answers. The response goes back up the tunnel.
- The visitor gets the page over HTTPS, with a valid certificate.
What each part does, and the full diagram, step by step: Flow.
Install
Linux and macOS:
$ curl -fsSL https://remotehost.onio.in/install.sh | sh Installed rh to ~/.local/bin/rh
The script puts rh in ~/.local/bin (or /usr/local/bin as root). Windows and manual downloads:
- rh-darwin-amd64
- rh-darwin-arm64
- rh-freebsd-amd64
- rh-linux-amd64
- rh-linux-arm
- rh-linux-arm64
- rh-linux-riscv64
- rh-windows-amd64.exe
- rh-windows-arm64.exe
Checksums: SHA256SUMS. On first run rh downloads the
official frp client frpc 0.71.1 from GitHub into
~/.remotehost/ and checks it against SHA256 sums built into rh.
rh commands
$ rh login <token> # token from the dashboard; saved in ~/.remotehost/config.json $ rh http 3000 # tunnel to http://127.0.0.1:3000 on a random name $ rh http 192.168.1.20:8123 # any host:port your machine can reach $ rh http 3000 --name myapp # a name you reserved (paid plans) $ rh service install 3000 # run in the background, restart when it stops $ rh status # account, plan and usage $ rh version
$ rh http 3000 remotehost: https://demo.t.onio.in → http://127.0.0.1:3000 Free plan: browsers see a short warning page first; sessions last 2 hours, then rh reconnects. Press Ctrl+C to stop.
rh http keeps running and reconnects by itself; to keep it running after you close the terminal, see
Run in the background. Stop it with Ctrl+C. A random name stays yours for 10 minutes
after the tunnel stops, so a restart keeps the same URL.
Run in the background
rh service install registers the tunnel with your system's own service manager. It starts at boot (or when you
log in) and starts again whenever it stops: after a crash, a reboot, or the network coming back.
$ rh service install 3000 # or: rh service install 3000 --name myapp Installed rh-3000 (/etc/systemd/system/rh-3000.service). $ rh service list 3000 active $ rh service status 3000 # is it running, restarts, last lines $ rh service logs 3000 # follow the log $ rh service uninstall 3000 # stop it and remove it
| System | What rh sets up | Starts |
|---|---|---|
| Linux (systemd) | A unit rh-<id>.service with Restart=always: for the whole system when run as root,
else in your user (~/.config/systemd/user). For a user unit, run sudo loginctl enable-linger $USER once so it
runs without a login session. | At boot |
| macOS | A launchd agent ~/Library/LaunchAgents/in.bitgeek.remotehost.<id>.plist with KeepAlive.
Log: ~/.remotehost/logs/. | At login |
| Windows | A scheduled task \remotehost\rh-<id> that restarts every minute after a failure, with no time limit.
Log: %USERPROFILE%\.remotehost\logs\. | At login |
The service runs rh http, which already keeps itself up: if frpc stops, rh starts it again (wait 1 s, doubling up
to 32 s), and if remotehost or your network cannot be reached yet, it waits and tries again. It stops only for errors that
waiting cannot fix, such as a revoked token. A random name stays the same across restarts, but changes if the service is
down for more than 10 minutes; a reserved name (--name, paid plans) never changes. Other init systems: run
rh http 3000 from your own supervisor (runit, s6, supervisord, Docker --restart=always).
Libraries: open a tunnel from your code
Start a tunnel from your own program with an API key: tests that need a public webhook URL, a dev server that shares
itself, a device that phones home. Create the key in the dashboard (Tokens) and pass it, or set
REMOTEHOST_API_KEY. No key yet? Leave it out: you get an anonymous tunnel and a link
to claim it. Each library returns once the tunnel is online, keeps it up (restarting the client if it stops)
and closes it when you ask. Each also has a Client for the REST API below.
| Language | Install | How it runs the tunnel |
|---|---|---|
| Go 1.26+ | go get remotehost.onio.in/go/sdk | The frp client inside your process. Nothing else to install. |
| Node.js 18+ | npm install @bitgeek/remotehost publishing soon | The official frpc, downloaded once and checked (SHA-256). No dependencies. |
| Python 3.9+ | pip install bitgeek-remotehost publishing soon | The official frpc, downloaded once and checked (SHA-256). Standard library only. |
Go
import remotehost "remotehost.onio.in/go/sdk" tun, err := remotehost.Forward(ctx, "3000", remotehost.Options{APIKey: os.Getenv("REMOTEHOST_API_KEY")}) if err != nil { log.Fatal(err) } defer tun.Close() fmt.Println(tun.URL) // https://k7m2xq9p.t.onio.in
Node.js
import { forward } from "@bitgeek/remotehost"; const tunnel = await forward(3000, { apiKey: process.env.REMOTEHOST_API_KEY }); console.log(tunnel.url); // https://k7m2xq9p.t.onio.in await tunnel.close();
Python
import remotehost with remotehost.forward(8000, api_key=os.environ["REMOTEHOST_API_KEY"]) as tunnel: print(tunnel.url) # https://k7m2xq9p.t.onio.in ... # the tunnel closes when the block ends
Options in every library: name (a reserved name, paid plans), reuse_name / reuseName
(keep last run's random URL if it is still yours), a start timeout, and a log callback. A refused tunnel (plan limit, paused
name, revoked key) fails at once with the server's reason; network trouble is retried.
Without an account: anonymous tunnels
rh http 3000 before rh login, or a library call with no API key, still opens a tunnel. It is anonymous:
1 tunnel, sessions of 60 minutes, 200 MB, the warning page for visitors, and it stops after 24 hours. It comes with a
claim link, printed when the tunnel starts:
$ rh http 3000 remotehost: https://x7k2m9qp.t.onio.in → http://127.0.0.1:3000 Anonymous tunnel (no rh login): 1 tunnel, 60-minute sessions, gone after 24 hours. Keep it: open https://remotehost.onio.in/claim/… and sign in.
Open the link, sign in with your email (new here: that creates your account), and claim it. The tunnel, its URL and its
key move into your account, and the program running it keeps going. Anyone with the link can claim it until someone does,
so share it only with people you trust. Libraries: tunnel.ClaimURL (Go), tunnel.claimUrl (Node),
tunnel.claim_url (Python); pass Quiet / quiet to not print the notice. The key is kept in
~/.remotehost/anonymous.json and reused until it expires. Background services (rh service) need an account.
Plain frpc
remotehost is a standard frp server. If you already run frpc (0.52 or newer), get a
name with the API (POST /api/v1/names) or from rh http, then use this config:
serverAddr = "connect.t.onio.in" serverPort = 443 auth.token = "remotehost" # the same for everyone user = "<your account id>" metadatas.token = "<your rh_ token>" # this one signs you in transport.protocol = "wss" transport.heartbeatInterval = 30 transport.heartbeatTimeout = 90 loginFailExit = false [[proxies]] name = "web" type = "http" localIP = "127.0.0.1" localPort = 3000 subdomain = "<your name>"
$ frpc -c frpc.toml
auth.token is the same for everyone; your rh_ token is what signs you in. Only type = "http"
works for now. The dashboard shows your account id; GET /api/v1/client-config?name=<name>&local=3000 returns this file filled in.
Names
Every tunnel is https://<name>.t.onio.in.
- Random names (all plans): 8 characters, given to you by
rh httpor the API. Yours while the tunnel is online and for 10 minutes after. - Reserved names (Pro 3, Team 20): 3 to 40 characters: lowercase letters, digits and single hyphens. Yours until you release them.
Access rules
Paid plans can protect a name with basic auth (user + password) and an IP allowlist (IPs or CIDR ranges). Set them in the
dashboard or with PUT /api/v1/names/<name>/rules. remotehost checks them before a request reaches your machine.
Warning page (Free)
On Free, the first time a browser opens your tunnel it sees a short page saying the site runs through remotehost, with a Continue button (remembered for 7 days). This keeps phishing off t.onio.in. It never shows for:
- requests that are not a browser page load (API calls, webhooks,
curl, fetch withouttext/html); - requests with the header
remotehost-skip-warning: 1; - paid plans.
Limits
| Free | Pro | Team | |
|---|---|---|---|
| Tunnels online at once | 1 | 5 | 25 |
| Reserved names | 0 | 3 | 20 |
| Bandwidth per period | 1 GB | 100 GB | 500 GB |
| Session length | 2 hours | Unlimited | Unlimited |
| Warning page for first-time visitors | Yes | No | No |
| Basic auth and IP allowlists | No | Yes | Yes |
| Custom domains | 0 | 1 | 5 |
| Seats | 1 | 1 | 10 |
Bandwidth counts bytes in and out. When it runs out, new tunnels are refused and visitors see a notice until the period resets. Free periods are calendar months (UTC).
API
Base URL https://remotehost.onio.in/api/v1. Send Authorization: Bearer rh_…. Errors are
{"detail": "…"} with a 4xx status.
| Call | What it does |
|---|---|
GET /me | Account, plan limits, usage, and the frp settings for your client. |
GET /names | Your names with their access rules. |
POST /names {"name"?} | Get a name to start a tunnel on: one of yours, else a new random one. |
POST /names/reserve {"name"} | Reserve a name (paid). |
PUT /names/<name>/rules | {"basic_user", "basic_password", "allow_cidrs": []}; null turns one off. |
DELETE /names/<name> | Release a name. |
GET /tunnels | Online tunnels and those of the last 24 hours, with traffic. |
GET /client-config?name=&local= | An frpc.toml with your details filled in. |
GET /tokens, POST /tokens, DELETE /tokens/<id> | API tokens. A new token is shown once. |
$ curl -s -X POST -H "Authorization: Bearer $RH_TOKEN" https://remotehost.onio.in/api/v1/names {"name": "k7m2xq9p", "url": "https://k7m2xq9p.t.onio.in", "kind": "random", ...}
MCP
MCP server (Streamable HTTP, JSON responses) at https://remotehost.onio.in/mcp with Authorization: Bearer rh_….
Tools: account_status, list_names, get_name, reserve_name, release_name,
set_access_rules, list_tunnels, client_config, list_tokens,
create_token, revoke_token. Claude Code:
$ claude mcp add --transport http remotehost https://remotehost.onio.in/mcp --header "Authorization: Bearer $RH_TOKEN"
About
remotehost runs the frp server and uses the unmodified frp client, both under the Apache License 2.0. The frp project does not endorse remotehost. Report abuse at /report.