282ad9b4d7609a4da4461ce5a8d2b34ee93fa449
Browsers need color-scheme: dark on the document (html/body) to properly style native form controls like select dropdown options. Previously, color-scheme was only set on select elements themselves, which didn't propagate to the OS-rendered dropdown options. Added bodykw to fast_app() to set color-scheme: dark on body element. This tells the browser the entire page prefers dark mode, and native controls use dark system colors. Includes E2E tests verifying body and select elements have dark color-scheme. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
AnimalTrack
Event-sourced animal tracking for poultry farm management. Built with FastHTML, MonsterUI, and SQLite.
Features
- Egg Collection - Quick capture of daily egg counts by location
- Feed Tracking - Record purchases and distribution with cost tracking
- Animal Registry - Track cohorts, movements, and lifecycle events
- Historical Queries - Point-in-time resolution using interval projections
- Event Sourcing - Full audit trail with edit/delete support
Development Setup
Prerequisites
Quick Start
# Clone the repo
git clone <repo-url>
cd animaltrack
# Enter dev environment
direnv allow # or: nix develop
# Run the server
animaltrack serve
The server starts at http://localhost:3366 (3366 = EGG in leetspeak).
CLI Commands
# Run database migrations
animaltrack migrate
# Load seed data (users, locations, species, products, feed types)
animaltrack seed
# Start the web server
animaltrack serve [--port 3366] [--host 0.0.0.0]
# Create a new migration
animaltrack create-migration "add users table"
Running Tests
pytest tests/ -v
Deployment
Docker
Build the Docker image using Nix:
nix build .#dockerImage
docker load < result
Run the container:
docker run -d \
-p 8080:3366 \
-v /data/animaltrack:/var/lib/animaltrack \
-e CSRF_SECRET="your-secret-here" \
-e TRUSTED_PROXY_IPS="10.0.0.1,10.0.0.2" \
gitea.v.paler.net/ppetru/animaltrack:latest
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
CSRF_SECRET |
Yes | - | Secret for CSRF token generation |
DB_PATH |
No | animaltrack.db |
SQLite database path |
PORT |
No | 3366 |
Server port |
AUTH_HEADER_NAME |
No | X-Oidc-Username |
Header containing username from reverse proxy |
TRUSTED_PROXY_IPS |
No | (empty) | Comma-separated IPs that can set auth headers |
CSRF_COOKIE_NAME |
No | csrf_token |
CSRF cookie name |
SEED_ON_START |
No | false |
Run seeds on startup |
LOG_LEVEL |
No | INFO |
Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL) |
METRICS_ENABLED |
No | true |
Enable Prometheus metrics at /metrics |
DEV_MODE |
No | false |
Bypasses auth, sets default user for development |
BASE_PATH |
No | / |
URL base path for reverse proxy setups |
Reverse Proxy Configuration
AnimalTrack expects authentication to be handled by a reverse proxy (e.g., Authelia, Authentik, oauth2-proxy). The proxy should:
- Authenticate users via OIDC/OAuth2
- Forward the username in
X-Oidc-Usernameheader (configurable viaAUTH_HEADER_NAME) - Be listed in
TRUSTED_PROXY_IPSto be trusted
Example Caddy configuration:
animaltrack.example.com {
forward_auth authelia:9091 {
uri /api/verify?rd=https://auth.example.com
copy_headers Remote-User Remote-Groups Remote-Email
header_up X-Oidc-Username {http.auth.resp.Remote-User}
}
reverse_proxy animaltrack:3366
}
Data Persistence
Mount a volume to /var/lib/animaltrack for the SQLite database:
-v /path/on/host:/var/lib/animaltrack
The database file will be created at $DB_PATH (default: /var/lib/animaltrack/animaltrack.db in Docker).
Health Check
GET /healthz- Returns 200 if database is writable, 503 otherwiseGET /metrics- Prometheus metrics (whenMETRICS_ENABLED=true)
Architecture
- Event Sourcing - All state changes are events. Events are immutable.
- Projections - Materialized views updated synchronously in transactions.
- Interval Tables - Track animal locations, tags, and attributes over time.
- ULID IDs - Time-ordered, sortable, unique identifiers.
- Integer Timestamps - Milliseconds since Unix epoch for all timestamps.
- Integer Money - All prices stored as cents for precision.
License
Proprietary - All rights reserved.
Description
Languages
Python
99.3%
Nix
0.6%
JavaScript
0.1%