---
name: treehouse-apps
description: Treehouse tenant app runtime — containerd, nerdctl, app.yml contract, deploying and inspecting tenant-hosted apps. Use for any task involving tenant app deployments, container status, or the app hosting infrastructure.
allowed-tools: Bash(ssh:*), Bash(sudo:*), Read, Write, Edit
---

# Treehouse Tenant App Runtime

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

Tenant apps run on **tenant-local rootless containerd + buildkitd + nerdctl** — not the shared host Docker daemon.

## Key Facts

- App source: `/home/<tenant>/workspace/apps/<app>/`
- App spec: `/home/<tenant>/workspace/apps/<app>/app.yml`
- Runtime services (under tenant's `systemd --user`):
  - `nanoclaw-containerd.service`
  - `nanoclaw-buildkitd.service`
  - `nanoclaw-app-<app>.service`
- All app state (images, layers, build cache, containers, networks) lives under `/home/<tenant>/` — counts against tenant loopback volume

## app.yml Contract

Platform-owned spec (inspired by Compose, not a full Docker Compose file). New work uses `app.yml`; `nanoclaw.app.yml` and `docker-compose.yml` are legacy paths.

Supported per-service fields:
- `image`, `build.context`, `build.dockerfile`, `build.args`
- `internal_port`, `public`
- `environment`, `volumes`, `depends_on`
- `command`, `entrypoint`
- `sdk` — opt the service into SDK + IPC access (see SDK Integration below)

Rules:
- Exactly one service should set `public: true`
- Host port allocation is platform-managed; do not put `ports:` in `app.yml`
- Services communicate over a tenant-local bridge network by service name
- Volume sources must stay inside the tenant home
- Use IPC `app-apply` / `app-remove` for deploys — do not use Compose directly

## Deploying Apps

Apps are deployed via IPC from inside the tenant container. The `app-hosting` skill handles this for tenant agents. From the admin side:

```bash
# List running tenant apps
treehouse apps list

# Check container status for a tenant
treehouse apps list --tenant <id>

# Tail app logs
treehouse apps logs --tenant <id> --app <app> --tail 50

# Trigger a deploy/remove from the admin side
treehouse apps deploy --tenant <id> --app <app>
treehouse apps remove --tenant <id> --app <app>

# Restart an app
treehouse apps restart --tenant <id> --app <app>

# Inspect or repair control-plane drift
treehouse apps reconcile --tenant <id>
treehouse apps reconcile --tenant <id> --fix

# Run the end-to-end app lifecycle smoke harness
treehouse apps smoke --tenant <id> --yes
```

## Inspecting Running Containers

Tenant app containers run in the tenant's rootless containerd — not visible via `docker ps`.

```bash
TENANT=blu
UID=$(id -u "$TENANT")
XDG_RUNTIME_DIR=/run/user/$UID \
CONTAINERD_ADDRESS=/run/user/$UID/containerd/containerd.sock \
nerdctl ps

# Include stopped containers
nerdctl ps -a

# Check the app systemd unit
sudo -u "$TENANT" \
  XDG_RUNTIME_DIR=/run/user/$UID \
  DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$UID/bus \
  systemctl --user status nanoclaw-app-<app>.service
```

Note: App deploy units are oneshot services — they often show as `active (exited)` even while the container runs.

## Disk Usage

```bash
treehouse runtime df            # per-tenant volume usage
treehouse runtime ping          # check rootless containerd socket is alive
```

## SDK Integration

Services can opt into Treehouse SDK access via `sdk: true` in `app.yml`. When set, the platform injects into that service's container:
- `/opt/treehouse/sdk` bind-mounted read-only from `/home/admin/treehouse/sdk` on the host
- `/workspace/ipc` bind-mounted read-write from the tenant group's IPC directory
- Env vars: `TENANT_ID`, `NANOCLAW_GROUP_FOLDER`, `NANOCLAW_CHAT_JID`

This allows the container to use `@treehouse/sdk` (Node.js) or the `tc` CLI to send messages, deploy apps, manage tasks, etc.

Only services that genuinely need platform comms should have `sdk: true`. Sidecars (Postgres, Redis, etc.) should not.

Validation: `sdk` is a recognized field in `validateTenantAppSpec`. Unsupported fields are rejected, except legacy `ports:` blocks which are tolerated for backward compatibility and should be cleaned up via `treehouse apps reconcile`.

`treehouse apps reconcile --fix` auto-prunes safe tombstones: DB/Caddy entries whose workspace spec is gone, user unit is gone, and no live containers remain. If the workspace still exists, treat the app as broken and redeploy or remove it explicitly.
