{"id":51152854,"url":"https://github.com/bigg01/claude-ci-agent","last_synced_at":"2026-06-26T07:31:00.504Z","repository":{"id":364588089,"uuid":"1268355364","full_name":"bigg01/claude-ci-agent","owner":"bigg01","description":"Autonomous Claude Code CI agent in a rootless Podman sandbox — GitLab CI \u0026 GitHub Actions, two personalities (read-write Agent, read-only Advisor), OpenTelemetry audit to Elastic, Helm chart for AKS/OpenShift.","archived":false,"fork":false,"pushed_at":"2026-06-13T17:16:58.000Z","size":223,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-13T17:24:30.626Z","etag":null,"topics":["ai-agent","anthropic","cert-manager","ci-cd","claude-code","devops","elasticsearch","github-actions","gitlab-ci","helm","kubernetes","llm","llm-gateway","observability","openbao","openshift","opentelemetry","podman","rootless","sre"],"latest_commit_sha":null,"homepage":"https://bigg01.containerize.ch/claude-ci-agent/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bigg01.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":"SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-13T12:38:27.000Z","updated_at":"2026-06-13T16:53:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bigg01/claude-ci-agent","commit_stats":null,"previous_names":["bigg01/claude-ci-agent"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/bigg01/claude-ci-agent","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bigg01%2Fclaude-ci-agent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bigg01%2Fclaude-ci-agent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bigg01%2Fclaude-ci-agent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bigg01%2Fclaude-ci-agent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bigg01","download_url":"https://codeload.github.com/bigg01/claude-ci-agent/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bigg01%2Fclaude-ci-agent/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34808043,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-26T02:00:06.560Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agent","anthropic","cert-manager","ci-cd","claude-code","devops","elasticsearch","github-actions","gitlab-ci","helm","kubernetes","llm","llm-gateway","observability","openbao","openshift","opentelemetry","podman","rootless","sre"],"created_at":"2026-06-26T07:31:00.375Z","updated_at":"2026-06-26T07:31:00.489Z","avatar_url":"https://github.com/bigg01.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/assets/banner.svg\" alt=\"Claude CI Agent\" width=\"820\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/bigg01/claude-ci-agent/actions/workflows/docs.yml\"\u003e\u003cimg alt=\"Docs\" src=\"https://github.com/bigg01/claude-ci-agent/actions/workflows/docs.yml/badge.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/bigg01/claude-ci-agent/actions/workflows/claude-agent.yml\"\u003e\u003cimg alt=\"Claude Agent\" src=\"https://github.com/bigg01/claude-ci-agent/actions/workflows/claude-agent.yml/badge.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg alt=\"License: MIT\" src=\"https://img.shields.io/badge/License-MIT-25e8ff?style=flat-square\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"Python 3.12\" src=\"https://img.shields.io/badge/python-3.12-25e8ff?style=flat-square\u0026logo=python\u0026logoColor=white\"\u003e\n  \u003cimg alt=\"Podman\" src=\"https://img.shields.io/badge/containers-Podman%20(rootless)-ff9d2e?style=flat-square\u0026logo=podman\u0026logoColor=white\"\u003e\n  \u003cimg alt=\"OpenTelemetry\" src=\"https://img.shields.io/badge/telemetry-OpenTelemetry-25e8ff?style=flat-square\u0026logo=opentelemetry\u0026logoColor=white\"\u003e\n  \u003cimg alt=\"Built with Claude Code\" src=\"https://img.shields.io/badge/built%20with-Claude%20Code-ff9d2e?style=flat-square\u0026logo=anthropic\u0026logoColor=white\"\u003e\n  \u003ca href=\"https://zensical.org\"\u003e\u003cimg alt=\"Docs: Zensical\" src=\"https://img.shields.io/badge/docs-Zensical-25e8ff?style=flat-square\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nAn autonomous engineering teammate that runs inside a **rootless, unprivileged\nPodman sandbox**. Every action is captured, scrubbed for secrets, and streamed to\nElastic via an OpenTelemetry (OTel) Collector sidecar at `http://localhost:4318`.\n\nThe agent runs in two CI flavors— **GitLab CI** and **GitHub Actions**— and\ndetects which one it is in via `$GITLAB_CI` / `$GITHUB_ACTIONS`. The sandbox is the\nrunner's job container; on a self-hosted GitLab Runner that runs on OpenShift, the\ncluster's `restricted` SCC enforces the rootless, unprivileged boundary.\n\n### 📖 [Read the full documentation →](https://bigg01.containerize.ch/claude-ci-agent/)\n\nThis README is a summary; the complete docs (architecture, spec-driven development,\nCI setup, sandboxing, observability, and more) live on the\n**[documentation site](https://bigg01.containerize.ch/claude-ci-agent/)**.\n\nReusable across repos as a [GitLab component](templates/claude-agent.yml)\n(`include: component:`) and an equivalent [GitHub Action](action.yml)\n(`uses: bigg01/claude-ci-agent@v1`).\n\n\u003e Operating guide for the agent itself lives in [`CLAUDE.MD`](CLAUDE.MD).\n\u003e\n\u003e **Where this repo lives:** developed on **GitHub** — all CI runs in\n\u003e [`.github/workflows/`](.github/workflows/) — and mirrored read-only to\n\u003e **GitLab**, where the only pipeline is a tagged release that publishes the\n\u003e [component](templates/claude-agent.yml) to the CI/CD Catalog. There is no\n\u003e agent/test pipeline on the GitLab side.\n\n## How it runs\n\nThe agent runs **in your pipeline**— an *advisor* that reviews MRs/PRs, or an\n*agent* that applies a fix and opens a new branch. Triggered by pipelines and\n`@claude` comments, it runs in the same rootless, OTel-audited sandbox on both\nplatforms: GitLab [component](templates/claude-agent.yml) (`include: component:`)\nand GitHub [action](action.yml) / [`.github/workflows/`](.github/workflows/). See\n[CI versions](#ci-versions).\n\n## Requirements\n\n- [uv](https://docs.astral.sh/uv/)— Python package \u0026 environment management\n- [Podman](https://podman.io/)— rootless container builds (do **not** use Docker)\n\n## Quick start\n\n```bash\nmake install      # sync Python deps (incl. Zensical) via uv\nmake serve        # live-preview the docs at http://localhost:8000\nmake build        # build the container image (app-test) with Podman\n```\n\nRun `make help` to list all targets.\n\n## Make targets\n\n| Target | Description |\n| --- | --- |\n| `make install` | Sync Python deps (incl. Zensical) into the uv environment |\n| `make serve` | Live-preview the docs at `http://localhost:8000` |\n| `make docs-build` (`make docs`) | Build the static docs site into `site/` |\n| `make docs-clean` | Remove the built docs site |\n| `make build` | Build the container image (`app-test`) from `Containerfile` |\n| `make run` | Run the built image as a detached `test-service` |\n| `make test` | Run the pytest suite (unit tests + config validation) |\n| `make test-e2e` | Run the container end-to-end test locally (`SKIP_BUILD=1` to reuse the image) |\n| `make ci-local` | Local CI test: bring up the stack + telemetry e2e into Elasticsearch |\n| `make stack-up` / `make stack-down` | Start / stop the local stack (Elasticsearch + Kibana + OTel) |\n| `make compose-build` | Build the agent image via the compose file |\n| `make dashboard` | Create Kibana data views for `claude-agent-*` |\n| `make clean` | Remove `site/` and the container image |\n\nThe container engine defaults to `podman`; override with\n`make build CONTAINER_ENGINE=docker`. Image name and Containerfile are also\nconfigurable via `IMAGE` and `CONTAINERFILE`.\n\n## Testing\n\n**Unit + config tests (`make test`).** A fast [pytest](tests/) suite— no\ncontainers— covering the OTLP cost emitter ([`otel/emit_cost.py`](otel/emit_cost.py))\nand static validation of the configs and manifests (`zensical.toml`, the GitLab\ncomponent, the k8s manifests, and `compose.yaml`). It runs in CI via\n[`.github/workflows/ci.yml`](.github/workflows/ci.yml) (`uv run pytest`, JUnit\nreport uploaded) and gates everything downstream.\n\n```bash\nmake test     # uv run pytest\n```\n\n**End-to-end test (`make test-e2e`).** [`tests/e2e.sh`](tests/e2e.sh) runs **locally\nfirst**, then unchanged in CI. It runs five fail-fast stages:\n\n1. **Validate configs**— Zensical docs build, plus `zensical.toml` / GitLab component parse checks\n2. **Build image**— `podman build` of the rootless sandbox (`SKIP_BUILD=1` reuses an existing image)\n3. **Toolchain \u0026 connectivity smoke**— confirms `claude` / `node` / `podman` are present and that outbound HTTPS egress works (`curl ipinfo.io`; skip with `SKIP_NET=1`)\n4. **Sandbox containment**— proves software-install attempts are **denied** (non-root, no system writes, no escalation); with `ANTHROPIC_API_KEY`, Claude itself tries to install and is blocked\n5. **Live agent run**— a real `claude` prompt, only when `ANTHROPIC_API_KEY` is set\n\n```bash\nmake test-e2e                 # full run (builds the image)\nSKIP_BUILD=1 make test-e2e    # reuse the existing image\nANTHROPIC_API_KEY=… make test-e2e   # also exercise the live agent\n```\n\n## Local stack (Podman Compose)\n\n[`compose.yaml`](compose.yaml) brings up the full observability pipeline locally—\n**Elasticsearch + Kibana + OTel Collector + agent**:\n\n```\nagent ──OTLP/HTTP──▶ OTel Collector ──(secret scrub)──▶ Elasticsearch ──▶ Kibana\n```\n\n```bash\nmake stack-up      # Elasticsearch (9200) + Kibana (5601) + OTel Collector (4318)\nmake ci-local      # bring up the stack and run the telemetry e2e test\nmake dashboard     # create Kibana data views for claude-agent-*\nmake stack-down    # tear down (removes volumes)\n```\n\n`make ci-local` ([`tests/ci-local.sh`](tests/ci-local.sh)) needs **no API key**— it\nposts a synthetic OTLP log carrying a *fake* secret and asserts the event reaches\nElasticsearch **with the secret redacted**, proving the collector's scrubbing works\nend-to-end. Elasticsearch requires `vm.max_map_count \u003e= 262144` on the host\n(`sudo sysctl -w vm.max_map_count=262144`). See the\n[Local stack docs](docs/local-stack.md) for details.\n\n## Documentation\n\nDocs are built with [Zensical](https://zensical.org) (the static site generator\nfrom the Material for MkDocs team). Sources live in [`docs/`](docs/), config in\n[`zensical.toml`](zensical.toml), and output is generated into `site/`.\n\n```bash\nuv run zensical serve     # preview\nuv run zensical build      # build into site/\n```\n\nPages: **Home**, **Architecture** (CI → sandbox → OTel → Elastic; editable source\nin [`architecture.drawio`](architecture.drawio)), **CI Versions**,\n**[Spec-driven development](docs/spec-driven.md)**, **Tooling \u0026 Commands**,\n**[Sandboxing \u0026 YOLO Mode](docs/yolo-mode.md)**, **Observability**,\n**[LLM Gateway](docs/llm-gateway.md)** (prompt caching + guardrails), and\n**Secrets (OpenBao)**.\n\nThe [`.github/workflows/docs.yml`](.github/workflows/docs.yml) workflow builds the\ndocs with uv and deploys them to GitHub Pages on pushes to `main`/`master`.\n\n## CI versions\n\nHow a **consuming project** runs the agent on each platform:\n\n| | GitLab CI | GitHub Actions |\n| --- | --- | --- |\n| **Config** | `include:` the [component](templates/claude-agent.yml) | `.github/workflows/*.yml` |\n| **Detection** | `$GITLAB_CI == \"true\"` | `$GITHUB_ACTIONS == \"true\"` |\n| **Runner** | OpenShift GitLab Runner (rootless Podman) | Hosted / self-hosted |\n| **Builds** | Podman | Podman (Docker fallback if provisioned) |\n| **Credentials** | Zero-credential | Workflow-scoped `GITHUB_TOKEN` / secrets |\n\n\u003e This repository's *own* CI lives entirely on GitHub. Its GitLab mirror ships\n\u003e no agent pipeline — only the component, published to the CI/CD Catalog on tags\n\u003e (see [`.gitlab-ci.yml`](.gitlab-ci.yml)).\n\n## Why we don't need a separate sandbox to run \"YOLO mode\"\n\n\"YOLO mode\"— running the agent with `--dangerously-skip-permissions` so it\nexecutes commands without per-action approval— is normally risky because the\nagent can run arbitrary code. The usual mitigation is to wrap it in a dedicated\nsandbox runtime (e.g. Anthropic's sandbox-runtime for bash/filesystem isolation,\nor a vendor GPU/container sandbox such as NVIDIA's). **We don't need that extra\nlayer, because the agent already runs fully contained.** The isolation those\nsandboxes provide is already true of our execution environment:\n\n- **Rootless, unprivileged container.** The agent runs as a non-root user in a\n  Podman container (see [`Containerfile`](Containerfile)) on an OpenShift GitLab\n  Runner, with an arbitrary, non-root UID and no privilege escalation. The blast\n  radius of any command is the throwaway container, not a host.\n- **Ephemeral CI workspace.** Each run starts from a fresh image and is destroyed\n  afterwards. There is no persistent state for a bad command to corrupt.\n- **Zero-credential environment.** No global Git credentials, SSH keys, or\n  production tokens are present. The agent cannot reach the host OS environment,\n  and an escaped secret has nothing to steal. GitHub Actions runs use only\n  workflow-scoped `GITHUB_TOKEN`/secrets.\n- **Secret-scrubbed, fully audited.** Every command, output, and git mutation is\n  streamed as OTLP events through the OTel Collector sidecar (with secret\n  scrubbing) to Elastic— so even \"skip approvals\" runs remain reviewable.\n\nIn other words, the security boundary that a bolt-on sandbox would add is\n*already* the boundary we run inside. Adding Anthropic's or NVIDIA's sandbox on\ntop would be redundant isolation, not new isolation. YOLO mode here means\n\"skip the prompts,\" not \"skip the containment.\"\n\n\u003e This justifies enabling bypass-permissions mode for unattended CI runs. It is\n\u003e **not** a recommendation to run YOLO mode on a developer workstation or any\n\u003e host without equivalent containment.\n\n📖 **Full write-up:** [Sandboxing \u0026 YOLO Mode](docs/yolo-mode.md) in the docs.\n\n## Project layout\n\n```\n.\n├─ CLAUDE.MD                 # agent operating guide\n├─ Containerfile            # rootless Podman workspace image\n├─ Makefile                 # docs + container build targets\n├─ zensical.toml            # Zensical site config\n├─ architecture.drawio      # editable architecture diagram\n├─ tests/                   # end-to-end test\n├─ docs/                    # documentation sources\n└─ .github/workflows/       # docs deploy workflow\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbigg01%2Fclaude-ci-agent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbigg01%2Fclaude-ci-agent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbigg01%2Fclaude-ci-agent/lists"}