# DEVX-173: Resolve container digest via registry v2 API ## Problem `resolve_container_digest` returns the sha256 of the `manifest.json` blob listed by the Gitea packages API (`/packages//container/ //files`). That blob digest is NOT the OCI manifest digest — `docker pull repo@sha256:` fails with "not found." Production deploy run 6251 died pulling `sso-bridge@sha256:5ca9...` which the dep PR had recorded in `deploy/sso-bridge-release.json`. The registry serves 0.4.1 as `sha256:c0212ed1...` — different digest entirely. ## Approach REQ-1: Keep the packages-API call as the existence check (it has the 404-retry semantics needed for the publish race) but resolve the pullable digest via the registry v2 API: `GET /v2/token` with `scope=repository:/:pull` (basic-auth with the Gitea token), then `GET /v2///manifests/` with OCI/Docker manifest Accept headers and read `Docker-Content-Digest`. Registry base URL is derived from `GITEA_API_URL` (strip `/api/v1`). REQ-2: If the v2 digest lookup fails (non-2xx, missing header), fail closed with a ClickException — never record a blob sha256 as a pullable digest. ## Test Plan - Mocked v2 token + manifest endpoints return digest; recorded value is the `Docker-Content-Digest` header. - 404 retry semantics on the packages-API existence check unchanged. - Missing digest header / non-2xx manifest response → ClickException. - Unit tests cover token request scope and Accept headers. ## Deploy Plan devx release tag; the sso-bridge dep-PR pin bump follows in its own PR. The wrong digest already recorded in infra's manifest is corrected by re-running the dep-PR with the fixed version. ## Rollback Plan Revert; dep-PR records the (unpullable) blob digest again — deploys must then use tag pulls until a corrected manifest lands. ## Acceptance Criteria - [x] REQ-1: Digest resolved from `Docker-Content-Digest` via v2 API. - [x] REQ-2: Lookup failures fail closed; no blob-sha256 fallback.