This guide walks through obtaining every value needed in .env. Copy .env.example to .env and fill in each variable.
cp .env.example .envPostgreSQL connection string. The database must have the pgvector extension available.
# Install PostgreSQL (macOS)
brew install postgresql@16
brew services start postgresql@16
# Create the database
createdb saltybytes_db
# Your DATABASE_URL
DATABASE_URL=postgres://$(whoami)@localhost:5432/saltybytes_db?sslmode=disableWhen you provision a PostgreSQL database on your hosting platform, it will provide a DATABASE_URL automatically. Make sure the provider supports the pgvector extension:
- Railway: Supported natively. Enable via
CREATE EXTENSION IF NOT EXISTS vector(the app does this automatically on startup). - Render: Use their managed PostgreSQL. pgvector is available on paid plans.
- Supabase: pgvector is enabled by default.
The port the API server listens on. Defaults to 8080 if not set.
Most hosting platforms set PORT automatically. You only need to set this for local development if 8080 is taken.
PORT=8080
A random secret used to sign authentication tokens. Generate one:
openssl rand -base64 32Paste the output as your JWT_SECRET_KEY. Use a different value for production vs development.
A secret header value used for internal request validation. Generate one:
openssl rand -base64 32Recipe images are stored in AWS S3. You need an S3 bucket and IAM credentials.
- Go to AWS S3 Console
- Click Create bucket
- Name:
saltybytesrecipeimages(or your preferred name) - Region:
us-east-2(or your preferred region) - Uncheck "Block all public access" (images need to be publicly readable)
- Create the bucket
- Add a bucket policy for public read:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::saltybytesrecipeimages/*"
}
]
}- Go to AWS IAM Console
- Create a new user (e.g.,
saltybytes-api) - Attach a policy with S3 access to your bucket:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::saltybytesrecipeimages/*"
}
]
}- Create an access key for the user
- Copy the values:
AWS_REGION=us-east-2
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
S3_BUCKET=saltybytesrecipeimages
Used for all text generation and reasoning (recipe creation, forking, allergen analysis, dietary interviews, cooking Q&A, voice intent classification).
- Go to console.anthropic.com
- Sign up or log in
- Go to API Keys in the left sidebar
- Click Create Key
- Copy the key (starts with
sk-ant-)
ANTHROPIC_API_KEY=sk-ant-...
Pricing: Pay-per-use. The app uses Claude 3.5 Sonnet. Typical recipe generation costs ~$0.01-0.03 per request.
Used for three specific services that Anthropic doesn't offer:
- Whisper — speech-to-text for voice commands in cooking mode
- DALL-E — recipe image generation
- text-embedding-3-small — vector embeddings for recipe similarity search
- Go to platform.openai.com
- Sign up or log in
- Go to API Keys in the left sidebar
- Click Create new secret key
- Copy the key (starts with
sk-)
OPENAI_API_KEY=sk-...
Pricing: Pay-per-use. DALL-E image generation is the most expensive at ~$0.04 per image. Whisper and embeddings are very cheap.
Used for web recipe search — finding recipes across the internet. Brave Search is the active provider. Google CSE support exists in the codebase but is disabled because Google no longer allows Custom Search Engines to search the entire web (a curated site list is required).
If BRAVE_SEARCH_KEY is not configured, web search returns an error gracefully; all other features work.
Pricing: $5 per 1,000 requests, with $5 in free monthly credits (~1,000 queries/month at no cost). Credit card required for signup.
- Go to brave.com/search/api
- Sign up for the Search plan
- Go to your dashboard → API Keys
- Copy the key
BRAVE_SEARCH_KEY=BSA...
Google CSE is disabled in the code. These variables are accepted but ignored at runtime. If Google re-enables full-web search for Custom Search Engines in the future, the provider can be re-enabled in internal/ai/web_search.go.
GOOGLE_SEARCH_KEY=AIza...
GOOGLE_SEARCH_CX=a1b2c3d4e...
[ ] DATABASE_URL — PostgreSQL running locally or hosted
[ ] JWT_SECRET_KEY — openssl rand -base64 32
[ ] ID_HEADER — openssl rand -base64 32
[ ] AWS_REGION — e.g., us-east-2
[ ] AWS_ACCESS_KEY_ID — (optional if using IAM role)
[ ] AWS_SECRET_ACCESS_KEY
[ ] S3_BUCKET — your bucket name
[ ] ANTHROPIC_API_KEY — from console.anthropic.com
[ ] OPENAI_API_KEY — from platform.openai.com
[ ] BRAVE_SEARCH_KEY — from brave.com/search/api (optional)
[ ] GOOGLE_SEARCH_KEY — from Google Cloud Console (disabled, optional)
[ ] GOOGLE_SEARCH_CX — from Programmable Search Engine (disabled, optional)
Once all variables are set:
cd saltybytes-api
go run ./cmd/api