{"id":50392782,"url":"https://github.com/steig/boring","last_synced_at":"2026-05-30T19:01:22.486Z","repository":{"id":360091277,"uuid":"1247743141","full_name":"steig/boring","owner":"steig","description":"Profile-driven dev containers for any stack — turn any repo into a one-command, isolated dev environment your whole team can think inside.","archived":false,"fork":false,"pushed_at":"2026-05-24T23:43:21.000Z","size":364,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-25T01:32:59.036Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://steig.github.io/boring/","language":"Shell","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/steig.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-23T18:08:11.000Z","updated_at":"2026-05-24T23:43:25.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/steig/boring","commit_stats":null,"previous_names":["steig/boring"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/steig/boring","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steig%2Fboring","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steig%2Fboring/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steig%2Fboring/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steig%2Fboring/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/steig","download_url":"https://codeload.github.com/steig/boring/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steig%2Fboring/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33705207,"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-05-30T02:00:06.278Z","response_time":92,"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":[],"created_at":"2026-05-30T19:01:21.813Z","updated_at":"2026-05-30T19:01:22.476Z","avatar_url":"https://github.com/steig.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# boring\n\n\u003e A CLI that turns any repo into a one-command, isolated dev environment where mixed teams — engineers, marketers, managers — use code as a thinking medium. Wireframes, mockups, prototypes, pitches, with Claude as the collaborator at the keyboard.\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\n📖 **Docs:** [steig.github.io/boring](https://steig.github.io/boring/) — getting started, profile reference, ARDs.\n🛡 **Security:** [SECURITY.md](SECURITY.md) for responsible disclosure.\n📋 **Examples:** [`examples/`](examples/) — three sample profiles to copy-modify.\n\n```text\n$ boring open .\n==\u003e Loading profile from ./.boring/profile.yaml\n==\u003e Resolving secret URIs (in memory; never written to disk)\n==\u003e Generating .devcontainer/{docker-compose.yml,devcontainer.json}\n==\u003e Generating .devcontainer/boring-runtime/ (guardrails)\n==\u003e Bringing dev container up (devcontainer up)\n==\u003e Running restore: entries against sidecars  (if declared)\n==\u003e Verifying setup completion marker\n[OK] Ready. Attach your editor, or:  devcontainer exec --workspace-folder . -- bash\n```\n\n## Status — v0.6.0-dev\n\nCode surface covers [ARD-0008](docs/ards/ard-0008-v03-to-v10-release-plan-and-thesis-evolution.md)'s v0.3 through v0.6 slices end-to-end. v1.0 polish (brew formula, marketing final pass, broader real-world dogfood) is the gap to a tagged release.\n\nWhat works today:\n\n- **Five curated presets** (`shopify`, `django-node`, `python`, `node`, `node-postgres`) — toolchain versions parameterizable via `preset_version:`. Or bring your own Dockerfile via `stack.dockerfile:`.\n- **Multi-service compose** — declare sidecars (postgres, redis, mongo, anything compose accepts), top-level named volumes, healthcheck-aware auto-wired `depends_on`.\n- **Secret URI resolution at container start** — `secret://op://...`, `secret://keychain:...`, `secret://vault://...`, `secret://aws-sm:...`, `secret://dbx-vault:...`, `secret://env:...`, `secret://file:...`. Resolved in memory; never written to compose or devcontainer.json.\n- **Guardrails codegen** — `forbid_branches:` → pre-push hook, `forbid_commands:` → PATH-shadowing wrappers, `allowed_claude_tools:` → merged into Claude `settings.json`. All host-generated, bind-mounted RO into the container so an in-container agent can't disable them.\n- **Audit log + prompt tracing** — FIFO + host-side collector for tamper-resistance. Tiered visibility: security events shared across the team, prompt content per-user by default with opt-in shared.\n- **Egress enforcement** (`egress.allow:` + iptables-in-container) **with cross-platform `--learn-mode`** that observes a session and proposes the allowlist. Works on Mac+Orbstack via a ulogd2 sidecar.\n- **`boring run \"\u003cprompt\u003e\" --profile \u003cname\u003e`** — headless one-shot Claude in a fresh container with the profile's sandbox shape.\n- **`boring restore [\u003cpath\u003e] [--refresh]`** — declarative `restore:` profile field pipes prod-shape data through `dbx restore --transform=\u003csanitizer\u003e --into \u003csidecar\u003e` (requires dbx with PR #42; live integration when dbx cuts a release).\n- **`boring audit security \u003cprofile\u003e` / `boring audit prompts \u003cprofile\u003e`** — read the JSONL audit logs.\n- **`boring doctor`** — pre-flights docker, devcontainer CLI, dbx (with feature-flag check), jq, yq (mikefarah variant), plus optional secret-resolver CLIs.\n\nWhat's deferred:\n\n- **`boring open \u003cgit-url\u003e`** — URL-cloning path; today clone manually then `boring open \u003clocal-path\u003e`.\n- **dbx PR release** — live `boring restore` requires `dbx` ≥ a version that includes PR #42 (`--transform`, `--into`). Landed on dbx `main`; awaiting release cut.\n- **brew + winget packaging** — v1.0 polish.\n\n## Install\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/steig/boring/main/install.sh | bash\n```\n\nThe installer clones the repo to `~/.local/share/boring/` and symlinks `boring` into `~/.local/bin/`. After install:\n\n```bash\nboring doctor   # diagnose env; reports any missing deps with install hints\nboring help     # CLI reference\n```\n\nTo uninstall: `rm ~/.local/bin/boring \u0026\u0026 rm -rf ~/.local/share/boring`.\n\n### Requirements\n\n| Tool | Purpose | Install hint |\n|---|---|---|\n| [`docker`](https://www.docker.com/) | container runtime | Orbstack on Mac (free for personal), Docker Desktop on Win, Docker Engine on Linux |\n| [`devcontainer`](https://github.com/devcontainers/cli) | container lifecycle | `npm i -g @devcontainers/cli` |\n| [`dbx`](https://github.com/steig/dbx) | backups + vault | `curl -fsSL https://raw.githubusercontent.com/steig/dbx/main/install.sh \\| bash` |\n| `jq`, `yq` (mikefarah Go variant) | profile parsing | `brew install jq yq` |\n| `git` | cloning + hooks | usually preinstalled |\n\nOptional, only if your profile uses the matching `!secret` URI scheme:\n\n| CLI | URI scheme it enables |\n|-----|-----------------------|\n| `op` | `op://` (1Password) |\n| `vault` | `vault://` (HashiCorp Vault) |\n| `aws` | `aws-sm:` (AWS Secrets Manager) |\n| `security` | `keychain:` (macOS) |\n| `secret-tool` | `keychain:` (Linux, libsecret) |\n\n`boring doctor` reports the status of all of the above and flags any version-flag gaps (e.g., dbx without `--transform`).\n\n## First profile\n\nPick one of the [`examples/`](examples/) as a starting point and drop it into your repo's `.boring/profile.yaml`. Three are shipped:\n\n- [`examples/minimal/`](examples/minimal/) — smallest possible profile (Shopify preset, no sidecars).\n- [`examples/django-postgres/`](examples/django-postgres/) — Django + Postgres with secret URIs + setup hook.\n- [`examples/node-with-redis/`](examples/node-with-redis/) — Node + Redis sidecar, no DB.\n\nThen:\n\n```bash\nboring open .\n```\n\nFull walkthrough: [steig.github.io/boring/getting-started/](https://steig.github.io/boring/getting-started/).\n\n## Why boring\n\nThe hard parts of making a mixed team productive on an existing codebase — isolation, realistic data, secrets handling, network containment, AI containment — are partly solved by tools that don't talk to each other. boring is the glue, so a non-engineer (or an AI agent) can work on real code with real-shape data **without being handed the keys to production**.\n\nBuilt around three priorities, in order: **security \u003e practicality \u003e time-to-running**.\n\n## How it's built\n\n- **Profile-in-repo.** `.boring/profile.yaml` lives in the wrapped repo as the single source of truth — GitOps'd, reviewable in a PR. No `export/import` ceremony, no drift across laptops.\n- **Composes existing tools rather than reimplementing them.** [`dbx`](https://github.com/steig/dbx) for backups and the dbx vault, [`@devcontainers/cli`](https://github.com/devcontainers/cli) for container lifecycle, `docker compose` for sidecars, `ulogd2` for egress observation. boring is glue; the heavy lifting belongs to tools that already do it well.\n- **Owns zero secret storage.** A pure URI resolver into whatever store you already use (1Password, Keychain, Vault, dbx vault, etc.). No new attack surface, no new \"where did boring put my key?\" question.\n- **The profile is the trust anchor.** In-container agents can't modify `.boring/*`, `~/.claude/settings.json`, the audit-emit shims, or the generated guardrails — enforced by Claude `deny` rules + a system-wide git pre-commit hook + read-only bind-mounts. The policy is not modifiable by the actor it constrains.\n- **Per-profile egress allowlist + tiered audit log.** Allowlists are observation-derived (`--learn-mode`) rather than guessed. Security events are tamper-resistant (host-side collector reads a FIFO; the container can write but never rewrite).\n\n## Architecture \u0026 decisions\n\nEvery material design decision is an **ARD** (Architectural Decision Record) under [`docs/ards/`](docs/ards/). Full index lives at [steig.github.io/boring/ards/](https://steig.github.io/boring/ards/). The current set:\n\n| ARD | Subject |\n|---|---|\n| [0001](docs/ards/ard-0001-v1-architecture.md) | Full v1 architecture |\n| [0002](docs/ards/ard-0002-dbx-as-runtime-dependency.md) | dbx as runtime dependency; boring owns no secret storage |\n| [0003](docs/ards/ard-0003-devcontainer-cli-as-runtime-dependency.md) | `devcontainer` CLI for container lifecycle |\n| [0004](docs/ards/ard-0004-shopify-first-as-dogfood-path.md) | Shopify-first as the v1 dogfood path |\n| [0005](docs/ards/ard-0005-security-model-inversion.md) | Security model: contain non-engineer + AI from prod |\n| [0006](docs/ards/ard-0006-profile-is-the-trust-anchor.md) | Profile is the trust anchor; in-container agents cannot modify `.boring/*` |\n| [0007](docs/ards/ard-0007-django-node-and-multi-service-compose.md) | `preset: django-node`, multi-service compose, schema versioning |\n| [0008](docs/ards/ard-0008-v03-to-v10-release-plan-and-thesis-evolution.md) | v0.3 → v1.0 release plan + thesis evolution |\n| [0009](docs/ards/ard-0009-guardrails-codegen-architecture.md) | Guardrails codegen architecture |\n| [0010](docs/ards/ard-0010-audit-log-and-prompt-tracing-infrastructure.md) | Audit log + prompt tracing infrastructure |\n| [0011](docs/ards/ard-0011-egress-enforcement-via-iptables.md) | Egress enforcement via iptables-in-container + `--learn-mode` |\n| [0012](docs/ards/ard-0012-dbx-restore-integration.md) | dbx restore integration via the `restore:` profile field |\n| [0013](docs/ards/ard-0013-headless-boring-run.md) | Headless `boring run` |\n| [0014](docs/ards/ard-0014-preset-versioning-and-v10-preset-list.md) | Preset versioning + canonical v1.0 preset list |\n| [0015](docs/ards/ard-0015-ulogd2-sidecar-for-cross-platform-learn-mode.md) | ulogd2 sidecar (cross-platform `--learn-mode`) |\n| [0016](docs/ards/ard-0016-repo-side-safety-nets-as-prerequisite.md) | Repo-side safety nets (branch protection, PR templates) as a boring prerequisite |\n| [0017](docs/ards/ard-0017-agent-workflow-rules-derived-from-guardrails.md) | Agent workflow rules derived from guardrails |\n\nThe convention for writing new ARDs (full vs. mini, numbering, supersession, when to write one) is in [`docs/ards/README.md`](docs/ards/README.md). New design decisions get an ARD at the time of the decision, not after.\n\n## Status — honest version\n\nThis is a one-maintainer project in active dogfood. Currently validated against two production repos (a Shopify theme and a Django + React + Postgres app), both private. The thesis — \"mixed teams use code as a thinking medium with AI as the collaborator\" — is not yet validated by external users. If you try it and find a sharp edge, [open an issue](https://github.com/steig/boring/issues) or email [tom@steig.io](mailto:tom@steig.io).\n\n## License\n\n[MIT](LICENSE) © 2026 Tom Steigerwald\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsteig%2Fboring","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsteig%2Fboring","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsteig%2Fboring/lists"}