Security Model
ASF50 is designed to be fail-closed. This page documents the trust boundaries, the security controls, and the rules an operator must follow.
Trust boundaries
Public/private key model
- The owner public key lives at
agent-harness/governance/allowed_signers. This is a plain text file containing onessh-ed25519line per principal. - The owner private key lives only on the owner's workstation. It never enters the ASF50 repo, runtime DB, evidence directory, chat history, or this documentation portal.
If you see a private key anywhere in the canonical ASF50 repository, that is a security incident. Open a REPAIR_GOAL and revoke the key.
GitHub identity
GitHub identities are bound to github-review-approval sources. The orchestrator records the GitHub login of the reviewer in the approver field. A GitHub login that looks like an agent role (e.g., builder, auditor) is rejected at write time.
Least privilege
Each role has the minimum capability needed for its task. The orchestrator enforces:
allowed_paths— files the role may modify.forbidden_paths— files the role must not touch.role_required— the role a task must be dispatched under.
Violating any of these is recorded as a finding and may block the goal.
Fail-closed gate behavior
When a gate's scope, source, approver, or subject_sha is unrecognized or missing, the orchestrator refuses to consume it. There is no "auto-approve on miss" path.
Single-use authorization
Every gate has dispatches_used and max_dispatches. A MERGE_APPROVAL is single-use. A PAID_MODEL_DISPATCH_APPROVAL may have max_dispatches > 1 for bounded multi-dispatch goals, but each consumption is recorded.
Subject binding
The gate's subject_sha must match the runtime HEAD at consumption time. A different SHA → stale gate → not consumed.
Stale-subject rejection
If the PR head moves between review and merge, the gate is rejected. Re-issue the gate at the new head.
Replay protection
A consumed gate cannot be replayed. The orchestrator records the consumption and rejects subsequent submissions of the same ticket_id.
Branch protection
The main branch is protected. Direct pushes are rejected. The only path to land on main is a governed merge of an approved PR.
Runtime-state integrity
The runtime SQLite DB is opened in mode=ro for read operations and is mutated only through RuntimeStore. Direct SQL writes are not supported by any operator-facing CLI.
Secret-handling rules
- No private keys in repo, runtime, evidence, chat, or docs.
- No real credentials in repo, runtime, evidence, chat, or docs.
- No real sensitive tokens in repo, runtime, evidence, chat, or docs.
- Use environment variables for runtime credentials.
- Use a separate, non-canonical secret store for production secrets.
If a secret accidentally lands in the repo, rotate it immediately and open a REPAIR_GOAL to scrub the history (via a frozen history rewrite, which itself requires explicit owner authorization).