Reach a Box over the private network
Connect your computer to Cubicle's private WireGuard network with cubicle connect, reach granted Boxes on any port by private DNS names such as job-1.acme.cubicle.internal, and manage tunnel sessions, devices and platform requirements on macOS and Linux.
The private network routes IPv4 traffic from your computer to the Boxes you are granted, through
Cubicle's Gateway over WireGuard. While it is connected, a service listening on any port in a Box
is reachable from your computer by name, for example http://job-1.acme.cubicle.internal:8080,
and ssh, curl, browsers and database clients work unchanged. The network needs an interactive
CLI login: API keys, service accounts and the SDK cannot use it, and the SDK has no
box.fetch or box.forward.
Before you begin
- The
cubicleCLI on macOS or Linux, signed in withcubicle login. See Install the CLI and Sign in and API keys. - A computer that meets the platform requirements, and
sudorights on it to install the tunnel. - An explicit grant on each Box you want to reach. The
ownerandadminroles alone are not enough. No SSH key is needed. See Grant access to a Box. - Outbound UDP from your network to Gateway. WireGuard uses only UDP.
Install the tunnel
The tunnel is a background network helper that runs as root, because creating the WireGuard
interface, adding Box routes and resolving *.cubicle.internal need administrator rights. It only
routes Box addresses, never your other traffic, and it never receives your login credentials.
-
Install it. The command explains what it does, asks
Install the tunnel? [y/N]and then runssudo, which asks for your password.cubicle tunnel installPass
--yesto skip the question; it is required when no terminal is attached. On macOS, a "Background Items Added" notification appears. That is expected. -
Check it.
cubicle tunnel status # installed, disconnected
The CLI installer offers this step after installing the CLI. After you upgrade the CLI, run
cubicle tunnel install again (the installer offers this too) to update the helper to the same
version.
Connect and disconnect
-
Connect. The command starts a background process, waits until the tunnel is up and prints its status as JSON. It gives up after 15 seconds.
cubicle connect -
Check what is routed.
cubicle statusalways prints JSON.cubicle status{ "connected": true, "apiUrl": "https://api.cubicle.sh", "identity": "dev@example.com", "sessionId": "…", "boxes": [ { "boxId": "…", "name": "job-1", "org": "acme", "address": "…", "generation": 3, "hostname": "job-1.acme.cubicle.internal" } ], "endpoint": "…", "expiresAt": "…" } -
Disconnect when you are done. This removes the routes and the DNS configuration.
cubicle disconnect
- The connection stays up after
cubicle connectreturns and after ansshsession ends.cubicle sshconnects it automatically. - It does not come back after your computer restarts: run
cubicle connectagain.cubicle logoutdisconnects it. - The background process logs to
~/.config/cubicle/vpn.log.
Networking and SSH commands describes each command's output and errors.
Which Boxes are reachable
A Box is routed to your computer while you hold a grant on it, you are an active member of its
organization, and it runs at its current generation on a healthy Node, as every Box whose
ssh.ready is true does. Boxes in every organization you belong to are included. The
background process renews its 30-second session lease every 10 seconds, and each refresh updates
the list: a Box you start or are granted after connecting appears within about 10 seconds, while
one that stops, hibernates or loses your grant disappears and its open connections close. Only a
/32 route for each routed Box and one for the private DNS server are added, so your default
route and all other traffic are untouched. See
Which Boxes a session can reach and
Access sessions and expiry.
- If refreshes stop for more than 30 seconds, the helper removes the routes and DNS configuration by itself.
- While your computer sleeps, nothing refreshes the session and its lease lapses. When the
computer wakes, the background process removes the routes and opens a new session. If that
fails, it tries again every 10 seconds, and
cubicle statusshows theerror. - Your CLI login lasts 30 days from
cubicle login, and the lease never outlives it. Once the login expires, the tunnel can no longer refresh, andcubicle statusshows"connected": falsewith"error": "login expired; run `cubicle login`". Runcubicle login, thencubicle connect.
Private DNS names
Each routed Box resolves at <box>.<org>.cubicle.internal, so Box job-1 in organization acme
is job-1.acme.cubicle.internal. Only routed Boxes resolve. See
Private DNS names for how names are built and answered.
- The Dashboard shows each Box's name under Internal hostname on the Access tab of its
details, and
cubicle statuslists it ashostname. - Only
cubicle.internallookups go to the tunnel, on Linux through systemd-resolved on the interfacecbclient0. Your other DNS settings and/etc/hostsare not changed. - Programs that bypass the operating system's resolver cannot resolve these names. Give them the Box's address instead; see Find a Box's address.
Find a Box's address
Each Box has a private IPv4 address, privateIp, besides its DNS name.
const info = await cubicle.boxes.get("acme/job-1")
console.log(info.privateIp) // a private IPv4 address, or nullprivateIpisnulluntil the Box's Node has allocated an address. The address then stays the same until the Box is deleted; see Private IPv4 addresses.- The Dashboard shows it as Private endpoint on the State tab of the Box's details.
- An API key can read the address, but only a computer connected to the private network can reach it.
Reach a service in a Box
-
Start a service in the Box that listens on all interfaces. This one keeps running in its terminal:
cubicle ssh acme/job-1 -- python3 -m http.server 8080 --bind 0.0.0.0 -
From another terminal on your computer, request it by name:
curl http://job-1.acme.cubicle.internal:8080/ # <!DOCTYPE HTML> … Directory listing for / …
- The service must listen on the Box's network interface (
0.0.0.0), not only on127.0.0.1. For a service bound tolocalhost, use SSH local forwarding. - TCP, UDP and ICMP work on every port, except TCP 5900, which is reserved for the native console. A service of your own on port 5900 is not reachable.
- Private names have no HTTPS certificates. Use plain HTTP, or TLS with a certificate you manage.
- The service sees Gateway's private address as the client, not your computer's address.
- Boxes cannot open connections to your computer or to each other. See Networking.
Devices
The first cubicle connect registers this computer as a device: a WireGuard key that the root
helper generates and keeps, one per login (API URL and identity), named after the computer's
hostname.
cubicle devices list # always JSON: id, name, address, created_at, revoked_at
cubicle devices revoke DEVICE_IDRevoking a device is permanent: its open connections close, and its key can never connect
again. The helper keeps the revoked key, so cubicle connect on that computer then fails with
key_unavailable. To enroll the computer again, remove the stored key as root and connect. The
helper then creates a new key:
cubicle disconnect
profile=$(printf '%s\n%s' https://api.cubicle.sh dev@example.com | shasum -a 256 | cut -d' ' -f1)
sudo rm "/var/lib/cubicle/cli/devices/$profile.json"
cubicle connectUse your own identity, as cubicle whoami prints it. On Linux, use sha256sum in place of
shasum -a 256 if shasum is missing.
Platform requirements
| Linux | macOS | |
|---|---|---|
| Processors | x64, arm64 | x64, arm64 |
| WireGuard | Kernel WireGuard support | Bundled userspace WireGuard on a utun interface |
| DNS | systemd-resolved, active, with resolvectl | /etc/resolver/cubicle.internal, written by the helper |
| Background service | systemd unit cubicle-cli-helper.service | launchd job com.cubicle.network-helper |
The CLI bundle includes everything else, so no Homebrew, system Node.js or WireGuard package is needed. Windows is not supported.
Limitations
- One operating-system user per computer: the helper serves the user who installed it.
- One login at a time: connecting with a different login or API URL disconnects the current one.
- Routes are system-wide. While connected, every user and program on your computer can reach your granted Boxes.
- If a Box address conflicts with a route that already exists on your computer, for example on your local network, the helper reports the conflict instead of overriding the route.
Uninstall
Remove the tunnel and keep the CLI:
cubicle tunnel uninstall
# Tunnel removed. Device identity is retained in /var/lib/cubicle/cli.It asks for confirmation (--yes skips it), then disconnects. The device key stays in
/var/lib/cubicle/cli, so a later cubicle tunnel install reuses the same device. To remove the
CLI as well, see Uninstall.
To forget the device completely, run cubicle devices revoke DEVICE_ID while still signed in,
then sudo rm -rf /var/lib/cubicle/cli.
Verify
cubicle tunnel status
# installed, connected
ping -c 3 job-1.acme.cubicle.internalping resolves the name and gets replies from the Box. connected means the session is set up,
not that WireGuard traffic flows: if names do not resolve and ping gets no replies, check that
your network allows outbound UDP to the endpoint that cubicle status shows.
Troubleshooting
Troubleshooting covers errors shared with other pages. One you may see here
is api_key_not_allowed: API keys cannot use this route, because CB_CLI_API_KEY is set and the
private network needs an interactive login. Unset it and run cubicle login; see
API keys. These are the private network's own errors.
the Cubicle tunnel is not installed; run `cubicle tunnel install`
cubicle connect and cubicle ssh need the tunnel. Run cubicle tunnel install; it asks for
confirmation and your sudo password. If the message says
the Cubicle tunnel helper is not responding; reinstall it with `cubicle tunnel install`,
run the same command to reinstall the helper.
VPN startup timed out; inspect vpn.log
cubicle connect got no session within 15 seconds and no error was recorded, for example because
the API answered slowly. Read ~/.config/cubicle/vpn.log, then run cubicle connect again.
confirmation required; pass --yes
cubicle tunnel install or uninstall ran without a terminal to ask in. Pass --yes.
systemd-resolved is required or systemd-resolved must be active
On Linux, the tunnel resolves cubicle.internal through systemd-resolved. Install and enable it,
then run cubicle tunnel install again.
cubicle.internal resolver already exists
On macOS, /etc/resolver/cubicle.internal exists but Cubicle did not write it. Move that file
away, then run cubicle connect again.
key_unavailable: device key is already registered or revoked
This computer's device was revoked. Enroll it again.
vpn_unavailable: …
Cubicle cannot give this computer a private network address right now. Ask a Cubicle operator.
Connection refused
The Box answered, but nothing listens on that port on its network interface. Check that the
service binds 0.0.0.0 rather than 127.0.0.1, or reach it with SSH local forwarding.
Related
- Networking: Gateway, routing, DNS and what a Box can reach.
- SSH into a Box: SSH aliases on top of the private network.
- Use the desktop console: the Box's display, in the browser or a VNC client.
- Networking and SSH commands: every tunnel, connect and device command.
- Organizations and access: grants and roles.
- Remote development environments: dev servers in a Box, opened from your laptop by name.
SSH into a Box
Open an SSH session to a Box with cubicle ssh, or write an OpenSSH alias with cubicle ssh-config for ssh, file copies and VS Code or Cursor Remote-SSH, using ed25519 keys, Box grants and pinned host keys.
Use the desktop console
Open a Box's graphical desktop from the Cubicle Dashboard in a full-screen VNC console, or from a native VNC client on port 5900 over the private network, and understand console readiness, clipboard, keyboard and session limits.

