# Release Signing Service Contract

The MPV Manager application repository generates an unsigned release-manifest
candidate. A separately administered service is the only component allowed to
use the Ed25519 private key. This is a trust-boundary contract, not an
implementation inside tagged application code.

## Request

`POST $RELEASE_SIGNING_SERVICE_URL` uses `multipart/form-data` with:

- `candidate`: unsigned schema-v2 manifest JSON;
- `provenance`: reviewed `release/provenance-<tag>.json`.

The request carries a GitLab OIDC bearer token with audience
`https://signing.mpv.rocks` and the project, pipeline, commit, and tag headers
shown in `.gitlab-ci.yml`. The service must derive authority from verified OIDC
claims, not trust those informational headers.

## Required signer checks

Before signing, the service must:

1. verify issuer, audience, signature, expiry/not-before, project ID, protected
   tag/ref, commit SHA, pipeline ID, and a one-time `jti`;
2. require the tag version, candidate version, and lock version to match;
3. strictly decode the lock, require an authorized independent reviewer, and
   validate each immutable verification-evidence reference;
4. reconstruct every upstream URL from the locked versions and require exact
   candidate URL/digest equality, with no missing or extra entries;
5. obtain the six manager build artifacts through an authenticated,
   pipeline-bound channel and require the candidate size/hash to match those
   bytes—not a mutable registry re-download;
6. validate the complete manifest schema and canonical payload;
7. enforce channel monotonicity and reject replay, downgrade, duplicate tag,
   or unauthorized key-ID changes.

The service then signs the canonical payload and returns only the complete JSON
manifest. It must not return key material or generic signing primitives.

## Channel authorization

Stable candidates must have a final version and `channel: stable`; prerelease
tags select `channel: rc`. RC authorization must never authorize a stable
publication. A final version may be announced separately on RC only through an
explicit approved promotion, signing a second manifest over the same final
artifacts with `channel: rc`. Keep per-channel monotonic publication records.
The publisher must check destination authorization against the signed channel,
not merely trust the request header. See [release channels](RELEASE_CHANNELS.md).

## Service isolation

- Pin the reviewed service image/code independently of application tags.
- Keep the key non-exportable (HSM/KMS when available) with manifest-only use.
- Deny general outbound network access; allow only the identity provider,
  pipeline artifact source, audit sink, and response path required by policy.
- Record append-only audit data: token subject/JTI, project, tag, commit,
  pipeline, lock/candidate/output digests, key ID, decision, and timestamp.
- Require a protected environment/manual approval owned outside ordinary
  application Developer permissions.

## Failure behavior

Any ambiguity or unavailable evidence fails closed. A failed signing request
must not publish a tag manifest or advance the stable channel. Key compromise
halts publication until a separately distributed recovery release trusts a new
key.
