Skip to main content

Metadata Rotation

Start or complete a root metadata rotation (root key rotation) for a TUF repository. You send the new root metadata; the server either finalizes and publishes it to S3 (when the signature threshold is met) or stores a draft in Redis for later signing.

Use this endpoint when you need to rotate root keys, extend root expiration, or update the root metadata. The operation may complete immediately or require additional signatures via the sign endpoint.

Two flows

1. Root with all signatures (threshold met)

Send metadata.root with a full set of signatures that satisfy the root role's threshold.

Result: The server validates the root, finalizes the update, saves it to S3, and returns 200 OK with a task_id. The new root is published.

2. Root with no or incomplete signatures

Send metadata.root with signatures: [] or with fewer signatures than the threshold.

Result: The server validates the root content but cannot finalize (signatures insufficient). It stores this root in Redis under a key such as ROOT_SIGNING_<admin>_<app> and returns 200 OK. You then add signatures one by one via POST unsigned root metadata until the threshold is met; the server will then finalize and publish.

Endpoint

POST /tuf/v1/metadata?appName=<app_name>

Headers

HeaderValue
Content-Typeapplication/json
AuthorizationBearer <token>

Query Parameters

ParameterTypeRequiredDescription
appNamestringName of the application whose TUF repository to rotate

Request Body

The body must be a JSON object with the new root metadata under metadata.root.

FieldTypeRequiredDescription
metadata.rootobjectNew root metadata: signatures (array, may be empty or partial) and signed (root payload)
metadata.root.signaturesarrayList of signatures (keyid + sig); may be [] or incomplete for draft flow
metadata.root.signedobjectRoot payload: _type, version, spec_version, expires, consistent_snapshot, keys, roles

The signed object must include keys (keyid → key definition with keytype, scheme, keyval.public), roles (root, timestamp, snapshot, targets with keyids and threshold), and standard TUF root fields.

Example Request

curl --location 'http://localhost:9000/tuf/v1/metadata?appName=<app-name>' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <jwt_token>' \
--data '{
"metadata": {
"root": {
"signatures": [],
"signed": {
"_type": "root",
"consistent_snapshot": true,
"expires": "2026-12-21T12:52:51.723305Z",
"keys": {
"<key_id>": {
"keytype": "ed25519",
"scheme": "ed25519",
"keyval": { "public": "<public_key_hex>" }
}
},
"roles": {
"root": { "keyids": ["<key_id>", ...], "threshold": 2 },
"timestamp": { "keyids": ["<key_id>"], "threshold": 1 },
"snapshot": { "keyids": ["<key_id>"], "threshold": 1 },
"targets": { "keyids": ["<key_id>"], "threshold": 1 }
},
"spec_version": "1.0.31",
"version": 2
}
}
}
}'

Use TUF tooling or the FaynoSync admin panel to generate the correct root payload and, when ready, signatures.

Response

Success Response (200 OK)

The server accepts the root and either finalizes it (if threshold met) or stores a draft in Redis:

{
"data": {
"task_id": "97423b94-b1b8-4649-a9b1-7de18b30e9a9",
"last_update": "2026-02-05T13:39:59.899287+02:00"
},
"message": "Metadata Update Processed"
}

Response Fields

FieldTypeDescription
data.task_idstringUUID of the background task; use Check Task to verify completion (e.g. when finalization runs)
data.last_updatestringISO8601 timestamp when the update was processed
messagestring"Metadata Update Processed"

Notes

  • Requires a valid JWT in the Authorization header (admin user).
  • The repository must already be bootstrapped for this app.
  • If you send a root with no or incomplete signatures, the server stores the draft in Redis; add signatures via POST unsigned root metadata until the root threshold is met, then the server finalizes and publishes to S3.
  • If you send a root with enough signatures, the server validates, finalizes, and publishes in the background; use task_id with Check Task to confirm completion.
  • Root metadata (keys, roles, version, expires) is TUF-specific; generate it with TUF tooling or the FaynoSync admin panel.