Boxes are computers in your own cloud

A computer in your own cloud, running lnk. Connect your AWS or Google Cloud account once, and one command makes a machine there with lnk on it.

lnk up boxes                 # the cloud, box, exit and accounts plugins
lnk cloud connect aws        # your aws login or an access key, and makes nothing in it
lnk box create --cloud aws           # aws-1: an Ubuntu machine in that account, lnk on it
lnk box ssh aws-1            # a shell on it
lnk box list                 # your boxes, also those made on your other computers
lnk box stop aws-1           # stopped: its disk stays, and is billed
lnk box start aws-1          # running again
lnk box delete aws-1         # gone, with its disk

What it needs

You need a Mac or Linux with OpenSSH, and your own AWS or Google Cloud account. lnk up boxes adds the plugins, and lnk cloud connect offers to install the cloud's CLI.

VM guests

lnk box create --here --vm vm.toml creates a retained VM guest. Its VM manager must already have an approved compatible base and a host resource policy. Add --env env.toml to prepare that environment privately inside the guest. Omitting the environment leaves an SSH-only base usable without application setup. Boxes on this machine describe configuration and readiness.

Machine inputs

--vm and --env are independent inputs that every placement captures once, before anything is made. A local or Local Cloud box takes either, both or neither. A cloud account's box takes --env, set up on the machine as Environments describe, and refuses --vm visibly, with no nested or native fallback.

Who owns a box

Boxes use your signed-in Link account when available; otherwise the local user and machine name identify their owner. Link login is optional.

What it costs

Boxes cost money, billed to your account by your cloud. Local Link bills nothing.

Connect a cloud account

A box is made in a cloud account you connect once, which keeps your buckets too: Cloud Accounts has connecting and disconnecting one.

Create a box

A box costs money, so create shows the machine it picked, its disk and the price when the cloud gives one, then asks.

lnk box create --cloud aws                                       # a new box, aws-1
lnk box create --cloud gcp --place europe-west1-b --size e2-standard-4   # gcp-1, that machine type there
lnk box create --cloud aws --cpus 4 --memory 8GB..16GB           # the smallest machine with those

A new box is Ubuntu 24.04 on x86_64 with a 20 GB disk and only SSH open. It gets lnk, and what you run on it adds its own plugins. lnk agent move aws makes a box itself when you have none in aws.

Its login

When this computer is signed in, it signs the box in to your account with a token of the box's own, with no browser; lnk iam machine list lists it, signed in by this computer, and lnk iam machine remove <box> signs it out. With no Link account login here, the box stays signed out and creation needs no browser. --no-login also leaves a box signed out when this computer has an account login. A box left signed out gets no accounts plugin, so a release of Link without one makes boxes all the same, and none of them reaches a relay.

Get into a box

lnk box ssh opens a shell or runs one command on a box, and lnk box run runs lnk there.

lnk box ssh                              # a shell on your only box
# one command there
lnk box ssh gcp-1 -- uptime
# lnk on the box, wherever it is installed
lnk box run gcp-1 -- agent status

Let people in

On a box, each person gets a Linux user of their own, with their own home, and every SSH session of theirs runs in their own sandbox. Run it on the box, as root:

lnk box ssh gcp-1
# what dev.toml opens is shown first
sudo lnk box user add alice --public-key alice.pub --sandbox dev.toml
sudo lnk box user list
# --delete-folder deletes their home too
sudo lnk box user remove alice

They connect as themselves, with their key: ssh alice@<the box>. A shell, a command, scp or sftp all run in their sandbox, a spec whose ~ is their home, which is the one folder they write. Its [commands] must let /bin/bash run, which a session is. Adding a user again replaces their key and sandbox.

Forward home to a box

A box can send its traffic out through this computer, so sites see your home address.

lnk exit start --on gcp-1                      # a SOCKS proxy on the box, leaving from here
lnk box forward gcp-1 --socket proxy.sock --proxy-port 1080   # proxy.sock in the home of the box, to your own SOCKS proxy on port 1080
lnk box forward gcp-1 --port 40780 --proxy-port 1080          # port 40780 on the box instead

lnk exit start keeps the forward up in the background, and Exit covers it. lnk box forward alone lasts until the SSH connection drops.

Set it up your way

Describe the machine in a file, set it up with a script, and save the result as an image, and the next box is one command away.

Describe a box in a file

A box spec says the machine in a file, so the same file makes the same box, on any cloud you've connected.

lnk box size list --cloud aws --spec web.toml             # the machines that fit in aws, best first, making nothing
lnk box create --cloud aws --spec web.toml            # web-1, the best fit for web.toml in aws
lnk box create --cloud gcp --spec web.toml --spot     # the same box in gcp, a spot machine
lnk box spec web-1 > web-1.toml               # the file that makes web-1 again
lnk box spec --cpus 4 --disk 40GB             # the file those flags make

A box spec is a TOML file that describes the machine the way you would at a store.

# web.toml
name = "web"                        # its boxes: web-1, web-2, ...

[machine]
cpus = { min = 2, max = 8 }
memory = { min = "4 GB", max = "16 GB" }
disk = { min = "40 GB" }

