custom
code
sovereign-compute
pax-coder / docs /adr /0009-protected-execution-capability.md
SNAPKITTYWEST's picture
chore: push pax-coder from SNAPKITTYWEST GitHub
ef6eb55 verified
|
Raw
History Blame Contribute Delete
12.4 kB

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:

  1. generate_release.sh β€” Sign an official release

    • Requires: Valid capability + private signing key
    • Gate: pax-coder-gate enforces capability check
  2. (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:

  1. βœ… No capability β†’ denied (exit 2)
  2. βœ… Expired capability β†’ denied
  3. βœ… Invalid signature β†’ denied
  4. βœ… Wrong commit β†’ denied
  5. βœ… Modified release + valid capability β†’ denied (integrity fails first)
  6. βœ… 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 point
  • scripts/verify-pax-coder β€” Security status report
  • scripts/test_protection_gate.sh β€” Test suite
  • sovereign/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)