Deployment Identity
Every Kyper deployment gets a cryptographic identity, enabling federation, license verification, and instance coordination.
Overview
When Kyper provisions a deployment, it generates a unique Ed25519 keypair and a platform-signed attestation token (JWT). These are injected as environment variables, giving your app a provable identity without any configuration.
This enables two powerful patterns:
- Attestation verification — Prove a request comes from a legitimate, paid Kyper deployment. Verify the JWT against Kyper's public key (JWKS). Zero API calls needed.
- Per-request signing — Each deployment can sign outbound requests with its private key. Recipients verify using the public key embedded in the attestation token.
Environment variables
Three reserved environment variables are automatically injected into every deployment:
| Variable | Description |
|---|---|
| KYPER_IDENTITY_KEY | Ed25519 private key (PEM). Use this to sign outbound requests. |
| KYPER_IDENTITY_PUBLIC_KEY | Ed25519 public key (PEM). Convenience copy — also embedded in the attestation token. |
| KYPER_ATTESTATION_TOKEN | Platform-signed JWT proving this is a legitimate Kyper deployment. |
Attestation token (JWT)
The attestation token is a JWT signed by Kyper's platform RSA key. It contains deployment metadata and the deployment's public key. To verify, fetch Kyper's JWKS and validate the signature.
Claims:
{
"iss": "https://kyper.shop",
"sub": "deployment:42",
"app_slug": "my-app",
"app_name": "My App",
"deployment_id": 42,
"consumer_id": 7,
"tier": "pro",
"public_key": "-----BEGIN PUBLIC KEY-----\n...",
"iat": 1711152000,
"exp": 1742688000
}
JWKS endpoint: GET https://kyper.shop/api/v1/identity/jwks (public, no authentication required)
Verification examples
These examples show how to verify that a request comes from a legitimate Kyper deployment by validating the attestation token.
Ruby
require "jwt"
require "net/http"
require "json"
# Fetch Kyper's JWKS (cache this — it rarely changes)
jwks_uri = URI("https://kyper.shop/api/v1/identity/jwks")
jwks = JSON.parse(Net::HTTP.get(jwks_uri))
# Extract the RSA public key from JWKS
jwk = jwks["keys"].first
n = OpenSSL::BN.new(Base64.urlsafe_decode64(jwk["n"]), 2)
e = OpenSSL::BN.new(Base64.urlsafe_decode64(jwk["e"]), 2)
key = OpenSSL::PKey::RSA.new.tap { |k| k.set_key(n, e, nil) }
# Verify the attestation token from an incoming request
token = request.headers["X-Kyper-Attestation"]
claims = JWT.decode(token, key, true, algorithm: "RS256").first
puts claims["app_slug"] # => "my-app"
puts claims["deployment_id"] # => 42
puts claims["tier"] # => "pro"
Node.js
import jwt from "jsonwebtoken";
import jwksClient from "jwks-rsa";
const client = jwksClient({ jwksUri: "https://kyper.shop/api/v1/identity/jwks" });
function getKey(header, callback) {
client.getSigningKey(header.kid, (err, key) => {
callback(err, key?.getPublicKey());
});
}
// Verify the attestation token
const token = req.headers["x-kyper-attestation"];
jwt.verify(token, getKey, { algorithms: ["RS256"] }, (err, claims) => {
if (err) throw new Error("Invalid attestation token");
console.log(claims.app_slug); // "my-app"
console.log(claims.deployment_id); // 42
});
Go
import (
"github.com/golang-jwt/jwt/v5"
"github.com/MicahParks/keyfunc/v3"
)
// Create JWKS client (cache automatically)
jwks, _ := keyfunc.NewDefault([]string{"https://kyper.shop/api/v1/identity/jwks"})
// Verify the attestation token
token := r.Header.Get("X-Kyper-Attestation")
parsed, err := jwt.Parse(token, jwks.KeyFunc,
jwt.WithValidMethods([]string{"RS256"}))
if err != nil {
// Invalid token
}
claims := parsed.Claims.(jwt.MapClaims)
fmt.Println(claims["app_slug"]) // "my-app"
fmt.Println(claims["deployment_id"]) // 42
Python
import jwt
import requests
# Fetch Kyper JWKS (cache this)
jwks = requests.get("https://kyper.shop/api/v1/identity/jwks").json()
public_key = jwt.algorithms.RSAAlgorithm.from_jwk(jwks["keys"][0])
# Verify the attestation token
token = request.headers.get("X-Kyper-Attestation")
claims = jwt.decode(token, public_key, algorithms=["RS256"])
print(claims["app_slug"]) # "my-app"
print(claims["deployment_id"]) # 42
Per-request signing
For instance-to-instance communication (federation), each deployment can sign requests with its Ed25519 private key. The recipient verifies using the public key from the sender's attestation token.
Signing (sender)
# In your Kyper-deployed app
key = OpenSSL::PKey::Ed25519.new(ENV["KYPER_IDENTITY_KEY"])
# Sign the request body
body = { action: "sync", data: "..." }.to_json
signature = Base64.strict_encode64(key.sign(nil, body))
# Send with attestation + signature
http.post(peer_url,
body: body,
headers: {
"X-Kyper-Attestation" => ENV["KYPER_ATTESTATION_TOKEN"],
"X-Kyper-Signature" => signature,
"Content-Type" => "application/json"
}
)
Verifying (recipient)
# First, verify the attestation token (see examples above) claims = JWT.decode(token, kyper_public_key, true, algorithm: "RS256").first # Extract the sender's public key from the attestation claims sender_key = OpenSSL::PKey::Ed25519.new(claims["public_key"]) # Verify the request signature signature = Base64.strict_decode64(request.headers["X-Kyper-Signature"]) sender_key.verify(nil, signature, request.raw_post) # Raises OpenSSL::PKey::PKeyError if invalid
Key rotation
Identity keys persist across redeployments to avoid breaking federation. To rotate a deployment's keypair:
# Rotate via API (developer authentication required) curl -X POST https://kyper.shop/api/v1/apps/my-app/deployments/42/rotate_identity \ -H "Authorization: Bearer YOUR_API_TOKEN"
This generates a new keypair, re-signs the attestation token, and triggers an environment reload. The deployment will receive the new credentials within seconds.
API endpoints
| Endpoint | Auth | Description |
|---|---|---|
| GET /api/v1/identity/jwks | Public | Kyper platform JWKS for verifying attestation tokens |
| GET /api/v1/apps/:slug/deployment_keys | Bearer | List all deployment public keys for your app |
| GET /api/v1/apps/:slug/deployments/:id/identity | Bearer | Get a single deployment's identity details |
| POST /api/v1/apps/:slug/deployments/:id/rotate_identity | Bearer | Rotate a deployment's keypair |