Run and test
URLs, routes and shares
Where a forwarded service answers on this machine, its browser route through the local proxy, and an opt-in remote share.
Reviewed 2026-10-16
This page covers three ways to reach a service: its local port, a browser route
through the local proxy, and a remote share. The chalupa urls, open and proxy
commands are in the CLI command reference. The chalupa share
and chalupa share revoke actions are in
Actions and confirmations.
Local routes
With tunnel.local: leased every forwarded port is a number in 40000-48999.
For a browser, chalupa proxy start gives each web service a name instead:
chalupa proxy start
chalupa urls --config chalupa.yml # a ROUTE column appears
chalupa open web --route # http://web.harbor.localhost:7355/
The proxy is one background process per machine, listening on 127.0.0.1 and
[::1] only, on port 7355 (CHALUPA_PROXY_PORT changes it; a busy port is
refused). It routes by the Host header (<service>.<environment>.localhost)
to that environment's leased port, passes HTTP/1.1, WebSocket upgrades and
streamed responses (SSE) through unchanged, keeps the Host the browser sent
(so an application that builds callbacks or CORS rules from it sees the route
name) and adds X-Forwarded-Host, X-Forwarded-Proto and X-Forwarded-For.
CHALUPA_PROXY_HOST_HEADER=target sends 127.0.0.1:<port> as Host instead.
- It never opens SSH. It only connects to
127.0.0.1:<leased port>; the lazy tunnel opens the SSH connection on that first connection and closes it after the idle grace, so using the proxy does not keep the server awake any longer than the requests themselves. - A route whose tunnel is not running answers
502and nameschalupa tunnel keep --config …. An unknown name answers a404page that lists the routes. - Only environments with
tunnel.local: leasedthat were tunnelled or listed withchalupa urlsat least once have routes (the proxy finds the config from the lease). Names are lowercased and_becomes-; a name that is still not a valid hostname label, or two names that become the same label, get no route (chalupa proxy statussays which). - A web page on another site cannot write through the proxy, or read from it.
A WebSocket upgrade, or any request other than
GET,HEADandOPTIONS, is refused with403when the browser sendsSec-Fetch-Site: cross-siteor anOriginoutside the route's own environment (the route's host, or a sibling such asapi.<environment>.localhost). A dev server onlocalhost:3000or a page of another environment cannot drive a route either. AGET,HEADorOPTIONSthat the browser marks cross-site is refused too, unless it is a top-level navigation (Sec-Fetch-Mode: navigate), so a public page cannotfetch()a local API through its predictable name. (CORS stops a foreign page reading an answer, not sending the request, and a WebSocket has no CORS.)curland other clients that send none of these headers are unaffected.CHALUPA_PROXY_ALLOW_CROSS_SITE=1(set when you runchalupa proxy start) turns the checks off; do that only if you accept that any site you visit can then reach every route while the proxy runs. - The proxy forwards one request per connection and refuses what it cannot
frame safely: a request body goes through only as far as its
Content-Lengthor its lastchunkedchunk (other transfer codings answer501), and an upgrade is passed on only once the service answers101. - The background process starts with a minimal environment (
PATH,HOME,CHALUPA_HOME,CHALUPA_PROXY_*, locale and temp variables), not a copy of yours, so provider tokens in your shell do not reach it. If a Compose file's${VAR}interpolation decides a published port, list the variable inCHALUPA_PROXY_PASS_ENV(comma separated) when you start the proxy. - Agents should keep using
127.0.0.1:<port>fromchalupa urls --json: a route changes theHostandOriginthe application sees. - It replaces the optional portless
alias for people.
chalupa execandchalupa launchstill publish theirchalupa-<environment>-ollama.localhostportless alias when portless is installed.
Shares
A share is a remote address for one web service of a running environment,
https://<service>--<environment>.box.chalupa.run, for when a teammate or an
agent elsewhere needs it. It is off unless the console you are linked to has
shares enabled, and nothing is ever shared automatically: the service must
declare expose.<service>.share, and you run:
chalupa share gateway --config chalupa.yml # until the declared ttlMinutes (default 60)
chalupa share gateway --ttl 240 --config chalupa.yml
chalupa share ls --config chalupa.yml
chalupa share revoke gateway --config chalupa.yml # or --all
chalupa share needs the account that chalupa setup linked (the account of
the console in cloud.url). The console creates the share and a credential that can serve
only that hostname; Chalupa installs a pinned, checksum-verified ngrok agent on
the box over SSH and starts it. The agent only dials out; no port opens on the
box. Shares end at their expiry, when you revoke them, and when the box sinks or
is reaped.
- People open a
consoleshare from the environment's Connect card (the Open button) or withchalupa open <service> --remote: the console signs them in with a short token kept in a cookie for that hostname only. - Agents get a token valid for at most an hour with
chalupa share token <service> --config chalupa.ymland send it as theChalupa-Tokenheader. The header is removed before the request reaches the service, so the application's ownAuthorizationis untouched. chalupa urlsfillsremotewith the share address while one is live (it asks the console only for configs that declare a share, and staysnulloffline).auth: nonemakes the address public and needs--confirm "share <environment> <service> publicly"; its hostname ends in---public.
Traffic through a share counts as activity for the idle reaper; a share nobody uses does not keep the box alive.