Files
honeyDueAPI/README.md
T
Trey t 1347ffadf5
Backend CI / Test (push) Has been cancelled
Backend CI / Contract Tests (push) Has been cancelled
Backend CI / Lint (push) Has been cancelled
Backend CI / Secret Scanning (push) Has been cancelled
Backend CI / Build (push) Has been cancelled
docs: presigned-URL upload flow + B2 lifecycle setup
09-storage.md:
  - Replaced the "Upload flow" section. The previous text described the
    multipart-via-API path that was removed in b7f8329. Now documents
    the three-step direct-to-B2 flow (presign → POST to B2 → attach
    via upload_ids[]) with an ASCII diagram and a server-side
    enforcement-points table.
  - Replaced the "Future: signed URLs" placeholder (since presigned
    URLs are now the present, not the future).
  - Added "Lifecycle and retention" subsections covering the
    pending_uploads cleanup cron (worker, 30 * * * *), the B2 bucket
    lifecycle as backstop (uploads/ prefix, 7-day hide + 1-day delete),
    and the still-open user-deletion cascade gap.

14-deployment-process.md:
  - Added a "One-time B2 bucket lifecycle (manual)" section explaining
    why the rule can't live in the deploy script (B2's S3 lifecycle
    API is partial), the exact rule to apply via the Backblaze
    console, and a verification command.

docs/deployment/README.md:
  - Updated the chapter 9 description to mention presigned-URL uploads.

README.md (root):
  - Added a paragraph under "Object storage" pointing to the new
    upload architecture and the relevant deployment-book chapters.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 17:44:08 -07:00

17 KiB

honeyDue API

Go REST API for the honeyDue property management platform. Powers iOS and Android mobile apps built with Kotlin Multiplatform.

Tech Stack

  • Language: Go 1.25
  • HTTP Framework: Echo v4
  • ORM: GORM with PostgreSQL
  • Background Jobs: Asynq (Redis-backed)
  • Push Notifications: APNs (apns2) + FCM HTTP API
  • Caching: Redis
  • Object Storage: Backblaze B2 via minio-go/v7 (S3-compatible)
  • Email: SMTP via wneessen/go-mail (Fastmail in prod)
  • Logging: zerolog
  • Configuration: Viper
  • Admin Panel: Next.js 16 (separate build target; auto-seeded via ADMIN_EMAIL/ADMIN_PASSWORD)
  • Production Orchestrator: K3s v1.34.6 HA on Hetzner Cloud, fronted by Cloudflare. See docs/deployment/ for the full book.

Prerequisites

  • Go 1.25+install
  • PostgreSQL 16+ — via Docker (recommended) or native install
  • Redis 7+ — via Docker (recommended) or native install
  • Docker & Docker Composeinstall (recommended for local development)
  • Make — pre-installed on macOS; apt install make on Linux

Getting Started on a New Machine

This starts PostgreSQL, Redis, the API server, background worker, and admin panel in containers using the self-contained dev compose file.

# 1. Clone the repo
git clone <repo-url>
cd honeyDueAPI-go

# 2. Create your environment file
cp .env.example .env
# Edit .env — at minimum set:
#   - SECRET_KEY to a random 32+ char string
#   - ADMIN_EMAIL + ADMIN_PASSWORD if you want the Next.js admin panel seeded

# 3. Build and start all services
make docker-dev

# 4. Verify the API is running
# Lookups, admin user, and task templates auto-seed on first boot
# via internal/database/migration_seed_initial_data.go. The data_migrations
# table tracks what's been applied so re-runs are safe.
curl http://localhost:8000/api/health/

# 5. (Optional) Seed dev test data — creates test users, residences, tasks
docker exec -i honeydue-db psql -U honeydue -d honeydue < seeds/002_test_data.sql

The API is now available at http://localhost:8000.

Option B: Run Locally (No Docker for the API)

Use Docker for PostgreSQL and Redis, but run the Go server natively for faster iteration.

# 1. Clone and enter the repo
git clone <repo-url>
cd honeyDueAPI-go

# 2. Install Go dependencies
make deps

