A daily briefing app that aggregates news, weather, and work updates into a single morning summary. Built with Go, HTMX, and background job processing.
- Go 1.24+
- Docker & Docker Compose
- templ —
go install github.com/a-h/templ/cmd/templ@latest - golangci-lint
# 1. Start infrastructure (Postgres, Redis, Asynqmon)
make db-up
# 2. Set up environment
cp env.local env.local # already exists with defaults
source env.local
# 3. Configure Google OAuth (see below)
# 4. Run the app
make devOpen http://localhost:8080 in your browser.
The app uses Google OAuth for authentication. You need to create credentials in the Google Cloud Console.
- Go to Google Cloud Console
- Create a new project (or select an existing one)
- Navigate to APIs & Services > OAuth consent screen
- Select External user type (or Internal if using Google Workspace)
- Fill in the required fields:
- App name: First Sip (or whatever you like)
- User support email: your email
- Developer contact: your email
- Under Scopes, add:
email,profile,openid - Under Test users, add your Google email address
- Save
- Navigate to APIs & Services > Credentials
- Click Create Credentials > OAuth client ID
- Select Web application
- Set the Authorized redirect URI:
http://localhost:8080/auth/google/callback - Click Create and copy the Client ID and Client Secret
Edit env.local and fill in your credentials:
export GOOGLE_CLIENT_ID="your-client-id-here.apps.googleusercontent.com"
export GOOGLE_CLIENT_SECRET="your-client-secret-here"
export GOOGLE_CALLBACK_URL="http://localhost:8080/auth/google/callback"
export SESSION_SECRET="$(openssl rand -hex 32)"Then reload: source env.local
| Variable | Required | Default | Description |
|---|---|---|---|
GOOGLE_CLIENT_ID |
Yes | — | Google OAuth client ID |
GOOGLE_CLIENT_SECRET |
Yes | — | Google OAuth client secret |
GOOGLE_CALLBACK_URL |
Yes | — | OAuth redirect URI |
SESSION_SECRET |
Yes | — | Cookie encryption key (generate with openssl rand -hex 32) |
DATABASE_URL |
Yes | — | Postgres connection string |
ENCRYPTION_KEY |
Yes | — | AES-256-GCM key for token encryption (openssl rand -base64 32) |
REDIS_URL |
Yes | — | Redis connection URL |
N8N_STUB_MODE |
No | true |
Use mock briefing data instead of calling n8n |
N8N_WEBHOOK_URL |
No | — | n8n webhook endpoint (only when stub mode is off) |
N8N_WEBHOOK_SECRET |
No | — | n8n webhook auth secret (only when stub mode is off) |
ENV |
No | development |
Environment (development or production) |
PORT |
No | 8080 |
HTTP server port |
LOG_LEVEL |
No | debug |
Log level (debug, info, warn, error) |
LOG_FORMAT |
No | text |
Log format (text or json, forced json in production) |
Docker Compose provides Postgres, Redis, and Asynqmon:
make db-up # Start services
make db-down # Stop services
make db-reset # Wipe data and restart| Service | Port | URL |
|---|---|---|
| App | 8080 | http://localhost:8080 |
| Postgres | 5432 | postgres://first_sip:local_dev_password@localhost:5432/first_sip |
| Redis | 6379 | redis://localhost:6379 |
| Asynqmon | 8081 | http://localhost:8081 |
| Target | Description |
|---|---|
dev |
Run the server with embedded worker (development) |
worker |
Run standalone worker process |
build |
Build the Go binary |
test |
Run tests with race detection |
lint |
Run golangci-lint |
templ-generate |
Regenerate Go code from .templ files |
docker-build |
Build the Docker image |
db-up |
Start Docker Compose services |
db-down |
Stop Docker Compose services |
db-reset |
Wipe volumes and restart services |
clean |
Remove build artifacts |
GET /health → {"status":"ok"}