---
name: treehouse-tenants
description: Treehouse tenant management — lifecycle, provisioning, deletion, NixOS config, disk volumes, usage tracking, and metering. Use for any task involving creating, updating, or removing tenants.
allowed-tools: Bash(ssh:*), Bash(sudo:*), Read, Write, Edit
---

# Treehouse Tenant Management

SSH: `ssh treehouse-host '<command>'`

## Key Paths

- `/etc/nixos/nanoclaw-tenants.nix` — tenant provisioning (NixOS, declarative)
- `/home/<tenant>/nanoclaw/` — per-tenant data (groups, sessions, skills, IPC)
- `/home/<tenant>/workspace/` — per-tenant workspace (apps, files, scripts)
- `/var/lib/tenant-volumes/<tenant>.img` — tenant loopback disk image
- `/var/lib/nanoclaw/store/messages.db` — central DB (tenants, groups, usage, limits)

## Tenant Lifecycle

```bash
treehouse tenants list
treehouse tenants show --id <id>
treehouse tenants usage            # daily usage summary
```

Provisioning and deletion go through Mission Control (MC). Always use MC or the treehouse CLI — do not manually edit DB rows or home directories.

### Deletion checklist

When a tenant is deleted, clean up:
- Discord: channels, category, role
- DB tables: `registered_groups`, `sessions`, `scheduled_tasks`, `chats`, `messages`, `usage_log`, `usage_limits`
- NixOS config: remove from `nanoclaw-tenants.nix`, run `sudo nixos-rebuild switch`
- Disk volume: `/var/lib/tenant-volumes/<tenant>.img`
- Tenant-local containerd/buildkitd/nerdctl runtime state under `/home/<tenant>/`
- Tenant Caddyfile: `/etc/caddy/tenants/<tenant>.caddy`
- Leftover home directory: `rm -rf /home/<tenant>`

## Disk Volumes

Each tenant home (`/home/<tenant>/`) is an ext4 loopback image at `/var/lib/tenant-volumes/<tenant>.img`.

- Formatted with **1% reserved blocks** (`mkfs.ext4 -m 1`), reserved-blocks owner set to `nanoclaw` UID 994
- Tenants can fill up to 99%; nanoclaw can always write pre-launch files (skills sync, IPC dirs)
- If a workload fills the volume, the agent launches and notifies the user of full disk

```bash
# Check disk usage per tenant
treehouse runtime df

# Inspect reserved block config
sudo tune2fs -l /var/lib/tenant-volumes/<tenant>.img | grep -E "Reserved|Block count|Free blocks"

# Backfill reserved-blocks owner on all volumes (if missing)
sudo bash -c 'for img in /var/lib/tenant-volumes/*.img; do tune2fs -u nanoclaw "$img"; done'
```

If pre-launch writes fail with `ENOSPC`, `runAgent()` catches it and sends the user a "disk full" message.

## Usage Tracking & Metering

Per-tenant usage is logged in `usage_log` (SQLite). Each agent run records `duration_ms`, `input_tokens` (estimated), `status`, and `group_folder`.

### Limits (`usage_limits` table)

- `daily_requests` — max agent runs per day
- `daily_duration_ms` — max total agent runtime per day (e.g. `3600000` = 1 hour)

Set via MC tenant edit modal (Resources section) or directly in SQLite. Limits are checked before each spawn; if exceeded, messages are silently skipped.

```bash
# View usage in MC dashboard or via CLI
treehouse tenants usage

# Direct DB query
sqlite3 /var/lib/nanoclaw/store/messages.db \
  "SELECT tenant_id, COUNT(*) as runs FROM usage_log WHERE date(timestamp) = date('now') GROUP BY tenant_id"
```

## NixOS Tenant Config

Tenant provisioning is fully declarative. Changes require `sudo nixos-rebuild switch`.

- **Never** add Linux users, create home directories, or set up services imperatively
- All tenant resource limits (CPU slice, memory) are in `nanoclaw-tenants.nix`
- Tenant-local containerd/buildkitd services are bootstrapped declaratively via:
  - `nanoclaw-tenant-containerd-rootless`
  - `nanoclaw-tenant-buildkitd-rootless`
  - `nanoclaw-tenant-runtime`
