No description
  • Python 53.1%
  • TypeScript 44.4%
  • CSS 1%
  • JavaScript 0.7%
  • Dockerfile 0.5%
  • Other 0.3%
Find a file
Brian 5aabd3476a
All checks were successful
Build and Deploy / test-backend (push) Successful in 35s
Build and Deploy / build (push) Successful in 25s
Build and Deploy / deploy (push) Successful in 47s
Adopt shared claude-chat-client-py / @brian/claude-chat-ui packages
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).
2026-07-20 16:07:29 -06:00
.forgejo/workflows Adopt shared claude-chat-client-py / @brian/claude-chat-ui packages 2026-07-20 16:07:29 -06:00
api Adopt shared claude-chat-client-py / @brian/claude-chat-ui packages 2026-07-20 16:07:29 -06:00
deploy ci: add Forgejo build/test/deploy pipeline 2026-07-17 12:30:47 -06:00
web Adopt shared claude-chat-client-py / @brian/claude-chat-ui packages 2026-07-20 16:07:29 -06:00
.dockerignore Initial commit: CareBoard care-coordination app 2026-07-17 11:47:54 -06:00
.env.example Initial commit: CareBoard care-coordination app 2026-07-17 11:47:54 -06:00
.gitignore Initial commit: CareBoard care-coordination app 2026-07-17 11:47:54 -06:00
CLAUDE.md Initial commit: CareBoard care-coordination app 2026-07-17 11:47:54 -06:00
docker-compose.yml Initial commit: CareBoard care-coordination app 2026-07-17 11:47:54 -06:00
README.md Initial commit: CareBoard care-coordination app 2026-07-17 11:47:54 -06:00

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.

  1. In MedSync, create an API key (msk_…) for a user who is a caretaker of the relevant patients.
  2. In CareBoard, Admin → Settings: set the MedSync address and paste the key, then press Test connection.
  3. 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)

79. 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.