---
id: w1/m2.5/t006
title: Extract internal/apikeys + internal/postgres (the two smaller features)
worker: worker1
status: done
estimate: 30m
depends_on: [w1/m2.5/t001]
---

## Objective

Move the two remaining smaller features into their own packages: `internal/apikeys` (machine credentials / OAuth2 clients in Hydra) and `internal/postgres` (Render's managed `/v1/postgres` noun), each with its service + models + REST/GraphQL/MCP fragments and tests.

## Context

- API keys: `internal/api/apikeys.go` + `apikeys_test.go`; the `APIKeyStore` seam and `ErrAPIKeysUnavailable` (nil store ⇒ unavailable). Data lives in **Hydra**, not Postgres — another reason a uniform per-feature `data/` package didn't fit.
- Managed Postgres: `internal/api/postgres.go` and the Postgres-specific bits of `render.go` (`PostgresView`, the `/v1/postgres` shape).
- Keep Render-consistent shapes (REST `/v1/postgres`, the GraphQL `database(id)` op, MCP naming).

## Steps

1. Create `internal/apikeys` (`service.go`, `models.go`, fragments, moved `apikeys_test.go`); depend on `core` + the `APIKeyStore` interface.
2. Create `internal/postgres` (`service.go`, `models.go`, fragments); move the Postgres-specific `render.go` helpers with it; leave any genuinely shared Render helpers in `core`.
3. Delete moved code from the old files.

## Files

- `lego/backend/internal/apikeys/{service.go,models.go,rest.go,graphql.go,mcp.go,*_test.go}`
- `lego/backend/internal/postgres/{service.go,models.go,rest.go,graphql.go,mcp.go}`
- `lego/backend/internal/api/{apikeys.go,postgres.go,render.go}` (remove moved code)

## Acceptance criteria

- [ ] `go build ./... && go test ./...` green.
- [ ] API-key verbs reachable over all surfaces; nil store ⇒ `ErrAPIKeysUnavailable` as before.
- [ ] Managed-Postgres shapes unchanged (`/v1/postgres`, `database(id)`).

## Out of scope

- Deleting the now-empty flat package + docs (t007).
