Files
yolkbook/README.md
derekc dcfc605579 Update README: auth, security, env vars, project structure
- Fix authentication section: JWT now in HttpOnly cookie, not localStorage
- Fix API section: session cookie auth, no Authorization header needed
- Add SECURE_COOKIES and ALLOWED_ORIGINS to setup instructions
- Add Security section documenting headers, CORS, rate limiting, etc.
- Document server-side impersonation (no admin token in browser)
- Add logout endpoint to API table
- Update project structure with login.js
- Note HTTPS reverse proxy requirement

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 00:18:54 -07:00

8.5 KiB

🥚 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

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

  1. Clone the repo:

    git clone https://git.chns.tech/CooperandGoodman/yolkbook.git
    cd yolkbook
    
  2. Copy .env.example to .env and fill in your values:

    cp .env.example .env
    

    Key variables:

    Variable Description
    MYSQL_ROOT_PASSWORD MySQL root password — generate with openssl rand -hex 16
    MYSQL_PASSWORD MySQL app user password
    ADMIN_USERNAME Username for the built-in admin account
    ADMIN_PASSWORD Password for the built-in admin account
    JWT_SECRET JWT signing secret — generate with openssl rand -hex 32
    SECURE_COOKIES Set to true (default) when behind HTTPS; false for local HTTP testing only
    ALLOWED_ORIGINS Comma-separated list of external origins allowed to call the API. Leave empty if accessed only through the bundled nginx frontend.
  3. Start the stack:

    docker compose up -d --build
    
  4. Point your reverse proxy at port 8056 with HTTPS enabled.

  5. Open your browser at https://<your-domain>/login and 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), and SameSite=Strict (CSRF protection). Valid for 30 days.
  • Logout — the /api/auth/logout endpoint clears the session cookie server-side.
  • Admin password — always sourced from the ADMIN_PASSWORD env var. Changing it in .env and 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.

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

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)
│   │   ├── login.html
│   │   ├── admin.html
│   │   ├── index.html    # Dashboard
│   │   ├── log.html      # Log Eggs + full collection history
│   │   ├── js/
│   │   │   ├── api.js      # Shared fetch helpers
│   │   │   ├── auth.js     # Auth utilities, nav, settings modal
│   │   │   ├── login.js    # Login/register page logic
│   │   │   ├── admin.js    # Admin panel
│   │   │   ├── history.js  # Collection history table (used on log page)
│   │   │   └── dashboard.js
│   │   └── 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