# 3. Start PostgreSQL and Redis via Docker
docker compose -f docker-compose.dev.yml up -d db redis

# 4. Create your environment file
cp .env.example .env
# Edit .env:
#   - Set SECRET_KEY to a random 32+ char string
#   - Set DB_HOST=localhost
#   - Set DB_PORT=5433  (docker-compose maps 5433 externally)
#   - Set REDIS_URL=redis://localhost:6379/0
#   - Set ADMIN_EMAIL + ADMIN_PASSWORD if you want the Next.js admin panel seeded

# 5. Run the API server
# First boot auto-seeds lookups, admin user, and task templates
make run

# 6. (Optional, dev only) Seed dev test data in a separate terminal
psql -h localhost -p 5433 -U honeydue -d honeydue < seeds/002_test_data.sql

# 7. (Optional) Run the background worker in a separate terminal
make run-worker

Option C: Fully Native (No Docker)

Install PostgreSQL and Redis natively on your machine.

# macOS with Homebrew
brew install postgresql@16 redis

# Start services
brew services start postgresql@16
brew services start redis

# Create the database
createdb honeydue

# Then follow Option B steps 2-7, using:
#   DB_HOST=localhost, DB_PORT=5432, POSTGRES_USER=<your-user>, POSTGRES_PASSWORD=<your-password>

Environment Variables

Copy .env.example to .env and configure. The table below covers what's needed for local dev. For the complete production env var reference (~60 vars), see docs/deployment/10-secrets-config.md.

Core (required)

Variable Description Default
SECRET_KEY Signing secret (32+ chars)
POSTGRES_DB Database name honeydue
POSTGRES_USER Database user postgres
POSTGRES_PASSWORD Database password
DB_HOST Database host localhost
DB_PORT Database port 5432
DB_SSLMODE TLS mode for Postgres (disable for local, require for Neon) disable
REDIS_URL Redis connection URL redis://localhost:6379/0

Server (optional)

Variable Description Default
PORT Server port 8000
DEBUG Enable debug logging true
BASE_URL Canonical base URL used in generated links http://localhost:8000
ALLOWED_HOSTS Comma-separated Host header allowlist *
CORS_ALLOWED_ORIGINS Comma-separated CORS origins *
ADMIN_PANEL_URL URL of the Next.js admin panel http://localhost:3000
NEXT_PUBLIC_API_URL API URL baked into the admin bundle http://localhost:8000

Admin panel seed (new — see commit 4ec4bbb)

Variable Description
ADMIN_EMAIL Email for the initial admin_users row. Created on first boot.
ADMIN_PASSWORD Password for that admin (bcrypt-hashed at seed time). Change in the admin UI after first login, then blank this out.

Email (for transactional mail)

Variable Description Default
EMAIL_HOST SMTP server smtp.gmail.com
EMAIL_PORT SMTP port 587
EMAIL_HOST_USER SMTP username
EMAIL_HOST_PASSWORD SMTP app password
DEFAULT_FROM_EMAIL From address
EMAIL_USE_TLS Opportunistic STARTTLS true

Push notifications (optional; gated behind FEATURE_PUSH_ENABLED)

Variable Description
APNS_AUTH_KEY_PATH Path to APNs .p8 key file
APNS_AUTH_KEY_ID APNs key ID
APNS_TEAM_ID Apple Team ID
APNS_TOPIC Bundle ID
FCM_SERVER_KEY Firebase server key

Object storage (Backblaze B2 in prod; local volume in dev)

Variable Description
B2_ENDPOINT S3-compatible endpoint (e.g. s3.us-east-005.backblazeb2.com)
B2_KEY_ID B2 app key ID
B2_APP_KEY B2 app key secret
B2_BUCKET_NAME Bucket name
B2_REGION Region code (e.g. us-east-005)
B2_USE_SSL Use HTTPS

Leave all four B2_* empty in dev to fall back to a local /app/uploads volume.

