Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🥚 Yolkbook
A self-hosted, multi-user web app for backyard chicken keepers to track egg production, flock size, feed costs, and egg economics over time.
Features
- Dashboard — at-a-glance stats: total eggs, 7/30-day totals, averages (green), flock size (orange), cost per egg and per dozen
- Daily log — record egg collections with one entry per day; includes full collection history with date filtering, edit, and delete below the form
- Flock management — track changes to your flock size over time so per-hen averages stay accurate
- Feed tracking — log feed purchases (bags + price per bag)
- Budget — cost per egg and cost per dozen, all-time and over the last 30 days
- Monthly summary — month-by-month breakdown of production, averages, feed cost, and cost per egg
- Multi-user — each user has their own isolated data; self-registration on the login page
- Admin panel — view all users, reset passwords, disable/enable accounts, delete accounts, and log in as any user
- Timezone support — each user sets their own timezone so dates and stat windows are always accurate
- Push notifications — optional ntfy alerts for security events: new registrations, admin logins, account lockouts, user changes, and impersonation
Tech Stack
| Layer | Technology |
|---|---|
| Frontend | Vanilla HTML/CSS/JS |
| Backend | FastAPI (Python) |
| Database | MySQL 8.0 |
| Server | Nginx (static + proxy) |
| Runtime | Docker Compose |
Getting Started
Prerequisites
- Docker and Docker Compose
- A reverse proxy with HTTPS (e.g. Nginx Proxy Manager) — required for secure cookie authentication
Setup
-
Clone the repo:
git clone https://git.chns.tech/CooperandGoodman/yolkbook.git cd yolkbook -
Copy
.env.exampleto.envand fill in your values:cp .env.example .envRequired variables:
Variable Description MYSQL_ROOT_PASSWORDMySQL root password — generate with openssl rand -hex 16MYSQL_DATABASEDatabase name (default: eggtracker)MYSQL_USERMySQL app username MYSQL_PASSWORDMySQL app user password ADMIN_USERNAMEUsername for the built-in admin account ADMIN_PASSWORDPassword for the built-in admin account JWT_SECRETJWT signing secret — generate with openssl rand -hex 32Optional variables (with defaults):
Variable Default Description SECURE_COOKIEStrueSet to falsefor local HTTP testing only; leavetruewhen behind HTTPSALLOWED_ORIGINS(empty) Comma-separated list of external origins allowed to call the API. Leave empty if accessed only through the bundled nginx frontend. NTFY_URL(empty) ntfy topic URL for push notifications (e.g. https://ntfy.sh/your-secret-topic). Leave empty to disable.NTFY_TOKEN(empty) Bearer token for authenticated ntfy topics. Leave empty if your topic is public or not needed. -
Start the stack:
docker compose up -d --build -
Point your reverse proxy at port
8056with HTTPS enabled. -
Open your browser at
https://<your-domain>/loginand sign in with the admin credentials you set in.env.
The database schema is applied automatically on first start via mysql/init.sql. The admin user is created (or synced) automatically every time the API starts.
Authentication
- Login / Register — the landing page (
/login) has both a sign-in form and a self-registration link. - Sessions — after login, a session cookie is set by the server. It is
HttpOnly(inaccessible to JavaScript),Secure(HTTPS only), andSameSite=Strict(CSRF protection). Valid for 30 days. - Logout — the
/api/auth/logoutendpoint clears the session cookie server-side. - Admin password — always sourced from the
ADMIN_PASSWORDenv var. Changing it in.envand restarting updates the admin's password automatically.
Admin Panel
Accessible at /admin for admin accounts. Features:
| Action | Description |
|---|---|
| Reset password | Set a new password for any user |
| Disable / Enable | Block or restore a user's access |
| Delete | Permanently remove a user and all their data |
| Login As | Impersonate a user to view or edit their data directly |
When impersonating a user, an amber banner appears in the nav with a Return to Admin button. The original admin session is preserved server-side — no admin credentials are stored in the browser during impersonation.
Push Notifications
Yolkbook can send alerts via ntfy for security-relevant events. Set NTFY_URL in .env to enable.
| Event | Priority |
|---|---|
| New user registered | default |
| Admin login | high |
| Account locked after failed attempts | urgent |
| Login attempt on locked account | high |
| User disabled | high |
| User deleted | urgent |
| Admin impersonation started | high |
Use https://ntfy.sh/your-secret-topic for the hosted service or a self-hosted ntfy URL. Set NTFY_TOKEN if your topic requires authentication. Notifications are fire-and-forget — a failed delivery is logged but never interrupts a request.
User Settings
The gear icon (⚙) in the top-right nav opens the Settings panel:
- Timezone — choose from a full list of IANA timezones or click Detect automatically. Affects what "today" is when pre-filling date fields and the 30-day/7-day windows on the dashboard and budget pages.
- Change Password — update your own password (requires current password).
Security
| Feature | Details |
|---|---|
| Authentication | HttpOnly + Secure + SameSite=Strict session cookie; no token in JS-accessible storage |
| CSRF protection | SameSite=Strict cookie prevents cross-site request forgery without explicit tokens |
| Password hashing | bcrypt |
| CORS | Locked to same origin by default; configurable via ALLOWED_ORIGINS |
| Rate limiting | Login: 5 req/min · Register: 3 req/min · Admin endpoints: 10 req/min (nginx) |
| Login lockout | Account locked for 15 minutes after 5 consecutive failed attempts |
| Security headers | X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, Referrer-Policy, Content-Security-Policy |
| Subresource Integrity | Chart.js CDN script pinned with SHA-384 hash |
| Input validation | Server-side via Pydantic; all user-rendered content HTML-escaped |
| SQL injection | SQLAlchemy ORM with parameterized queries throughout |
| Container security | API runs as non-root user; all volume mounts read-only except database |
Footer
All authenticated pages include a footer with a Created by: CHNS.tech credit and a Buy Me a Coffee button linking to buymeacoffee.com/CHNS.
Migrating an Existing Install (pre-multi-user)
If you have an existing single-user install, run the migration script before rebuilding:
# 1. Run the migration while the database is still running
docker compose exec db mysql -u root -p"${MYSQL_ROOT_PASSWORD}" eggtracker < mysql/migrate_v2.sql
# 2. Rebuild and restart
docker compose up -d --build
All existing data will be automatically assigned to the admin account on first startup.
API
The FastAPI backend is available at /api. Interactive docs (Swagger UI) are at /api/docs.
| Prefix | Description |
|---|---|
/api/auth |
Login, logout, register, change password, timezone |
/api/admin |
User management (admin only) |
/api/eggs |
Egg collection records |
/api/flock |
Flock size history |
/api/feed |
Feed purchase records |
/api/other |
Other purchases (bedding, snacks, etc.) |
/api/stats |
Dashboard, budget, and monthly summary |
All data endpoints require an authenticated session cookie. Requests are automatically authenticated by the browser — no Authorization header is needed.
Project Structure
yolkbook/
├── backend/
│ ├── main.py # FastAPI app entry point + startup seeding
│ ├── auth.py # JWT utilities, password hashing, cookie helpers, auth dependencies
│ ├── models.py # SQLAlchemy models
│ ├── schemas.py # Pydantic schemas
│ ├── database.py # DB connection
│ ├── routers/
│ │ ├── auth_router.py # /api/auth — login, logout, register, settings
│ │ ├── admin.py # /api/admin — user management + impersonation
│ │ ├── eggs.py
│ │ ├── flock.py
│ │ ├── feed.py
│ │ ├── other.py
│ │ └── stats.py
│ ├── requirements.txt
│ └── Dockerfile
├── nginx/
│ ├── html/ # Frontend (HTML, CSS, JS)
│ │ ├── index.html # Dashboard
│ │ ├── log.html # Log Eggs + full collection history
│ │ ├── flock.html # Flock management
│ │ ├── budget.html # Budget / cost analysis
│ │ ├── summary.html # Monthly summary
│ │ ├── admin.html # Admin panel
│ │ ├── login.html
│ │ ├── 404.html
│ │ ├── 50x.html
│ │ ├── js/
│ │ │ ├── api.js # Shared fetch helpers
│ │ │ ├── auth.js # Auth utilities, nav, settings modal
│ │ │ ├── login.js # Login/register page logic
│ │ │ ├── dashboard.js
│ │ │ ├── log.js # Egg logging interface
│ │ │ ├── history.js # Collection history table (used on log page)
│ │ │ ├── flock.js
│ │ │ ├── budget.js
│ │ │ ├── summary.js
│ │ │ └── admin.js # Admin panel
│ │ └── css/style.css
│ └── nginx.conf
├── mysql/
│ ├── init.sql # Schema for fresh installs
│ └── migrate_v2.sql # Migration for pre-multi-user installs
├── .env.example # Template — copy to .env and fill in values
├── docker-compose.yml
└── .env # Secrets — not committed