[machine.aws]
region = "eu-west-2"

The box gets the smallest machine that fits. The min is what it needs and max the most you'll pay for, and the same file makes the best fit on AWS and on Google Cloud. A flag wins over the file. Every field is in the box spec.

The spec a box was made from

lnk box spec <box> prints the spec a box was made from, with its machine pinned for its cloud, so the file makes exactly that box there (what it prints).

Set a box up in stages

A setup script sets a box up in stages: plain sh, each command in it a stage, lnk box copy putting its files on the box, and its image saved once by a hash of what made it.

lnk box copy web-1 clone.toml deps.toml build.toml setup   # files into ~/setup on web-1, over SSH
# Your script, run here, the box named in LNK_BOX.
LNK_BOX=web-1 sh -e setup.sh

Run each stage that fetches or builds in a sandbox of its own on the box, so cloning reaches only GitHub, installing only the package registry, and building nothing. The sandbox costs nothing beyond the box.

# setup.sh: LNK_BOX=web-1 sh -e setup.sh
box=${LNK_BOX:?}
# Outside any sandbox: installing system packages needs root, which no sandbox gives.
lnk box ssh "$box" -- sudo apt-get update -q
lnk box ssh "$box" -- sudo apt-get install -y python3-pip
lnk box run "$box" -- plugin add sandbox
lnk box run "$box" -- sandbox ready --yes
lnk box copy "$box" clone.toml deps.toml build.toml setup
lnk box ssh "$box" -- mkdir -p app
# Each stage in its own sandbox.
lnk box run "$box" -- sandbox run --spec setup/clone.toml --yes -- git clone https://github.com/me/app app
lnk box run "$box" -- sandbox run --spec setup/deps.toml --yes -- python3 -m pip install --no-cache-dir --target app/.deps -r app/requirements.txt
lnk box run "$box" -- sandbox run --spec setup/build.toml --yes -- sh app/build.sh
# clone.toml: cloning reaches GitHub alone, as git, and writes ~/app
name = "clone"
version = 1

[files]
write = ["~/app"]

[internet]
hosts = ["github.com"]

[commands]
allow = ["git", "/usr/lib/git-core/"]
# deps.toml: installing reaches PyPI alone, never GitHub
name = "deps"
version = 1

[files]
write = ["~/app"]

[internet]
hosts = ["pypi.org", "files.pythonhosted.org"]

[commands]
allow = ["python3"]

build.toml is the same with no [internet], allowing sh and python3. A spec's sections are in the sandbox docs.

A stage that fails

sh -e stops at the first stage that fails. The stages before it ran, so fix the command and run the rest by hand, or start again on a new box. The script can make the box itself, with lnk box create --cloud aws --name "$LNK_BOX" --yes as its first stage.

Folders a stage writes

Write lnk box copy's folder without a ~, which your shell would turn into this computer's home folder. A stage's spec can write only a folder that exists, so make it first, as mkdir -p app does above. It can't write a dot folder in your home, such as ~/.cache, so keep a stage's caches in its own folder or turn them off, as --no-cache-dir does.

Save a box as an image

An image is a box's disk, kept in your cloud account so new boxes can start from it.

lnk box image create web-1 web-ready       # the disk of web-1, kept as image web-ready in its cloud account
lnk box image list --cloud aws                   # the images you saved in aws
lnk box image delete web-ready --cloud aws       # deletes it, and its copy of the disk

Saving takes a few minutes and needs a running box with no agent on it, because an image would carry the agent to every box made from it. The box's lnk is signed out first, so the image holds no login, and the box is stopped while its disk is saved, so nothing is saved half-written. It then starts again, and in a terminal lnk signs it in again: open the link it prints.

Secrets it refuses

Saving refuses a box holding secrets where they're usually left, and lists what it found (where it looks). Remove them, then save again.

What it costs

The image keeps a copy of the disk in your account, billed every month by its size until you remove it. On AWS it's an AMI and its snapshot, in the box's region, and on Google Cloud it's an image in the project.

Create a box from an image

A box from an image starts with that image's disk, and gets its own machine ID, SSH host key and lnk login, as any new box does.

lnk box create --cloud aws --image web-ready                  # a new box, its disk as that of web-1 was
lnk box create --cloud aws --image web-ready --spec web.toml  # the machine web.toml picks, from the image

On AWS, a box from an image loads each part of its disk from the image's snapshot the first time it's read, so its first reads are slower than a stopped box's.

Where it's made

The box is made where the image is, in its AWS region or any zone of the Google Cloud project. Its disk is at least the image's, and lnk says so when that is more than you asked for. It keeps the image's processor, lnk and SSH keys.

Reuse an image while what made it stays the same

An image saved with a hash of what made it is found again by that hash, so a script saves it once.

lnk box image create build-1 app-3f2a9c --hash 3f2a9c   # the image, tagged with a hash of what made it
lnk box image list --cloud aws --hash 3f2a9c                  # the images with that hash, newest first
lnk box create --cloud aws --name web-1 --image-hash 3f2a9c   # a box from the newest ready one

