Make Personal Agents available on extremely limited hardware. #36
Replies: 1 comment · 1 reply
-
|
PicoClaw 10mb |
Beta Was this translation helpful? Give feedback.
All reactions
-
|
Let me pull up the earlier conversation details to make sure I'm building on everything we've established. Now let me look at the existing host integration script and the pin layout to understand what's available for microSD SPI. Now let me look at the PicoClaw GitHub repo for its actual structure and configuration format. Now let me look at PicoClaw's internal architecture more closely — the tool system and provider interface. Good. Now I have a complete picture of PicoClaw's architecture, the BABB dongle's existing firmware, and the integration surfaces. Let me build the comprehensive response. Now I have everything I need. Let me create a comprehensive, focused response document. Now let me build the complete focused document. This will be better as inline conversation rather than a Word document. Let me write the comprehensive response directly. Alright. Here's the focused picture. The dongle's microSD holds PicoClaw. The host machine — whatever it is — provides the CPU, RAM, and network. The dongle provides the secrets, the scanning, and the portable workspace. Every component has a specific role and a specific boundary. The Core ConceptThe operator plugs in one USB device and gets a complete AI-secured working environment. PicoClaw doesn't "install" on the host. It runs transiently from the dongle's storage using the host's processor, then vanishes when unplugged. The dongle is the source of truth — for identity, for keys, for configuration, for data. The host is disposable compute. This matters for tradesmen because they move between machines. Their laptop today, a site PC tomorrow, a client's workstation next week. The dongle carries everything. The host carries nothing after disconnect. USB Composite Device: The FoundationThe current RP2040 firmware exposes a single USB CDC serial interface. To support PicoClaw-from-dongle, the TinyUSB configuration needs to become a composite device exposing two interfaces simultaneously: The TinyUSB config change: /* tusb_config.h — updated for composite CDC + MSC */
#define CFG_TUD_CDC 1 /* Command serial interface */
#define CFG_TUD_MSC 1 /* Mass storage for microSD */
#define CFG_TUD_HID 0
#define CFG_TUD_MIDI 0
#define CFG_TUD_VENDOR 0
/* MSC Configuration */
#define CFG_TUD_MSC_EP_BUFSIZE 512 /* One SD sector per transfer */
/* Power — bump to 200mA for microSD active reads */
#define BOARD_TUD_MAX_POWER 200The RP2040 acts as a USB-to-SPI bridge for the microSD. When the host reads a file from the "drive," the RP2040 translates the USB MSC SCSI commands into SPI reads to the microSD card. TinyUSB handles the protocol translation; you provide callbacks for There's a critical subtlety here: the RP2040 needs to share the microSD between USB mass storage reads from the host and its own firmware reads (log writes, model loading, classification data). You can't have the host and the RP2040 writing simultaneously — FAT32 has no locking. The solution is a mode switch: In practice, the launch script ejects the drive immediately after copying the PicoClaw binary to MicroSD Hardware IntegrationThe T-PicoC3 uses SPI1 for the TFT display (GP8-GP13). The microSD needs its own SPI bus or shares SPI1 with chip-select arbitration. Since the TFT display and microSD are both SPI slaves, they can share the bus with separate CS pins: On a custom PCB you'd give the SD its own SPI0 instance for full-speed parallel access. On the T-PicoC3 prototype, bus sharing works because TFT updates (status display) are infrequent and short, while SD operations are bursty (read a sector, done). SPI clock: 25MHz for microSD in SPI mode (the SD card negotiates this during initialization). At 25MHz with single-bit SPI, theoretical throughput is ~3.1 MB/s. Real-world with protocol overhead and FAT32 cluster lookups: ~1.5-2 MB/s sustained reads. PicoClaw's 10MB binary loads in about 5-7 seconds from the SD to the host via USB MSC. PicoClaw on the MicroSD: File LayoutTotal PicoClaw footprint: ~55 MB for all platform binaries + config + prompts. On a 32GB card, that's 0.17% of storage. The rest is available for logs, workspace, and quarantined content. The Launch Script: What Ties It TogetherThis is the critical piece. The launch script is the bridge between the dumb USB drive the host sees and the secure agent environment the operator gets. It has to handle platform detection, key injection, serial port discovery, and clean teardown. #!/bin/bash
# ─────────────────────────────────────────────────────
# BABB Dongle — PicoClaw Launch Script
# Run from the mounted microSD drive:
# /mnt/babb/picoclaw/launch/launch.sh
#
# This script:
# 1. Detects host platform and architecture
# 2. Copies correct PicoClaw binary to /tmp
# 3. Discovers BABB dongle serial port
# 4. Authenticates with the dongle
# 5. Requests API keys from dongle hardware
# 6. Injects keys into runtime config
# 7. Launches PicoClaw
# 8. On exit: wipes all key material from host
# ─────────────────────────────────────────────────────
set -euo pipefail
BABB_TMP="/tmp/babb-$$"
BABB_CONFIG="$BABB_TMP/config.json"
BABB_BIN=""
BABB_PORT=""
BABB_SESSION_KEY=""
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
cleanup() {
echo "[BABB] Cleaning up..."
# Wipe the runtime config (contains decrypted API key)
if [ -f "$BABB_CONFIG" ]; then
# Overwrite with zeros before deleting
dd if=/dev/zero of="$BABB_CONFIG" bs=1 \
count=$(stat -f%z "$BABB_CONFIG" 2>/dev/null || \
stat -c%s "$BABB_CONFIG" 2>/dev/null) \
2>/dev/null
rm -f "$BABB_CONFIG"
fi
# Wipe the temp directory
if [ -d "$BABB_TMP" ]; then
rm -rf "$BABB_TMP"
fi
# Tell the dongle to end the session
if [ -n "$BABB_PORT" ] && [ -c "$BABB_PORT" ]; then
echo "SESSION_END" > "$BABB_PORT" 2>/dev/null || true
fi
echo "[BABB] Session ended. No credentials remain on this machine."
}
trap cleanup EXIT INT TERM
# ── Step 1: Detect platform ─────────────────────────
detect_platform() {
local os arch
os="$(uname -s | tr '[:upper:]' '[:lower:]')"
arch="$(uname -m)"
case "$arch" in
x86_64|amd64) arch="amd64" ;;
aarch64|arm64) arch="arm64" ;;
riscv64) arch="riscv64" ;;
*)
echo "[BABB] Unsupported architecture: $arch" >&2
exit 1
;;
esac
case "$os" in
linux) BABB_BIN="$SCRIPT_DIR/bin/picoclaw-linux-$arch" ;;
darwin) BABB_BIN="$SCRIPT_DIR/bin/picoclaw-darwin-$arch" ;;
*)
echo "[BABB] Unsupported OS: $os" >&2
exit 1
;;
esac
if [ ! -f "$BABB_BIN" ]; then
echo "[BABB] Binary not found: $BABB_BIN" >&2
echo "[BABB] Available binaries:" >&2
ls -la "$SCRIPT_DIR/bin/" >&2
exit 1
fi
echo "[BABB] Platform: $os/$arch"
echo "[BABB] Binary: $(basename "$BABB_BIN")"
}
# ── Step 2: Find dongle serial port ─────────────────
detect_dongle() {
local port=""
# Linux: by-id symlink (most reliable)
if [ -d /dev/serial/by-id ]; then
port=$(ls /dev/serial/by-id/*BABB* 2>/dev/null | head -1)
fi
# macOS: usbmodem
if [ -z "$port" ]; then
port=$(ls /dev/cu.usbmodem*BABB* 2>/dev/null | head -1)
fi
# Fallback: common CDC ACM paths
if [ -z "$port" ]; then
for p in /dev/ttyACM0 /dev/ttyACM1 /dev/ttyUSB0; do
if [ -c "$p" ]; then
port="$p"
break
fi
done
fi
if [ -z "$port" ]; then
echo "[BABB] ERROR: Dongle serial port not found" >&2
echo "[BABB] Is the dongle plugged in?" >&2
exit 1
fi
BABB_PORT="$port"
# Configure serial: raw mode, no echo
stty -F "$BABB_PORT" raw -echo 2>/dev/null || \
stty -f "$BABB_PORT" raw -echo 2>/dev/null
echo "[BABB] Dongle found: $BABB_PORT"
}
# ── Step 3: Authenticate ────────────────────────────
authenticate() {
local token response
# Check if BLE proximity auto-auth is available
echo "AUTH_CHECK" > "$BABB_PORT"
response=$(timeout 2 head -1 "$BABB_PORT" 2>/dev/null || echo "")
if echo "$response" | grep -q "AUTH: Already active"; then
echo "[BABB] Authenticated via BLE proximity"
return 0
fi
# Manual token entry
echo -n "[BABB] Enter auth token: "
read -rs token
echo
if [ -z "$token" ]; then
echo "[BABB] No token provided" >&2
exit 1
fi
echo "AUTH $token" > "$BABB_PORT"
response=$(timeout 5 head -1 "$BABB_PORT" 2>/dev/null || echo "TIMEOUT")
if echo "$response" | grep -q "Verified"; then
echo "[BABB] Authentication successful"
else
echo "[BABB] Authentication failed: $response" >&2
exit 1
fi
# Wipe token from shell variable memory
token="0000000000000000000000000000000000"
}
# ── Step 4: Request API keys from hardware ──────────
inject_keys() {
mkdir -p "$BABB_TMP"
# Copy template config from SD
cp "$SCRIPT_DIR/config/config.json" "$BABB_CONFIG"
# Request LLM provider API key from dongle
# The dongle's ESP32-C3 decrypts it from flash-encrypted
# NVS using the eFuse master key, then sends it over
# UART to RP2040, which forwards via CDC.
echo "API_KEY_REQUEST openrouter" > "$BABB_PORT"
local api_key
api_key=$(timeout 5 head -1 "$BABB_PORT" 2>/dev/null || echo "")
if [ -z "$api_key" ] || echo "$api_key" | grep -q "ERROR"; then
echo "[BABB] Failed to retrieve API key from dongle" >&2
exit 1
fi
# Strip the "API_KEY: " prefix from response
api_key="${api_key#API_KEY: }"
# Inject into runtime config, replacing sentinel
# Using sed with a temp file to avoid leaving key in
# shell history or process listings
sed -i "s/BABB_HARDWARE_INJECT/$api_key/" "$BABB_CONFIG" 2>/dev/null || \
sed "s/BABB_HARDWARE_INJECT/$api_key/" "$BABB_CONFIG" > "$BABB_CONFIG.tmp" && \
mv "$BABB_CONFIG.tmp" "$BABB_CONFIG"
# Request Brave search API key if configured
echo "API_KEY_REQUEST brave_search" > "$BABB_PORT"
local search_key
search_key=$(timeout 3 head -1 "$BABB_PORT" 2>/dev/null || echo "")
if [ -n "$search_key" ] && ! echo "$search_key" | grep -q "ERROR"; then
search_key="${search_key#API_KEY: }"
sed -i "s/BABB_HARDWARE_INJECT/$search_key/" "$BABB_CONFIG" 2>/dev/null || true
fi
# Wipe key variables from shell memory
api_key="0000000000000000000000000000000000000000"
search_key="0000000000000000000000000000000000000000"
# Set restrictive permissions on runtime config
chmod 600 "$BABB_CONFIG"
# Inject dongle serial port path for BABB tools
sed -i "s|BABB_AUTO_DETECT|$BABB_PORT|" "$BABB_CONFIG" 2>/dev/null || true
echo "[BABB] API keys injected from hardware"
}
# ── Step 5: Eject mass storage ──────────────────────
eject_drive() {
# Tell the host to stop using the drive.
# The RP2040 needs exclusive microSD access for scanning.
local mount_point
mount_point=$(mount | grep -i babb | awk '{print $3}' | head -1)
if [ -n "$mount_point" ]; then
echo "[BABB] Ejecting drive: $mount_point"
# Linux
if command -v udisksctl &>/dev/null; then
udisksctl unmount -b "$(mount | grep -i babb | awk '{print $1}' | head -1)" 2>/dev/null || true
elif command -v umount &>/dev/null; then
sudo umount "$mount_point" 2>/dev/null || true
fi
# macOS
if command -v diskutil &>/dev/null; then
diskutil unmount "$mount_point" 2>/dev/null || true
fi
# Tell dongle that MSC is ejected, safe to write SD
echo "MSC_EJECTED" > "$BABB_PORT"
sleep 0.5
echo "[BABB] Drive ejected. Dongle has exclusive SD access."
else
echo "[BABB] Drive not mounted (already ejected or running from copy)"
fi
}
# ── Step 6: Launch PicoClaw ─────────────────────────
launch_picoclaw() {
# Copy binary to temp (so we're not running from the drive
# that's about to be ejected)
cp "$BABB_BIN" "$BABB_TMP/picoclaw"
chmod +x "$BABB_TMP/picoclaw"
echo "[BABB] ─────────────────────────────────────"
echo "[BABB] PicoClaw ready. Dongle: $(basename "$BABB_PORT")"
echo "[BABB] Model: $(grep '"model"' "$BABB_CONFIG" | head -1 | \
sed 's/.*: *"//;s/".*//')"
echo "[BABB] Workspace: $BABB_TMP/workspace"
echo "[BABB] Type 'exit' or Ctrl+C to end session"
echo "[BABB] ─────────────────────────────────────"
echo
# Set HOME-like env for PicoClaw to find config
# PicoClaw looks in ~/.picoclaw/config.json
# We redirect it to our temp directory
mkdir -p "$BABB_TMP/.picoclaw"
cp "$BABB_CONFIG" "$BABB_TMP/.picoclaw/config.json"
mkdir -p "$BABB_TMP/workspace"
# Launch with redirected home
HOME="$BABB_TMP" "$BABB_TMP/picoclaw" agent "$@"
# PicoClaw exited. Cleanup trap handles the rest.
}
# ── Main ────────────────────────────────────────────
echo "[BABB] BABB Secure Agent Launcher v1.0"
echo
detect_platform
detect_dongle
authenticate
inject_keys
eject_drive
launch_picoclaw "$@"The Key Injection ProtocolWhen the launch script sends The new protocol commands for this: /* Added to babb_protocol.h */
#define CMD_API_KEY_REQUEST 0x60 /* RP→C3: request decrypted API key */
#define CMD_API_KEY_RESPONSE 0x61 /* C3→RP: decrypted key (one-shot) */
#define CMD_SESSION_START 0x62 /* RP→C3: PicoClaw session opened */
#define CMD_SESSION_END 0x63 /* RP→C3: PicoClaw session closed */
#define CMD_SCAN_QUERY 0x64 /* RP→C3: classification log query */
#define CMD_SCAN_RESULT 0x65 /* C3→RP: classification results */
#define CMD_FILE_READ 0x66 /* CDC→RP: read file from SD */
#define CMD_FILE_WRITE 0x67 /* CDC→RP: write file to SD */
#define CMD_FILE_DATA 0x68 /* RP→CDC: file content chunk */
#define CMD_MSC_STATE 0x69 /* RP internal: MSC mode toggle */PicoClaw Custom Tools: The BABB Integration LayerPicoClaw's tool system works through its config.json. When the agent decides to use a tool, PicoClaw executes it and returns the result to the LLM. For BABB integration, PicoClaw needs custom tool executors that communicate with the dongle over CDC serial. Since PicoClaw is Go, and its tool system supports external executors, the simplest integration is a small Go package or shell-script tools that PicoClaw calls. The tool definitions on the microSD tell PicoClaw what tools exist. The tool executors talk to the dongle: The tool executors are simple shell scripts that live on the microSD: #!/bin/bash
# /picoclaw/config/tools/babb_scan_executor.sh
# Called by PicoClaw when agent invokes babb_scan_report tool
PORT="${BABB_PORT:-/dev/ttyACM0}"
DATE="${1:-$(date +%Y-%m-%d)}"
FORMAT="${2:-summary}"
echo "SCAN_REPORT $DATE $FORMAT" > "$PORT"
timeout 5 head -1 "$PORT" 2>/dev/nullThe File Proxy: When PicoClaw Needs SD Access After EjectionAfter the drive is ejected and the RP2040 owns the microSD, PicoClaw can still read and write workspace files through the CDC serial interface. The RP2040 acts as a file server: This is slower than direct file access (~50 KB/s through CDC serial versus ~2 MB/s from mounted drive), but it means PicoClaw can work with dongle storage even after the host has ejected the USB drive. For the typical files involved — text reports, CSV logs, small config files — the latency is unnoticeable. Power ImplicationsThe microSD adds to the power budget: USB 2.0 provides 500mA. USB 3.0 provides 900mA. You're well within budget even at peak. The critical optimization is putting the microSD to sleep when not actively reading or writing. The RP2040 sends The launch script's eject step is also a power optimization. While the host has the drive mounted, the RP2040 is handling USB MSC SCSI commands continuously, even if no files are being read — the OS polls the device. After ejection, the MSC interface quiesces and the RP2040 can drop to a lighter processing loop. Session Lifecycle: Plug to UnplugHere's the complete timeline of what happens: What Makes This Different From a Regular USB Drive With ScriptsThree things separate this from just putting PicoClaw on a normal thumb drive: The keys never exist as files. On a normal USB drive, you'd have The dongle monitors its own output. While PicoClaw runs and the operator queries Claude through it, the LLM responses flow through the USB CDC interface. The RP2040's scanner sees that traffic. If Claude accidentally includes a client's SSN in a response, the dongle catches it, encrypts it into quarantine, and alerts the operator — all without PicoClaw or the host being involved. The scanning is below the application layer. PicoClaw doesn't need to implement content filtering because the hardware does it. The workspace is tamper-protected. Files on the microSD are written by the RP2040, which mediates all access. The file proxy validates paths (no The end result: an operator carries a single USB device that turns any computer into their secure AI-assisted workstation, with hardware-protected credentials, real-time content scanning, and a portable workspace that follows them between machines. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
List options.
Beta Was this translation helpful? Give feedback.
All reactions