File size: 5,060 Bytes
ef6eb55 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | # ADR-0004: Private Key Separation
**Status:** Accepted
**Date:** 2026-08-18
**Author:** SNAPKITTYWEST PAX-Coder Security Team
---
## Context
The Sovereign Node private key is the most sensitive cryptographic material in the system. It signs official releases, establishes prior-art timestamps, and proves PAX-Coder provenance.
If the private key is distributed to the public clone, assume it can eventually be extracted:
- Reversed from binaries
- Dumped from memory
- Recovered from CUDA/.so/.pyd files
- Extracted from encrypted constants
## Decision
The private Sovereign Node key MUST:
1. **Never be committed to git** β .gitignore enforces this
2. **Never be distributed in releases** β Only public key is published
3. **Never be embedded in binaries** β No compiled constants
4. **Never be in encrypted secrets** β No key-derivation in code
5. **Never be in environment variables** β Except for signing operations in secure environments
The public clone must contain ONLY:
```
node ID
public key (PEM)
public key fingerprint
release signatures
verification metadata
prior-art timestamps
```
The private key is held ONLY by the release signer and protected by access controls.
## Rules
```yaml
rules:
- private Sovereign Node key MUST NOT be committed to git
- private key MUST NOT appear in releases
- private key MUST NOT be distributed in source tarballs
- private key MUST NOT be embedded in binaries
- private key MUST NOT be derived from environment configuration
- private key MUST NOT be recoverable from public cryptographic material
- .gitignore MUST explicitly block all private-key patterns
- CI MUST scan for accidentally committed private keys
- CI MUST reject commits containing private-key patterns
```
## Protected Patterns
CI MUST reject commits matching:
```
-----BEGIN.*PRIVATE
-----END.*PRIVATE
-----BEGIN.*KEY
-----BEGIN.*RSA
-----BEGIN.*EC
-----BEGIN.*OPENSSH
private_key.*=
secret_key.*=
PRIVATE_KEY.*=
AWS_SECRET_ACCESS_KEY
AZURE_CLIENT_SECRET
api_key.*secret
```
## Public Material Only
The clone MUST contain:
```
sovereign/node.json
ββ node_id
ββ algorithm
ββ public_key_hex
ββ created_at_utc
ββ repository
ββ git_commit
ββ version
sovereign/node_pk.pem
ββ [Public key in PEM format]
sovereign/release.json
ββ node_id
ββ node_public_key_hex
ββ signature_hex (signed by private key, but signature is public)
ββ git_commit
ββ release_version
ββ manifest_sha256
ββ timestamp_utc
sovereign/manifest-*.json
ββ [File hashes and release metadata]
```
MUST NOT contain:
```
.node_sk (private key binary)
node_sk.pem (private key PEM)
Any file with private key material
Any encrypted constants used to recover the key
```
## Key Storage & Management
Release signing happens in a secure environment:
```
Offline Environment
ββ private_key.pem
ββ git clone
ββ sovereign/generate_release.sh
ββ Sign release
ββ Hash manifest
ββ Sign hash with private key
ββ Generate release.json
```
The public release artifact contains:
```
release.json (public)
ββ public key
ββ signature (hex)
ββ manifest hash
ββ timestamp
```
The private key is NOT transmitted or stored in the public repository.
## Key Rotation
When rotating to a new private key:
1. Generate new Sovereign Node key
2. Create rotation record with old public key signature
3. Document reason and timestamp
4. Do NOT delete old public key (preserve release history)
5. Mark old key as superseded
```yaml
rotation:
old_node_id: pax-coder-1787047913
new_node_id: pax-coder-1787048999
rotation_reason: scheduled rotation
rotation_timestamp: 2026-12-18T00:00:00Z
signature_by_old_key: ...
```
## Tests Required
- `test_no_private_keys_in_repo`: Scan for accidentally committed keys
- `test_no_embedded_secrets`: Scan binaries for hardcoded keys
- `test_public_key_only_distribution`: Verify releases contain only public material
- `test_gitignore_coverage`: Verify all private-key patterns are ignored
- `test_ci_rejects_private_keys`: Verify CI blocks key commits
## CI Gate
```bash
#!/bin/bash
# Pre-commit hook
git diff --cached --name-only | xargs -I {} bash -c '
if grep -l "PRIVATE\|BEGIN.*KEY\|secret_key" {} 2>/dev/null; then
echo "ERROR: Private key material detected in: {}"
exit 1
fi
'
```
## Consequences
- Private key never leaks through version control
- Public clone contains only verifiable public material
- Key rotation does not break release history
- Compromise of one key does not affect other releases
- Private key management is manual and external to the repository
---
**Related ADRs:**
- ADR-0001: Public Clone Integrity
- ADR-0002: Authorization Boundary
- ADR-0006: Server Challenge Protocol
|