# Agent

Name: Agent 02 - Database And Migrations

Stage: Database And Migrations

Date: 2026-04-13

## Scope

Created the SQLite data layer foundation for the update server.

Completed in this stage:

- added SQLite config values for `SQLITE_PATH` and `MIGRATIONS_DIR`;
- added a database connection layer with SQLite pragmas, health checks, and transaction scaffolding;
- implemented an ordered SQL migration runner with checksum tracking in `schema_migrations`;
- created the initial schema for `users`, `projects`, `tags`, `project_tags`, `releases`, `api_keys`, `api_key_project_access`, `api_key_tag_access`, `sessions`, and `audit_logs`;
- added indexes and update triggers for mutable tables;
- added schema guards so API key project/tag access rows match the key’s selected `scope_mode`;
- wired database open + migrate into app startup without changing the existing route-group layout;
- added a standalone `cmd/migrate` entrypoint and `just migrate`;
- added migration tests and refreshed the health endpoint to report SQLite readiness.

## Files Changed

- `/Users/delete/projects/update_server/go.mod`
- `/Users/delete/projects/update_server/go.sum`
- `/Users/delete/projects/update_server/Justfile`
- `/Users/delete/projects/update_server/cmd/migrate/main.go`
- `/Users/delete/projects/update_server/internal/app/app.go`
- `/Users/delete/projects/update_server/internal/config/config.go`
- `/Users/delete/projects/update_server/internal/db/models.go`
- `/Users/delete/projects/update_server/internal/db/open.go`
- `/Users/delete/projects/update_server/internal/db/migrate.go`
- `/Users/delete/projects/update_server/internal/db/store.go`
- `/Users/delete/projects/update_server/internal/db/migrate_test.go`
- `/Users/delete/projects/update_server/internal/http/router.go`
- `/Users/delete/projects/update_server/internal/http/handlers.go`
- `/Users/delete/projects/update_server/migrations/README.md`
- `/Users/delete/projects/update_server/migrations/0001_initial_schema.sql`
- `/Users/delete/projects/update_server/migrations/0002_indexes_and_triggers.sql`
- `/Users/delete/projects/update_server/migrations/0003_api_key_scope_guards.sql`

## Database Changes

Added migrations:

- `0001_initial_schema.sql`
- `0002_indexes_and_triggers.sql`
- `0003_api_key_scope_guards.sql`

Schema notes:

- timestamps are stored as UTC RFC3339-like text values;
- `api_keys.scope_mode` is constrained to the agreed modes:
  - `all_projects`
  - `project_allow_list`
  - `project_deny_list`
  - `tag_allow_list`
  - `tag_deny_list`
- `api_key_project_access` only accepts keys in project-based modes;
- `api_key_tag_access` only accepts keys in tag-based modes;
- changing an API key to an incompatible scope mode is blocked if incompatible access rows already exist;
- `sessions` stores hashed session tokens, not raw tokens;
- `audit_logs` storage is included, but no runtime writes are wired yet.

## API Or Route Changes

- no new route groups or business endpoints were added;
- `GET /healthz` now includes SQLite readiness and returns `503` if the store is unavailable;
- updated the placeholder `/` and `/api/v1` responses to reflect database readiness.

## Commands And Tests Run

- `gofmt -w ./cmd ./internal` - completed successfully;
- `go mod tidy` - initially failed in the sandbox due Go cache/network restrictions; completed successfully after rerunning with normal access and temporary Go caches;
- `GOCACHE=/tmp/go-build-sqlite3 GOMODCACHE=/tmp/go-mod-sqlite3 go test ./...` - passed;
- `GOCACHE=/tmp/go-build-sqlite3 GOMODCACHE=/tmp/go-mod-sqlite3 go build -o /tmp/update-server ./cmd/server` - passed;
- `GOCACHE=/tmp/go-build-sqlite3 GOMODCACHE=/tmp/go-mod-sqlite3 go build -o /tmp/update-migrate ./cmd/migrate` - passed;
- `APP_BASE_URL=http://127.0.0.1:18080 DATA_DIR=/tmp/update-server-db-dev-2 /tmp/update-migrate` - applied migrations successfully to a fresh temp SQLite database;
- `sqlite3 /tmp/update-server-db-dev-2/db.sqlite 'SELECT name FROM schema_migrations ORDER BY name;'` - confirmed all three migrations were recorded;
- started `/tmp/update-server` on `127.0.0.1:18080` and verified:
  - `GET /` -> `200 OK`
  - `GET /api/v1` -> `200 OK`
  - `GET /healthz` -> `200 OK` with `"database":"ok"`

## Known Limitations

- repository structs are intentionally scaffolding-only in this stage; feature-specific query methods are still for later agents to add;
- no auth/session business logic is implemented yet beyond the schema and store foundation;
- `audit_logs` exists in schema only; event production is deferred;
- scope-mode enforcement now covers schema consistency for access-link rows, but full project/tag access evaluation logic is still for the API key stage;
- existing local databases created before the final scope-guard addition need the new `0003_api_key_scope_guards.sql` migration applied, which the updated startup path now does automatically.

## Recommended Next Step

Agent 03 should implement admin authentication next using the new `users` and `sessions` tables: bootstrap the first admin user from environment, hash passwords, create/invalidate session records, and protect the `/admin` route group with session middleware.

## Notes For Validator

- pay extra attention to migration immutability: `schema_migrations` stores a checksum and should reject edited applied files;
- validate that the default server startup path now opens SQLite and auto-applies `0001` -> `0003`;
- validate that project/tag access link rows are rejected when the parent API key uses the wrong `scope_mode`;
- the workspace is not a git repository, so diff-based review may need to rely on direct file inspection.