Upload architecture (since b7f8329): Image and document uploads go directly from the client to B2 via a presigned POST policy issued by POST /api/uploads/presign. Bytes never traverse the api server. B2 enforces a 10 MB per-object cap at the protocol level. The worker reaps orphaned upload sessions hourly via the maintenance:upload_cleanup cron. See docs/deployment/09-storage.md for the full flow, and docs/deployment/14-deployment-process.md for the one-time bucket lifecycle setup.

Worker schedules (UTC hours)

Variable Description Default
TASK_REMINDER_HOUR When to send task reminders 14
OVERDUE_REMINDER_HOUR When to send overdue reminders 15
DAILY_DIGEST_HOUR When to send daily digest 3

Feature flags (default true in dev, configurable in prod)

Variable Description
FEATURE_PUSH_ENABLED APNs / FCM push notifications
FEATURE_EMAIL_ENABLED Transactional email
FEATURE_WEBHOOKS_ENABLED Outbound webhooks
FEATURE_ONBOARDING_EMAILS_ENABLED Onboarding drip emails
FEATURE_PDF_REPORTS_ENABLED PDF task report generation
FEATURE_WORKER_ENABLED Run the Asynq worker

Apple / Google auth + IAP (optional)

APPLE_CLIENT_ID, APPLE_TEAM_ID, GOOGLE_CLIENT_ID, GOOGLE_ANDROID_CLIENT_ID, GOOGLE_IOS_CLIENT_ID, plus APPLE_IAP_* and GOOGLE_IAP_* for receipt validation. See Chapter 10 of the deployment book for details.

Project Structure

honeyDueAPI-go/
├── cmd/
│   ├── api/main.go              # API server entry point
│   └── worker/main.go           # Background worker entry point
├── internal/
│   ├── config/                  # Viper configuration
│   ├── database/                # PostgreSQL connection setup
│   ├── models/                  # GORM models (map to PostgreSQL tables)
│   ├── repositories/            # Data access layer
│   ├── services/                # Business logic
│   ├── handlers/                # HTTP handlers (Echo)
│   ├── middleware/              # Auth, timezone, logging middleware
│   ├── router/                  # Route registration
│   ├── dto/                     # Request/Response DTOs
│   │   ├── requests/            # Incoming request structs
│   │   └── responses/           # Outgoing response structs
│   ├── task/                    # Centralized task logic
│   │   ├── predicates/          # IsCompleted, IsOverdue, etc.
│   │   ├── scopes/              # GORM query scopes
│   │   └── categorization/      # Kanban column assignment
│   ├── apperrors/               # Structured error types
│   ├── push/                    # APNs + FCM push notification clients
│   ├── worker/                  # Asynq background jobs
│   ├── i18n/                    # Internationalization (en, es, fr)
│   ├── validator/               # Input validation
│   ├── monitoring/              # Health checks
│   └── integration/             # Integration + contract tests
├── admin/                       # Next.js admin panel
├── migrations/                  # SQL migration files
├── seeds/                       # Seed data (lookups, test users, admin, templates)
├── templates/emails/            # Email templates
├── docs/                        # API docs, OpenAPI spec
│   └── deployment/              # Full production deployment book (26 chapters)
├── deploy-k3s/                  # Production Kubernetes manifests + migration notes
├── deploy/                      # Legacy Docker Swarm config (to be removed)
├── docker-compose.yml           # Legacy (Swarm-era); prod now runs on K3s
├── docker-compose.dev.yml       # Self-contained local dev config
├── Dockerfile                   # Multi-stage build (api, worker, admin)
├── Makefile                     # Build, test, Docker commands
└── .env.example                 # Environment variable template

Development

Common Commands

make run              # Run API server
make run-worker       # Run background worker
make deps             # Install/tidy Go dependencies
make build            # Build API binary
make build-all        # Build API + worker binaries
make fmt              # Format code
make vet              # Vet code
make lint             # Run golangci-lint
make clean            # Remove build artifacts

Testing

make test             # Run all tests with race detection + coverage
make test-coverage    # Run tests and generate HTML coverage report
make contract-test    # Run route + KMP contract validation tests

# Run specific packages
go test ./internal/handlers -v
go test -run TestTaskHandler_CreateTask ./internal/handlers

