- Python 53.1%
- TypeScript 44.4%
- CSS 1%
- JavaScript 0.7%
- Dockerfile 0.5%
- Other 0.3%
Moves the assistant's Anthropic call/tool-loop and the voice input's speech-recognition handling onto packages shared with HomeManagement/homeclaude/kittycharactersheet, so fixes (like the Android dictation-duplication bug) are maintained in one place instead of separately in each app. - api/app/services/assistant.py: run_turn's hand-rolled httpx loop (_call_anthropic) replaced with ClaudeClient.chat() from claude-chat-client-py, bridged via asyncio.run() since this backend is fully synchronous (sync Session, threading.Lock) unlike HomeManagement's async one. Same tool-execution semantics (reads execute immediately, writes become proposals), same error messages for auth/rate-limit/ connection failures. - api/tests/: the 12 tests that monkeypatched _call_anthropic directly now monkeypatch ClaudeClient.chat via a shared conftest.py helper. Rewriting these surfaced two pre-existing bugs in test_care_log_tools_respect_access: it set the Anthropic key via the caretaker client against an admin-only endpoint (silently 403'd, so the test's real assertions never ran), and its "Donna not in system" check collided with a hardcoded example name in the tool description text unrelated to actual recipient visibility. Both fixed; the test now actually exercises the access-control path it claims to. - web/src/components/AssistantPanel.tsx: WebSpeechProvider (continuous= false, avoiding the Android bug by never entering continuous mode) replaced with the shared useSpeechToText. This is a real UX change, not just a swap: continuous mode means the caretaker can pause mid-sentence without losing their place, but there's no more "send automatically on natural pause" - tapping Stop now marks the utterance done and sends it. Recipient-name grammar hints (best-effort Web Speech API feature) preserved via @brian/claude-chat-ui's new optional `hints` support (published as v0.1.2) rather than silently dropped. - api/Dockerfile, .forgejo/workflows/build-deploy.yaml: both packages come from git.thenymans.com's package registry rather than PyPI/npmjs, threaded through as BuildKit secrets (docker buildx build, not plain docker build - needs docker-cli-buildx + an explicit docker-container builder for --secret support). Validated structurally with dummy credentials against the real registry (got real 401s, confirming the URLs/auth plumbing are correct, not just that something responded). |
||
|---|---|---|
| .forgejo/workflows | ||
| api | ||
| deploy | ||
| web | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| CLAUDE.md | ||
| docker-compose.yml | ||
| README.md | ||
CareBoard
A self-hosted board for one person coordinating care for several other people. One question, answered within two seconds of opening it: what do I need to do today?
- Today — a ruled planner page: everything happening today across everyone, color-tabbed by person, overdue items floating to the top. Medications due today are pulled live from MedSync and can be marked taken/skipped (with confirmation) right from the list.
- Calendar — agenda list by default (month grid available), filtered by tapping people's name chips. The filter is the combined/separate mode.
- People — the recipients of care. Archived, never deleted.
- Admin — accounts, settings (encrypted at rest, write-only secrets, test buttons), and the audit log.
Light mode is a warm paper day-planner; dark mode matches a phone-widget agenda. Installable as a PWA. Designed at 390px first.
Stack
FastAPI + SQLAlchemy + Alembic + PostgreSQL 16 · React 18 + Vite + TypeScript + Tailwind · single production container (static web served by the API, one origin) behind Caddy.
Configuration
The environment holds exactly three variables (see .env.example):
DATABASE_URL, SESSION_SECRET, ENCRYPTION_KEY. Everything else — Anthropic
key, MedSync URL/key, timezone, app name — lives in the database and is edited
in Admin → Settings. Secrets are Fernet-encrypted and write-only: the API never
returns them, only •••• and the last four characters.
Run it
cp .env.example .env
# fill in SESSION_SECRET and ENCRYPTION_KEY (commands are in the file)
docker compose up -d --build
Open http://localhost:8080 — the first visit serves a one-time setup page that creates the admin account. After that, registration is closed; admins create caretaker accounts in Admin → People.
MedSync link-up
- In MedSync, create an API key (
msk_…) for a user who is a caretaker of the relevant patients. - In CareBoard, Admin → Settings: set the MedSync address and paste the key, then press Test connection.
- On each person's edit sheet, set their MedSync patient number. Their due medications appear on Today.
Development
# api
cd api && python -m venv .venv && .venv/bin/pip install -r requirements.txt pytest
DATABASE_URL=postgresql+psycopg://careboard:careboard@localhost:5432/careboard \
SESSION_SECRET=$(openssl rand -hex 32) \
ENCRYPTION_KEY=$(.venv/bin/python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())") \
.venv/bin/uvicorn app.main:app --reload
# web (proxies /api to :8000)
cd web && npm install && npm run dev
# tests
cd api && .venv/bin/python -m pytest
Migrations: alembic revision --autogenerate -m "..." for every schema change.
No create_all().
Not yet built (by design, in order)
7–9. The assistant (typed, then writes-with-confirmation, then voice input).
10. Deeper MedSync links. 11. ✅ (audit view shipped early — it was cheap).
12. Voice output — last, SpeechSynthesis only, off by default.