Whiskers
A talking cat companion for a young child. Site: whiskers.lmjtfy.fun · code: code.lmjtfy.fun/whiskers.git. The child taps the cat, speaks, and hears a spoken answer. The parents see every conversation, get a summary of the day, set time limits, and can make Whiskers forget things.
Status: a working demo. The Rust core, the service (whiskersd) and the Android app run and
their tests pass. Speech recognition, the camera and the natural voice depend on the device and
are not verified on every Android flavour; the target is an Amazon Fire HD Kids tablet (Fire OS 8,
Android 11, no Google services).
How it is meant to be used
- A Kotlin app is installed on the child's tablet. The mascot is a robot cat; the child taps it, it shows it is listening, transcribes the child's voice, and sends the text on.
- The first time the app is opened the parents open About your child (behind the grown-up lock) and enter the child's name and age. Everything Whiskers says is written for that child: the greeting, the persona, how carefully each message is judged, the parents' summary. Until it is filled in Whiskers speaks neutrally and judges as carefully as it would for the youngest supported age (3).
- Every conversation is logged and visible to the parents; that is a requirement, not an option. Nothing reaches the child without passing the guard, and a failure of the model, the guard or the network ends in a safe spoken fallback, never in silence or raw error text.
- The grown-ups choose daily limits, quiet hours, a thinking allowance and a PIN. These, and the child's profile, are one document the devices keep level with the service.
- It remembers things the child mentions (a toy's name, a friend), shows them to the parents, and lets them be forgotten. The child can show it pictures; they are described by a separate model call and the description is judged before the cat sees anything.
What it looks like
These are the Android screenshot tests' goldens, so they are always the current look (all of them:
android/README.md).
| the grey cat listening | the ginger cat speaking, with captions | the parents' side |
|---|---|---|
![]() | ![]() | ![]() |
Build it
Use mise: it installs the whole toolchain from
mise.toml, the one place every version lives, and runs every task. It needs only git and a C compiler on the machine; no root.
mise trust
mise install
mise run check
mise run android:debug
mise run dev
mise tasks
mise install fetches Rust (with the Android and wasm targets), the Android SDK tools, a JDK, Gradle, Node, wrangler and the other tools once; the first run is large. mise run check runs the fast gates. mise run android:debug fetches the Android platform and NDK it needs and builds the APK. mise run dev starts the service and the website under a supervisor. mise tasks lists everything else (tests, screenshot goldens, release APKs, git hooks, the preflight check). The git hooks are mise run hooks:install; the service's secrets can come from fnox, see tools/README.md.
Optional: nix
Nix is not required. If you already use it, the flake wraps the same toolchain: nix develop gives a shell with mise and the native tools a downloaded toolchain needs, then everything above works the same. packages.whiskersd builds the service. See tools/README.md.
Architecture
One Rust core of portable crates with no user interface, and a thin native shell per platform. Conversation state, memory, the log, the guard policy and the gateway client are in the core; a Kotlin and Jetpack Compose shell draws the cat and listens. Never a web view or a self-drawing framework.
| Part | What |
|---|---|
crates/ | The Rust core, whiskersd (the service that holds the keys) and whiskers-art (the grey cat's drawing, shared by the app and the website). |
android/ | The app. |
assets/ | The ginger cat's SVGs and the script that writes them, and mise's logo. |
tools/ | Build, install and operate scripts. |
mise.toml, hk.pkl, fnox.toml | The toolchain, environment, tasks and daemons; the git hooks; the secrets whiskersd needs (declared, no values). |
flake.nix | Optional nix wrapper: a shell with mise, and the service package. |
The model is reached through a gateway you run (an Anthropic Messages API), proxied by whiskersd
so the tablet holds no keys. The guard is a Jev (TypeSafe) judge; the natural voice is ElevenLabs
(optional; the device falls back to an on-device voice). whiskersd needs your own keys and
addresses: see crates/whiskersd/README.md and
android/README.md.
The website
web/ is the public page, a Rust and Datastar Cloudflare Worker for
whiskers.lmjtfy.fun, in the style of lmjtfy. It is live; see its README.
Other hosts for the backend
backend/ holds the adapters of the ports that run on Cloudflare and on celld (a Worker with one Durable
Object per household), each its own workspace, and the tool that checks every host against the same questions. Not deployed.
Privacy of the repository
Nothing about a particular family or machine belongs in this repository: names, addresses, device
serials and tailnet or LAN details are configuration, not code. tools/check-private.sh fails if a
term from your own gitignored .private-terms file appears in any tracked file; see
tools/README.md.