Docker (local dev)

  • docker-compose.dev.yml — self-contained local dev (build from source, container_name, depends_on, dev defaults)
  • docker-compose.yml — legacy (Swarm-era). Prod now runs on K3s (see deployment book). File retained temporarily.
# Dev
make docker-dev       # Build and start all dev containers (foreground)
make docker-up        # Start dev containers (detached)
make docker-down      # Stop dev containers
make docker-logs      # Tail dev container logs
make docker-restart   # Restart dev containers

# Build production amd64 images (push to registry handled separately)
make docker-build-prod

For the full production build + deploy workflow, see docs/deployment/14-deployment-process.md.

Database Migrations

make migrate-up       # Apply pending migrations
make migrate-down     # Roll back last migration
make migrate-create name=add_column  # Create a new migration pair

Seed Data

Seed files in seeds/:

File Purpose Loaded by
001_lookups.sql Residence types, task categories, priorities, frequencies, contractor specialties Auto-seeded on first API boot
003_admin_user.sql Placeholder admin panel user (also seeded via ADMIN_EMAIL / ADMIN_PASSWORD env vars) Auto-seeded on first API boot
003_task_templates.sql Pre-built task templates for onboarding Auto-seeded on first API boot
002_test_data.sql Dev-only: test users, residences, tasks, contractors, documents, notifications Manual (psql or docker exec)

Auto-seeded files run once per database — tracked in the data_migrations table by internal/database/migration_seed_initial_data.go. Each INSERT uses ON CONFLICT DO UPDATE, so re-runs are safe.

Test Users (from 002_test_data.sql; dev only)

All test users have password: password123

Username Email Tier Notes
admin admin@example.com Pro Admin user
john john@example.com Pro Owns 2 residences
jane jane@example.com Free Owns 1 residence, shared access
bob bob@example.com Free Owns 1 residence

API Documentation

  • OpenAPI spec: docs/openapi.yaml (81 paths, 104 operations, 81 schemas)
  • Health check: GET /api/health/
  • Auth: POST /api/auth/login/, POST /api/auth/register/
  • Static data: GET /api/static_data/ (ETag-cached lookups)

All protected endpoints require an Authorization: Token <token> header.

Production Deployment

Production runs on a 3-node K3s HA cluster on Hetzner Cloud, fronted by Cloudflare, with Neon Postgres, Backblaze B2, and a self-hosted Gitea container registry. Live observability (VictoriaMetrics + Jaeger + Grafana) runs on a separate Linode VPS at grafana.88oakapps.com and is fed by a vmagent sidecar in-cluster. See the full deployment book for every detail:

docs/deployment/ — The Deployment Book

26 chapters and ~42,000 words covering:

  • Part I — The System: overview, Hetzner infrastructure, why K3s (and not Swarm, full Kubernetes, or Nomad)
  • Part II — Networking: Flannel VXLAN, CoreDNS, kube-proxy, every UFW rule on every node, Cloudflare DNS setup
  • Part III — Security: RBAC, Pod Security, secrets, TLS chain
  • Part IV — Workloads: api, admin, worker, redis per-service deep dives; Neon Postgres config; Backblaze B2 storage; Gitea registry
  • Part V — Operation: end-to-end data flow, deploy process, observability, failure modes, operator runbook
  • Part VI — Context: cost breakdown, postmortem of the bugs from the Swarm→K3s migration, roadmap

Quick links:

Operational state lives under:

  • deploy-k3s/manifests/ — Kubernetes manifests (apply with kubectl)
  • deploy-k3s/MIGRATION_NOTES.md — notes from the Swarm → K3s migration
  • deploy/ — legacy Swarm config (retained temporarily; to be removed)
  • Deployment Book: docs/deployment/ — full production operations reference
  • Mobile App (KMM): ../HoneyDueKMM — Kotlin Multiplatform iOS/Android client
  • Task Logic Docs: docs/TASK_LOGIC_ARCHITECTURE.md — required reading before task-related work
  • Push Notification Docs: docs/PUSH_NOTIFICATIONS.md

License

Proprietary — honeyDue