Cubicle

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 cubicle CLI on macOS or Linux, signed in with cubicle login. See Install the CLI and Sign in and API keys.
  • A computer that meets the platform requirements, and sudo rights on it to install the tunnel.
  • An explicit grant on each Box you want to reach. The owner and admin roles 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.

  1. Install it. The command explains what it does, asks Install the tunnel? [y/N] and then runs sudo, which asks for your password.

    cubicle tunnel install

    Pass --yes to skip the question; it is required when no terminal is attached. On macOS, a "Background Items Added" notification appears. That is expected.

  2. 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

  1. 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
  2. Check what is routed. cubicle status always 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": "…"
    }
  3. Disconnect when you are done. This removes the routes and the DNS configuration.

    cubicle disconnect
  • The connection stays up after cubicle connect returns and after an ssh session ends. cubicle ssh connects it automatically.
  • It does not come back after your computer restarts: run cubicle connect again. cubicle logout disconnects 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 status shows the error.
  • 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, and cubicle status shows "connected": false with "error": "login expired; run `cubicle login`". Run cubicle login, then cubicle 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 status lists it as hostname.
  • Only cubicle.internal lookups go to the tunnel, on Linux through systemd-resolved on the interface cbclient0. Your other DNS settings and /etc/hosts are 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 null
  • privateIp is null until 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

  1. 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
  2. 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 on 127.0.0.1. For a service bound to localhost, 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_ID

Revoking 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 connect

Use your own identity, as cubicle whoami prints it. On Linux, use sha256sum in place of shasum -a 256 if shasum is missing.

Platform requirements

LinuxmacOS
Processorsx64, arm64x64, arm64
WireGuardKernel WireGuard supportBundled userspace WireGuard on a utun interface
DNSsystemd-resolved, active, with resolvectl/etc/resolver/cubicle.internal, written by the helper
Background servicesystemd unit cubicle-cli-helper.servicelaunchd 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.internal

ping 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.

On this page