CTO/4

secure-agent-workflow · an open-source software factory

Let agents build. Keep the controls out of reach.

Coding agents work approved issues through to merged pull requests with little supervision. Nothing reaches production without passing checks the agent can't edit, skip or see the credentials for.

Controls live outside the agent

They sit in the sandbox, GitHub permissions and cloud credentials. Instructions and settings files inside the sandbox are not controls, because the agent can rewrite them.

Risk decides who approves

A tier map sorts every changed path. Low-risk changes merge on green checks. Dependencies, auth and workflow changes wait for a human.

Each part is replaceable

v1 runs on Cloudflare and AWS. Every component sits in its own folder behind a stated contract, so a fork can rewrite one without touching the rest.

01

Where things run, and who holds the keys

The agent runs in a sandbox with no credentials and no direct internet. Everything it sends leaves through a handler that adds short-lived tokens on the way out. Deploy credentials sit behind separate roles: previews and staging in the dev target, where only staging can change infrastructure, and one that only a production release can assume.

Topology of secure-agent-workflow A Cloudflare orchestrator, in its own factory account, starts a credential-free sandbox per issue. Its traffic leaves through an outbound handler that injects tokens and allows only listed hosts. The agent pushes branches and opens pull requests on GitHub, where a merge gate decides what reaches main. Merges to main deploy to staging in the dev target; production deploys only from the production branch. Each deploy job gets a short-lived token from an AWS role that trusts only its environment's OIDC token. Infrastructure is OpenTofu, planned on the PR and applied only after merge, with its state in AWS. Model APIs Reached only through the outbound handler CLOUDFLARE · FACTORY ACCOUNT Orchestrator Worker + coordinator Queue, concurrency cap, dependency order One sandbox per issue; stops it on agent:stop App private key · model keys · Slack token Outbound handler Allowlisted hosts only · adds tokens Git pushes to agent/* branches only Sandbox container · one per issue Agent CLI and a checkout of the repo No credentials · no direct internet Never sees the tokens it works with Torn down when the run ends, blocks or is stopped start · stop GITHUB · YOUR REPO Issues Labels: agent:ready · agent:stop · agent:blocked Agent GitHub App Writes code, PRs, issues · no workflows · no admin agent/<issue> branch → pull request Auto-merge on · can't push to main Merge gate · reusable workflows, pinned CHECKS SCANNERS TIER CHECK PR AUDIT Tier 1 merges on green · tier 2 and 3 wait for you main → staging · production → prod Rulesets with no bypass · releases need you webhook push You Dispatch a wave of labeled issues Approve tier 2–3 on the latest commit Approve releases; run bootstrap CLOUDFLARE · DEPLOY TARGETS Dev target Previews from branches Staging from main Infra applied by CI only Prod target Deployed only from the production branch after a release PR AWS · BROKER ACCOUNT Dev roles Preview · Staging Prod role Trusts production only Parameter Store · a path per role Cloudflare dev token Cloudflare prod token OpenTofu state · one AWS account or two OIDC token from the deploy job, naming its environment short-lived token for the matching account
A control the agent can't change Holds no credentials Where a credential lives

What stops what

edits a workflow file…

GitHub rejects the push. The App has no workflows permission.

pushes to main or production…

Rulesets require a PR and bind everyone, admin included. Only you can release.

looks for a credential…

There is none in the sandbox. Tokens are added outside it.

calls an unknown host…

The outbound handler allows listed hosts only.

changes a dependency or auth code…

The tier check fails until you approve the latest commit.

rewrites the tier map or CODEOWNERS…

Tier 3. An agent-authored PR there always fails.

tries to change infrastructure…

It can only write OpenTofu. The plan is posted on the PR, it waits for you, and only the pipeline applies it.

deletes a database or bucket…

A plan that deletes or replaces stored data fails the gate unless you wrote the PR.

hides a command in a build script…

Previews get a Workers-only token. Only staging, after merge, holds the token that changes infrastructure.

02

From an approved issue to production

You plan and dispatch work. Agents build it. The merge gate decides what needs you. A tier 1 change can go from label to staging without anyone touching it. Production is a release you cut.

1 You + planning agent

Plan

Spec, implementation plan and ADRs. The plan becomes issues, with dependencies marked.

2 You

Dispatch

One command labels a wave's issues agent:ready. The label is the only start button.

3 Orchestrator

Queue

Runs each issue once its dependencies merge, within a concurrency cap and a budget.

4 Build agent

Build

Works in a fresh sandbox on its own branch, writing code and infrastructure alike. Opens a draft PR and gets a preview deploy.

5 Merge gate You, tier 2–3

Check

