totopo 0.9.0 → 1.0.0-rc-2
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.
- package/README.md +79 -68
- package/bin/totopo.js +0 -3
- package/dist/commands/onboard.js +5 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,127 +1,138 @@
|
|
|
1
1
|
# totopo
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<img src=".github/assets/logo.png" alt="totopo" width="100%" />
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A simple CLI to spin up a sandboxed Docker environment for AI coding agents.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+

|
|
8
|
+

|
|
9
|
+

|
|
8
10
|
|
|
9
|
-
##
|
|
11
|
+
## What is totopo?
|
|
10
12
|
|
|
11
|
-
`npx totopo`
|
|
13
|
+
`npx totopo` spins up a secure, isolated dev container for any git project — with AI coding tools pre-installed — in a single command.
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
Host machine
|
|
15
|
-
├── your editor → edits files normally (bind-mounted from container)
|
|
16
|
-
├── terminal → connected to container via docker exec
|
|
17
|
-
│ ├── opencode → AI tools run here, isolated
|
|
18
|
-
│ ├── claude
|
|
19
|
-
│ └── codex
|
|
20
|
-
└── git push/pull → only possible from host, blocked inside container
|
|
21
|
-
```
|
|
15
|
+
There are other solutions that offer more hardened security setups, and others with a richer feature set. totopo is neither of those. It is my own take on what makes a good balance between excellent developer experience and a sensible basic sandboxing setup.
|
|
22
16
|
|
|
23
17
|
---
|
|
24
18
|
|
|
25
|
-
##
|
|
19
|
+
## Features at a Glance
|
|
26
20
|
|
|
27
|
-
-
|
|
21
|
+
- **Sandboxed Docker container** — your code runs in an isolated environment with strict filesystem and privilege boundaries
|
|
22
|
+
- **Agents can't reach remote** — push, pull, fetch, and clone are blocked inside the container, preventing agents from accidentally affecting your remote repositories
|
|
23
|
+
- **Scoped mounts** — expose only the files and directories the agent needs, nothing more
|
|
24
|
+
- **AI coding CLIs with persistent sessions** — Claude Code, OpenCode, and Codex pre-installed, with conversation history that survives restarts and rebuilds
|
|
25
|
+
- **Host-mirror or generic runtime** — use a standard dev container, or let totopo match the container environment to your host so the agent works in the exact same setup as your codebase
|
|
28
26
|
|
|
29
27
|
---
|
|
30
28
|
|
|
31
|
-
##
|
|
29
|
+
## Requirements
|
|
30
|
+
|
|
31
|
+
- [Docker](https://www.docker.com/products/docker-desktop/)
|
|
32
|
+
- [git](https://git-scm.com/)
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
34
37
|
|
|
35
38
|
```bash
|
|
36
39
|
cd your-project
|
|
37
40
|
npx totopo
|
|
38
41
|
```
|
|
39
42
|
|
|
40
|
-
If `.totopo/` doesn't exist yet,
|
|
43
|
+
Select **Open session** from the menu. If `.totopo/` doesn't exist yet, a one-time onboarding flow runs first. The first run builds the Docker image. Subsequent starts are fast.
|
|
41
44
|
|
|
42
|
-
|
|
45
|
+
<!-- VIDEO: First-time setup — running `npx totopo` in a fresh repo, selecting a runtime mode, and waiting for the Docker image to build for the first time.
|
|
46
|
+

|
|
47
|
+
-->
|
|
43
48
|
|
|
44
|
-
|
|
49
|
+
<!-- VIDEO: Starting a session once the container is already built — opening a session, running an AI tool, exiting.
|
|
50
|
+

|
|
51
|
+
-->
|
|
45
52
|
|
|
46
|
-
|
|
53
|
+
---
|
|
47
54
|
|
|
48
|
-
|
|
55
|
+
## Features
|
|
49
56
|
|
|
50
|
-
|
|
51
|
-
status
|
|
52
|
-
```
|
|
57
|
+
### Sandboxed dev container
|
|
53
58
|
|
|
54
|
-
|
|
59
|
+
Every session runs inside a Docker container. Your code is bind-mounted from the host — edits are immediately visible in your editor. The container enforces several isolation boundaries:
|
|
55
60
|
|
|
56
|
-
|
|
61
|
+
| Control | Implementation |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| Non-root user | All processes run as `devuser` (uid 1001) — cannot modify system-level config |
|
|
64
|
+
| Filesystem isolation | Only the repo is mounted — host filesystem is not visible |
|
|
65
|
+
| Git remote block | `protocol.allow = never` in `/etc/gitconfig` — push, pull, fetch, and clone are all refused; requires root to override |
|
|
66
|
+
| No host credentials forwarded | Host git credentials are never copied into the container |
|
|
67
|
+
| Secrets never in image | API keys loaded at runtime from `~/.totopo/.env` — never baked into the image, never mounted into the container |
|
|
68
|
+
| No privilege escalation | `no-new-privileges:true` prevents any process from gaining elevated permissions |
|
|
57
69
|
|
|
58
|
-
|
|
70
|
+
Remote git operations are blocked inside the container. Push from your host terminal instead.
|
|
59
71
|
|
|
60
|
-
|
|
72
|
+
### Scoped sandboxing
|
|
61
73
|
|
|
62
|
-
|
|
63
|
-
your-project/
|
|
64
|
-
└── .totopo/
|
|
65
|
-
├── .env # API keys — gitignored, never committed
|
|
66
|
-
├── Dockerfile # Container image definition
|
|
67
|
-
└── post-start.mjs # Security + readiness check on every start
|
|
68
|
-
```
|
|
74
|
+
Mount only the files and directories you need into the container rather than the full repository. Two scoped modes are available: `cwd` (current directory only) and `selective` (hand-pick individual files and folders).
|
|
69
75
|
|
|
70
|
-
|
|
76
|
+
In both scoped modes, `.git` is intentionally not mounted. Mounting `.git` would expose the full commit history of every repository file — including files outside the mounted paths — which defeats the point of scoped access. As a result, git is unavailable inside a scoped session and the agent operates without repository history. The agent is instructed to surface these limitations at session start.
|
|
77
|
+
|
|
78
|
+
Scoped sessions are well-suited for focused tasks where you want to give the agent a narrow, explicit view of your codebase.
|
|
79
|
+
|
|
80
|
+
<!-- VIDEO: Using scoped mounts — selecting cwd and selective modes, showing what the agent can and can't see inside the container.
|
|
81
|
+

|
|
82
|
+
-->
|
|
71
83
|
|
|
72
|
-
|
|
84
|
+
### AI tools pre-installed
|
|
73
85
|
|
|
74
|
-
|
|
86
|
+
The container comes with the major AI coding CLIs ready to use out of the box:
|
|
75
87
|
|
|
76
88
|
```bash
|
|
77
89
|
opencode # OpenCode
|
|
78
90
|
claude # Claude Code (Anthropic)
|
|
79
91
|
codex # Codex (OpenAI)
|
|
80
|
-
status # Re-run security + readiness check
|
|
81
92
|
```
|
|
82
93
|
|
|
83
|
-
|
|
94
|
+
### Dev container runtime
|
|
84
95
|
|
|
85
|
-
|
|
96
|
+
Choose between two modes:
|
|
86
97
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
| Non-root user | All processes run as `devuser` (uid 1001) |
|
|
90
|
-
| Filesystem isolation | Only the repo is mounted — host is not visible |
|
|
91
|
-
| Git remote block | `protocol.allow never` in `/etc/gitconfig` — enforced at the git layer, requires root to override |
|
|
92
|
-
| No privilege escalation | `no-new-privileges:true` security opt |
|
|
93
|
-
| Secrets never in image | API keys loaded at runtime from `~/.totopo/.env` — never baked into the image, never mounted into the container |
|
|
98
|
+
- **Host-mirror** — the container runtime matches your host Node.js version and selected tools, keeping the environment consistent with your local setup.
|
|
99
|
+
- **Generic** — a full dev container with the latest stable versions of all tools. Good default if you don't need version parity with your host.
|
|
94
100
|
|
|
95
|
-
|
|
101
|
+
Either way, basic dev tools and all three AI CLIs are always included.
|
|
96
102
|
|
|
97
|
-
|
|
103
|
+
<!-- VIDEO: Switching runtime modes in the settings menu, selecting tools, and triggering a container rebuild.
|
|
104
|
+

|
|
105
|
+
-->
|
|
98
106
|
|
|
99
107
|
---
|
|
100
108
|
|
|
101
|
-
##
|
|
102
|
-
|
|
103
|
-
```bash
|
|
104
|
-
# Inside container — local operations ✅
|
|
105
|
-
git add .
|
|
106
|
-
git commit -m "message"
|
|
107
|
-
git log / diff / branch
|
|
109
|
+
## What gets created in your project
|
|
108
110
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
+
```
|
|
112
|
+
your-project/
|
|
113
|
+
└── .totopo/
|
|
114
|
+
├── Dockerfile # container image definition
|
|
115
|
+
├── post-start.mjs # security checks + readiness summary on every start
|
|
116
|
+
├── settings.json # runtime mode + selected tools (committed with project)
|
|
117
|
+
├── README.md # .totopo reference
|
|
118
|
+
└── agents/ # agent session data — gitignored, created on first session start
|
|
119
|
+
├── claude/ # mounted as ~/.claude/
|
|
120
|
+
├── opencode/ # mounted as ~/.config/opencode/ + ~/.local/share/opencode/
|
|
121
|
+
└── codex/ # mounted as ~/.codex/
|
|
122
|
+
|
|
123
|
+
~/.totopo/.env # API keys — global, outside all repos, never mounted into container
|
|
111
124
|
```
|
|
112
125
|
|
|
126
|
+
Agent session history and conversation data are persisted in `agents/` across container rebuilds and restarts. This directory is gitignored — session data stays local to your machine.
|
|
127
|
+
|
|
113
128
|
---
|
|
114
129
|
|
|
115
130
|
## Limitations
|
|
116
131
|
|
|
117
|
-
**
|
|
132
|
+
**Audio / microphone** — the image includes `sox` (required by Claude Code for voice mode), but audio passthrough from the host depends on your OS. macOS, Linux, and Windows each require different device configuration. If you need voice mode, set up audio passthrough manually for your platform.
|
|
118
133
|
|
|
119
134
|
---
|
|
120
135
|
|
|
121
|
-
##
|
|
122
|
-
|
|
123
|
-
**Container fails to start** — the startup check prints exactly which check failed and why.
|
|
124
|
-
|
|
125
|
-
**API key warnings** — check `.totopo/.env` has the correct variable names, then use **Rebuild** from the totopo menu to rebuild the container.
|
|
136
|
+
## Disclaimer
|
|
126
137
|
|
|
127
|
-
|
|
138
|
+
totopo is MIT licensed and fully open source — fork it, adapt it, build on it. Issues are welcome but response times aren't guaranteed. Use at your own risk.
|
package/bin/totopo.js
CHANGED
|
@@ -40,9 +40,6 @@ try {
|
|
|
40
40
|
process.exit(1);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
process.env.TOTOPO_PACKAGE_DIR = packageDir;
|
|
44
|
-
process.env.TOTOPO_REPO_ROOT = repoRoot;
|
|
45
|
-
|
|
46
43
|
// ─── Guard: dist/ must exist ─────────────────────────────────────────────────
|
|
47
44
|
if (!existsSync(new URL("../dist/commands/sync-dockerfile.js", import.meta.url))) {
|
|
48
45
|
console.error("");
|
package/dist/commands/onboard.js
CHANGED
|
@@ -15,9 +15,12 @@ export async function run(packageDir, repoRoot) {
|
|
|
15
15
|
const templatesDir = join(packageDir, "templates");
|
|
16
16
|
const totopoDir = join(repoRoot, ".totopo");
|
|
17
17
|
const projectName = basename(repoRoot);
|
|
18
|
+
const tildefy = (p) => (p.startsWith(homedir()) ? p.replace(homedir(), "~") : p);
|
|
18
19
|
// ─── Intro ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
process.stdout.write("\n");
|
|
19
21
|
intro("totopo — First-time setup");
|
|
20
|
-
|
|
22
|
+
log.message("");
|
|
23
|
+
box(`project : ${projectName}\nlocation : ${tildefy(totopoDir)}`, "", {
|
|
21
24
|
contentAlign: "center",
|
|
22
25
|
titleAlign: "center",
|
|
23
26
|
width: "auto",
|
|
@@ -109,7 +112,7 @@ export async function run(packageDir, repoRoot) {
|
|
|
109
112
|
writeFileSync(gitignorePath, content);
|
|
110
113
|
}
|
|
111
114
|
}
|
|
112
|
-
log.info(`Optionally add API keys to ${globalEnvPath} —
|
|
115
|
+
log.info(`Optionally add API keys to ${tildefy(globalEnvPath)} — they are injected into every totopo container at runtime.`);
|
|
113
116
|
outro("Setup complete.");
|
|
114
117
|
return true;
|
|
115
118
|
}
|