GitHub Action
The Loopback Action runs jobs in your own GitHub Actions runners: the checkout and your AI provider key stay there, and Loopback receives the results of each job. Code understanding and verification are powered by vexp.
The workflow
The onboarding pull request adds this file, shown here for a runner with Claude Code and an API key. For your repository it also sets up the Node version the repository declares and the sign-in to Bedrock or Vertex when you use them, and derives the time limit from your job limit. Loopback starts a job with a repository_dispatch event whose payload carries only the job id and the API address.
# .github/workflows/loopback.ymlname: Loopbackrun-name: Loopback ${{ github.event.client_payload.job_id }}on: repository_dispatch: types: [loopback-job]permissions: contents: write pull-requests: write id-token: write # OIDC sign-in to Loopbackconcurrency: group: loopback-${{ github.event.client_payload.job_id }}jobs: run: runs-on: ubuntu-latest timeout-minutes: 75 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 persist-credentials: false - uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 if: ${{ github.event.repository.private }} with: path: .vexp key: loopback-index-${{ github.run_id }}-${{ github.run_attempt }} restore-keys: loopback-index- - name: Install bubblewrap run: sudo apt-get update -q && sudo apt-get install -y bubblewrap - uses: loopback-dev/action@v1 with: agent-key: ${{ secrets.LOOPBACK_AGENT_KEY }} api-url: "https://api.loopback.so" - uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 if: ${{ !cancelled() && github.event.repository.private }} with: path: .vexp key: loopback-index-${{ github.run_id }}-${{ github.run_attempt }}What one run does
- Reads the job id from the dispatch event.
- Asks GitHub for an OIDC ID token with audience
loopbackand exchanges it for a job token, valid for that job of that repository for 60 minutes, and an agent token valid only for the tools of that job. Each ID token is accepted once. - Downloads the job and prepares the checkout at the job’s base commit, removing the credentials that
actions/checkoutleaves behind. - Runs the agent of the workspace or of
.loopback.yml, and streams progress and heartbeats to Loopback; the job can be followed and cancelled from the dashboard. - For a fix, runs the verification and your tests, commits only when both pass, and pushes the
loopback/branch with a short-lived token; Loopback opens the pull request. - Reports the result. Reporting is idempotent: running the workflow again never creates a second pull request.
Inputs
| Input | Default | Description |
|---|---|---|
agent-key | API key of your AI provider (Anthropic, OpenAI or OpenRouter), as a secret. Only the agent process receives it. Leave it empty for Bedrock, Vertex or a Codex login stored in Loopback. | |
api-url | https://api.loopback.dev | Loopback API. The OIDC token is sent only here: a dispatch that names another API is refused. |
oidc-audience | loopback | Audience of the GitHub OIDC token. Change it only when your install of Loopback verifies another one; the onboarding pull request writes it for you. |
command | run | run runs the dispatched job; upload-sourcemaps uploads the source maps of a build. |
sourcemaps-path | dist | For upload-sourcemaps: the build directory with the .map files. |
url-prefix | / | For upload-sourcemaps: the URL path the build directory is served under. |
release | For upload-sourcemaps: the release your app reports, for example my-app@1.8.3. Required for that command. | |
github-token | Optional: a token to fetch history missing from a shallow checkout. Never given to the agent, never used to push. |
Outputs
| Output | Description |
|---|---|
status | Outcome of the last job: succeeded, needs_human, failed or cancelled. With needs_human the ticket went to your team and the run stays green. |
job-id | Id of the last job run. |
branch | The loopback/ branch of a fix, if one was pushed. |
pr-url | The pull request of a fix, if one was opened. |
uploaded | For upload-sourcemaps: the number of source maps uploaded. |
Permissions
id-token: writelets the job ask GitHub for an OIDC ID token: that is how it signs in to Loopback, with no long-lived secret in your repository.- The fix branch is pushed with a short-lived installation token that Loopback issues for that job only, limited to the repository and to
contents: write, and only to the job’sloopback/branch. - The tag is always created by the GitHub App after the approval, never by
GITHUB_TOKEN: events ofGITHUB_TOKENdo not start other workflows, so your release workflows would not start.
Security model
- No long-lived Loopback secret in your repository: the job signs in with OIDC.
- The job token covers one job and expires in 60 minutes; renewals never outlive the job’s time limit and never add permissions. Only fix jobs can get a push token.
- Every token is masked in the workflow log as soon as the Action holds it.
- The agent cannot push: credentials persisted in the checkout are removed before it starts, and its git commands run without hooks and without any transport.
- The agent runs with a private home, no global git config, and an environment without the Action’s inputs,
GITHUB_TOKENor the OIDC request token. - Credential files in the checkout, such as
.env, private keys and registry credentials, are not readable by the built-in agent, Claude Code or opencode. - A change that touches
.github/,.loopback.ymlor.vexp/is rejected, and the tree is frozen before verification: the commit contains exactly what was verified. - Ticket text and logs reach the agent wrapped as untrusted content, and secrets are masked out of everything the harness reports.
Agents and credentials
builtin, the default: the built-in agent with the provider of your key, Bedrock or Vertex.claude-code: an Anthropic API key, Bedrock or Vertex. Subscription logins are refused outside MCP mode.codex: an OpenAI API key, or a ChatGPT login stored in Loopback, refreshed and saved back after each job, one job at a time.opencode: the API key of its provider.- Bedrock: add
aws-actions/configure-aws-credentialswith OIDC before the step and setagent.auth: bedrock. Vertex: addgoogle-github-actions/authand setagent.auth: vertex.
bubblewrap for Claude Code, Codex and your tests
Claude Code runs the agent’s commands through bubblewrap, which removes credentials from their environment. Codex runs only inside bubblewrap, where it cannot see the job’s credentials or the machine’s logins. The harness also runs your installs, tests and type-check in bubblewrap, where they cannot see the job’s credentials. On GitHub-hosted runners the Loopback step installs it. On a self-hosted runner, install it before the Loopback step and allow unprivileged user namespaces; without it the agent does not run your tests, and the harness reports them as not run.
- name: Install bubblewrap run: sudo apt-get update -q && sudo apt-get install -y bubblewrap - uses: loopback-dev/action@v1 with: agent-key: ${{ secrets.LOOPBACK_AGENT_KEY }} api-url: "https://api.loopback.so"Source maps
Add a step after your production build, so that minified stack traces point to real files and lines. The embedded sources are removed before the upload: your code does not leave the runner.
- run: npm run build - uses: loopback-dev/action@v1 with: command: upload-sourcemaps sourcemaps-path: dist url-prefix: / release: my-app@${{ github.ref_name }} api-url: https://api.loopback.so