Tests, scanners, tier check, an infrastructure plan, and an LLM audit that can reject but never approve. You approve what needs you, in GitHub.

6 GitHub Actions

Stage

Every merge to main applies infrastructure, then deploys to staging, in the dev target.

7 You

Release

A versioned PR from main into production, with a summary of the whole batch. Only you approve it.

Later phase

Press play to follow one issue from plan to production. Switch to a tier 2 change to see where you come in.

Tier 1Merges itself

App source, tests, general docs

All checks green and no blocking audit finding. Zero touches from you.

Tier 2Waits for you

Dependencies, auth code, migrations, infrastructure, specs, ADRs and agent instructions, and anything the map doesn't name

Your approval on the PR's latest commit. A new push needs a new approval.

Tier 3Human-authored

.github/, CODEOWNERS, the tier map itself

An agent can't author these at all. You write them and approve them.

Waves keep approvals few. The plan groups issues into waves that you dispatch and review together. Dependency, auth and migration changes go into one or two foundation issues at the start of a wave, so the rest of it stays tier 1. A wave of twenty issues might need three approvals, not twenty.

Blocked

The agent pushes its work, writes a handoff note on the PR, labels it agent:blocked and exits. Your reply on GitHub or Slack starts a fresh sandbox that picks up from the note.

Stopped

agent:stop revokes the agent's token and tears down its sandbox. The orchestrator enforces it, so it works even if the agent ignores instructions.

Out of budget

The outbound handler refuses model calls once a run's token budget is spent, and all runs share a daily spend cap. Provider spend limits sit behind both, outside this system's code.

03

Putting a new project on it

Protecting a repo is one command. After that, your work is planning, releasing issues and approving the changes that need you.

  1. Install the agent's GitHub App once per repo

    The agent acts as this App. It can write code, PRs and issues, and nothing else: no workflows, no settings.

  2. Run bootstrap one command

    bootstrap.sh owner/repo applies the ruleset, Actions policy, environments and labels, and opens one PR with the gate wiring and a tier map. Safe to re-run; it reads back every setting it writes.

  3. Merge the bootstrap PR

    It runs the gate on itself first. From then on, every PR into main passes the same checks.

  4. Connect deploy credentials one command

    broker.sh add-repo owner/repo creates the repo's preview and staging roles. You put each environment's tokens in Parameter Store; no deploy secret is stored in GitHub.

  5. Plan in waves

    Spec, plan and ADRs, broken into issues sized for one agent run and grouped into waves, with their dependencies marked.

  6. Dispatch a wave, then review only what needs you

    /dispatch labels the wave's issues agent:ready. Tier 1 PRs merge on their own and reach staging. Tier 2 and 3 PRs arrive with the reason they need you. When staging looks right, you cut a release.

Keeping the gate current

Every repo pins the gate to a commit of this repo, so a change here reaches a project only when that project accepts it.

bootstrap.sh owner/repo --update-gate <sha> moves the pin through a PR. It refuses a commit that isn't on this repo's main.

Swapping a part

SandboxE2B, Docker Sandboxes, gVisor or Kata containers
Egress proxyEnvoy or Squid with an allowlist
Credential brokerVault or OpenBao, GCP or Azure federated credentials
InfrastructureTerraform, Pulumi
ScannersGitHub Advanced Security, a commercial scanner
Agent CLIAny CLI that runs headless

GitHub stays. Whatever you replace, keep the property the design depends on: the agent can't change the control.

—

Where it stands

The agent's identity, bootstrap, the merge gate, the credential broker and deploys to preview and staging are built and tested on testbeds. Next is the orchestrator, then the first wave of a real project. Production releases are designed and wait until that works.

Built and tested

  • Agent GitHub App, with no workflows or admin permission
  • Bootstrap: ruleset, Actions policy, environments, labels
  • Merge gate: checks and scanners, pinned tools
  • Tier check, re-run on every review
  • Gate wiring installed and updated by bootstrap
  • Specs, ADRs and agent instructions are tier 2
  • Templates, wave planning standard and /dispatch
  • AWS credential broker: preview and staging roles, state bucket
  • Credentials declared per repo, checked before storing and daily
  • Preview and staging deploys through the dev roles
  • This repo's own PRs go through the pinned gate

Next

  • Orchestrator, sandbox, outbound handler and budgets
  • Static assets in deploys, and an onboarding guide for a new project
  • Infrastructure as OpenTofu: plan on the PR, apply after merge, drift checks
  • Red-team runs, with results published
  • PR audit: LLM reviewers and an approver summary
  • Production releases: versioned, batch-reviewed
  • Operator console that works on a phone, and a separate identity for the planning agent