---
title: "On-Device Assistant"
url: "https://toddpaulbrownjr.com/builds/on-device-assistant/"
author: "Todd Paul Brown Jr."
description: "An on-device AI resident built on OpenClaw. Every security probe passed. Parked: it needed babysitting to be worth having, which defeated the purpose."
kind: "build"
status_word: "parked"
revised: "2026-09-28T06:34:48+00:00"
origin: "written"
canonical: true
updated: "2026-09-28T06:34:48+00:00"
---

# On-Device Assistant

## What it is

A local-first desktop app that gave a persistent AI character a home on my own machine, running on a local model over a plugin layer on top of OpenClaw, a real open-source personal-assistant gateway. It was built to nudge rhythm (lunch, hydration, movement, bedtime, meeting warnings), hold light conversation, and read my notes vault for context, never write to it.

It's **parked**. Four active build days in an 11-day span, then 55 days of silence.

**Why it's on this site:** parked, with the reason stated plainly — it was a resource hog that had a hard time holding continuity, and typically failed to provide value unless I was babysitting it, which defeated the purpose. Revival condition: a strong, capable model I can actually run on my laptop.

## Why it exists

Two directives governed the build: "read everything, write almost nothing," and the model should not determine event detection. The rhythm-tracking engine that decided when to nudge me was deterministic code with no model call in it at all; the model only supplied the wording.

## How it's built

The project ran as a gated, four-phase workflow — architecture review, security review, implementation plan, then MVP build and verification — each phase its own document. A verification pass at install time ran a vault escape-probe suite: 7 of 7 checks passed, including a protected key file that refused to surface and an out-of-vault path that got rejected.

## What broke

Security wasn't the problem; usefulness was. The local model would execute a tool call and fail to produce reply text, a known local-model failure mode I never got stable. Three platform bugs surfaced by testing live behavior, not by reading docs: an SDK-imported function that was a silent no-op because the plugin loader compiled it into a separate module instance from the one the gateway actually read; a global setting that stripped 13 tools before its own allow-list was consulted; and a login screen that looked stuck because a CSS rule painted an overlay with no rule to hide it, so the app was connecting successfully behind a screen that only looked broken. The fix in each case was small once found; finding them wasn't.

## Receipts

| Figure | What | Source |
|---|---|---|
| 7 of 7 | vault security escape probes passed | verification record |
| 4 / 55 | active build days / days of silence before this page was written | file timestamps |
| 5,798 | lines of source code, zero automated tests | file count |
| 1 | journal entry the assistant ever wrote | file listing |

All measured. More on the [receipts page](/receipts/).
