CCOSIGNET

Blog · Deployment guide

Agent deployment approval workflow

If an AI agent can trigger a production deploy, the approval should be tied to the exact deploy facts: commit, environment, service, migration, actor, and rollback plan.

The deployment risk

Traditional CI/CD approvals often answer a vague question: “may this pipeline continue?” That is weak for agent-driven work. An agent may summarize the intent correctly but still pass the wrong commit, target the wrong environment, include an unsafe migration, or retry with changed parameters.

A safer workflow asks a person to approve the exact operation the runner will execute. The approval is useful only if the deploy code compares the signed payload with the final deploy inputs and fails closed on any mismatch.

Payload checklist

For a production deploy, include enough detail that the approver can recognize the operation and the runner can prove it is still the same one:

GitLab CI example

The job creates a Cosignet approval request and long-polls over an outbound HTTP connection. The runner proceeds only when the decision is approved.

deploy_production:
  stage: deploy
  script:
    - export DEPLOY_PAYLOAD="{\"service\":\"api\",\"environment\":\"production\",\"commit\":\"$CI_COMMIT_SHA\",\"pipeline\":\"$CI_PIPELINE_URL\"}"
    - node scripts/cosignet-deploy-gate.mjs
    - ./scripts/deploy-production.sh "$CI_COMMIT_SHA"
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      when: manual

The same shape works in GitHub Actions, Buildkite, Jenkins, or a plain deployment script. The important invariant is that the deploy command receives the same commit and environment that were signed.

Gate script sketch

const payload = JSON.parse(process.env.DEPLOY_PAYLOAD);

const created = await fetch('https://cosignet.com/api/confirmations', {
  method: 'POST',
  headers: {
    'content-type': 'application/json',
    'X-Api-Key': process.env.COSIGNET_API_KEY
  },
  body: JSON.stringify({
    username: process.env.COSIGNET_APPROVER,
    action: `Deploy ${payload.service} to ${payload.environment}`,
    payload
  })
}).then((r) => r.json());

let decision = { status: 'pending' };
while (decision.status === 'pending') {
  decision = await fetch(
    `https://cosignet.com/api/confirmations/${created.id}?wait=25`,
    { headers: { 'X-Api-Key': process.env.COSIGNET_API_KEY } }
  ).then((r) => r.json());
}

if (decision.status !== 'approved') {
  throw new Error(`deployment not approved: ${decision.status}`);
}

const finalDeploy = {
  service: 'api',
  environment: 'production',
  commit: process.env.CI_COMMIT_SHA
};
if (
  payload.service !== finalDeploy.service ||
  payload.environment !== finalDeploy.environment ||
  payload.commit !== finalDeploy.commit
) {
  throw new Error('signed payload does not match the operation about to run');
}

In production, also compare the final deploy variables with the signed payload immediately before the deploy command runs. If the commit, environment, migration, or service name changed, stop and request a new approval.

Operational rules

For AI agents

If an agent proposes the deployment, make the gate a required tool or script step, not a prompt-only convention. The agent may draft the payload, but your deploy wrapper should compute and validate the final payload from CI variables. For direct tool-calling agents, use Cosignet as an MCP approval gate.