Imported from sopa301/BetterShowUp (
AGENTS.md). Install upstream withnpx skills add sopa301/BetterShowUp. Copyright stays with the author.
AGENTS.md
Repository Summary
BetterShowUp is a Go backend API for the BetterShowUp application. It can run as a Vercel Go Function or as a long-lived local Docker server, uses feature-focused packages under pkg/services, and has optional Postgres persistence through pgx.
The API runs through Vercel in production and through Docker Compose locally. Authentication currently centers on Google OAuth ID tokens, local fixture tokens for development, app session tokens stored as hashes, group ownership/membership behavior, and Google Calendar connection/event retrieval.
Requirements
- Go 1.25 or newer, matching
go.mod. - Docker Desktop for local API/Postgres execution.
- Vercel CLI only when validating the function emulator with
make vercel-dev. - Make is optional but supported by the repository.
- Network access is only needed for downloading Go modules or calling real Google OAuth/Calendar APIs.
Important environment variables:
APP_LOG_LEVEL:debug,info,warn,warning,error, or numeric slog level.APP_ALLOWED_ORIGIN: configured browser origin; localhost-style origins are also allowed for local development.DATABASE_URL: enables Postgres-backed stores. Without it, the function can still start, but DB-backed workflows are limited.GOOGLE_CLIENT_ID: required for real Google sign-in.GOOGLE_CLIENT_SECRET: required for Google Calendar OAuth code exchange.AUTH_ALLOW_LOCAL_FIXTURES: enables local fixture ID tokens. Keep this off outside local development.
Project Structure
api/index.go: Vercel Go Function entrypoint.cmd/server/main.go: long-lived HTTP server entrypoint for Docker/local API execution.pkg/app/server.go: route registration, service/store wiring, and middleware chain.pkg/app/runtime.go: config load, logger setup, optional DB pool, and handler construction.pkg/config: environment-driven runtime configuration.pkg/health: health/readiness handlers.pkg/middleware: CORS, request logging, panic recovery, and auth middleware scaffolding.pkg/services: feature modules.pkg/services/httpapi: shared HTTP helpers for JSON responses/decoding and bearer token parsing.pkg/services/auth: OAuth login, provider verification, identity linking, session persistence.pkg/services/user: account-level user lookup/creation.pkg/services/profile: profile service scaffold.pkg/services/calendar: Google Calendar OAuth connection and event listing.pkg/services/friendgroup: group ownership, invite-token joins, and membership management.pkg/services/scheduler,pkg/services/notification: service scaffolds.db/schema: full current schema SQL run by local Postgres only when the Docker volume is first created.db/migrations: append-only SQL migrations for hosted/prod databases.db/fixtures/local: opt-in local fixture data.docker-compose.yml: Postgres and fixture service definitions..github/workflows/ci.yml: GitHub Actions validation workflow.scripts/smoke/group_crud.sh: repeatable curl-based group CRUD smoke test against a running API.Makefile: common local commands.
Files To Read First
For most backend tasks, start with:
README.md: current setup, endpoint, auth, database, and local fixture notes.pkg/app/server.go: how dependencies are currently wired.pkg/config/config.go: supported configuration and defaults.- The relevant service package under
pkg/services/<feature>. - Existing tests near the touched package, for example
pkg/services/auth/handler_test.goorpkg/middleware/cors_test.go.
For persistence or auth/calendar work, also read:
db/schema/01-create-users.sqldb/schema/02-create-auth-identities.sqldb/schema/03-create-auth-sessions.sqldb/fixtures/local/01-local-auth-fixtures.sqlpkg/services/auth/store.gopkg/services/calendar/store.go
For group work, also read:
pkg/services/friendgroup/service.gopkg/services/friendgroup/handler.gopkg/services/friendgroup/store.gopkg/services/friendgroup/service_test.goscripts/smoke/group_crud.shdb/schema/05-create-groups.sql
Architecture And Conventions
- Prefer the standard library HTTP stack already in use:
http.ServeMux, method-aware route patterns such asGET /healthz, andhttp.Handlermiddleware. - Keep service modules self-contained. A feature package usually owns request/response types, a
Serviceinterface, aManager, route registration, and any store interface needed by that feature. - Depend on narrow interfaces between services. For example, auth depends on a
UserService, identity store, and session store rather than concrete user or database types. - Wire concrete implementations in
pkg/app/server.go, not inside handlers. - Keep request handling thin: decode/validate request shape, call the service, map domain errors to HTTP status codes, and write JSON.
- Reuse
pkg/services/httpapifor shared handler concerns such as JSON decoding/writing and bearer token parsing. - Use
context.Contextthrough service and store methods. - Normalize user input at service boundaries where possible, as seen with trimmed provider/token values and standardized user email/display name handling.
- Treat Postgres as optional at startup. Code paths that require stores should return explicit configuration/authorization errors rather than panic on nil dependencies.
- Store session tokens hashed in the database. Do not persist plaintext app session tokens.
- Local fixture auth is development-only. Real Google sign-in must verify issuer, audience, verified email, and configured client ID.
Coding Guidelines
- Use idiomatic Go and keep code formatted with
gofmt. - Prefer small package-level sentinel errors for expected domain failures, and use
errors.Iswhen mapping them in handlers. - Wrap unexpected lower-level errors with useful context using
fmt.Errorf("...: %w", err). - Avoid introducing web frameworks, ORMs, dependency injection frameworks, or mocking libraries unless explicitly requested.
- Keep JSON response structs explicit and tagged.
- Return stable frontend-facing response shapes. Check
README.mdbefore changing endpoint paths or response fields. - Keep logs structured in startup/server paths through
slog. Existing handlers currently use simple internal error prints; if improving this, do it consistently and avoid leaking secrets. - Do not log OAuth ID tokens, access tokens, refresh tokens, session tokens, passwords, or full
DATABASE_URLvalues. - Keep SQL in store implementations,
db/schema, ordb/migrationsfiles. Use parameterized pgx queries. - Add indexes or constraints to both
db/schemaand a new append-only migration when schema semantics require them. - Keep comments sparse and useful. Remove or improve comments that merely repeat the next line of code.
Refactoring Guidance
- Refactor around existing package boundaries instead of creating broad shared utility packages prematurely.
- If route registration or JSON/error helpers become duplicated across services, extract only after there are several concrete call sites with the same behavior.
- Be careful changing auth, user, and calendar boundaries. Auth owns provider verification, identity linking, and app session creation. User owns account-level creation/lookup. Calendar owns Google Calendar tokens and event retrieval.
- Preserve local fixture behavior when changing auth unless the task explicitly removes it.
- Keep nil-store behavior intentional. Some managers can be constructed without DB-backed dependencies for tests or limited local operation.
- Keep authorization checks in the service layer even when handlers require bearer tokens. HTTP handlers are transport boundaries; service managers enforce business rules.
- For schema changes, update the current SQL in
db/schema, add a new append-only migration indb/migrations, update store queries and fixture SQL if needed, and update README/db docs when behavior changes. - Avoid large unrelated cleanups in this repo. It is small enough that focused edits are easier to review.
Useful Commands
Run the API through Docker Compose:
make dev
Run the API through Vercel's function emulator when needed:
make vercel-dev
Run tests:
make test
Equivalent:
go test ./...
Format Go code:
make fmt
Run vet:
make vet
Start Postgres:
docker compose up -d postgres
Stop containers:
docker compose down
Reset the local Postgres volume and rerun schema SQL:
docker compose down -v
docker compose up -d postgres
Apply local fixtures after Postgres is running:
make fixtures
Run the group CRUD smoke test against a running DB-backed API:
AUTH_ALLOW_LOCAL_FIXTURES=true docker compose up -d --build api
make fixtures
make smoke-group-crud
The smoke test defaults to http://localhost:8080. Override with API_URL if the API is running elsewhere:
API_URL=http://localhost:8081 make smoke-group-crud
Connect to local Postgres:
docker compose exec postgres psql -U better_show_up -d better_show_up
Validation Expectations
Before finishing Go code changes, run:
make fmt
make test
make vet
For handler, auth, group, persistence, Docker, fixture, or CI changes, also run the smoke test when practical:
docker compose down -v
AUTH_ALLOW_LOCAL_FIXTURES=true docker compose up -d --build api
make fixtures
make smoke-group-crud
docker compose down -v
For a narrow documentation-only change, tests are usually not required. State that tests were not run because only documentation changed.
For database or Docker Compose changes, additionally validate the local container path when practical:
docker compose up -d --build api
For auth changes, include focused coverage for:
- invalid JSON or missing required fields,
- invalid provider,
- local fixture token behavior when enabled,
- Google client ID requirement,
- issuer/audience/email verification checks,
- store or user-service missing cases when relevant.
For group changes, include focused coverage for:
- owner-only update/delete behavior,
- member join by invite token,
- member self-leave,
- owner cannot leave without deleting,
- owner member-removal behavior,
- non-owner edit/delete/remove rejection,
- smoke-test coverage when route, schema, fixture, or store behavior changes.
For calendar changes, include focused coverage for:
- missing or malformed bearer token,
- missing OAuth code,
- unconfigured Google OAuth settings,
- not-connected calendar state,
- token refresh/persistence behavior where practical.
Local Data And Fixtures
Docker Compose creates a Postgres database named better_show_up with user better_show_up and password better_show_up_password by default.
Schema files in db/schema are only applied when the Postgres volume is first initialized. If schema SQL changes do not appear locally, reset the Docker volume with docker compose down -v. Hosted databases should advance through append-only files in db/migrations.
Local fixture tokens are documented in README.md and backed by db/fixtures/local/01-local-auth-fixtures.sql. Fixture SQL is intentionally separate from schema SQL and should remain idempotent. Fixture login requires AUTH_ALLOW_LOCAL_FIXTURES=true; CI sets this explicitly for the smoke-test job.
The group CRUD smoke test creates a unique group and deletes it before exit. It also attempts cleanup on failure, but a failed or interrupted local run can leave a Smoke CRUD ... group behind in the local database.
API Notes
Currently documented endpoints include:
POST /auth/login/oauthPOST /groupsGET /groupsPOST /groups/joinGET /groups/{groupID}PATCH /groups/{groupID}DELETE /groups/{groupID}GET /groups/{groupID}/membersDELETE /groups/{groupID}/members/meDELETE /groups/{groupID}/members/{userID}POST /calendar/google/connectGET /calendar/eventsGET /healthzGET /readyz
Use Authorization: Bearer <token> for group and calendar endpoints that require an app session token.
CI Notes
GitHub Actions runs .github/workflows/ci.yml on pull requests and pushes to main.
The Validate Go API job checks module tidiness, formatting, tests, vet, and API build.
The Group CRUD Smoke Test job starts Docker Compose with a fresh Postgres volume, applies local fixtures, waits for /healthz, runs make smoke-group-crud, then tears the stack down. This job requires Docker Compose and uses AUTH_ALLOW_LOCAL_FIXTURES=true only inside CI.
Pull Request Checklist
- The change is scoped to the requested behavior.
- Relevant docs are updated when setup, endpoints, schema, or environment variables change.
- New behavior has focused tests, especially around auth, calendar, persistence, or middleware.
gofmthas been applied.go test ./...andgo vet ./...pass, or any inability to run them is clearly reported.- Group/API workflow changes have passed
make smoke-group-crudagainst a fixture-backed local API when practical. - No secrets, tokens, or local-only credentials are committed.
Editing Approval
Before creating, modifying, deleting, formatting, or regenerating any file, ask the user for explicit approval and list the files you intend to change.
Do not apply patches or run write-capable commands until the user approves.