| T1 |
An agent approves its own work, or a bot review counts as a human pass |
Approvals count only from type != Bot accounts not in [identity] bot_logins; agents write as a bot identity |
In place: identity.HumanPass and ship's pass rule (WP1.4, WP5.5). This repository has no bot App installed, so agents write as the founder (identity:human-fallback), and in solo mode agent lanes are told apart by AAYU_AGENT=1 or a host's agent marker, the installed pre-tool hook blocks human-only shell commands in Claude Code, Codex, and Cursor, and a human-only action with no marker needs an interactive terminal (Bug 170); an agent that clears its host's markers inside a pseudo-terminal still looks like a person |
| T2 |
A PR changes the gates, skills, or thresholds that judge it |
CODEOWNERS on protected paths; protected-paths check runs from the base branch (pull_request_target, never checks out PR code); solo-mode merges only through the recorded admin bypass (Appendix B D2) |
In place (WP0.1) |
| T3 |
Defaults are loosened quietly |
CONSTRAINTS.md is generated from internal/config/defaults.toml; both are protected paths; generate-drift fails on any mismatch |
In place (WP0.5) |
| T4 |
An agent forges or edits a proof pack |
The authoritative pack is the one the base-branch merge gate built on the PR head; ship, run by a human, accepts it only from the aayudha-pack artifact of a successful pull_request_target run of aayudha-merge-gate.yml on the exact head, with every artifact's hash matching pack.json, and lands it on ledger under the human's identity; a pack already on ledger, an advisory push, an agent's preview, an artifact from another workflow, or a pull_request run of the PR's own copy of the gate never counts |
In place (WP4.8, S-4.2b): gatePack in internal/cli/ship.go, TestShipRefusals. Residual: GitHub's run record does not name a pull_request_target run's base branch, so someone who can push a branch with an edited gate and open a PR from the same head into it could produce a qualifying run; the ship record names the run, so audit can check it later. The PR comment is a view, not proof: proof comment runs only in a pull_request_target run of the gate, but a workflow on a same-repository branch can post look-alike text, so ship re-checks the artifact itself |
| T5 |
An agent games the autonomy streak |
Streak computed by a protected workflow from merged-PR history; corrections detected automatically; never-layers always need a human |
In place: aayudha-streak.yml and status --streak (WP5.7) |
| T6 |
Prompt injection makes an agent write, ship, publish, or run an ops action |
Provenance wrapping; untrusted text cannot trigger respond, ship, tell publish, or catalog execution; tool allowlists; labels-only capability for triage; injection fixtures in evals |
In place: <untrusted> provenance blocks in inspect, triage, review lane A, the brief review, eval judging, and MCP grill prompts; the labels-only capability in internal/actions; human-only verbs refused for agent lanes; an injection fixture in evals/cases/stub/injection (WP1.5, WP2.9, WP3.4, WP4.8) |
| T7 |
A token leaks to a floor, tool, or dependency |
Scrubbed child environment; no tokens in files; App key only in repository secrets, laptops get ≤ 1 h tokens from the broker |
In place: internal/verify runs floors with a scrubbed environment (no AAYU_*, GITHUB_TOKEN, or GH_*); the token broker (aayudha-token.yml, internal/identity/broker.go) refuses tokens living over one hour (WP1.3, WP1.4, WP4.3) |
| T8 |
An over-scoped human token does damage through an agent |
doctor warns on workflow, admin:*, delete_repo scopes; init is the only admin-scope verb and needs a TTY confirmation |
In place: aayu auth and doctor warn on these scopes; init needs a TTY confirmation (WP1.4, WP1.7, WP2.5) |
| T9 |
A compromised GitHub Action or CI tool |
Every action pinned by full commit SHA; downloaded tools (gitleaks, osv-scanner, cosign in install.sh) checked against pinned SHA-256; workflows default to contents: read; checkout never persists credentials |
In place (WP0.4) |
| T10 |
A tampered or substituted release |
goreleaser builds in release.yml only for an annotated v* tag whose commit is on main (verify-tag, Appendix B D4); checksums.txt signed keylessly with cosign; SLSA build provenance; SBOM per archive; install.sh verifies the signature against release.yml@refs/tags/<exact tag> and then the archive checksum; CI proves it refuses a flipped byte, a rewritten checksum, and a non-release signer |
In place (WP0.4); self-update replaces the binary only with a signed, newer release and refuses agents (WP1.1) |
| T11 |
A package-manager manifest is hijacked |
brew, Scoop, and winget publishing run in separate jobs, each with a token scoped to its own GitHub environment; the build job never holds them |
In place (WP0.4); tokens are a founder action |
| T12 |
A malicious dependency or licence problem |
osv-scanner, go-licenses, npm licence scan, gitleaks, semgrep in CI; new dependencies recorded in THIRD_PARTY_NOTICES.md |
In place (WP0.4) |
| T13 |
A malicious or compromised skill |
Skills are protected paths; installed copies are hashed in lock.toml and checked by doctor; install --pin rolls back; hooks call only aayu hook |
In place: protected path; lock.toml hashes checked by doctor (WP3.2); the skill lint's hook rule (WP3.1) |
| T14 |
Personal data in proof media |
Media never go to ledger or Git LFS: they stay in the merge gate's run artifact, which GitHub deletes after [proof] retention_days (at most the repository's limit); an opt-in bucket expires them by its lifecycle rule; captures use seeded data; redaction pass blocks the pack |
In place (S-4.2b): proof push and ship never put media on ledger (TestMediaNeedABucket), the gate sets the artifact's retention (TestArtifactRetention); [verify] seed, refusal of production targets, and the redaction pass that blocks the pack (WP4.5). A run artifact of a public repository can be downloaded by any signed-in GitHub user until it expires, as a screenshot in the PR would be; delete it on GitHub to remove it sooner. The founder accepted this against V3 (2026-10-15, Appendix B D-4.2b.g) |
| T15 |
Unlogged or unattributed writes |
Every external write goes through internal/actions; audit reconciles it against GitHub's event history |
aayu audit reconciles timelines, workflow dispatches, ledger commits, and rule-suite bypasses against the actions segments on the ledger (WP7.6) |
| T16 |
Runaway model or CI spend |
[budget] breaker pauses agent lanes and pages once a day; per-PR inspect cap; expensive floors only where the footprint needs them |
In place: the [budget] breaker and the per-PR inspect cap (WP1.6, WP4.8). Each process seeds the breaker from its clone's actions log (Bug 177), so the budgets count every process in one clone; a CI run's fresh clone sees only that run's spend |
| T17 |
A promotion ships a regression without a human watching |
Promotions need no human approval (founder policy, CONTRIBUTING.md), so the gate is the control: the candidate merge commit must pass the full rolling or stable suite before the branch moves, and only the admin identity can move rolling, main, or create v* tags |
In place (WP0.4) |
| T18 |
The tool itself is compromised |
Tool-incident plan: halt, yank the release, pull bad skills, rotate credentials (SECURITY.md) |
Plan in place; aayu halt and aayu resume (WP5.7). Every job of every workflow here and of every init --gates template starts with the halt step, and TestEveryWorkflowJobStartsWithHaltCheck fails CI on a job without it (Bug 173) |
| T19 |
A human-only command runs in a shell that cannot tell a person from an agent (an agent's shell holding the person's token, or a person's orb Terminal tab that inherits the agent's marker) |
The presence check: aayu asks for an approval through GitHub's OAuth device flow, completed on any device with the person's GitHub sign-in and second factor; the approver must be a person, the account aayu acts as, and hold the access the command needs; the token proves identity only and is never stored; aayu's unattended lanes are refused at once |
In place: Env.requireHuman and presenceCheck in internal/cli/presence.go (TestPresenceCheckDeviceFlow, TestPresenceCheckAccess). The built-in client ID (DefaultDeviceClientID, the project's OAuth App) needs no setup; AAYUDHA_DEVICE_CLIENT_ID or [identity] device_client_id replaces it. GitHub's page names the app, not the command, so the terminal and the batch comment carry the command and each effect (TestDecideBatchDeviceCode). An agent driving a browser signed in as the person could approve; keep agents out of such browsers. Device flow may be off under an organisation's OAuth App policy, for Enterprise Managed Users, or on GHES |
| T20 |
An agent writes an /aayu approve reply, or edits a batch or a reply, to approve its own human-only steps |
A reply counts only when the bot identity is configured, the author is in [approvals] approvers (a protected path) with write access and is not a bot, neither reply nor batch was edited, the reply came after the batch, the hash matches, the Decisions are unchanged, and no item is a door [approvals] device_for keeps on the device code (one-way by default); a session that can reach an approver's GitHub credential never offers reply approval, and doctor fails its approvals row there |
In place: approveFromComment and commentApprovalsOff in internal/cli/approve.go, the aayudha-approve.yml workflow on issue_comment (default-branch copy), and checkApprovals in internal/doctor (TestDecideBatchApprovedByReply, TestBatchFallsBackWhenApproverCredentialIsReachable, TestChecksGolden). Credential hygiene is checked, not enforced: an approver credential that aayu cannot see (another machine, a browser session) defeats it. In an Amp orb the git credential helper returns the user's GitHub token to any process, so orbs stay on the device code until Amp's GitHub connection is an agent-only account |
| T21 |
A collaborator forges a security Bug's events to hold or release a promotion |
Only issues and comments by github-actions[bot] count, but any workflow's token writes as that account, so events are not trusted to release a hold: each promotion also needs a completed green push run of aayudha-security.yml on the source tip, read from the Actions run list, which a branch cannot forge (a run on the source branch at its tip runs that tip's own workflow file) |
In place: openSecurityBugs and securityGate in internal/cli/verifysecurity.go (TestReleaseSecurityBugsHold, TestVerifySecurityBugs). Residual: a forged gone event can release a secret's Bug whose secret sits in history before the pushed commits (a push run reads only those commits), and a forged seen event holds a promotion until a person waives it |