This runbook defines how to manage compile signer keys for compile_and_load.
1) Threat Model
allow_compile_signers is used to ensure hot-loaded source is authorized by a trusted signer.
Ward enforcement flow:
- Caller provides
key_idandsignature. - Circle finds
key_idinallow_compile_signers. - Signature is verified over
sourcebytes with that public key. - Compile proceeds only on successful verification.
2) Key Generation (RSA example)
Generate private/public keypair (OpenSSL):
openssl genrsa -out cantrip_signer_private.pem 2048
openssl rsa -in cantrip_signer_private.pem -pubout -out cantrip_signer_public.pem
Keep private keys out of the repo.
3) Signing Flow
Create detached signature (base64):
openssl dgst -sha256 -sign cantrip_signer_private.pem source.ex \
| base64 > source.sig.b64
Gate args should include:
modulesourcekey_idsignature(base64)
4) Circle Configuration
Configure trusted public keys by key_id:
%{
gates: [:done, :compile_and_load],
wards: [
%{max_turns: 10},
%{allow_compile_modules: ["Elixir.My.Module"]},
%{
allow_compile_signers: %{
"dev-key-1" => File.read!("cantrip_signer_public.pem")
}
}
]
}5) Rotation
- Add new key with a new
key_idalongside current key. - Start signing new artifacts with the new private key.
- Observe successful verification in environments.
- Remove old
key_idafter migration window.
6) Incident Response
If private key compromise is suspected:
- Remove compromised
key_idfrom wards immediately. - Rotate to new keypair.
- Re-sign trusted source with replacement key.
- Audit prior compile events in loom storage.