trusty-cage 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- trusty_cage-0.2.0/.claude/settings.local.json +12 -0
- trusty_cage-0.2.0/.env.example +17 -0
- trusty_cage-0.2.0/.gitignore +35 -0
- trusty_cage-0.2.0/CLAUDE.md +128 -0
- trusty_cage-0.2.0/LICENSE +21 -0
- trusty_cage-0.2.0/Makefile +25 -0
- trusty_cage-0.2.0/PKG-INFO +186 -0
- trusty_cage-0.2.0/README.md +154 -0
- trusty_cage-0.2.0/docs/v1-spec.md +345 -0
- trusty_cage-0.2.0/pyproject.toml +58 -0
- trusty_cage-0.2.0/src/trusty_cage/__init__.py +3 -0
- trusty_cage-0.2.0/src/trusty_cage/assets/Dockerfile +96 -0
- trusty_cage-0.2.0/src/trusty_cage/assets/__init__.py +0 -0
- trusty_cage-0.2.0/src/trusty_cage/assets/env.template +20 -0
- trusty_cage-0.2.0/src/trusty_cage/assets/init-network.sh +61 -0
- trusty_cage-0.2.0/src/trusty_cage/auth.py +98 -0
- trusty_cage-0.2.0/src/trusty_cage/cli.py +574 -0
- trusty_cage-0.2.0/src/trusty_cage/config.py +43 -0
- trusty_cage-0.2.0/src/trusty_cage/constants.py +40 -0
- trusty_cage-0.2.0/src/trusty_cage/docker.py +238 -0
- trusty_cage-0.2.0/src/trusty_cage/dotfiles.py +109 -0
- trusty_cage-0.2.0/src/trusty_cage/environment.py +131 -0
- trusty_cage-0.2.0/src/trusty_cage/image.py +90 -0
- trusty_cage-0.2.0/src/trusty_cage/network.py +33 -0
- trusty_cage-0.2.0/tests/__init__.py +0 -0
- trusty_cage-0.2.0/tests/conftest.py +26 -0
- trusty_cage-0.2.0/tests/test_auth.py +86 -0
- trusty_cage-0.2.0/tests/test_cli.py +194 -0
- trusty_cage-0.2.0/tests/test_config.py +103 -0
- trusty_cage-0.2.0/tests/test_docker.py +133 -0
- trusty_cage-0.2.0/tests/test_environment.py +154 -0
- trusty_cage-0.2.0/tests/test_image.py +53 -0
- trusty_cage-0.2.0/tests/test_init.py +58 -0
- trusty_cage-0.2.0/tests/test_network.py +40 -0
- trusty_cage-0.2.0/v2-spec.md +32 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# trusty-cage user configuration
|
|
2
|
+
#
|
|
3
|
+
# IMPORTANT: Do NOT use this file at the project root.
|
|
4
|
+
# Copy it to ~/.trusty-cage/.env — that's where trusty-cage reads it from.
|
|
5
|
+
#
|
|
6
|
+
# mkdir -p ~/.trusty-cage
|
|
7
|
+
# cp .env.example ~/.trusty-cage/.env
|
|
8
|
+
# # then edit ~/.trusty-cage/.env with your values
|
|
9
|
+
#
|
|
10
|
+
# This file lives at ~/.trusty-cage/.env so your config persists across
|
|
11
|
+
# installs and works regardless of where you cloned the repo.
|
|
12
|
+
|
|
13
|
+
TRUSTY_CAGE_DOTFILES_REPO=https://github.com/youruser/dotfiles
|
|
14
|
+
TRUSTY_CAGE_PYTHON_VERSION=3.12
|
|
15
|
+
TRUSTY_CAGE_DEFAULT_SHELL=zsh
|
|
16
|
+
TRUSTY_CAGE_DEFAULT_AUTH_MODE=api_key
|
|
17
|
+
TRUSTY_CAGE_TMUX_PREFIX=C-a
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
|
|
8
|
+
# Virtual environment
|
|
9
|
+
venv/
|
|
10
|
+
|
|
11
|
+
# Environment / secrets
|
|
12
|
+
.env
|
|
13
|
+
set_creds.sh
|
|
14
|
+
|
|
15
|
+
# IDE
|
|
16
|
+
.idea/
|
|
17
|
+
.vscode/
|
|
18
|
+
.zed/
|
|
19
|
+
|
|
20
|
+
# OS
|
|
21
|
+
.DS_Store
|
|
22
|
+
|
|
23
|
+
# pytest
|
|
24
|
+
.pytest_cache/
|
|
25
|
+
|
|
26
|
+
# Ruff
|
|
27
|
+
.ruff_cache/
|
|
28
|
+
|
|
29
|
+
# Docker image export artifacts
|
|
30
|
+
info/
|
|
31
|
+
isolated-dev-*/
|
|
32
|
+
trusty-cage:*/
|
|
33
|
+
|
|
34
|
+
# trusty-cage working files
|
|
35
|
+
TODO.md
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
**trusty-cage** is a host-side Python CLI tool that creates fully isolated, persistent Docker-based development environments on macOS (via OrbStack). Each environment is scoped to a single git repo, contains no git credentials, and provides an interactive dev experience (tmux + Neovim/LazyVim + Claude Code) inside a container. Work is exported back to the host for git operations.
|
|
8
|
+
|
|
9
|
+
v1 is feature-complete. The archived v1 specification is at `docs/v1-spec.md`. Planned enhancements are in `v2-spec.md`.
|
|
10
|
+
|
|
11
|
+
## Build & Development
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Setup
|
|
15
|
+
python -m venv venv
|
|
16
|
+
source venv/bin/activate
|
|
17
|
+
pip install -e ".[dev]"
|
|
18
|
+
|
|
19
|
+
# Run
|
|
20
|
+
trusty-cage --help
|
|
21
|
+
|
|
22
|
+
# Lint & format
|
|
23
|
+
ruff format .
|
|
24
|
+
ruff check --fix .
|
|
25
|
+
|
|
26
|
+
# Tests
|
|
27
|
+
pytest
|
|
28
|
+
pytest tests/test_foo.py # single file
|
|
29
|
+
pytest tests/test_foo.py::test_bar # single test
|
|
30
|
+
|
|
31
|
+
# Build & publish (requires build + twine in dev deps)
|
|
32
|
+
make build # Build wheel and sdist
|
|
33
|
+
make publish-test # Upload to TestPyPI
|
|
34
|
+
make publish # Upload to PyPI
|
|
35
|
+
make help # Show all targets
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Architecture
|
|
39
|
+
|
|
40
|
+
### CLI Framework
|
|
41
|
+
|
|
42
|
+
- **Typer** for CLI with **Rich** for terminal output
|
|
43
|
+
- All Docker operations via **subprocess calls to `docker` CLI** (not the Docker Python SDK)
|
|
44
|
+
|
|
45
|
+
### CLI Commands
|
|
46
|
+
|
|
47
|
+
The CLI is available as `trusty-cage` or the short alias `tc`.
|
|
48
|
+
|
|
49
|
+
| Command | Purpose |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `init [--force]` | Create config directory and default `.env` file |
|
|
52
|
+
| `create <git-repo-url> [--name] [--no-attach]` | Clone repo, build image, create container+volume, copy files in, init local git, apply dotfiles, attach |
|
|
53
|
+
| `attach <name>` | Start container if stopped, apply iptables, create/attach tmux session |
|
|
54
|
+
| `stop <name>` | Stop container (preserves volume) |
|
|
55
|
+
| `list` | Show all environments with status, date, repo URL |
|
|
56
|
+
| `export <name>` | Copy container files → host clone via rsync (preserving host `.git/`) |
|
|
57
|
+
| `destroy <name>` | Remove container + volume (keeps host clone) |
|
|
58
|
+
| `rebuild-image` | Force rebuild Docker image |
|
|
59
|
+
|
|
60
|
+
### Host File Layout
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
~/.trusty-cage/
|
|
64
|
+
.env # User config (created by `trusty-cage init`)
|
|
65
|
+
image.sha # SHA of Dockerfile for rebuild detection
|
|
66
|
+
envs/<name>/
|
|
67
|
+
meta.json # Source of truth: repo_url, created_at, volume_name, host_clone_path, auth_mode
|
|
68
|
+
repo/ # Full host clone with remotes (for export)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Container Setup
|
|
72
|
+
|
|
73
|
+
- Base: `ubuntu:24.04` (ARM64)
|
|
74
|
+
- Non-root user: `trustycage` (UID 1000), home `/home/trustycage`
|
|
75
|
+
- Working directory: `/home/trustycage/project` (Docker named volume: `isolated-dev-<name>`)
|
|
76
|
+
- No bind mounts, no SSH keys, no git credentials, no `.netrc`, no `~/.config/gh`
|
|
77
|
+
- `ANTHROPIC_API_KEY` injected at runtime via `docker exec -e` (never on disk)
|
|
78
|
+
- Dotfiles: cloned on host → `docker cp` into container (`.git/` stripped) → run install script if present
|
|
79
|
+
|
|
80
|
+
### Network Policy
|
|
81
|
+
|
|
82
|
+
Applied via `init-network.sh` at attach time (run as root, then drops to `trustycage`). Must be idempotent (use `iptables -C` before adding rules).
|
|
83
|
+
|
|
84
|
+
- **Blocked**: Port 22 (SSH) to all hosts; `hub.docker.com` / `registry-1.docker.io`
|
|
85
|
+
- **Allowed**: Everything else (HTTPS git, packages, web, DNS)
|
|
86
|
+
- Primary protection is **credential absence**, not network blocking
|
|
87
|
+
|
|
88
|
+
### Authentication Modes
|
|
89
|
+
|
|
90
|
+
Chosen at `create` time, stored in `meta.json`:
|
|
91
|
+
|
|
92
|
+
- **api_key**: Reads host env var at attach time, injected via `docker exec -e`
|
|
93
|
+
- **subscription**: Copies `~/.claude/` into container via `docker cp` at create time; persists in volume
|
|
94
|
+
|
|
95
|
+
## Code Conventions
|
|
96
|
+
|
|
97
|
+
### Imports
|
|
98
|
+
- All imports must be at the top of the file — no inline or lazy imports inside functions
|
|
99
|
+
- This applies to all Python files in the project
|
|
100
|
+
|
|
101
|
+
## Git Workflow
|
|
102
|
+
|
|
103
|
+
- Work happens on feature branches off `develop`
|
|
104
|
+
- Merges to `main` are done via PR on GitHub — never merge locally
|
|
105
|
+
- Push the feature/develop branch and open a PR
|
|
106
|
+
|
|
107
|
+
## Versioning
|
|
108
|
+
|
|
109
|
+
This project follows [Semantic Versioning](https://semver.org/): `MAJOR.MINOR.PATCH`
|
|
110
|
+
|
|
111
|
+
- **MAJOR** — breaking changes (renamed commands, changed config format, dropped features)
|
|
112
|
+
- **MINOR** — new features, backwards-compatible (new commands, new config options)
|
|
113
|
+
- **PATCH** — bug fixes, docs-only changes, no behavior change
|
|
114
|
+
|
|
115
|
+
Version is set in two places (keep in sync):
|
|
116
|
+
- `pyproject.toml` → `version`
|
|
117
|
+
- `src/trusty_cage/__init__.py` → `__version__`
|
|
118
|
+
|
|
119
|
+
**When to prompt about version bumps:** Before committing work that adds new features (minor bump) or fixes bugs (patch bump), suggest the appropriate version increment to the user. Don't bump automatically — ask first.
|
|
120
|
+
|
|
121
|
+
## Key Design Constraints
|
|
122
|
+
|
|
123
|
+
- `meta.json` is the source of truth for environment state — never infer from Docker state alone
|
|
124
|
+
- All operations should be **idempotent** where possible
|
|
125
|
+
- The tool must **never** run `git push` or configure git credentials
|
|
126
|
+
- Container's local git repo has no remotes — Claude Code uses git locally only
|
|
127
|
+
- Export uses rsync to overlay container files onto host clone, preserving host's `.git/`
|
|
128
|
+
- Published on PyPI as `trusty-cage` — installable via `pip install trusty-cage` or `pipx install trusty-cage`
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Adam Reese
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
.PHONY: help build publish publish-test lint format test clean
|
|
2
|
+
|
|
3
|
+
help: ## Show this help
|
|
4
|
+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " %-15s %s\n", $$1, $$2}'
|
|
5
|
+
|
|
6
|
+
build: clean ## Build wheel and sdist
|
|
7
|
+
python -m build
|
|
8
|
+
|
|
9
|
+
publish: build ## Build and upload to PyPI
|
|
10
|
+
. ./set_creds.sh && twine upload dist/*
|
|
11
|
+
|
|
12
|
+
publish-test: build ## Build and upload to TestPyPI
|
|
13
|
+
. ./set_creds.sh && twine upload --repository testpypi dist/*
|
|
14
|
+
|
|
15
|
+
lint: ## Run ruff check
|
|
16
|
+
ruff check .
|
|
17
|
+
|
|
18
|
+
format: ## Run ruff format
|
|
19
|
+
ruff format .
|
|
20
|
+
|
|
21
|
+
test: ## Run pytest
|
|
22
|
+
pytest -q
|
|
23
|
+
|
|
24
|
+
clean: ## Remove build artifacts
|
|
25
|
+
rm -rf dist/ build/ src/*.egg-info
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: trusty-cage
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Isolated Docker-based development environments for AI coding agents
|
|
5
|
+
Project-URL: Homepage, https://github.com/areese801/trusty-cage
|
|
6
|
+
Project-URL: Repository, https://github.com/areese801/trusty-cage
|
|
7
|
+
Project-URL: Issues, https://github.com/areese801/trusty-cage/issues
|
|
8
|
+
Author: Adam Reese
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agents,ai,claude,containers,development,docker,isolation
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Requires-Dist: python-dotenv>=1.0
|
|
24
|
+
Requires-Dist: typer[all]>=0.12
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: build; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest-mock>=3.12; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
30
|
+
Requires-Dist: twine; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# trusty-cage
|
|
34
|
+
|
|
35
|
+
Isolated Docker-based development environments for AI coding agents. Run Claude Code (or any agent) with full autonomy inside a disposable container — no risk to your host machine, no credentials exposed, no accidental pushes.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install trusty-cage
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Or with [pipx](https://pipx.pypa.io/) for isolated CLI installs:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pipx install trusty-cage
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`tc` is available as a shorthand for `trusty-cage` (e.g. `tc create ...`, `tc attach ...`).
|
|
50
|
+
|
|
51
|
+
## Quick Start
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# One-time setup: create config directory and default .env file
|
|
55
|
+
trusty-cage init
|
|
56
|
+
|
|
57
|
+
# Create an environment from any git repo
|
|
58
|
+
trusty-cage create https://github.com/octocat/Hello-World
|
|
59
|
+
|
|
60
|
+
# You're now inside a tmux session (prefix: Ctrl-a) with:
|
|
61
|
+
# Left pane (60%) — Neovim at the project root
|
|
62
|
+
# Top-right pane — Claude Code running with --dangerously-skip-permissions
|
|
63
|
+
# Bottom-right pane — plain shell
|
|
64
|
+
|
|
65
|
+
# Switch panes with Ctrl-a <arrow>, detach with Ctrl-a d
|
|
66
|
+
|
|
67
|
+
# When done, export your work back to the host:
|
|
68
|
+
trusty-cage export hello-world
|
|
69
|
+
|
|
70
|
+
# Review and push from the host clone:
|
|
71
|
+
cd ~/.trusty-cage/envs/hello-world/repo/
|
|
72
|
+
git diff
|
|
73
|
+
git add -A && git commit -m "work from trusty-cage"
|
|
74
|
+
git push
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Example: Hello World
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Create (environment name is derived as lowercase: "hello-world")
|
|
81
|
+
trusty-cage create https://github.com/octocat/Hello-World --no-attach
|
|
82
|
+
|
|
83
|
+
# Verify
|
|
84
|
+
trusty-cage list
|
|
85
|
+
docker ps -a | grep isolated-dev
|
|
86
|
+
|
|
87
|
+
# Attach — drops you into tmux inside the container
|
|
88
|
+
trusty-cage attach hello-world
|
|
89
|
+
|
|
90
|
+
# Inside the container:
|
|
91
|
+
# Ctrl-a <arrow> — switch tmux panes
|
|
92
|
+
# git remote -v — empty (no remotes, by design)
|
|
93
|
+
# curl example.com — works (outbound web allowed)
|
|
94
|
+
# Ctrl-a d — detach
|
|
95
|
+
|
|
96
|
+
# Export work back to host
|
|
97
|
+
trusty-cage export hello-world
|
|
98
|
+
|
|
99
|
+
# Clean up
|
|
100
|
+
trusty-cage destroy hello-world
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Commands
|
|
104
|
+
|
|
105
|
+
| Command | Description |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `trusty-cage init [--force]` | Create config directory and default `.env` file |
|
|
108
|
+
| `trusty-cage create <url> [--name NAME] [--no-attach]` | Create a new environment from a git repo |
|
|
109
|
+
| `trusty-cage attach <name>` | Attach to an existing environment |
|
|
110
|
+
| `trusty-cage stop <name>` | Stop a container (preserves work) |
|
|
111
|
+
| `trusty-cage list` | List all environments with status |
|
|
112
|
+
| `trusty-cage export <name>` | Copy work back to host clone |
|
|
113
|
+
| `trusty-cage destroy <name>` | Remove container and volume (keeps host clone) |
|
|
114
|
+
| `trusty-cage rebuild-image` | Force rebuild the Docker image |
|
|
115
|
+
|
|
116
|
+
## Configuration
|
|
117
|
+
|
|
118
|
+
Configuration is resolved in order: CLI flags > environment variables > `~/.trusty-cage/.env` > defaults.
|
|
119
|
+
|
|
120
|
+
| Variable | Default | Description |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
| `TRUSTY_CAGE_DOTFILES_REPO` | *(empty)* | HTTPS URL of dotfiles repo to clone into containers |
|
|
123
|
+
| `TRUSTY_CAGE_PYTHON_VERSION` | `3.12` | Python version installed via pyenv |
|
|
124
|
+
| `TRUSTY_CAGE_DEFAULT_SHELL` | `zsh` | Default shell inside the container |
|
|
125
|
+
| `TRUSTY_CAGE_DEFAULT_AUTH_MODE` | `api_key` | Auth mode: `api_key` or `subscription` |
|
|
126
|
+
| `TRUSTY_CAGE_TMUX_PREFIX` | `C-a` | tmux prefix key inside containers (default `Ctrl-a` to avoid conflict with host `Ctrl-b`) |
|
|
127
|
+
| `ANTHROPIC_API_KEY` | *(none)* | API key for Claude Code (required for `api_key` auth mode) |
|
|
128
|
+
|
|
129
|
+
Run `trusty-cage init` to create `~/.trusty-cage/.env` with a commented template you can customize.
|
|
130
|
+
|
|
131
|
+
## Dotfiles
|
|
132
|
+
|
|
133
|
+
If you set `TRUSTY_CAGE_DOTFILES_REPO`, your dotfiles are automatically applied to every new container at `create` time. The repo is cloned on the host, `.git/` is stripped, and the files are copied into the container's home directory. If an install script is found (`install.sh`, `setup.sh`, `bootstrap.sh`, etc.), it runs automatically. GNU Stow layouts are detected and handled (files are copied from `common/` if present).
|
|
134
|
+
|
|
135
|
+
This means your shell config, tmux settings, Neovim config, aliases, and other personalizations carry over — the container feels like your own machine.
|
|
136
|
+
|
|
137
|
+
**Without dotfiles**, the container ships with sensible defaults: oh-my-zsh (robbyrussell theme), LazyVim starter config, pyenv on PATH, and `vim`/`vi` aliased to `nvim`. Everything works out of the box, just without your personal customizations.
|
|
138
|
+
|
|
139
|
+
## Authentication
|
|
140
|
+
|
|
141
|
+
Chosen at `create` time:
|
|
142
|
+
|
|
143
|
+
- **api_key** — Reads `ANTHROPIC_API_KEY` from your host shell at attach time. Injected via `docker exec -e`, never written to disk.
|
|
144
|
+
- **subscription** — Copies `~/.claude/` credentials into the container at create time. Persists in the volume.
|
|
145
|
+
|
|
146
|
+
## Security Model
|
|
147
|
+
|
|
148
|
+
The container is the blast radius. If an agent does something destructive, your host is unaffected.
|
|
149
|
+
|
|
150
|
+
**What agents can do inside:**
|
|
151
|
+
- Clone/fetch public repos over HTTPS
|
|
152
|
+
- Browse the web, read docs, hit public APIs
|
|
153
|
+
- Install packages (pip, apt, npm)
|
|
154
|
+
- Full read/write access to the project directory
|
|
155
|
+
|
|
156
|
+
**What agents cannot do:**
|
|
157
|
+
- Push to any git remote (no credentials present)
|
|
158
|
+
- Use SSH (port 22 blocked)
|
|
159
|
+
- Pull Docker images from Docker Hub (blocked)
|
|
160
|
+
- Access any host files (no bind mounts)
|
|
161
|
+
|
|
162
|
+
Protection is enforced by **credential absence**, not network blocking. The container has no SSH keys, no `.netrc`, no `GH_TOKEN`, no git credential helper.
|
|
163
|
+
|
|
164
|
+
## Requirements
|
|
165
|
+
|
|
166
|
+
- macOS with OrbStack or Docker Desktop
|
|
167
|
+
- Python 3.11+
|
|
168
|
+
- Git
|
|
169
|
+
- rsync (pre-installed on macOS; used by `export`)
|
|
170
|
+
|
|
171
|
+
## Development
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
git clone https://github.com/areese801/trusty-cage.git
|
|
175
|
+
cd trusty-cage
|
|
176
|
+
python -m venv venv
|
|
177
|
+
source venv/bin/activate
|
|
178
|
+
pip install -e ".[dev]"
|
|
179
|
+
|
|
180
|
+
# Available make targets
|
|
181
|
+
make help
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## License
|
|
185
|
+
|
|
186
|
+
MIT
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# trusty-cage
|
|
2
|
+
|
|
3
|
+
Isolated Docker-based development environments for AI coding agents. Run Claude Code (or any agent) with full autonomy inside a disposable container — no risk to your host machine, no credentials exposed, no accidental pushes.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install trusty-cage
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or with [pipx](https://pipx.pypa.io/) for isolated CLI installs:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pipx install trusty-cage
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`tc` is available as a shorthand for `trusty-cage` (e.g. `tc create ...`, `tc attach ...`).
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# One-time setup: create config directory and default .env file
|
|
23
|
+
trusty-cage init
|
|
24
|
+
|
|
25
|
+
# Create an environment from any git repo
|
|
26
|
+
trusty-cage create https://github.com/octocat/Hello-World
|
|
27
|
+
|
|
28
|
+
# You're now inside a tmux session (prefix: Ctrl-a) with:
|
|
29
|
+
# Left pane (60%) — Neovim at the project root
|
|
30
|
+
# Top-right pane — Claude Code running with --dangerously-skip-permissions
|
|
31
|
+
# Bottom-right pane — plain shell
|
|
32
|
+
|
|
33
|
+
# Switch panes with Ctrl-a <arrow>, detach with Ctrl-a d
|
|
34
|
+
|
|
35
|
+
# When done, export your work back to the host:
|
|
36
|
+
trusty-cage export hello-world
|
|
37
|
+
|
|
38
|
+
# Review and push from the host clone:
|
|
39
|
+
cd ~/.trusty-cage/envs/hello-world/repo/
|
|
40
|
+
git diff
|
|
41
|
+
git add -A && git commit -m "work from trusty-cage"
|
|
42
|
+
git push
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Example: Hello World
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Create (environment name is derived as lowercase: "hello-world")
|
|
49
|
+
trusty-cage create https://github.com/octocat/Hello-World --no-attach
|
|
50
|
+
|
|
51
|
+
# Verify
|
|
52
|
+
trusty-cage list
|
|
53
|
+
docker ps -a | grep isolated-dev
|
|
54
|
+
|
|
55
|
+
# Attach — drops you into tmux inside the container
|
|
56
|
+
trusty-cage attach hello-world
|
|
57
|
+
|
|
58
|
+
# Inside the container:
|
|
59
|
+
# Ctrl-a <arrow> — switch tmux panes
|
|
60
|
+
# git remote -v — empty (no remotes, by design)
|
|
61
|
+
# curl example.com — works (outbound web allowed)
|
|
62
|
+
# Ctrl-a d — detach
|
|
63
|
+
|
|
64
|
+
# Export work back to host
|
|
65
|
+
trusty-cage export hello-world
|
|
66
|
+
|
|
67
|
+
# Clean up
|
|
68
|
+
trusty-cage destroy hello-world
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Commands
|
|
72
|
+
|
|
73
|
+
| Command | Description |
|
|
74
|
+
|---|---|
|
|
75
|
+
| `trusty-cage init [--force]` | Create config directory and default `.env` file |
|
|
76
|
+
| `trusty-cage create <url> [--name NAME] [--no-attach]` | Create a new environment from a git repo |
|
|
77
|
+
| `trusty-cage attach <name>` | Attach to an existing environment |
|
|
78
|
+
| `trusty-cage stop <name>` | Stop a container (preserves work) |
|
|
79
|
+
| `trusty-cage list` | List all environments with status |
|
|
80
|
+
| `trusty-cage export <name>` | Copy work back to host clone |
|
|
81
|
+
| `trusty-cage destroy <name>` | Remove container and volume (keeps host clone) |
|
|
82
|
+
| `trusty-cage rebuild-image` | Force rebuild the Docker image |
|
|
83
|
+
|
|
84
|
+
## Configuration
|
|
85
|
+
|
|
86
|
+
Configuration is resolved in order: CLI flags > environment variables > `~/.trusty-cage/.env` > defaults.
|
|
87
|
+
|
|
88
|
+
| Variable | Default | Description |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| `TRUSTY_CAGE_DOTFILES_REPO` | *(empty)* | HTTPS URL of dotfiles repo to clone into containers |
|
|
91
|
+
| `TRUSTY_CAGE_PYTHON_VERSION` | `3.12` | Python version installed via pyenv |
|
|
92
|
+
| `TRUSTY_CAGE_DEFAULT_SHELL` | `zsh` | Default shell inside the container |
|
|
93
|
+
| `TRUSTY_CAGE_DEFAULT_AUTH_MODE` | `api_key` | Auth mode: `api_key` or `subscription` |
|
|
94
|
+
| `TRUSTY_CAGE_TMUX_PREFIX` | `C-a` | tmux prefix key inside containers (default `Ctrl-a` to avoid conflict with host `Ctrl-b`) |
|
|
95
|
+
| `ANTHROPIC_API_KEY` | *(none)* | API key for Claude Code (required for `api_key` auth mode) |
|
|
96
|
+
|
|
97
|
+
Run `trusty-cage init` to create `~/.trusty-cage/.env` with a commented template you can customize.
|
|
98
|
+
|
|
99
|
+
## Dotfiles
|
|
100
|
+
|
|
101
|
+
If you set `TRUSTY_CAGE_DOTFILES_REPO`, your dotfiles are automatically applied to every new container at `create` time. The repo is cloned on the host, `.git/` is stripped, and the files are copied into the container's home directory. If an install script is found (`install.sh`, `setup.sh`, `bootstrap.sh`, etc.), it runs automatically. GNU Stow layouts are detected and handled (files are copied from `common/` if present).
|
|
102
|
+
|
|
103
|
+
This means your shell config, tmux settings, Neovim config, aliases, and other personalizations carry over — the container feels like your own machine.
|
|
104
|
+
|
|
105
|
+
**Without dotfiles**, the container ships with sensible defaults: oh-my-zsh (robbyrussell theme), LazyVim starter config, pyenv on PATH, and `vim`/`vi` aliased to `nvim`. Everything works out of the box, just without your personal customizations.
|
|
106
|
+
|
|
107
|
+
## Authentication
|
|
108
|
+
|
|
109
|
+
Chosen at `create` time:
|
|
110
|
+
|
|
111
|
+
- **api_key** — Reads `ANTHROPIC_API_KEY` from your host shell at attach time. Injected via `docker exec -e`, never written to disk.
|
|
112
|
+
- **subscription** — Copies `~/.claude/` credentials into the container at create time. Persists in the volume.
|
|
113
|
+
|
|
114
|
+
## Security Model
|
|
115
|
+
|
|
116
|
+
The container is the blast radius. If an agent does something destructive, your host is unaffected.
|
|
117
|
+
|
|
118
|
+
**What agents can do inside:**
|
|
119
|
+
- Clone/fetch public repos over HTTPS
|
|
120
|
+
- Browse the web, read docs, hit public APIs
|
|
121
|
+
- Install packages (pip, apt, npm)
|
|
122
|
+
- Full read/write access to the project directory
|
|
123
|
+
|
|
124
|
+
**What agents cannot do:**
|
|
125
|
+
- Push to any git remote (no credentials present)
|
|
126
|
+
- Use SSH (port 22 blocked)
|
|
127
|
+
- Pull Docker images from Docker Hub (blocked)
|
|
128
|
+
- Access any host files (no bind mounts)
|
|
129
|
+
|
|
130
|
+
Protection is enforced by **credential absence**, not network blocking. The container has no SSH keys, no `.netrc`, no `GH_TOKEN`, no git credential helper.
|
|
131
|
+
|
|
132
|
+
## Requirements
|
|
133
|
+
|
|
134
|
+
- macOS with OrbStack or Docker Desktop
|
|
135
|
+
- Python 3.11+
|
|
136
|
+
- Git
|
|
137
|
+
- rsync (pre-installed on macOS; used by `export`)
|
|
138
|
+
|
|
139
|
+
## Development
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
git clone https://github.com/areese801/trusty-cage.git
|
|
143
|
+
cd trusty-cage
|
|
144
|
+
python -m venv venv
|
|
145
|
+
source venv/bin/activate
|
|
146
|
+
pip install -e ".[dev]"
|
|
147
|
+
|
|
148
|
+
# Available make targets
|
|
149
|
+
make help
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
MIT
|