# Your own Den, on your own machine

The Den is the computer your crittos work in: a shell, files, a browser and a
live desktop you can watch. On critto.app it is a cloud VM on Boat. A
self-hosted server can run every Den on its own hardware instead, with no Boat
account: each person on the server gets a small virtual machine of their own.

## How it works

- Each Den is a KVM microVM running Ubuntu 24.04 (the same userland as the
  cloud Dens) with the desktop, computer use (trycua's cua-driver, MIT) and the
  AI tools (Codex, Claude Code, Pi, Grok, Antigravity, Muse Code)
  preinstalled. Google Chrome is in images you build yourself; images we
  publish can't include it, so each Den installs it from Google the first
  time it starts (about 15 seconds).
- It runs only while it is used. After the idle time set in the app (10
  minutes by default) it shuts down; everything on its disk stays. Starting it
  again takes a couple of seconds.
- Your crittos use your own sign-ins (ChatGPT, Claude) and your own AI
  connections in it, exactly as in the cloud.
- Nothing needs root while running: the server's user starts its Dens itself.

### What a Den can and can't reach

| | |
| - | - |
| The internet | yes |
| Your home or office network (192.168.x.x, 10.x.x.x, …) | no |
| Anything on the server itself (the Critto server, databases, …) | no, except ports you list in `CRITTO_DEN_HOST_PORTS` |
| Other people's Dens | no |
| Into the Den | only the Critto server, over SSH with that Den's own key |

Each Den sits in its own network namespace with a firewall that drops private
and link-local addresses, inside a filesystem sandbox (bubblewrap) that only
shows the virtual machine its own disk, and the virtual machine monitor runs
under a seccomp filter. The Den can't see the server's files or other Dens'
disks even if the virtual machine itself were broken out of.

## Requirements

- Linux on x86-64 with hardware virtualization (`/dev/kvm`). Most PCs and
  servers have it; in a cloud VM you need one with nested virtualization.
- Unprivileged user namespaces (on by default on NixOS, Debian, Ubuntu,
  Fedora, Arch).
- Packages: `qemu-system-x86` (or `qemu_kvm`), `qemu-utils`, `passt` (provides
  `pasta`), `bubblewrap`, `nftables`, `openssh-client`, `e2fsprogs` (1.47.1 or
  newer, to build the image), and `podman` to build the image.

### Resources per Den

| | default | setting |
| - | - | - |
| CPUs | 2 | `CRITTO_DEN_CPUS` |
| Memory | 4 GiB while running | `CRITTO_DEN_MEMORY_MIB` |
| Disk | up to 32 GiB, used as it fills | `CRITTO_DEN_DISK_GIB` |
| Dens running at once | 4 | `CRITTO_DEN_MAX_RUNNING` |

The shared base image takes about 1.5 GB once; each Den's own disk starts at a
few megabytes and grows with what is installed and saved in it. A stopped Den
uses no CPU or memory.

## Turning it on

1. Let the server's user use KVM:

   ```sh
   sudo usermod -aG kvm critto
   ```

2. Build the Den image (once, and again to update the tools). Any user with
   rootless Podman can build it (about 10 minutes); then copy it to where the
   server looks:

   ```sh
   deploy/den/build-image.sh ~/den-image
   sudo mkdir -p /var/lib/critto/den-image
   sudo cp -a ~/den-image/. /var/lib/critto/den-image/
   sudo chown -R critto:critto /var/lib/critto/den-image
   ```

   `CRITTO_DEN_IMAGE` (default `/var/lib/critto/den-image`) is the folder that
   holds `current`, not `current` itself. Existing Dens keep the image version
   they were made from; new Dens use the newest one.

3. In `/etc/critto/critto.env`:

   ```sh
   CRITTO_DEN_BACKEND=local
   ```

   The packaged service sets `NoNewPrivileges=yes`, which stops the Den's
   sandbox (bubblewrap) from mapping its user. Lift it for this service only:

   ```sh
   sudo mkdir -p /etc/systemd/system/critto.service.d
   printf '[Service]\nNoNewPrivileges=no\n' | sudo tee /etc/systemd/system/critto.service.d/local-den.conf
   sudo systemctl daemon-reload
   ```

   and restart: `sudo systemctl restart critto`. The log says whether the
   machine is ready (`journalctl -u critto | grep Den`). On NixOS the module
   does this for you when `den.backend = "local"`.

To let the Den use something running on the server (for example Ollama on
port 11434), add `CRITTO_DEN_HOST_PORTS=11434`; inside the Den it is
`http://10.0.2.2:11434` (`http://127.0.0.1:11434` in container mode). The
Critto server's own port is never passed through, even if listed.

### What works in a local Den today

- ChatGPT (Codex), Claude (Claude Code), Meta Muse and Google Antigravity
  crittos, with your own sign-ins.
- Your own AI connections and Critto AI, when the Den can reach this server's
  relay: set `CRITTO_RELAY_PUBLIC_URL` (or `CRITTO_PUBLIC_URL`) to an address
  of this server that the Den can reach over the internet.
- Grok crittos can't work in a local Den yet; they still chat.
- You can't send a critto messages while it is working in a local Den; send
  them when the reply is done.

### NixOS

```nix
services.critto = {
  enable = true;
  den.backend = "local";          # "boat" (default), "local" or "none"
  den.local = {
    cpus = 2;
    memoryMiB = 4096;
    diskGiB = 32;
    maxRunning = 4;
  };
};
```

The module adds the service user to `kvm` and puts QEMU, passt, bubblewrap and
nftables on its path.

### Without KVM

`CRITTO_DEN_ISOLATION=container` runs each Den as a rootless Podman container
instead. It works where there is no `/dev/kvm`, but the Den then shares the
server's kernel and can reach your local network, so only use it on a server
that is yours alone.

## Where things are

| | |
| - | - |
| `/var/lib/critto/den-image/` | base images (`current` is the newest) |
| `/var/lib/critto/dens/<name>/` | one Den: its disk, SSH key, saved keys (`vault.json`), logs (`console.log`, `den.log`) |

Deleting an account deletes its Den. To reset a Den, stop the server and remove
its directory.
