TG-KEY-DISCOVERY
Key Discovery and Management Specification
Abstract
This document specifies the key discovery mechanism for TrigGuard Execution Receipts. Public keys used to verify receipt signatures are published at a well-known URL, enabling offline verification without runtime dependency on TrigGuard services.
1. Overview
TrigGuard uses Ed25519 public-key cryptography for receipt signing. To verify signatures, clients need access to public keys. This specification defines:
- How keys are published
- Key metadata format
- Key rotation procedures
- Caching recommendations
2. Well-Known Endpoint
2.1 URL
GET /.well-known/trigguard-keys.json
2.2 Production URL
https://api.trigguardai.com/.well-known/trigguard-keys.json
2.3 Response Headers
| Header | Value |
|---|---|
Content-Type | application/json |
Cache-Control | max-age=3600 |
Access-Control-Allow-Origin | * |
2.4 Response Status
| Status | Meaning |
|---|---|
| 200 | Success |
| 503 | Service unavailable |
3. Response Schema
3.1 JSON Schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://trigguardai.com/schemas/keys.json",
"title": "TrigGuard Key Set",
"type": "object",
"required": ["keys", "issuer"],
"properties": {
"keys": {
"type": "array",
"items": { "$ref": "#/definitions/key" }
},
"issuer": {
"type": "string",
"format": "uri"
},
"documentation": {
"type": "string",
"format": "uri"
}
},
"definitions": {
"key": {
"type": "object",
"required": ["key_id", "algorithm", "public_key", "status"],
"properties": {
"key_id": { "type": "string" },
"algorithm": { "type": "string", "enum": ["Ed25519"] },
"public_key": { "type": "string" },
"status": { "type": "string", "enum": ["active", "deprecated", "revoked"] },
"created_at": { "type": "string", "format": "date-time" },
"expires_at": { "type": "string", "format": "date-time" },
"deprecated_at": { "type": "string", "format": "date-time" }
}
}
}
}3.2 Example Response
{
"keys": [
{
"key_id": "tg_prod_02",
"algorithm": "Ed25519",
"public_key": "MCowBQYDK2VwAyEAn8j/xb4Df2vB1sP+pzRw3Y5kLfKm9vE7h8QaXcW2rD0=",
"status": "active",
"created_at": "2026-03-01T00:00:00Z",
"expires_at": "2026-06-01T00:00:00Z"
},
{
"key_id": "tg_prod_01",
"algorithm": "Ed25519",
"public_key": "MCowBQYDK2VwAyEAz7Y2xK4pE8vN3mJ1cR9wB6fT5hL2qS0nG8jD4aX1kM0=",
"status": "deprecated",
"created_at": "2026-01-01T00:00:00Z",
"expires_at": "2026-04-01T00:00:00Z",
"deprecated_at": "2026-03-01T00:00:00Z"
}
],
"issuer": "https://api.trigguardai.com",
"documentation": "https://trigguardai.com/protocol"
}4. Key Fields
4.1 key_id
| Property | Value |
|---|---|
| Type | string |
| Format | tg_<environment>_<sequence> |
| Environment | prod, staging, dev |
Examples: tg_prod_01, tg_prod_02, tg_staging_01
4.2 algorithm
| Property | Value |
|---|---|
| Type | string |
| Allowed Values | Ed25519 |
Only Ed25519 is supported in this version of the specification.
4.3 public_key
| Property | Value |
|---|---|
| Type | string |
| Encoding | Base64 (RFC 4648) |
| Format | SubjectPublicKeyInfo (SPKI) |
4.4 status
| Status | Meaning | Verification |
|---|---|---|
active | Current signing key | Accept signatures |
deprecated | Previous key, still valid | Accept signatures |
revoked | Compromised or retired | Reject signatures |
5. Key Lifecycle
5.1 States
5.2 Rotation Schedule
| Event | Timeline |
|---|---|
| New key created | Q1, Q2, Q3, Q4 |
| Previous key deprecated | At new key creation |
| Deprecated key revoked | 90 days after deprecation |
5.3 Emergency Rotation
In case of key compromise:
- New key created immediately
- Compromised key revoked (not deprecated)
- Alert published to status page
- Affected receipts identified
6. Client Implementation
6.1 Key Fetching
Clients MUST:
- Request
/.well-known/trigguard-keys.json - Parse JSON response
- Store keys locally
- Handle HTTP errors gracefully
6.2 Caching
Clients SHOULD:
- Cache keys for at least 1 hour
- Refresh cache on signature verification failure
- Store cache persistently for offline verification
Clients MUST NOT:
- Cache keys indefinitely without refresh
- Ignore key expiration
6.3 Key Selection
When verifying a receipt:
- Extract
key_idfrom receipt - Look up key in cached key set
- If not found, refresh key cache
- If still not found, verification fails
6.4 Status Validation
| Key Status | Verification Action |
|---|---|
active | Accept |
deprecated | Accept |
revoked | Reject |
| not found | Refresh cache, then reject if still not found |
7. Examples
7.1 Fetching Keys (curl)
curl -s https://api.trigguardai.com/.well-known/trigguard-keys.json | jq
7.2 Key Lookup (Python)
import requests
import json
def fetch_keys():
response = requests.get(
"https://api.trigguardai.com/.well-known/trigguard-keys.json",
timeout=10
)
return response.json()
def get_public_key(key_id):
keys = fetch_keys()
for key in keys["keys"]:
if key["key_id"] == key_id and key["status"] != "revoked":
return key["public_key"]
return None7.3 Full Verification (Python)
from nacl.signing import VerifyKey
import base64
import json
def verify_receipt(receipt, keys):
# Find key
key_id = receipt["key_id"]
key_entry = next(
(k for k in keys["keys"] if k["key_id"] == key_id),
None
)
if not key_entry or key_entry["status"] == "revoked":
return False
# Decode public key (last 32 bytes of SPKI)
spki = base64.b64decode(key_entry["public_key"])
public_key_bytes = spki[-32:]
# Construct payload
payload = json.dumps({
"context_hash": receipt["context_hash"],
"decision": receipt["decision"],
"receipt_id": receipt["receipt_id"],
"surface": receipt["surface"],
"timestamp": receipt["timestamp"]
}, separators=(',', ':'), sort_keys=True)
# Verify
signature = bytes.fromhex(receipt["signature"].replace("ed25519:", ""))
verify_key = VerifyKey(public_key_bytes)
try:
verify_key.verify(payload.encode(), signature)
return True
except:
return False8. Security Considerations
8.1 Transport Security
Clients MUST:
- Use HTTPS for key fetching
- Verify TLS certificates
- Reject connections with invalid certificates
8.2 Key Pinning
Organizations with strict security requirements MAY:
- Pin specific keys in configuration
- Verify key hashes match expected values
- Alert on unexpected key changes
8.3 Offline Operation
For air-gapped environments:
- Keys can be distributed out-of-band
- Local key stores must be updated manually
- Document key hashes for manual verification
9. Conformance
Implementations MUST:
- Support the JSON schema defined in Section 3
- Respect key status (reject revoked keys)
- Use HTTPS for key fetching
Implementations SHOULD:
- Cache keys appropriately
- Handle key rotation gracefully
- Log key refresh events
References
Copyright © 2026 TrigGuard AI Limited. UK Company No. 16597262.