The hash is yours to make, from your setup script, its specs and the lockfile its dependencies come from, so a change to any of them makes a new hash. A script can then save the image once and make every later box from it:

# boxes.sh: a box set up as setup.sh says, from an image when nothing that made it changed
hash=$(cat setup.sh clone.toml deps.toml build.toml requirements.txt | shasum -a 256 | cut -c1-32)
if [ "$(lnk box image list --cloud aws --hash "$hash" --json)" = "[]" ]; then
  lnk box create --cloud aws --name build-1 --yes --no-login
  LNK_BOX=build-1 sh -e setup.sh
  lnk box image create build-1 "app-$(printf '%s' "$hash" | cut -c1-8)" --hash "$hash" --yes
  lnk box delete build-1 --yes
fi
lnk box create --cloud aws --name web-1 --image-hash "$hash" --yes

On a box from the image, only what changed since needs a stage, such as a git -C app pull in the clone's spec, then the dependencies' stage again.

Attach existing resources

A box spec's [attach] names a preexisting network, identity, address and disks. They are checked before launch and survive removal. The box spec gives each cloud's names. lnk box list --tag team=research --json queries all owners and regions; add --cloud <cloud> to query one account. Removal by immutable cloud ID uses lnk box delete --id <id> --cloud <account> --place <region-or-zone> --yes; an already gone VM is done.

See, stop or delete

A stopped box still bills for its disk, and removing the box deletes the disk too.

See your boxes

lnk box list shows every box tagged as yours, with its account, place, size, address and state.

lnk box list                 # name, account, place, size, address, state
lnk box list --refresh       # also ask for the state of each box the clouds did not list
lnk box list --all-regions   # on AWS, look in every enabled region

list asks each connected account for the boxes tagged as yours, so it finds boxes made on your other computers too. On AWS it looks in the regions your boxes are in, or in every enabled region when it knows none.

Stop, start or remove a box

A stopped box keeps its disk and comes back at a new address; a removed one is gone with its disk.

lnk box stop gcp-1           # stops the machine, but its disk stays and is billed
lnk box start gcp-1          # running again, at a new address
lnk box delete gcp-1         # deletes the machine and its disk, after asking
lnk box delete gcp-1 --yes   # without asking

Limit a box's lifetime

lnk box create --cloud aws --deadline 4h --yes sets a maximum lifetime; deadline = "4h" is the box spec's equivalent. A deadline is at most seven days. AWS installs a persistent root-owned timer and terminates on shutdown. Google Cloud uses its own run limit with deletion, at least thirty seconds, and can begin deletion up to thirty seconds after the limit. lnk box list shows the absolute deadline.

Get into your boxes from a new computer

Sign in to the same Link account and connect the same cloud account, and the new computer finds your boxes by their tags.

lnk auth login               # the same Local Link account
lnk cloud connect aws        # the same cloud account
lnk box list                 # finds your boxes by their tags
lnk box ssh aws-1            # lets this computer in through the cloud account

The first connection pins the box's host keys from the cloud, lets this computer's SSH key in through your account, and has the box trust this computer's SSH certificates. Then the key goes. Each computer's certificate authority has the comment lnk-ca <machine id> <machine name>, so you can find and remove a lost computer's on a box (Certificates).

Where things live

What Link keeps for your boxes, on this computer and on each box; every file is on Configuration.

  • ~/.lnk/cloud/accounts.toml holds your accounts, access keys included. On a Mac, secrets are in the Keychain.
  • ~/.lnk/box/boxes.toml is this computer's copy of your boxes, each with the spec it was made from. The clouds hold the record, by tags.
  • ~/.lnk/box/id_ed25519 and known_hosts are Link's SSH key for boxes, and their host keys. lnk box key prints the key's public half, for a machine that lets you in by it, and lnk box key --secret-file where its secret half is. A box takes it only with a certificate.
  • ~/.lnk/box/ssh-ca.pub and certs/ are this computer's SSH certificate authority and the certificates it signed, its secret in the Keychain on a Mac, else in ssh-ca.
  • ~/.lnk/machine.toml on a box holds its machine ID and name.
  • /etc/lnk/users/<name>/ on a box is root's record of a user: their folder and key, their sandbox's spec and the key's one line; /usr/local/lib/lnk/ holds the programs their sessions run.

More about it

Every flag is in lnk cloud <command> --help and lnk box <command> --help. Security has what Link guarantees and its gaps, Decisions why it works this way, and Troubleshooting what to do when a box won't.

Docs

  • Configuration: The files lnk cloud and lnk box keep, and what each one holds
  • Interface: What other programs can rely on from lnk box
  • Security: What's protected: your cloud accounts, whose keys lnk cloud keeps, and the machines lnk box makes in them, against anyone but you reaching them
  • Decisions: Why clouds and boxes work the way they do
  • Box Images: Which image a new box starts from, and what an image you save holds, refuses and gives each box made from it (Security)
  • What's on a Box: What a box holds, how it installs lnk, how its working users and its people are kept apart, and how a spec and a setup reach it (Security)
  • Troubleshooting: What to do when a box won't make, start, show or save, symptom by symptom (Boxes)