Skip to content

About this article

  • Audience: Operators relieving internal storage pressure on a shadow device
  • Goal: After reading, you can mount a USB-C SSD on the host, redirect H.265 recordings to it, and verify chunks land on the external volume
  • Type: How-to

Summary

Shadow devices with tight internal overlay space can run out of recording headroom during long soaks. A USB 3.x portable SSD (for example a 256 GB Samsung T7/T9 class drive) mounted at /data/ext-recordings gives the recorder several times more durable space. The firmware already detects that mount, sets H265_RECORDINGS_DIR, and symlinks /app-storage/recordings—no custom agent changes required.

Prerequisites

  • SSH access: root@<device-ip> with YOUR_DEVICE_PASSWORD
  • Phantom proxy: export PHANTOM_PROXY=https://<phantom-proxy>:8788
  • USB-C portable SSD (200 GB+ recommended) and a short, high-quality cable
  • Helper script in [repo-root]: scripts/setup-external-recordings.sh
  • Target device with chronic storage_health pressure (often shadow-secondary / site-role-b)

Host-mode check

Some RVC4 builds expose USB in gadget mode until host enumeration is enabled. If lsblk shows no new block device after plug-in, confirm kernel USB host support and cable orientation before formatting.

1. Probe storage before plug-in

From your workstation, inspect current disks and USB buses:

bash
ssh root@<device-ip> '
  echo "=== USB & Storage Probe ==="
  lsusb
  dmesg | grep -iE "usb|sd[a-z]|uas|storage" | tail -15
  lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL
  df -h /overlay /app-storage 2>/dev/null || df -h
  echo "=== End ==="
'

Note internal partitions (typically sda and overlay mounts). External media appears as a new sd* device when host mode enumerates it.

2. Plug in and mount the SSD

Connect the SSD to the device USB-C port, wait a few seconds, then run the host helper (copy it over SSH first if needed):

bash
# Plan only — shows candidate device and mount target
bash scripts/setup-external-recordings.sh --dry-run

# Mount an already-formatted drive at /data/ext-recordings
bash scripts/setup-external-recordings.sh

# First-time setup: format to ext4 (destroys existing data)
bash scripts/setup-external-recordings.sh --format --yes

Manual alternative (when you prefer explicit control):

bash
# Replace <device-partition> with the new partition (e.g. /dev/sdi1)
mkfs.ext4 -L SAMSUNG-256-RECORDINGS <device-partition>
mkdir -p /data/ext-recordings
mount <device-partition> /data/ext-recordings
df -h /data/ext-recordings

Use /dev/disk/by-id/ or /dev/disk/by-label/ in scripts—internal sda names are not stable for externals. Prefer ext4 over factory exFAT for Linux recording workloads.

3. Restart the app

The container entrypoint activates external recording when /data/ext-recordings exists:

bash
oakctl app stop && oakctl app start

On start, the runtime exports H265_RECORDINGS_DIR=/data/ext-recordings/recordings and symlinks /app-storage/recordings to that path. Config and logs remain on internal storage.

4. Verify recordings and storage health

Confirm chunks are landing on the external volume:

bash
ssh root@<device-ip> '
  df -h /overlay /data/ext-recordings
  ls -la /data/ext-recordings/recordings/*/*/*chunk*.h265 2>/dev/null | tail -5
  cat /app-storage/recordings/.h265_status 2>/dev/null | head -20
'

Probe through the proxy:

bash
curl -s -X POST "$PHANTOM_PROXY/tools/call?target=<device-ip>" \
  -H "Content-Type: application/json" \
  -d '{"tool":"get_recording_status","arguments":{}}' | jq '.storage_health, .storage_free_gb, .is_writing_data'

Verify: storage_health is OK or WARNING on the large mount, is_writing_data is true, and new chunk_*.h265 files appear under /data/ext-recordings/recordings/YYYY/MM/DD/. Internal overlay free space should stop shrinking as fast.

Run a short soak with prune-first to exercise the full path:

bash
python3 tools/v3_shadow_orchestrator.py --shadow <device-ip> --duration 5 \
  --prune-first --scenarios normal_short,disk_critical

The fleet prune script already prefers large external roots when mounted. See prune and soak gates.

5. Persist across reboot

Add a fstab entry or udev rule so the mount returns after reboot or hot-plug. The helper prints ready-to-copy examples when run with --auto-mount. Example fstab line:

LABEL=SAMSUNG-256-RECORDINGS  /data/ext-recordings  ext4  defaults,noatime  0  2

After reboot, confirm the mount, restart the app, and re-probe get_recording_status.

Safe eject and risks

  • Hot-unplug: Stop the app, wait for the current chunk to flush (check .h265_status), then umount /data/ext-recordings. Do not remove the drive mid-write.
  • Mount failure: Entrypoint falls back to internal /app-storage/recordings when the external path is missing.
  • Power: Low-power SSDs usually work on device USB-C; use a powered hub only if you see disconnects under sustained encoding.
  • Backup: Playback and pull tools use the active recording root—offload with your standard proxy or rsync workflow.

Next steps

Operator depth

Live device probes, mount recipes, soak results, and harness evidence live in private operator handoff (not published) (private).