# Chat-Driven Project Bootstrap Design overview for going from "I want a new project" to a running, container-isolated, editor-accessible huskies project in one chat command. ## Goal A user can say to Timmy in chat: ``` new project myapp --stack rust new project legacy-rails --git git@github.com:me/legacy-rails.git ``` and end up with: 1. A fresh docker container running the project's huskies node. 2. The project's source code bind-mounted from the host so the user can edit it in any editor. 3. SSH into the container so editors can run LSPs, builds, and tests inside the container — never on the host. 4. Optional git remote configured for push to GitHub or Gitea. 5. The new sled registered with the gateway, so Timmy can drive coders / mergemaster / etc. on the project via existing chat commands. Manual repo creation on GitHub/Gitea remains the user's job. Everything downstream of that is orchestrated. ## Architecture at a Glance ``` ┌──────────────────────┐ │ Browser / Matrix │───┐ └──────────────────────┘ │ ▼ ┌───────────────────────┐ │ Gateway (huskies-gw) │ │ • chat dispatcher │ │ • new-project │ │ • routing │ └─────────┬─────────────┘ │ ┌─────────┴───────────────────────────────────┐ │ docker engine (host) │ │ ┌────────────┐ ┌────────────┐ ┌─────────┐ │ │ │ project-A │ │ project-B │ │ ... │ │ │ │ sled + │ │ sled + │ │ │ │ │ │ sshd + │ │ sshd + │ │ │ │ │ │ LSPs │ │ LSPs │ │ │ │ │ └─────┬──────┘ └─────┬──────┘ └─────────┘ │ └────────┼──────────────┼─────────────────────┘ │ │ bind mount │ │ bind mount ┌────────┴───┐ ┌─────┴──────┐ │ ~/code/A │ │ ~/code/B │ ◄── host └────────────┘ └────────────┘ editor opens these paths ``` - One container per project. The container runs the project's huskies binary (sled), an SSH server, and the stack-appropriate LSP(s). - Source lives on the host (e.g. `~/code/`), bind-mounted into the container at a known path. Host can git-diff, back up, or edit. - The gateway is editor-agnostic and project-agnostic — it talks to each sled via the existing rendezvous / CRDT-sync protocol. ## Three Personas | Persona | What they do | What they need | |---------|--------------|----------------| | Chat-only user | Drives everything via Matrix/web chat | Installed huskies binary; chat client | | Editor-using technical user | Same + edits source in their editor | SSH config to the container + editor-specific remote-dev setup | | Multi-project user | Several projects running in parallel | Gateway-listed projects, all routable from one chat | Chat-only users never touch SSH. Editor users go through a one-time "copy this SSH command into your editor's remote settings" handoff at project creation time. ## The Bootstrap Chat Command ``` new project [--stack ] [--git ] [--path ] ``` Flow: 1. **Validate**: name unique among existing projects; host path doesn't already exist; stack (if declared) is one of the supported overlays. 2. **Allocate** a fresh per-project port range (gateway picks). 3. **Create host directory** at `--path` (default `~/huskies//`). 4. If `--git` provided, `git clone` into that directory; else `git init`. 5. **Detect stack** from cloned content if not declared: - `Cargo.toml` → `rust` - `package.json` → `node` - `go.mod` → `go` - `pyproject.toml` / `requirements.txt` / `setup.py` → `python` - `Gemfile` → `ruby` - `pom.xml` / `build.gradle` → `jvm` - Multiple → pick the dominant, warn. - None → minimal base image, user can install tooling later. 6. **Compose the container** from `huskies-project-base` + the stack overlay (Dockerfile fragments under `docker/stacks//`). 7. **Launch** the container with bind mount + port forwards + an auto-generated SSH key. 8. **Seed `.huskies/project.toml`** with sensible defaults. 9. **Register** the project with the gateway (`gateway_projects` LWW-map). 10. **Reply in chat** with: project name, host path, SSH command, and a `huskies status ` invocation to verify. ## Container Template Layered: - **`huskies-project-base`**: debian-slim + git + huskies binary + sshd + sudo + a `huskies` user with the SSH pubkey installed. - **`huskies-stack-`**: per-stack additions. E.g. rust gets `rustup` + `rust-analyzer` + `cargo-nextest`; node gets `node@22` + `typescript-language-server`; etc. - **Project layer**: the bind-mounted `/workspace` is the project source, written by the host's editor, read by the in-container tooling. The container's SSH server is bound to a host-local port (not exposed externally). Auth is the per-project keypair generated at bootstrap; the public key sits inside the container, the private key on host. ## Build Sandbox Model The threat: editing code in a host-side editor causes the editor (or its LSP plugin) to run `cargo check` / `npm install` / `pip install` / similar, which executes arbitrary code from project dependencies — `build.rs`, proc-macros, npm `postinstall`, Python `setup.py`, Ruby native-extension build scripts, etc. A malicious dependency compromises the host. The mitigation: all build / type-check / dependency-install commands execute **inside the project container**. The host's editor connects to the container over SSH; rust-analyzer (or equivalent) runs inside the container; the host process never `exec`s untrusted build scripts. Container isolation is the docker default plus: - No `--privileged`. - No host bind mounts beyond the project source and the SSH key. - No host network beyond the gateway's CRDT sync port. - `--cap-drop=ALL` plus the minimum caps needed (probably none). This isn't a hardened sandbox in the gvisor / Firecracker sense — a docker-escape exploit on a compromised container still escalates to host. For most consumer threat models (malicious crate from crates.io / npm), docker's default isolation is sufficient. Tighter sandboxing (gvisor) is a separate future spike if needed. ## Editor Connection — Editor-Agnostic SSH | Editor | Connection mechanism | |--------|----------------------| | VSCode | Remote-SSH extension | | JetBrains (IntelliJ/Rover) | JetBrains Gateway (SSH) | | Zed | Built-in SSH remoting (mac/linux only today) | | Vim/Neovim | SSH terminal session, or local nvim + LSP-over-SSH | | Emacs | TRAMP + remote LSP via lsp-mode | All converge on: `ssh huskies@127.0.0.1 -p -i ~/.huskies//id_ed25519`. That string is emitted in the bootstrap chat reply. ## Git Integration - Initial setup is `git init` or `git clone` inside the container. - For push: user's existing GitHub / Gitea SSH key is bind-mounted read-only into the container at `~/.ssh/id_*`, OR the user supplies a push token via `huskies secrets set GIT_TOKEN=...` (stored as a Fly secret equivalent — for now, a chmod 600 file in the container). - The container's `git` config gets `user.name` / `user.email` from the gateway-level user identity. ## Decisions | Decision | Choice | Alternative | |----------|--------|-------------| | Container per project | One container per project | One container many projects: simpler but breaks isolation, breaks per-project deps | | Editor model | SSH-remote (any editor) | VSCode Dev Containers only: simpler config but locks out everyone else | | Source location | Bind mount from host | Inside container only: breaks "I can also edit on my laptop" requirement | | Stack detection | Auto from project files, override with `--stack` | Always declared: more friction at bootstrap | | Push secrets | Bind-mounted host SSH key OR per-project token | Gateway holds tokens: bigger blast radius | ## Open Questions 1. **Per-project resource limits.** Should each container have a hard CPU / RAM cap so a runaway agent doesn't starve the host? 2. **Lifecycle / cleanup.** If the user deletes a project from chat, what gets removed? Container yes; host source no (data loss); git remotes yes? Need a confirm step. 3. **Multi-tenant.** Out of scope for this design (that's huskies.dev territory). This doc assumes single-user local-only. 4. **Windows specifics.** Bind mounts work but line-ending / permission edge cases. Probably document "use WSL2 for best experience" rather than fight Windows native paths. 5. **Gateway-on-host vs gateway-in-container.** The gateway today runs in its own container. New per-project containers connect via docker network. Need to confirm the network plumbing works for arbitrary per-project containers, not just the manually-configured ones. ## Phasing The work breaks naturally into: - **Phase 0 (now):** this design doc. - **Phase 1:** chat command exists and provisions a bare project container (no stack overlay, no SSH, no git clone — just "start a container, register with gateway"). Validates the orchestration shell. - **Phase 2:** stack-aware container template — base image + overlays; detection from project files. - **Phase 3:** SSH-remote editor access — sshd in the container, per-project keypair, chat-reply emits the connection string. - **Phase 4:** git integration — `--git ` clones, host SSH key mount, push verification. - **Phase 5:** per-project resource limits + cleanup chat commands. Each phase ships independently and is usable on its own. Phase 1 alone gives chat-only users a working project; later phases add the editor and git polish.