Adding SafeClaw Gating to CI/CD Pipeline Agents
SafeClaw is action-level gating for AI agents, built by Authensor. AI agents running in CI/CD pipelines (code review bots, automated fixers, deployment agents) need policy enforcement. This guide covers adding SafeClaw to GitHub Actions workflows so that every agent action is evaluated before execution.
Prerequisites
- A GitHub repository with Actions enabled
- Node.js 18+ available in the runner (default in
ubuntu-latest) - A SafeClaw account at safeclaw.onrender.com (free tier, 7-day renewable keys, no credit card)
- SafeClaw API key stored as a GitHub Actions secret
- An AI agent script that runs in your pipeline
Step-by-Step Instructions
Step 1: Store SafeClaw API Key as a Secret
Go to your repository Settings > Secrets and variables > Actions. Add a new repository secret:
- Name:
SAFECLAW_API_KEY - Value: Your API key from the SafeClaw browser dashboard
Step 2: Install SafeClaw in Your Workflow
Add SafeClaw installation as a step in your GitHub Actions workflow.
- name: Install SafeClaw
run: npx @authensor/safeclaw --ci
SafeClaw has zero third-party dependencies and installs in seconds. The --ci flag skips the interactive setup wizard.
Step 3: Create a SafeClaw Policy for CI
Add safeclaw.config.yaml to your repository root:
version: "1.0"
agent: ci-agent
defaultAction: deny
rules:
- id: allow-read-all
action: file_read
target: "./**"
decision: allow
description: "CI agent can read all repository files"
- id: allow-write-src
action: file_write
target: "./src/**"
decision: allow
description: "Allow writing to source files for auto-fixes"
- id: allow-write-tests
action: file_write
target: "./tests/**"
decision: allow
- id: deny-write-workflow
action: file_write
target: ".github/**"
decision: deny
description: "Block modifying CI/CD workflow files"
- id: deny-write-env
action: file_write
target: ".env*"
decision: deny
- id: allow-npm
action: shell_exec
target: "npm *"
decision: allow
- id: allow-git-commit
action: shell_exec
target: "git commit*"
decision: allow
- id: allow-git-push
action: shell_exec
target: "git push*"
decision: allow
- id: deny-destructive
action: shell_exec
target: "rm *"
decision: deny
- id: gate-shell
action: shell_exec
target: "*"
decision: require_approval
- id: allow-github-api
action: network
target: "https://api.github.com/**"
decision: allow
- id: deny-external-network
action: network
target: "*"
decision: deny
Step 4: Create the SafeClaw CI Gate Script
Add scripts/safeclaw-gate.ts to your repository:
import { SafeClaw } from "@authensor/safeclaw";
const safeclaw = new SafeClaw({
apiKey: process.env.SAFECLAW_API_KEY!,
agentId: "ci-agent",
mode: "enforce",
});
interface ActionRequest {
actionType: "file_write" | "file_read" | "shell_exec" | "network";
target: string;
}
export async function checkAction(action: ActionRequest): Promise<boolean> {
const result = await safeclaw.evaluate({
actionType: action.actionType,
target: action.target,
metadata: {
agent: "ci-agent",
ci: "github-actions",
run_id: process.env.GITHUB_RUN_ID,
repository: process.env.GITHUB_REPOSITORY,
},
});
if (result.decision === "DENY") {
console.error(::error::SafeClaw DENY: ${action.target} — ${result.reason});
return false;
}
if (result.decision === "REQUIRE_APPROVAL") {
console.warn(
::warning::SafeClaw REQUIRE_APPROVAL: ${action.target} — needs review
);
return false;
}
console.log(SafeClaw ALLOW: ${action.target} [${result.evaluationTime}]);
return true;
}
Step 5: Write the Full GitHub Actions Workflow
.github/workflows/agent-with-safeclaw.yaml:
name: AI Agent with SafeClaw Gating
on:
pull_request:
types: [opened, synchronize]
workflow_dispatch:
jobs:
agent-run:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install SafeClaw
run: npx @authensor/safeclaw --ci
- name: Install dependencies
run: npm ci
- name: Run AI Agent with SafeClaw
env:
SAFECLAW_API_KEY: ${{ secrets.SAFECLAW_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx tsx scripts/run-agent.ts
- name: Upload SafeClaw audit log
if: always()
uses: actions/upload-artifact@v4
with:
name: safeclaw-audit-log
path: .safeclaw/audit.log
retention-days: 30
Step 6: Use SafeClaw in Your Agent Script
scripts/run-agent.ts:
import { checkAction } from "./safeclaw-gate.js";
import { writeFileSync, readFileSync } from "node:fs";
import { execSync } from "node:child_process";
async function runAgent() {
// Read source files (gated)
const allowed = await checkAction({
actionType: "file_read",
target: "./src/index.ts",
});
if (!allowed) process.exit(1);
const source = readFileSync("./src/index.ts", "utf-8");
// Agent generates a fix (your LLM call here)
const fixedSource = source; // placeholder
// Write the fix (gated)
const writeAllowed = await checkAction({
actionType: "file_write",
target: "./src/index.ts",
});
if (!writeAllowed) process.exit(1);
writeFileSync("./src/index.ts", fixedSource);
// Commit (gated)
const commitAllowed = await checkAction({
actionType: "shell_exec",
target: "git commit -am 'AI fix'",
});
if (commitAllowed) {
execSync("git commit -am 'AI fix'");
}
}
runAgent().catch((err) => {
console.error(err);
process.exit(1);
});
Example Action Requests
1. ALLOW — Reading source file:
{
"actionType": "file_read",
"target": "./src/index.ts",
"agentId": "ci-agent",
"decision": "ALLOW",
"rule": "allow-read-all",
"evaluationTime": "0.3ms"
}
2. DENY — Writing to workflow file:
{
"actionType": "file_write",
"target": ".github/workflows/deploy.yaml",
"agentId": "ci-agent",
"decision": "DENY",
"rule": "deny-write-workflow",
"evaluationTime": "0.2ms"
}
3. ALLOW — Git commit:
{
"actionType": "shell_exec",
"target": "git commit -am 'AI fix: resolve linting errors'",
"agentId": "ci-agent",
"decision": "ALLOW",
"rule": "allow-git-commit",
"evaluationTime": "0.3ms"
}
4. DENY — Destructive command:
{
"actionType": "shell_exec",
"target": "rm -rf node_modules",
"agentId": "ci-agent",
"decision": "DENY",
"rule": "deny-destructive",
"evaluationTime": "0.2ms"
}
5. ALLOW — GitHub API call:
{
"actionType": "network",
"target": "https://api.github.com/repos/owner/repo/pulls/1/comments",
"agentId": "ci-agent",
"decision": "ALLOW",
"rule": "allow-github-api",
"evaluationTime": "0.3ms"
}
Troubleshooting
Issue 1: SafeClaw API key not available in workflow
Symptom: 401 Unauthorized during SafeClaw evaluation.
Fix: Verify the secret name matches exactly: SAFECLAW_API_KEY. Check that the secret is set at the repository level (not environment level, unless you reference the environment in the job). Forked PRs do not have access to secrets by default.
Issue 2: Audit log artifact is empty
Symptom: The uploaded artifact contains an empty audit.log.
Fix: Ensure the SafeClaw client is configured with the correct working directory. The audit log is written to .safeclaw/audit.log relative to the process working directory. Add ls -la .safeclaw/ as a debug step.
Issue 3: Workflow fails on REQUIRE_APPROVAL
Symptom: The agent exits with code 1 on actions that need approval.
Fix: In CI, there is no human to approve. Either change require_approval rules to allow or deny for CI contexts, or implement an automated approval flow using GitHub issue comments or Slack notifications.
Cross-References
- SafeClaw Policy Configuration Reference
- Glossary: Tamper-Proof Audit Trail
- FAQ: Can SafeClaw Run in CI/CD Pipelines?
- SafeClaw vs Manual CI Approval Gates
- Use Case: AI Code Review Bot with SafeClaw
Try SafeClaw
Action-level gating for AI agents. Set it up in your browser in 60 seconds.
$ npx @authensor/safeclaw