whiskers.git / README.md
1# Whiskers
2
3A talking cat companion for a young child. Site: [whiskers.lmjtfy.fun](https://whiskers.lmjtfy.fun) · code: [code.lmjtfy.fun/whiskers.git](https://code.lmjtfy.fun/whiskers.git). The child taps the cat, speaks, and hears a spoken
4answer. The parents see every conversation, get a summary of the day, set time limits, and can
5make Whiskers forget things.
6
7**Status:** a working demo. The Rust core, the service (`whiskersd`) and the Android app run and
8their tests pass. Speech recognition, the camera and the natural voice depend on the device and
9are not verified on every Android flavour; the target is an Amazon Fire HD Kids tablet (Fire OS 8,
10Android 11, no Google services).
11
12## How it is meant to be used
13
14- A Kotlin app is installed on the child's tablet. The mascot is a robot cat; the child taps it,
15  it shows it is listening, transcribes the child's voice, and sends the text on.
16- The first time the app is opened the parents open **About your child** (behind the grown-up
17  lock) and enter the child's name and age. Everything Whiskers says is written for that child:
18  the greeting, the persona, how carefully each message is judged, the parents' summary.
19  Until it is filled in Whiskers speaks neutrally and judges as carefully as it would for the
20  youngest supported age (3).
21- Every conversation is logged and visible to the parents; that is a requirement, not an option.
22  Nothing reaches the child without passing the guard, and a failure of the model, the guard or the
23  network ends in a safe spoken fallback, never in silence or raw error text.
24- The grown-ups choose daily limits, quiet hours, a thinking allowance and a PIN. These, and the
25  child's profile, are one document the devices keep level with the service.
26- It remembers things the child mentions (a toy's name, a friend), shows them to the parents, and
27  lets them be forgotten. The child can show it pictures; they are described by a separate model
28  call and the description is judged before the cat sees anything.
29
30## What it looks like
31
32These are the Android screenshot tests' goldens, so they are always the current look (all of them:
33[`android/README.md`](android/README.md)).
34
35| the grey cat listening | the ginger cat speaking, with captions | the parents' side |
36|---|---|---|
37| ![](android/app/screenshots/listening.png) | ![](android/app/screenshots/ginger-speaking.png) | ![](android/app/screenshots/parents.png) |
38
39## Build it
40
41<img src="assets/mise-logo.svg" alt="mise" height="32" align="left"> Use [mise](https://mise.jdx.dev): it installs the whole toolchain from [`mise.toml`](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.
42
43    mise trust
44    mise install
45    mise run check
46    mise run android:debug
47    mise run dev
48    mise tasks
49
50`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](fnox.toml), see [tools/README.md](tools/README.md).
51
52### Optional: nix
53
54Nix 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](tools/README.md).
55
56## Architecture
57
58One Rust core of portable crates with no user interface, and a thin native shell per platform.
59Conversation state, memory, the log, the guard policy and the gateway client are in the core; a
60Kotlin and Jetpack Compose shell draws the cat and listens. Never a web view or a self-drawing
61framework.
62
63| Part | What |
64|---|---|
65| [`crates/`](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). |
66| [`android/`](android/) | The app. |
67| [`assets/`](assets/) | The ginger cat's SVGs and the script that writes them, and mise's logo. |
68| [`tools/`](tools/) | Build, install and operate scripts. |
69| `mise.toml`, `hk.pkl`, `fnox.toml` | The toolchain, environment, tasks and daemons; the git hooks; the secrets whiskersd needs (declared, no values). |
70| `flake.nix` | Optional nix wrapper: a shell with mise, and the service package. |
71
72The model is reached through a gateway you run (an Anthropic Messages API), proxied by `whiskersd`
73so the tablet holds no keys. The guard is a Jev (TypeSafe) judge; the natural voice is ElevenLabs
74(optional; the device falls back to an on-device voice). `whiskersd` needs your own keys and
75addresses: see [crates/whiskersd/README.md](crates/whiskersd/README.md) and
76[android/README.md](android/README.md).
77
78## The website
79
80[`web/`](web/) is the public page, a Rust and Datastar Cloudflare Worker for
81[whiskers.lmjtfy.fun](https://whiskers.lmjtfy.fun), in the style of lmjtfy. It is live; see its README.
82
83## Other hosts for the backend
84
85[`backend/`](backend/) holds the adapters of the ports that run on Cloudflare and on celld (a Worker with one Durable
86Object per household), each its own workspace, and the tool that checks every host against the same questions. Not deployed.
87
88## Privacy of the repository
89
90Nothing about a particular family or machine belongs in this repository: names, addresses, device
91serials and tailnet or LAN details are configuration, not code. `tools/check-private.sh` fails if a
92term from your own gitignored `.private-terms` file appears in any tracked file; see
93[tools/README.md](tools/README.md).