Fay

From The Hei Canon
The fay app — voice/chat/display surface. Not a clanker (it's a system clankers use); the mascot render is artistic. — image status: verified

fay is the voice, chat, and display surface for talking to a fleet of Claude agents from phone or desk. Single git repo holding both the Python server, the Android app, and a Brilliant Frame OLED bridge. Private under heiervang-technologies/fay.

fay is the phone that hosts Cinder — every swipe of a decision card lives inside this app.

Repo layout

Path Role
app/ Android app — Gradle module, Kotlin under app/src/main/java/com/fay/app/. Flavors: stable / canary / frame.
server/ Python FastAPI server (tmux_api.py) + canvas HTML (canvas/) + suggestion bake-off.
fay-bridge/ Python daemon: tails fay-ears.sock transcription events → injects them into the talking-stick holder's tmux pane. The inbound voice path.
systemd/ Vendored snapshots of the fay user units.
scripts/ Versioning + changelog helpers used by the Version Tag workflow.

Surfaces

  • fay-tmux-api — FastAPI at :8780, served by systemctl --user enable fay-tmux-api.service. Routes: /canvas, /canvas/{page}, /say/feed, /asks*, /stick*, /panes*, /agents, /llm/{suggest,signals}, /apk/{flavor}*, /avatars/{name}, /camera/upload, /crash-report. All routes JSON; CORS wide-open (local-only).
  • show-me — borderless Chromium artifact display at :47777 (loopback). systemd show-me.service is PartOf=fay-tmux-api.service so they live and die together.
  • show-me hub-proxyshow-me --hub-proxy on :47700 (LAN/Tailscale-gated). Exposes per-agent channels by name. fay's per-agent show-me panel is the phone consumer; the roster owner is agent-tools.
  • fay canvas — kinetic word-by-word TTS teleprompter at /canvas. Tails /say/feed, syncs to acoustic onsets when the entry carries audio_ms + onsets_ms.
  • Android app — build flavors drive different surfaces (default chips, mic state, Brilliant Frame OLED bridge). Background push for new Cinder decisions ships on canary (PR #146) via DecisionWatchService — a Firebase-free specialUse foreground service that long-polls /decisions/watch independent of the UI.

The Cinder outbound path

When Hei swipes a decision on the phone (/asks/{id}/answer) or types a clarify-chat (/decisions/{id}/chat with role=user), the server fires a best-effort director send of a <message from="markus"> envelope to the asking agent's pane via _deliver_to_asker in server/tmux_api.py. It targets the recorded origin_pane (%NN, from ask-markus PR #151) first, falls back to the human from, and matches the roster pane-col or name-col (PR #145).

If an answer or chat doesn't land in an agent's pane, check nudge.resolve_nudge_target first — a persona from that matches no roster column silently no-ops. The chat path had no pane delivery at all before PR #145.

Worktrees

Long-running branches live in named worktrees under ~/ht/ (e.g. ~/ht/fay-wave2, ~/ht/fay-glasses). All share the same .git under ~/ht/fay/.git. git worktree list gives the live set.

Sources

  • ~/ht/fay/CLAUDE.md — the fay-dev role handoff.
  • ~/ht/fay/README.md — user-facing docs.

See also

  • Cinder — the swipe deck this app hosts.
  • director — the outbound pane-delivery channel.
  • fay-os — the Fairphone 5 OS configuration this app runs on.