Guides / kosa8 Cloud / GitHub Action
GitHub Action
The kosa8/sandbox action runs a command in a sealed kosa8 Cloud sandbox
from a workflow, prints its output and deletes the sandbox. Use it for untrusted code, an AI agent's
changes, or anything you would rather not run on the CI runner itself.
Add your key as a secret
Make a kosa8 Cloud API key with the sandboxes:write scope at
app.kosa8.com. In the repository, open
Settings → Secrets and variables → Actions and add it as KOSA8_API_KEY.
Use the action
jobs:
sandbox:
runs-on: ubuntu-latest
steps:
- id: box
uses: kosa8/sandbox@v1
with:
api-key: ${{ secrets.KOSA8_API_KEY }}
template: python
command: python3 -c "import platform; print(platform.platform())"
- run: echo "exit ${{ steps.box.outputs.exit-code }} in ${{ steps.box.outputs.sandbox-id }}"
Inputs and outputs
| Input | Default | What it does |
|---|---|---|
api-key | required | A kosa8 Cloud API key. Masked in the log. |
command | required | A shell line, run as sh -c in the sandbox. Several lines are fine. |
template | org default | base, python, node, or any template your organization has. |
timeout | 600 | Seconds to wait for the command. The sandbox is also created with a lifetime of timeout + 300 s, so a cancelled job leaves nothing running. |
keep | false | true keeps the sandbox (no lifetime) for later steps or for looking at afterwards; delete it yourself. |
fail-on-error | true | Fail the step when the command exits non-zero. |
api-url | https://api.kosa8.com | The API to call. |
| Output | Is |
|---|---|
exit-code | The command's exit code. |
sandbox-id | The sandbox's id, sbx_…. |
How it behaves
- stdout and stderr are printed, in collapsible groups, when the command finishes. The API returns a command's output at the end today, so a long command shows nothing until it is done.
- The sandbox is deleted when the step ends, whether the command passed, failed or the step errored. If that delete fails, the step warns, and the sandbox's lifetime removes it.
- A refusal is explained in the log. For
402 payment_requiredorspending_cap_reachedthe log says what to do and links app.kosa8.com/#usage. - It is plain bash with
curlandjq, both on GitHub's hosted runners: no Node.js build and nothing downloaded at run time. - It costs what any kosa8 Cloud sandbox costs: per second while it runs (kosa8 Cloud has the rates).
Keep untrusted text out of command. GitHub pastes
${{ … }} expressions into the line before it runs, so a branch name or PR title put
there becomes shell code in the sandbox. Values such as github.repository and
github.sha are safe.
A full workflow
This runs a public repository's tests inside a sandbox on every push:
name: tests in a kosa8 sandbox
on:
push:
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- id: box
uses: kosa8/sandbox@v1
with:
api-key: ${{ secrets.KOSA8_API_KEY }}
template: python
timeout: "900"
command: |
set -e
cd /workspace
python3 -c "import io, tarfile, urllib.request; tarfile.open(fileobj=io.BytesIO(urllib.request.urlopen('https://codeload.github.com/${{ github.repository }}/tar.gz/${{ github.sha }}').read())).extractall('src')"
cd src/*
python3 -m pip install -q -r requirements.txt pytest
python3 -m pytest -q
Plain HTTP calls work from any CI system too; the action is the same three calls
(POST /v1/sandboxes, POST /v1/sandboxes/{id}/exec,
DELETE /v1/sandboxes/{id}) shown in kosa8 Cloud.