GitHub Agentic Workflows (gh-aw) let you run an LLM agent - Claude, Codex, or Copilot - inside a GitHub Actions run with a constrained tool surface. The agent can run bash, install packages, edit files, call MCP servers, and open pull requests on your behalf. It is doing real work on your repository, on a real Linux runner, with real credentials in reach.
The first security question teams ask is simple: what did the agent actually do?
GitHub answers part of that in Under the hood: Security architecture of GitHub Agentic Workflows. As Landon Cox describes, gh-aw isolates the agent in a container with a chroot jail at /host, private networking, a firewall, an API proxy, and explicit secret separation outside container reach.
That substrate gives you strong control points and logs at the firewall, API proxy, MCP gateway, and agent container. Those are all valuable, but they are still on the agent side of the boundary. They show what the agent asked to do.
This guide covers the other vantage point: observing from the runner VM itself, outside the agent boundary, at the kernel. That is where Garnet and Jibril operate.
Why kernel-level, outside the boundary
Cox's trust-boundary model matters here. The runner VM is substrate. The agent container is workload. Jibril loads as an eBPF runtime on the runner VM, outside the agent container.
From that position:
- Every syscall the agent causes is visible, including libc/runtime side effects that the agent never logs.
- Outbound traffic is captured with ancestry (
bash -> npm -> node -> postinstall.js -> curl) instead of reconstructed from text logs. - The agent cannot disable observation from inside its own boundary.
- Observation is decoupled from the tool surface: direct shell, MCP path, or indirect sub-tool all converge to the same kernel evidence.
gh-aw tells you what the substrate negotiated. Garnet tells you what the kernel observed. You need both to answer "what did the agent do?" with confidence.
What you will see after first instrumentation
Once your run is instrumented, github.run_id maps to three operational views in dashboard.garnet.ai.
Flows
Every outbound TCP/UDP connection, including destination domain/IP/port, originating PID, full ancestry, and byte counts.
For a dependency-focused agent, expected egress usually includes model/provider APIs, GitHub APIs, and package registries. Anything beyond that appears immediately in run scope.
Profiles
A flattened process lineage for the run: who spawned what, with which argv, and when. Clean agent workflows develop stable shape quickly. Shape drift (for example, unexpected shell pivots under package install paths) is high-value signal.
Detections
MITRE-mapped runtime detections (for example T1059, T1071, T1567) tied to process, file, and time context. Most healthy runs are quiet. When they are not, you get concrete lineage and egress evidence attached to the detection.
Optionally, run-scoped snapshots can be written to .garnet/runtime/latest/ for downstream analysis (domains_for_run.md, summary.json, profile_for_run.*).
Instrumenting a workflow in 10 minutes
The end-to-end reference implementation is garnet-labs/ghaw-garnet-reference. The steps below are the shortest path from a vanilla gh-aw workflow to a Garnet-instrumented one.
Step 1 - Create a Garnet API token
In dashboard.garnet.ai:
Settings -> API Tokens -> New token
Name it for the target repository. Copy it once.
Step 2 - Add repository secrets
In your target repository:
Settings -> Secrets and variables -> Actions
| Name | Value |
|---|---|
GARNET_API_TOKEN | Token from Step 1 |
ANTHROPIC_API_KEY | Or equivalent provider key used by your workflow |
Use org-level secrets for multi-repo rollout.
Step 3 - Install the gh-aw CLI
gh extension install githubnext/gh-awStep 4 - Add or open the workflow you want to instrument
gh aw add githubnext/agentics/daily-repo-statusThis creates:
.github/workflows/daily-repo-status.md(authoring source).github/workflows/daily-repo-status.lock.yml(execution artifact)
Keep both in sync.
Step 5 - Apply four frontmatter/workflow edits
5a. Add explicit permissions with OIDC
id-token: write is required for OIDC exchange. Without it, Garnet action startup fails before probes attach.
permissions:
contents: read
issues: read
pull-requests: read
id-token: write5b. Use structured network.allowed (not shorthand only)
network:
allowed:
- defaults
- github
- api.garnet.ai
- dashboard.garnet.aiAdd additional destinations only for tooling your workflow truly needs.
5c. Put Garnet first in steps
steps:
- name: Start Garnet Agent
id: garnet
uses: garnet-org/action@v2
with:
api_token: ${{ secrets.GARNET_API_TOKEN }}
jibril_version: "2.12.0"Anything that runs before this step is outside Jibril's attached observation window.
5d. Pin action SHA in production
uses: garnet-org/action@9e819143e63d6dda04bca2e90ac85e3cf0e5289dPinning preserves detection reproducibility. The reference SHA above is aligned with jibril_version: 2.12.0 in the reference repo.
Step 6 - Recompile lock workflow
gh aw compileCommit both .md and .lock.yml.
Step 7 - Trigger and watch a run
gh workflow run daily-repo-status.lock.yml
gh run watchStep 8 - Verify boundary behavior
Within about 30 seconds after Garnet step completion, verify:
- Flows are populated.
- Profile contains expected agent engine lineage under runner worker context.
- Detections are empty or operationally expected for your repo baseline.
If these checks fail, use the troubleshooting table below.
Patterns worth knowing
Scheduled agent pattern (lowest blast radius)
Use daily-repo-status or daily-team-status to baseline first. After a few clean runs, profile shape becomes stable enough that structural drift stands out.
Package-installing agent pattern (highest risk-adjusted value)
Any workflow that resolves third-party dependencies (dependabot style checks, custom package testers, install-heavy CI tasks) benefits most from kernel visibility because postinstall side effects often never surface in agent narration.
One practical caveat in gh-aw environments is squid proxy mediation. For npm, apply workdir-scoped .npmrc when needed:
printf '%s\n' \
'registry=https://registry.npmjs.org/' \
'proxy=http://172.30.0.10:3128/' \
'https-proxy=http://172.30.0.10:3128/' \
'strict-ssl=true' \
> /tmp/gh-aw/pkg-install/.npmrcScope this to the workflow workdir only. Do not write this host-wide.
Agent-on-agent pattern (audit nested coding sessions)
When one workflow launches another coding agent, Garnet captures both layers in lineage. You can see outer orchestration and inner tooling ancestry in one runtime chain.
What this does not replace
- It does not expose the model's hidden plan text.
- It does not replace safe-outputs policy checks.
- It does not replace static code and dependency analysis on produced artifacts.
Static tooling answers "what code artifacts contain." Runtime profiling answers "what executed." For agentic CI, you need both.
Troubleshooting first run
| Symptom | Likely cause | Fix |
|---|---|---|
Resource not accessible by integration on Garnet step | Missing id-token: write | Apply Step 5a |
lookup api.garnet.ai: no such host or API 403 | Incorrect network.allowed | Apply Step 5b |
| Run completes but no dashboard data | Garnet step not first, or wrong token | Move step to position 1 and verify secret name |
| npm TLS/proxy errors during install | Proxy/TLS mismatch in agent network path | Use workdir-scoped .npmrc pattern |
.lock.yml out-of-sync warnings | Edited .md without compile | Run gh aw compile, commit both files |
Clone the working end-to-end repository with two instrumented workflows, required secret names, and dispatch-ready examples.
Further reading
- Under the hood: Security architecture of GitHub Agentic Workflows
- githubnext/gh-aw
- githubnext/agentics workflow catalog
Start instrumenting your workflows with Garnet.