# Container Image Signing (cosign) The Gitea deploy workflow (`.gitea/workflows/deploy.yml`) attempts to sign every container image it publishes to the Gitea Package Registry using [cosign](https://docs.sigstore.dev/cosign/overview/) with a long-lived keypair. The signing step is skip-safe: if `COSIGN_PRIVATE_KEY` or `COSIGN_PASSWORD` is not configured, the workflow logs a notice and continues without signatures. Promote runs never rebuild — they retag the staging digest as `:main` — so a single successful signing pass per commit covers both staging and production deployments. ## What gets signed For each push to `main`, the workflow signs the following images at their full-SHA tag: - `{registry}/{namespace}/web:` - `{registry}/{namespace}/api:` - `{registry}/{namespace}/worker:` Because cosign signatures are bound to the manifest digest (not the tag), the `:staging` and `:main` aliases that are later created via `docker buildx imagetools create` resolve to the same digest and therefore the same signature. ## Required Gitea secrets Configure the following repository-level secrets in Gitea. The signing step is SKIP-safe — if either secret is unset, the workflow logs a notice and continues. | Secret | Description | | --------------------- | -------------------------------------------------------------------- | | `COSIGN_PRIVATE_KEY` | PEM-encoded cosign private key (output of `cosign generate-key-pair`). | | `COSIGN_PASSWORD` | Password protecting the private key. | Generate the keypair locally with: ```bash cosign generate-key-pair # emits cosign.key (private, set as COSIGN_PRIVATE_KEY) and cosign.pub (public) ``` Commit the resulting `cosign.pub` to the repository root (optional but recommended) so downstream verifiers can fetch it directly from Git. ## Verifying an image Anyone pulling a published image can verify authenticity with the public key: ```bash # Using the public key from this repo (if checked in as cosign.pub): cosign verify --key cosign.pub \ {registry}/{namespace}/web:main # Or pin to a specific revision: cosign verify --key cosign.pub \ {registry}/{namespace}/web: ``` A successful verification prints the signed payload as JSON and exits 0. A mismatched, skipped, or missing signature exits non-zero. ## Operational notes - Signatures are stored as OCI artifacts alongside the image in the Gitea Package Registry. No extra infrastructure is required. - Rotating the keypair: generate a new pair, update the Gitea secrets, and replace `cosign.pub`. Older images remain verifiable against the previous public key kept in Git history. - The signing step runs only during the `stage` action so promote runs do not re-sign already-signed digests.