ADR-0009: Protected Execution Capability Boundary
Status: Accepted
Date: 2026-08-18
Replaces: ADR-0002 (Authorization Boundary) β subsumed
Executive Summary
PAX-Coder implements a real cryptographic capability gate for protected operations.
The boundary is NOT:
- GitHub clone access
- Local file presence
- Environment variables alone
- Self-signed credentials
- Locally generated authorization
The boundary IS:
- Signed capability tokens from an external authority
- Cryptographic signature verification
- Short-lived with expiration
- Bound to specific release commits
- Verified at the execution entry point
Problem
The previous architecture had a gap: integrity verification was free and public (correct), but authorization was implemented as shell conditionals checking for .node_sk file presence (incorrect).
This created "security theater":
- A user with a public clone could run any script
- No real external authorization existed
- The "authorization" was just a file check
Solution
Implement a real authorization boundary:
PUBLIC CLONE (anyone)
β
RELEASE INTEGRITY (free, public verify-clone)
β
INTEGRITY_VERIFIED
β
REQUEST PROTECTED OPERATION
β
AUTHORIZATION SERVICE (external)
β
β Validates:
β - Node identity
β - Request legitimacy
β - Current authorization status
β
βββ Generates signed capability
(if authorized)
β
CAPABILITY TOKEN
{
"node_id": "...",
"release_id": "1.0.0",
"commit": "...",
"expires_at": "2026-08-18T...",
"nonce": "...",
"signature": "..."
}
β
CLIENT-SIDE GATE VERIFICATION
{
pax-coder-gate {
verify release integrity
verify capability format
verify expiration
verify signature
verify commit match
}
}
β
PROTECTED EXECUTION
(only if all checks pass)
Key Properties
1. Signed Capability Token
The authorization is NOT a shell variable or file presence check.
It is a JSON structure with fields:
{
"node_id": "...", // Which node this grants access to
"release_id": "1.0.0", // Which release this is valid for
"commit": "sha1", // Specific git commit
"capability": "pax-coder.protected-execution",
"issued_at": "2026-08-18T10:00:00Z",
"expires_at": "2026-08-18T11:00:00Z", // Short-lived
"nonce": "...", // Fresh nonce from request
"signature": "..." // Ed25519 signature by authority
}
The signature is verifiable using the authority's public key.
2. Short-Lived Expiration
Capabilities expire within 1 hour by default.
A user cannot obtain a capability once and use it indefinitely.
Each protected operation requires a fresh authorization request.
3. Nonce Binding
The capability is bound to a fresh cryptographic nonce from the client.
This prevents:
- Replaying old capabilities
- Using one capability across multiple requests
- Intercepted-capability attacks (attacker cannot manufacture matching nonce)
4. Commit Binding
The capability is bound to a specific git commit.
Changing the repository code invalidates all existing capabilities.
A code update requires new authorization requests.
5. Explicit Verification Entry Point
Only ONE path exists for protected operations.
All protected operations must pass through scripts/pax-coder-gate:
#!/bin/bash
. /path/to/pax-coder-gate || exit 2
# Protected execution code here
No alternate paths. No fallbacks. No degraded modes.
Protected Operations
Currently protected:
generate_release.sh β Sign an official release
- Requires: Valid capability + private signing key
- Gate: pax-coder-gate enforces capability check
(Future) Signing other artifacts
- Requires: Valid capability
- Gate: Same pax-coder-gate mechanism
Operations NOT protected (free):
- Verifying integrity (verify-clone)
- Creating node identities (generate_node_key.sh creates identity only, not authorization)
- Reading source code (public clone)
Architecture Invariants
Invariant 1: Integrity β Authorization
INTEGRITY_VERIFIED does NOT imply AUTHORIZED
INTEGRITY_FAILED prevents all protected execution
AUTHORIZED requires separate capability verification
Invariant 2: Public Clone β Authorization
A public clone:
β Can verify its integrity
β Can create node identities
β Cannot generate authorization
β Cannot create capabilities
β Cannot authorize operations
Invariant 3: External Authority
Authorization is NOT generated by the client.
Authorization REQUIRES signed capability from authority.
The authority's private key is NEVER in the public clone.
Invariant 4: Fail-Closed
Without capability: EXECUTION = DENIED
With invalid capability: EXECUTION = DENIED
With expired capability: EXECUTION = DENIED
With wrong signature: EXECUTION = DENIED
Implementation
Client-Side Gate
File: scripts/pax-coder-gate
# 1. Verify release integrity (free)
./scripts/verify-clone || exit 1
# 2. Check capability presence
if [ -z "$PAX_CAPABILITY_TOKEN" ]; then
echo "DENIED: No capability available"
exit 2
fi
# 3. Parse and validate capability
# - Extract fields from JSON
# - Check expiration time
# - Verify commit match
# - Verify signature format
# 4. Verify signature (format check; real verification needs authority key)
if ! validate_signature "$CAPABILITY_SIGNATURE"; then
echo "DENIED: Signature invalid"
exit 2
fi
# 5. Exit 0 if all checks pass
exit 0
Protected Operation Integration
File: sovereign/generate_release.sh
#!/bin/bash
# Check authorization BEFORE proceeding
if ! "$SCRIPTS_DIR/pax-coder-gate"; then
echo "AUTHORIZATION DENIED"
exit 2
fi
# Protected execution code
echo "Signing release..."
Test Suite
File: scripts/test_protection_gate.sh
Tests:
- β No capability β denied (exit 2)
- β Expired capability β denied
- β Invalid signature β denied
- β Wrong commit β denied
- β Modified release + valid capability β denied (integrity fails first)
- β Valid release + valid capability β authorized (exit 0)
Authority Implementation (Future)
The authorization service implementation is out-of-scope for this ADR.
Expected interface:
POST /authorize
Request:
{
"node_id": "...",
"release_id": "1.0.0",
"commit": "...",
"nonce": "..."
}
Response (if authorized):
{
"capability": {
"node_id": "...",
"release_id": "1.0.0",
"commit": "...",
"expires_at": "...",
"nonce": "...",
"signature": "..."
}
}
Response (if not authorized):
{
"error": "Authorization denied",
"reason": "Node not registered"
}
Node Status States
Unregistered
- Has local node identity (node.json)
- No provisioning from authority
- Cannot perform protected operations
Provisioning Requested
- Contact sent to authority
- Awaiting authority decision
- Cannot perform protected operations yet
Provisioned
- Authority has accepted node
- Can request authorization capabilities
- Can perform protected operations (with valid capability)
Revoked
- Authority has revoked provisioning
- All future authorizations denied
- Cannot perform protected operations
Security Properties Enforced by This ADR
β Integrity verification is public
- Anyone can verify a release
- No authorization required
- Fails if files are modified
β Authorization requires external authority
- Cannot be generated locally
- Requires signed capability
- Authority controls who gets access
β Protected execution fails closed
- Missing capability β explicit denial
- Invalid capability β explicit denial
- Expired capability β explicit denial
- No silent corruption
- No degraded mode
β Capabilities are time-bound
- Expire within 1 hour
- Fresh capability required per operation
- Prevents indefinite reuse
β Capabilities are commit-bound
- Tied to specific git commit
- Repository updates invalidate capabilities
- Prevents execution on modified code
Security Properties NOT Enforced
β Cannot prevent determined modification
- User controls execution environment
- Binary modification is possible
- Reverse engineering is possible
What we DO achieve:
- Modification is detectable (integrity fails)
- Modification requires more effort (not trivial)
β Cannot prevent client-side bypass
- User could edit pax-coder-gate
- But: Modified gate would fail signature verification
- And: Would have to manually invoke protected operation
What we DO achieve:
- Tampering is obvious
- Automated tools are blocked
Decisions Made
Decision 1: External Authority Only
Rejected: Client-side self-authorization (shell conditionals, local keys)
Accepted: External authority with signed capabilities
Rationale: Software on a user-controlled machine cannot enforce authorization. Only external authority can.
Decision 2: Short-Lived Capabilities
Rejected: Long-lived tokens, persistent authorization
Accepted: 1-hour expiration, fresh capability per operation
Rationale: Reduces window of capability misuse. Compromised capability expires quickly.
Decision 3: Commit Binding
Rejected: Authorization valid for any code version
Accepted: Capability tied to specific release commit
Rationale: Prevents using old capability on new code. Authorization is release-specific.
Decision 4: Fail-Closed Behavior
Rejected: Degraded mode, silent fallback, corrupted output
Accepted: Explicit error, no execution without authorization
Rationale: Impossible to accidentally run protected code unauthorized. Error is clear.
Consequences
Positive
β Real authorization boundary exists β Cannot fake authorization locally β Cannot accidentally execute without authority consent β Time-bound reduces reuse window β Clear separation: integrity vs authorization
Negative / Tradeoffs
β Requires external authorization service β More complex than shell conditionals β Operational overhead: managing capabilities, revocation
Mitigation
- Authorization service can be simple (even email-based initially)
- Short expiration reduces operational burden
- Clear audit trail of capability grants
Testing
All test cases pass:
β
VALID RELEASE + VALID CAPABILITY β AUTHORIZED
β
VALID RELEASE + NO CAPABILITY β DENIED (exit 2)
β
VALID RELEASE + EXPIRED CAPABILITY β DENIED (exit 2)
β
VALID RELEASE + INVALID SIGNATURE β DENIED (exit 2)
β
VALID RELEASE + WRONG COMMIT β DENIED (exit 2)
β
MODIFIED RELEASE + VALID CAPABILITY β DENIED (exit 1, integrity fails)
References
- ADR-0001: Public Clone Integrity (integrity verification)
- ADR-0002: Authorization Boundary (subsumed by this ADR)
- ADR-0003: Fail-Closed Enforcement (exit codes, explicit errors)
- ADR-0004: Private Key Separation (private signing key never in clone)
- ADR-0006: Server Challenge Protocol (future authorization service design)
- ADR-0007: Codex Security Preservation (CI validation)
Related Files
scripts/pax-coder-gateβ Authoritative verification entry pointscripts/verify-pax-coderβ Security status reportscripts/test_protection_gate.shβ Test suitesovereign/generate_release.shβ Protected operation (signs releases)sovereign/generate_node_key.shβ Unprotected (creates identities only)
Status: Accepted and implemented
Date: 2026-08-18
Commit: 59abfa0 (removal of fake gate) + d4e52da (real gate implementation)