Security and Risk Model
Protege is intentionally powerful. Your agent can read and write files, execute shell commands, fetch web content, and send emails — all with the same permissions as the process running it. This page documents the threat model, built-in controls, and how to harden your deployment.
Threat Model
Protege v1 operates under these assumptions:
- Tools run unsandboxed — they execute with the Protege process's OS permissions
- Extensions are trusted code — hooks, tools, providers, and resolvers run in-process
- Local data is unencrypted — persona keys, memory, and databases are stored as regular files
- The relay is a transport bridge, not a trust boundary — it tunnels email but doesn't isolate your runtime
Built-In Security Controls
Gateway sender access policy
The access policy in configs/security.json controls who can email your agent:
{
"gateway_access": {
"enabled": true,
"default_decision": "deny",
"allow": ["alice@example.com", "*@mycompany.com"],
"deny": ["noisy-bot@mycompany.com"]
}
}Evaluation order:
- Check deny rules — if a deny rule matches, the message is rejected
- Check allow rules — if an allow rule matches, the message is accepted
- Fall through to
default_decision
Deny always wins when both deny and allow rules match the same sender.
Wildcard matching: * matches any sequence of characters. *@example.com matches every sender from that domain. Rules are case-insensitive.
Gateway sender authentication policy
The default scaffold also enables gateway authentication policy in monitor mode:
{
"gateway_auth": {
"enabled": true,
"mode": "monitor",
"policy": "require_dmarc_or_aligned_spf_dkim"
}
}monitor mode is non-blocking and preserves out-of-box behavior. Move to enforce after validating your inbound auth signals in logs.
Gateway allowlist rules do not bypass auth policy evaluation. Address-based allow/deny and sender-auth policy are independent controls.
When email arrives through relay tunneling, gateway auth signals come from signed relay attestation, not message headers. In enforce mode, relay-ingested messages without valid attestation are rejected.
Agent-to-agent recursion guard
When agents email each other, there's a risk of infinite reply loops. Protege prevents this with a recursion counter:
- Every outbound email includes an
X-Protege-Recursionheader set torecursion_depth(default:3) - Inbound messages carrying this header have their value decremented
- Messages arriving with
X-Protege-Recursion: 0(or lower) are rejected before processing
Configure the depth in configs/inference.json:
{
"recursion_depth": 3
}A depth of 3 means agents can exchange up to 3 rounds of replies before the chain is cut.
Failure alerting
When a runtime failure occurs (tool error, scheduler failure, etc.), Protege can send an alert email to a designated admin:
// configs/system.json
{
"admin_contact_email": "admin@example.com"
}You can also set a scheduler-specific override:
{
"scheduler": {
"admin_contact_email": "ops@example.com"
}
}Run protege doctor to verify that your alert configuration is valid and that the outbound channel can deliver.
Security Profiles
Personal use (single user, minimal exposure)
You're the only one emailing your agent. Low risk, but still worth basic hardening:
{
"gateway_access": {
"enabled": true,
"default_decision": "deny",
"allow": ["your-email@gmail.com"],
"deny": []
}
}- Enable the access policy and allowlist only your own address
- Set
admin_contact_emailso you hear about failures - Keep the tool set minimal — only enable tools your agent actually needs
Team or multi-user
Multiple people email your agent. You need tighter controls:
{
"gateway_access": {
"enabled": true,
"default_decision": "deny",
"allow": ["*@yourcompany.com"],
"deny": ["noisy-bot@yourcompany.com"]
}
}- Start with
default_decision: denyand add narrow allow rules - Use deny overrides for known problematic senders
- Monitor logs for unexpected inbound traffic
Internet-facing (via relay)
Your agent has a public email address. Highest risk profile:
{
"gateway_access": {
"enabled": true,
"default_decision": "deny",
"allow": ["specific-user@example.com"],
"deny": []
}
}- Never use
default_decision: allowwith public exposure - Don't rely on address obscurity — assume the address will be discovered
- Set a conservative
recursion_depth(3-6) - Configure SPF, DKIM, and DMARC for your relay domain
- Run Protege under a dedicated OS user with restricted permissions
Risks to Understand
Unsandboxed tool execution
The shell, read-file, write-file, and edit-file tools have full filesystem and shell access. A prompt injection attack via an inbound email could trick the LLM into:
- Reading sensitive files
- Writing or deleting files
- Running arbitrary shell commands
- Exfiltrating data via
web-fetchorsend-email
Mitigation:
- Run Protege under a dedicated OS user with minimal filesystem permissions
- Don't run Protege in directories containing sensitive data
- Disable tools you don't need (remove them from the manifest)
- Use the gateway access policy to restrict who can email your agent
Trusted extension code
Extensions (tools, providers, hooks, resolvers) run with full process permissions. A malicious extension could do anything the Protege process can do.
Mitigation:
- Review extension source code before enabling it
- Treat
extensions/extensions.jsonas a security-sensitive file - Keep your extension list explicit and minimal
Costly model invocations
Every inbound email that passes the access policy triggers an LLM inference run (which costs money). Without access controls, anyone who discovers your agent's address can rack up API costs.
Mitigation:
- Enable the gateway access policy with
default_decision: deny - Monitor your LLM provider's usage dashboard
Operational Checklist
- Run
protege doctorafter any config change - Watch logs during initial exposure:
protege logs --scope gateway --follow - Verify
admin_contact_emailis set and reachable - Test your access policy with real sender addresses before going public
- Review which tools are enabled and remove any you don't need
Enforce Rollout Checklist (Relay Ingress)
Use this sequence before moving gateway_auth.mode from monitor to enforce.
- Configure trusted relay keys in
configs/security.json:
{
"gateway_auth": {
"enabled": true,
"mode": "monitor",
"policy": "require_dmarc_or_aligned_spf_dkim",
"trusted_relays": [
{
"key_id": "relay-primary",
"public_key_pem_path": "/etc/protege/gateway/relay-attestation.public.pem"
}
]
}
}Ensure relay is configured with the matching attestation key pair in
relay/config.json:relay_auth_attestation.enabled: truerelay_auth_attestation.keyIdmatches gatewaytrusted_relays[].key_idrelay_auth_attestation.privateKeyPathpoints to readable private key file
In
monitormode, send one legit Gmail message and one spoof probe.Inspect gateway logs:
- legit message should show
reason=monitor_pass - spoof probe should show
reason=monitor_fail
- legit message should show
Switch to
mode: "enforce"and restart gateway.Re-run legit + spoof probes:
- legit should process normally
- spoof should be rejected with
gateway.relay.ingest_failedand auth failure reason
If legit messages are rejected in enforce mode, first check relayAuthAttestationReason in gateway logs. The most common cause is key mismatch or unreadable key path.

