---
id: w1/m17/t002
title: Lifecycle — suspend / resume / restart verbs (Core + REST/GraphQL/MCP)
worker: worker1
status: done
estimate: 40m
depends_on: []
---

## Objective

Add the `suspend` / `resume` / `restart` lifecycle verbs to a managed Postgres `Database`, mirroring the compute lifecycle verbs and Render's `POST /postgres/{id}/{suspend,resume,restart}`. Suspend should **hibernate** the CNPG Cluster (stop compute, keep the PVC) so a paused DB honors the sleep-is-free promise; resume wakes it; restart bounces the primary.

## Context

- The `Database`/CNPG Cluster "already supports scale-down" per `w1/011`; suspend parity should use CNPG **hibernation** (`cnpg.io/hibernation` annotation), not delete — see `docs/ADR009-postgresql-management.md` §Consequences (hibernation preserves the PVC) and `docs/ADR007-restart-suspend-and-resume.md` for the compute lifecycle contract these mirror.
- Postgres feature: `lego/backend/internal/postgres/service.go` (+ `rest.go`/`graphql.go`/`mcp.go`); `Database` CRD `lego/types/v1alpha1/database_types.go`; controller `lego/operator/internal/controller/database_controller.go`.
- These are currently listed under "Deferred" in `docs/ADR006-bex-api.md` (Postgres section, line ~128).
- The compute verbs write intent to the CR (`spec.suspended`, `spec.restartedAt`) and the operator converges — follow the same intent-only pattern for the `Database` CR (a `spec.suspended` / `spec.restartedAt` equivalent that the controller maps to the hibernation annotation / a rollout).

## Steps

1. Add lifecycle intent fields to the `Database` CRD (e.g. `spec.suspended`, `spec.restartedAt`) and map them in the controller to CNPG hibernation (suspend/resume) and a primary restart.
2. Add `Suspend` / `Resume` / `Restart` Core verbs to the postgres `Service`, returning the updated `postgres` object (async-converge, like the compute verbs).
3. Add adapter fragments: REST `POST /v1/postgres/{name}/{suspend,resume,restart}`, GraphQL mutations, MCP tools — all delegating to the same Core verbs.
4. Update `docs/ADR009-postgresql-management.md` + `docs/ADR006-bex-api.md` (move out of "Deferred"); prettier the markdown.

## Files

- `lego/types/v1alpha1/database_types.go`
- `lego/operator/internal/controller/database_controller.go`
- `lego/backend/internal/postgres/service.go`
- `lego/backend/internal/postgres/rest.go`
- `lego/backend/internal/postgres/graphql.go`
- `lego/backend/internal/postgres/mcp.go`
- `docs/ADR009-postgresql-management.md`, `docs/ADR006-bex-api.md`

## Acceptance criteria

- [ ] `POST /v1/postgres/{name}/suspend` hibernates the CNPG Cluster (compute stopped, PVC retained); `resume` wakes it back to healthy; `restart` bounces the primary.
- [ ] The `postgres` object reflects the suspended/lifecycle state (a status field the dashboard can read).
- [ ] GraphQL + MCP parity for all three verbs, delegating to the same Core methods as REST.
- [ ] `make test` green; docs updated + prettier-clean.

## Out of scope

- Dashboard lifecycle buttons (that's t004).
- Auto-hibernation on idle (that's a separate scheduling concern, not this verb).
- HA failover (deferred to `w1/013`).
