Files
sustenance/README.md
T

3.4 KiB

Sustenance

A small shared grocery list built with Rust, Axum, Maud, htmx, WebSockets, and SQLite.

Run locally

cargo run

Open http://localhost:3000. The application creates sustenance.db in the working directory on first start.

Note: use localhost (not 127.0.0.1) when testing passkeys locally — browsers reject IP addresses as WebAuthn RP IDs. The app defaults to localhost for loopback hosts, so passkeys work out of the box when you access the site via http://localhost:3000.

Configuration

Variable Default Purpose
DATABASE_PATH sustenance.db SQLite database path
BIND_ADDRESS 127.0.0.1:3000 Listen address
PUBLIC_BASE_URL derived from BIND_ADDRESS Base URL used in invitation links
COOKIE_SECURE false Add the Secure attribute to session cookies
REGISTRATION_MODE invite_only Use open for local development; otherwise registration requires a valid list invitation after the first account
SEED_CONFIG seed.json Optional JSON file with a default user to create when the database is first initialized
RP_ID derived from PUBLIC_BASE_URL WebAuthn relying party ID (the host users access the site from)
RP_NAME Sustenance WebAuthn relying party name shown to users
RUST_LOG sustenance=info,tower_http=info Log filter; HTTP requests are logged at info level

Seeding a default user

If a JSON config file exists at the path given by SEED_CONFIG (default seed.json), Sustenance creates the configured user on startup when the database has no users yet. The file is optional — if it is missing or invalid, seeding is silently skipped.

{
  "user": {
    "email": "you@example.com",
    "display_name": "You",
    "password": "a-strong-password"
  }
}

Current features

  • Email/password accounts with Argon2 password hashes
  • Optional WebAuthn passkeys for passwordless sign-in (managed from the account page)
  • Cookie-backed sessions and CSRF tokens for list mutations
  • Shared lists with one-time, seven-day invitation links
  • Invite-only registration by default after the first account
  • Add, edit, check, and delete grocery items
  • Global categories with common defaults seeded at startup and custom category creation
  • Items grouped by category and assigned from the add/edit forms
  • Meals with ingredients, markdown descriptions, and one-click "add meal to list"
  • Rewards cards with store name and number, rendered as scannable Code 128 / Code 39 barcodes
  • Server-authoritative last-write-wins updates
  • Per-list WebSocket updates with server-rendered htmx fragments
  • In-memory presence for members currently viewing a list
  • Responsive layout for phone, tablet, and desktop

The htmx scripts are currently loaded from unpkg. They can be vendored into static/ before production deployment.

Verification

cargo fmt --all -- --check
cargo check
cargo test

End-to-end tests (Playwright)

The e2e tests live in e2e/ and use Playwright with a real browser. Each test starts its own server against a fresh, throwaway database on a unique port, so tests are fully isolated from each other and from your real sustenance.db.

The tests launch target/debug/sustenance, so build the server first:

# one-time setup
cargo build
cd e2e
npm install
npx playwright install chromium

# run the tests (each test launches its own server against a fresh DB)
cd e2e
npx playwright test