A federated recipe sharing platform built with Rust and ActivityPub.
- User Authentication: Secure registration, login, and session management with email verification
- Recipe Management: Create, edit, and organize recipes with ingredients, instructions, and images
- Recipe Books: Organize recipes into collections
- Social Features: Follow users, save recipes, share with activity feed
- Federation: ActivityPub support for cross-instance recipe sharing
- RSS/Atom Feeds: Subscribe to user recipes via feed readers
- WebFinger: Discover users across federated instances
- oEmbed: Rich embeds for recipes shared on other platforms
- Backend: Rust with Axum 0.8
- Database: PostgreSQL 15+ with SQLx
- Templates: Askama (compile-time checked)
- Frontend: Server-rendered HTML + HTMX + Tailwind CSS
- Storage: S3-compatible (MinIO for development)
- Federation: activitypub-federation-rust 0.6
- Rust 1.75+ (stable)
- Docker/Podman with Compose
- Tailwind CSS CLI (optional, for CSS changes)
# Clone and enter directory
git clone https://github.com/scttpr/oppskrift.git
cd oppskrift
# Copy environment file
cp .env.example .env
# First-time setup (starts database and runs migrations)
make setup
# Run the app with auto-reload
make devThe app will be available at http://localhost:3000
# Development
make setup # First-time setup (db + migrations)
make dev # Run with auto-reload (requires cargo-watch)
make css # Build Tailwind CSS
make css-watch # Watch CSS for changes
# Database
make db # Start database container
make migrate # Run migrations
make reset-db # Drop, recreate, and migrate
# Quality
make lint # Run clippy + format check
make test # Run all tests
make fmt # Format code
# Cleanup
make clean # Clean build artifacts and stop containerssrc/
├── api/ # REST/JSON API endpoints
│ ├── auth.rs # Authentication (register, login, logout)
│ ├── account.rs # Account management (profile)
│ ├── recipes.rs # Recipe CRUD
│ ├── books.rs # Recipe book management
│ ├── social.rs # Follow, save, share
│ ├── activitypub.rs # Federation endpoints
│ ├── feeds.rs # RSS/Atom feeds
│ ├── webfinger.rs # WebFinger discovery
│ └── oembed.rs # oEmbed provider
├── handlers/ # HTML page handlers
├── services/ # Business logic layer
│ ├── auth_service.rs # Registration, login, sessions
│ ├── password_service.rs # Argon2id hashing, validation
│ ├── session_service.rs # Session management
│ └── email_service.rs # Email notifications
├── models/ # Database models
├── jobs/ # Background job processing
└── lib/ # Shared utilities
├── activitypub/ # AP protocol implementation
├── audit.rs # Security audit logging
├── config.rs # Configuration
├── error.rs # Error types
└── pagination.rs # Pagination helpers
templates/ # Askama HTML templates
static/ # CSS, JS (HTMX vendored)
migrations/ # SQLx database migrations
POST /auth/register- Register new account (requires email confirmation)GET /auth/confirm-email/{token}- Confirm email addressPOST /auth/resend-confirmation- Resend confirmation emailPOST /auth/login- Login and receive session cookiePOST /auth/logout- Terminate session
GET /account/profile- Get authenticated user's profile
GET/POST/PUT/DELETE /recipes- Recipe CRUDGET/POST/PUT/DELETE /books- Recipe book CRUDPOST /users/{id}/follow- Follow a userPOST /recipes/{id}/save- Save a recipeGET /feed- Activity feed
GET /users/{id}- Actor profile (Person)POST /users/{id}/inbox- Receive activitiesGET /users/{id}/outbox- User's activitiesGET /recipes/{id}- Recipe objectGET /books/{id}- Book collection
GET /.well-known/webfinger- WebFingerGET /oembed- oEmbed providerGET /feeds/recipes.rss- Public recipes RSSGET /feeds/users/{id}/recipes.atom- User recipes Atom
See .env.example for all options. Required:
DATABASE_URL=postgres://user:pass@localhost:5432/oppskrift
JWT_SECRET=your-secret-min-32-chars
TOTP_ENCRYPTION_KEY=your-64-hex-chars # For 2FA (openssl rand -hex 32)
S3_BUCKET=oppskriftOptional auth settings:
SESSION_EXPIRY_DAYS=7
LOCKOUT_DURATION_MINUTES=15
SMTP_HOST=smtp.example.com # Required for email confirmationmake test # Run all tests
cargo test test_name # Run specific test
RUST_LOG=debug cargo test # With loggingAGPL-3.0-or-later
See CONTRIBUTING.md for development setup and guidelines.