Aude Code

aude-workflow(7)

NAME

aude-workflow — an aude session in a GitHub workflow, steered from a phone

DESCRIPTION

aude's GitHub action runs one session on a hosted runner. Each workflow run is a new machine that makes its own key. The workflow lists the devices that can reach the machine through a tunnel. The session prints the machine's URL and QR code in the log.

A person with a listed device scans the QR code to read the transcript and steer the session, as on any machine (aude-rc(7)).

SETUP

Add these once in the repository settings:

  1. A deployment environment ‘aude’ that only the branch ‘main’ can deploy to.
  2. Its configuration variable AUDE_PEERS, a peers.json (aude-rc(7) FILES) with each device that can steer. An app shows its fingerprint on its attach sheet. A machine shows its fingerprint on the ‘this machine’ line of ‘aude devices’.
  3. Its secret: ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN for claude, OPENAI_API_KEY for codex. Give it a spend limit.

Nothing more is necessary for ‘trycloudflare’. For ‘tailnet’ or ‘funnel’, run ‘tailscale/github-action’ in an earlier step with a Tailscale OAuth client that makes only ‘tag:aude-workflow’ nodes. Then run ‘sudo tailscale set --operator=$USER’. In the tailnet's policy, let that tag reach nothing. aude-tunnel(7) gives the rest.

THE ACTION

‘dickolsson/aude/action’ takes seven inputs. Only agent is optional.

The release to install, such as 0.2.0. It must match SHA256SUMS and its attestation.
‘claude’ or ‘codex’. An earlier step must install it and log in.
The --agent of aude(1), by default ‘default’.
The session's standard input. Each line is a turn (aude(1) INPUT AND OUTPUT).
The peer list. The action writes it to ~/.aude/rc/peers.json.
‘trycloudflare’, ‘tailnet’ or ‘funnel’. An earlier step must put its program on PATH. The action writes it to ~/.aude/rc/tunnel.json. Then it prints both files with ‘aude devices’.

It runs only on the ‘schedule’ and ‘workflow_dispatch’ workflow events. The runner must have no ~/.aude. Then it runs ‘aude --harness harness --agent agent’ with the task on standard input until the task is done and no line waits. The job's ‘timeout-minutes’ stops the session.

Its output records has one line for each record directory of the session and its subagents. It uploads nothing.

STEERING

Open the job's log in a wide window. Scan the aude step's first QR code with the phone's camera. Tap Attach. On a computer, give the URL above the QR code to ‘aude attach’.

In the app, each run is a new machine with its own URL. The app keeps nothing of it. When the job ends, the app shows the machine out of reach. Close it.

EXIT STATUS

The aude step fails with the exit status of ‘aude devices’ or the session (aude(1)). After an ‘aude:’ line of DIAGNOSTICS, it fails with 1.

EXAMPLES

For claude:

on:
  schedule: [{ cron: "17 3 * * *" }]
  workflow_dispatch: {}
permissions: { contents: read }
jobs:
  nightly:
    runs-on: ubuntu-24.04
    environment: aude
    timeout-minutes: 120
    steps:
      - uses: actions/checkout@<sha>
        with: { persist-credentials: false }
      - run: |
          curl -fsSLo cloudflared https://github.com/cloudflare/cloudflared/releases/download/<version>/cloudflared-linux-amd64
          echo "<sha256>  cloudflared" | sha256sum -c
          sudo install -m 0755 cloudflared /usr/local/bin/cloudflared
      - run: npm install --global @anthropic-ai/claude-code@<version>
      - uses: dickolsson/aude/action@<sha>
        id: aude
        with:
          version: <version>
          harness: claude
          task: Do the task in .github/aude/nightly.md.
          peers: ${{ vars.AUDE_PEERS }}
          tunnel: trycloudflare
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
      - if: always() && steps.aude.outputs.records != ''
        uses: actions/upload-artifact@<sha>
        with:
          name: aude-nightly-${{ github.run_id }}
          path: ${{ steps.aude.outputs.records }}

For codex, the aude step takes ‘harness: codex’ and no ‘env’. Log in before it:

      - run: npm install --global @openai/codex@<version>
      - run: printenv OPENAI_API_KEY | codex login --with-api-key
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

After the lines of the checksum and the attestation, the aude step's log shows:

installed: aude <version>, linux-x86_64
rc: made this device's key; no machine lists it yet
this machine  <fingerprint>
tunnel  trycloudflare
my phone  <fingerprint>
  devices reach this machine at AUDE:<name>$/<fingerprint>: my phone

  <the QR code>

DIAGNOSTICS

aude: version '<version>' is not a release, such as 0.2.0
 
aude: no release for a <system> <architecture> hosted runner
 
aude: the workflow event <event> can put another's text in the task; aude runs on schedule and workflow_dispatch alone
 
aude: <home>/.aude exists; aude runs on an ephemeral hosted runner alone
 
rc could not read peers.json: <defect>
Correct AUDE_PEERS.
rc could not read tunnel.json: <defect>
Correct the tunnel input.
devices cannot reach this machine: the <tunnel> tunnel <finding> — see aude-tunnel(7)
Install or set up its program.
could not attach — unauthorised: this machine does not list this device
Add the device to AUDE_PEERS. The next run lists it.

SEE ALSO

aude(1), aude-rc(7), aude-tunnel(7)

CAVEATS

Do not give a secret or a write token to a step after aude. The model can use sudo on the runner. It can read each secret of the job, the run's key and a tunnel's state. It can change the steps after aude.

Scan the aude step's first QR code. The model can also print a QR code.

A device in AUDE_PEERS can run shell commands in each run of a workflow that lists it.

Anyone who can read the repository can read the log and the uploaded records. They show what the model printed.

The action is not tested on a hosted runner. The listener is not tested over Tailscale's tunnels.