Most 'AI Auditing' tools just save text files to a database. Here is how we cryptographically bind an ephemeral GitHub Action identity to a specific decision using Keyless Signing.
Posted on January 20, 2025 by Cabin crew team
Most “AI Auditing” tools just save text files to a database. Here is how we cryptographically bind an ephemeral GitHub Action identity to a specific decision using Keyless Signing.
Traditional CI/CD systems rely on long-lived API keys or service account tokens. These have fundamental problems:
API keys stored in environment variables, config files, or secret managers eventually get exposed. GitHub’s secret scanning finds thousands of leaked keys every day.
Once compromised, an API key remains valid until manually rotated. An attacker who steals a key has unlimited time to use it.
The same API key is often used across multiple workflows, making it impossible to attribute a specific action to a specific run.
Logs signed with API keys can be forged if the key is compromised. There’s no way to prove a log entry was created at a specific time by a specific identity.
OpenID Connect (OIDC) solves this by replacing long-lived keys with short-lived, cryptographically verifiable identity tokens.
Here’s how it works in the context of GitHub Actions:
When a GitHub Action runs, it can request an OIDC token from GitHub’s identity provider:
curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sigstore" \
| jq -r '.value'
This token contains:
https://token.actions.githubusercontent.comrepo:cabincrew/dev-engine:ref:refs/heads/mainsigstoreThe Cabin Crew Orchestrator sends this OIDC token to Sigstore’s Fulcio (a certificate authority):
POST https://fulcio.sigstore.dev/api/v2/signingCert
{
"credentials": {
"oidcIdentityToken": "eyJhbGc..."
}
}
Fulcio validates the token and issues a short-lived X.509 certificate (valid for 10 minutes). This certificate includes:
The Orchestrator uses the private key (which exists only in memory) to sign the audit.json file:
{
"workflow_id": "run-12345",
"timestamp": "2025-01-15T10:30:00Z",
"artifacts": [
{
"name": "changes.patch",
"hash": "sha256:a1b2c3d4...",
"role": "evidence"
}
],
"policy_verdict": {
"status": "pass",
"checks": ["no-secrets", "cost-limit"]
},
"signature": {
"algorithm": "ECDSA-SHA256",
"value": "MEUCIQDx..."
}
}
The signature and certificate are published to Rekor (Sigstore’s transparency log). This creates an immutable, timestamped record that:
Even with cryptographic signatures, an attacker who gains access to your storage backend could delete audit logs. To prevent this, we use hash chaining.
Each audit log includes the hash of the previous log:
{
"log_id": "log-456",
"previous_hash": "sha256:log-455-hash",
"artifacts": [...],
"signature": {...}
}
This creates a blockchain-like structure. If any log in the chain is deleted or modified, the chain breaks, and verification fails.
To verify the integrity of the audit trail:
previous_hash matches the hash of log N-1If any log is missing or tampered with, the verification fails.
Here’s a real example of what our audit.json looks like:
{
"version": "1.0",
"workflow": {
"id": "run-98765",
"repository": "cabincrew/demo-app",
"ref": "refs/heads/main",
"commit": "a1b2c3d4e5f6",
"actor": "github-actions[bot]"
},
"timestamp": "2025-01-15T14:22:33Z",
"orchestrator": {
"version": "0.5.2",
"mode": "governed"
},
"inputs": {
"issue_number": 42,
"task": "Add user authentication"
},
"artifacts": [
{
"role": "evidence",
"name": "auth-implementation.patch",
"path": "./artifacts/auth.patch",
"hash": "sha256:7f8e9d0c1b2a3456...",
"size_bytes": 4521
},
{
"role": "state",
"name": "planner-state.json",
"path": "./artifacts/state.json",
"hash": "sha256:3c4d5e6f7a8b9012..."
}
],
"policy": {
"engine": "opa",
"version": "0.58.0",
"verdict": "pass",
"checks": [
{
"name": "no-high-entropy-secrets",
"status": "pass"
},
{
"name": "require-jira-ticket",
"status": "pass",
"metadata": {"ticket": "PROJ-123"}
}
]
},
"signature": {
"algorithm": "ECDSA-SHA256",
"certificate": "-----BEGIN CERTIFICATE-----\nMIIC...",
"value": "MEUCIQDxY7...",
"transparency_log_entry": "https://rekor.sigstore.dev/api/v1/log/entries/24a8c..."
},
"previous_hash": "sha256:log-98764-hash"
}
This architecture provides non-repudiation. You can prove:
And you can prove all of this years later, even if:
| Feature | Traditional Logs | Cabin Crew Audit Logs |
|---|---|---|
| Identity | API Key (long-lived) | OIDC Token (5 min) |
| Signature | HMAC (shared secret) | ECDSA (public key) |
| Timestamp | Server clock (mutable) | Transparency log (immutable) |
| Deletion Protection | None | Hash chaining |
| Verification | Requires original key | Public certificate |
The Cabin Crew Orchestrator is open source (BSL 1.1). You can inspect the signing logic in our GitHub repository:
Or run a demo workflow to see the audit logs generated in real-time.
This is how you build trust in AI systems. Not with promises. With mathematics.
Want to learn more about our platform? Check out The Black Box or read the Cabin Crew Protocol.