# FreeBSD Jail Cleanup Model

## Overview

FreeBSD jails behave differently from Docker containers. This document explains the jail cleanup model for Clawdie.

## Docker vs Jails: Key Differences

### Docker Container Lifecycle

```
Created → Running → Stopped → (persisted)
                      ↓
                   Removed
```

- Containers exist in "stopped" state
- Can accumulate orphans
- Needs explicit cleanup: `docker rm`

### FreeBSD Jail Lifecycle

```
Created → Running → (destroyed on stop)
```

- **No "stopped" state**
- Jail completely removed when stopped
- **No orphan accumulation**
- Cleanup happens automatically

## What Can Persist After Jail Stops

### 1. Nullfs Mounts (CAN PERSIST)

```bash
# If jail crashes before unmounting
mount | grep clawdie-cp
/home/clawdie/clawdie-cp on /jails/clawdie-cp/home/clawdie/clawdie-cp (nullfs, local)
```

**Solution:** Log and alert, don't auto-unmount (safer)

### 2. PID Files (CAN PERSIST)

```bash
# If process crashes
ls /var/run/clawdie-cp.pid
```

**Solution:** Check if process exists before deleting

### 3. ZFS Datasets (SHOULD PERSIST)

```bash
# Persistent storage - this is GOOD
zfs list | grep clawdie-cp
```

**Solution:** No cleanup needed (persistent storage)

### 4. Jail Config (PERSISTS)

```bash
# /etc/jail.conf
clawdie-cp { ... }
```

**Solution:** Config is fine, defines jail structure

## Clawdie Cleanup Strategy

### For Jails (Minimal)

```typescript
export function cleanupOrphans(): void {
  if (runtime === 'jail') {
    // 1. Check for stale mounts (log only)
    // 2. Check for unexpected running jails
    // 3. Log findings for manual cleanup
    // 4. DON'T auto-unmount or kill
  }
}
```

**Why minimal?**

- Jails don't accumulate orphans
- Auto-unmount could break legitimate uses
- FreeBSD philosophy: explicit > implicit
- Admin should review before cleanup

### For Docker (Full Cleanup)

```typescript
export function cleanupOrphans(): void {
  if (runtime === 'docker') {
    // 1. Find stopped containers
    // 2. Kill orphaned processes
    // 3. Remove stale containers
    // 4. Clean up networks/volumes
  }
}
```

**Why full?**

- Docker containers DO accumulate
- Stopped containers consume resources
- Standard practice in Docker world

## Prevention Instead of Cure

### 1. Proper Shutdown Handler

```typescript
process.on('SIGTERM', () => {
  // Unmount before exit
  execSync('umount /jails/clawdie-cp/home/clawdie/*');

  // Remove PID file
  fs.unlinkSync('/var/run/clawdie-cp.pid');

  process.exit(0);
});
```

### 2. Idempotent Startup

```typescript
export function ensureCleanState(): void {
  // Unmount stale mounts
  execSync('umount -f /jails/clawdie-cp/* 2>/dev/null || true');

  // Remove stale PID
  fs.unlinkSync('/var/run/clawdie-cp.pid');

  // Kill leftover processes
  execSync('pkill -f clawdie-cp || true');
}
```

### 3. Jail.conf Hooks

```bash
# In /etc/jail.conf
clawdie-cp {
  # ... config ...

  # Cleanup on stop
  exec.poststop = "/path/to/cleanup.sh";
}
```

## Monitoring

### Check for Stale Artifacts

```bash
# Stale mounts
mount | grep clawdie-cp

# Running jails
jls -n | grep clawdie-cp

# Stale processes
ps -ax | grep clawdie-cp

# PID files
ls /var/run/clawdie-cp.pid
```

### Expected Output (Healthy)

```bash
# No stale mounts
$ mount | grep clawdie-cp
(no output)

# No unexpected jails
$ jls -n | grep clawdie-cp
(no output)

# No stale processes
$ ps -ax | grep clawdie-cp
(no output)
```

### Unexpected Output (Needs Cleanup)

```bash
# Stale mount found
$ mount | grep clawdie-cp
/home/clawdie/clawdie-cp on /jails/clawdie-cp/home/clawdie/clawdie-cp (nullfs)

# Fix
umount -f /jails/clawdie-cp/home/clawdie/clawdie-cp
```

## Test Script

Run the test script to verify jail cleanup logic:

```bash
tsx scripts/test-jail-runtime.ts
```

## Summary

| Artifact     | Can Persist?       | Auto-Cleanup?         | Manual Fix  |
| ------------ | ------------------ | --------------------- | ----------- |
| Running jail | No                 | N/A                   | N/A         |
| Stopped jail | No (doesn't exist) | N/A                   | N/A         |
| Nullfs mount | **Yes**            | No (log only)         | `umount -f` |
| PID file     | **Yes**            | Yes (if process dead) | `rm`        |
| ZFS dataset  | **Yes** (good!)    | No (keep)             | N/A         |
| Jail config  | **Yes** (good!)    | No (keep)             | N/A         |

**Key Insight:** Jails are simpler than Docker - no daemon, no orphan accumulation, no "stopped" state. Focus on preventing stale mounts and PID files instead of cleaning up orphaned containers.
