Backend for SaltyBytes — a recipe app for iOS and Android. Search the web for recipes without the ads and life stories, import from any source, generate with AI, and cook hands-free with voice-guided cooking mode.
Built with Go, Gin, PostgreSQL + pgvector, and Claude AI.
See also: saltybytes-app — the Flutter iOS client | saltybytes-dashboard — the operational metrics dashboard.
Recipe Search & Discovery — Search the web for recipes and get clean results — no ads, no SEO spam, no scrolling past someone's vacation story. Multi-tier pipeline: exact-match cache, pgvector semantic similarity, and Brave web search. Import any result directly into your collection.
Multi-Source Import — Import recipes from URLs (with JSON-LD extraction and Firecrawl fallback), photos (vision-based), freeform text, or manual entry. A canonical recipe cache deduplicates URL imports with automatic background refresh.
AI Recipe Generation — Create recipes through conversation with Claude when you can't find what you're looking for. Fork existing recipes into new variants, regenerate with feedback, and explore branching version history through a recipe tree.
Family Allergen Analysis — AI-powered ingredient analysis detects common allergens (dairy, nuts, shellfish, wheat, soy, sesame, etc.) with confidence scoring. Cross-reference results against family members' dietary profiles.
Real-Time Cooking Mode — WebSocket-based hands-free cooking. Voice commands are transcribed (Whisper), classified by intent (Claude), and answered contextually. Supports ephemeral recipe edits during cooking.
AI Dietary Interviews — Conversational dietary profiling for family members, covering allergies, intolerances, and preferences.
Handlers → Services → Repositories → PostgreSQL (GORM)
| Layer | Purpose |
|---|---|
internal/handlers/ |
HTTP request handling and validation |
internal/service/ |
Business logic, AI orchestration |
internal/repository/ |
Database access via interfaces (DI-friendly) |
internal/models/ |
Data models and GORM schema |
internal/ai/ |
Anthropic, OpenAI, and Brave Search providers |
internal/ws/ |
WebSocket server for cooking mode |
internal/middleware/ |
JWT auth, rate limiting, request context |
configs/ |
AI prompt templates (YAML) |
| Service | Used For |
|---|---|
| Anthropic Claude | Recipe generation, allergen analysis, dietary interviews, voice intent classification, cooking Q&A |
| OpenAI | DALL-E 3 (recipe images), Whisper (voice transcription), text-embedding-3-small (vector search) |
| Brave Search | Web recipe discovery |
| AWS S3 | Recipe image storage |
| PostgreSQL + pgvector | Data persistence and semantic similarity search |
- Go 1.24+
- PostgreSQL 16+ with the pgvector extension
- API keys for Anthropic, OpenAI, and an AWS account for S3
- Clone the repository
git clone https://github.com/windoze95/saltybytes-api.git
cd saltybytes-api- Configure environment variables
cp .env.example .envSee docs/env-setup.md for a detailed walkthrough of every variable, including how to create API keys and configure AWS.
- Run with Docker Compose (recommended)
docker compose upThis starts the API and a PostgreSQL instance. The database schema is auto-migrated on startup.
- Or run directly
go run ./cmd/api| Variable | Required | Purpose |
|---|---|---|
DATABASE_URL |
Yes | PostgreSQL connection string |
JWT_SECRET_KEY |
Yes | Token signing secret |
ANTHROPIC_API_KEY |
Yes | Claude — recipe gen, allergens, voice, dietary |
OPENAI_API_KEY |
Yes | DALL-E, Whisper, embeddings |
AWS_REGION |
Yes | AWS region for S3 |
S3_BUCKET |
Yes | Image storage bucket |
ID_HEADER |
Yes | Request validation header |
BRAVE_SEARCH_KEY |
No | Web recipe search (gracefully disabled if absent) |
AWS_ACCESS_KEY_ID |
No | S3 auth (falls back to IAM role) |
AWS_SECRET_ACCESS_KEY |
No | S3 auth (falls back to IAM role) |
PORT |
No | Server port (default: 8080) |
GIN_MODE |
No | Set to release for production |
POST /v1/users— Create accountPOST /v1/auth/login— LoginPOST /v1/auth/refresh— Refresh token
POST /v1/recipes/chat— Generate recipe via conversationPUT /v1/recipes/:id/chat— Regenerate with feedbackPOST /v1/recipes/:id/fork— Fork into a new variantGET /v1/recipes/:id/tree— Version history treeGET /v1/recipes— List user's recipesDELETE /v1/recipes/:id— Delete recipe
POST /v1/recipes/import/url— Import from URLPOST /v1/recipes/import/photo— Import from photoPOST /v1/recipes/import/text— Import from textPOST /v1/recipes/import/manual— Manual entryPOST /v1/recipes/preview/url— Quick URL preview
GET /v1/recipes/search— Search recipes (semantic + web)GET /v1/recipes/similar/:id— Find similar recipes
POST /v1/recipes/:id/allergens/analyze— Run allergen analysisGET /v1/recipes/:id/allergens— Get analysis resultsPOST /v1/recipes/:id/allergens/check-family— Cross-reference family dietary profiles
POST /v1/family— Create familyPOST /v1/family/members— Add memberPUT /v1/family/members/:id/dietary— Update dietary profilePOST /v1/family/members/:id/dietary/interview— AI dietary interview
GET /v1/ws/cook/:id— WebSocket connection for hands-free cooking
All tests run offline — no database, network, or external services required. Services accept repository interfaces for dependency injection.
# Run all tests
go test ./... -count=1
# Verbose
go test ./... -v -count=1
# Specific package
go test ./internal/service/ -v
# Lint
go vet ./...The CI/CD pipeline runs automatically via GitHub Actions:
- On PR to main — vet, test, build validation
- On merge to main — vet, test, Docker build, push to ECR, deploy to ECS (Fargate)
docker build --platform linux/amd64 -t saltybytes-api .The Dockerfile uses a multi-stage build (Go 1.24 builder → distroless runtime) for a minimal production image.
- Language: Go 1.24
- Framework: Gin
- ORM: GORM
- Database: PostgreSQL 17 + pgvector
- AI: Anthropic Claude, OpenAI (DALL-E, Whisper, Embeddings)
- Search: Brave Search API
- Storage: AWS S3
- Auth: JWT (golang-jwt)
- WebSocket: gorilla/websocket
- Logging: zap
- Container: Docker (distroless base)
- CI/CD: GitHub Actions → ECR → ECS Fargate
Licensed under the Business Source License 1.1. You may use, modify, and contribute to this code, but you may not offer it as a competing commercial product. The license converts to Apache 2.0 on the change date specified in the LICENSE file.