remotehost

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

The internet
Visitor
browser, webhook, API client
remotehost
HTTPS edge
myapp.t.onio.in
Gate: online, within plan, allowed
Your machine
rh
$ rh http 3000
connecting…tunnel active ✓
Your machine
Your app
localhost:3000
  1. rh opens an encrypted tunnel to remotehost, outbound on port 443. Nothing at home is opened.
  2. A visitor opens https://myapp.t.onio.in.
  3. The gate checks: is the tunnel online, within its plan, and is this visitor allowed?
  4. The request goes down the tunnel to rh.
  5. rh hands it to your app on localhost:3000.
  6. Your app answers. The response goes back up the tunnel.
  7. 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:

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
SystemWhat rh sets upStarts
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
macOSA launchd agent ~/Library/LaunchAgents/in.bitgeek.remotehost.<id>.plist with KeepAlive. Log: ~/.remotehost/logs/.At login
WindowsA 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.

LanguageInstallHow it runs the tunnel
Go 1.26+go get remotehost.onio.in/go/sdkThe frp client inside your process. Nothing else to install.
Node.js 18+npm install @bitgeek/remotehost publishing soonThe official frpc, downloaded once and checked (SHA-256). No dependencies.
Python 3.9+pip install bitgeek-remotehost publishing soonThe 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.

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:

Limits

FreeProTeam
Tunnels online at once1525
Reserved names0320
Bandwidth per period1 GB100 GB500 GB
Session length2 hoursUnlimitedUnlimited
Warning page for first-time visitorsYesNoNo
Basic auth and IP allowlistsNoYesYes
Custom domains015
Seats1110

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.

CallWhat it does
GET /meAccount, plan limits, usage, and the frp settings for your client.
GET /namesYour 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 /tunnelsOnline 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.