Register or Rotate Agent Public Key

Registers or rotates the AGENT-scoped caller's active RSA public key. The private key stays on the agent runtime and never traverses the server.

For new agents, the Platform wizard already generates the runtime key pair in the browser, registers the public key, applies selected security groups, and puts the private key in the one-time runtime JSON. Normal r4 configure agent --config <path> and r4 project env PROJECT_ID flows should use that JSON directly and do not need to call this endpoint.

POST /api/v1/machine/vault/public-key

Headers

HeaderTypeRequiredDescription
X-API-KeystringYesYour AGENT-scoped API key
X-R4-Agent-HostnamestringNoBest-effort runtime hostname claim from official SDK/runtime clients. Stored on the agent only for operator visibility

Request Body

{
  "encryptionKeyId": "507f1f77bcf86cd799439015",
  "publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...\n-----END PUBLIC KEY-----",
  "previousEncryptionKeyId": "507f1f77bcf86cd799439010",
  "rotationSignature": "base64-signature",
  "rewrappedVaultKeys": [
    {
      "vaultId": "507f1f77bcf86cd799439011",
      "encryptionKeyId": "507f1f77bcf86cd799439015",
      "signerEncryptionKeyId": "507f1f77bcf86cd799439015",
      "signerType": "AGENT_ENCRYPTION_KEY",
      "dekVersion": 3,
      "wrappedDek": "base64-ciphertext",
      "wrappedDekSignature": "base64-signature"
    }
  ]
}

Request Fields

FieldTypeRequiredDescription
encryptionKeyIdstringNoOptional client-chosen ID for the new active encryption key. Required when rotating a key that still has active wrapped vault access
publicKeystringYesPEM-encoded RSA public key generated and stored locally by the agent
previousEncryptionKeyIdstringNoPrevious active agent key ID. Required when rotating to a different key
rotationSignaturestringNoRSA-PSS signature from the previous private key approving the new public key
rewrappedVaultKeysarrayNoFull replacement wrapped-DEK batch for every still-active vault assignment on the old key. Required when rotating away from a key that still has active wrapped vault access

Response

Success (201 Created)

{
  "encryptionKeyId": "507f1f77bcf86cd799439015",
  "publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...\n-----END PUBLIC KEY-----",
  "fingerprint": "b5d44d6c7c1c1d4b8f31b1d9d8445d6c7c1c1d4b8f31b1d9d8445d6c7c1c1d4b",
  "previousEncryptionKeyId": "507f1f77bcf86cd799439010",
  "rotationSignature": "base64-signature"
}

Response Fields

FieldTypeDescription
encryptionKeyIdstringActive agent encryption key ID used as the wrapped-DEK recipient
publicKeystringPEM-encoded RSA public key currently active for the agent
fingerprintstringSHA-256 fingerprint of the public key
previousEncryptionKeyIdstring | nullPrevious key ID for continuity verification
rotationSignaturestring | nullContinuity signature verifiable by the previous public key

Error Responses

400 Bad Request - Rotation proof missing or invalid

{
  "message": "Key rotation requires previousEncryptionKeyId and rotationSignature."
}

400 Bad Request - Rotation missing the required re-wrap batch

{
  "message": "Rotating an active agent key with wrapped vault access requires encryptionKeyId so the runtime can pre-sign replacement wrapped DEKs."
}

403 Forbidden - Not an AGENT-scoped API key

{
  "error": {
    "code": "agent_scope_required",
    "message": "This endpoint requires an AGENT-scoped API key."
  }
}

404 Not Found - Agent not found

{
  "error": {
    "code": "agent_not_found",
    "message": "Agent not found or you do not have access to it."
  }
}

Example Request

curl -X POST "https://r4.dev/api/v1/machine/vault/public-key" \
  -H "X-API-Key: rk_abc123def456.ghijklmnopqrstuvwxyz" \
  -H "Content-Type: application/json" \
  -H "X-R4-Agent-Hostname: build-runner-01" \
  -d '{
    "publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...\n-----END PUBLIC KEY-----"
  }'

Security

  • Requires an AGENT-scoped API key
  • The server stores only the public key, fingerprint, and rotation continuity metadata
  • The server also records the observed client IP address and optional X-R4-Agent-Hostname claim on the agent so operators can see where the active runtime key most recently registered
  • Re-registering the same public key is idempotent
  • The Platform wizard performs the initial registration for new agents
  • Use this endpoint for explicit key rotation, idempotent repair of the existing key, or non-wizard legacy runtimes
  • Existing pre-registration access is not retroactively wrapped to the new key
  • Rotations require proof from the previous private key
  • Rotations with existing vault access must submit a complete rewrappedVaultKeys batch, and the backend archives the old wrapped-DEK rows plus the old key in one transaction
  • Losing the active private key prevents a normal self-service rotation to a different key

Runtime Flow

  1. Create the agent in the Platform wizard and download the one-time runtime JSON.
  2. Import that JSON with r4 configure agent --config <path> or pass the API key and private key to the SDK.
  3. Fetch GET /vault/:vaultId/wrapped-key for the vault DEK encrypted to the agent's active key.
  4. Fetch GET /vault/:vaultId/public-keys to verify the wrapped-DEK signer.
  5. Fetch GET /vault/:vaultId/environment-fields, GET /vault/:vaultId/fields/:fieldId, or GET /vault/:vaultId/items/:itemId for the encrypted field payloads you need.
  6. Use the locally stored private key to unwrap the DEK and decrypt vault field ciphertext.

If you are sending a different public key, include previousEncryptionKeyId and a rotationSignature from the current private key. If that old key still has active wrapped vault access, also include encryptionKeyId plus a rewrappedVaultKeys entry for every active vault DEK so the server can rotate and re-wrap atomically.

endpoint-vault-register-public-key - R4 Docs