> For the complete documentation index, see [llms.txt](https://spuff.avelino.run/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://spuff.avelino.run/adr/0006-project-config-spec.md).

# ADR-0006: Project Configuration Specification (spuff.yaml)

## Status

Accepted

## Date

2025-01-19

## Context

spuff currently uses a global configuration file (`~/.spuff/config.yaml`) for all environments. While this works for basic usage, teams and projects often need:

1. **Reproducible environments** - Every developer should get the same setup
2. **Project-specific tooling** - Different projects need different language stacks
3. **Infrastructure as code** - Environment configuration should be versioned with the codebase
4. **Automatic dependency installation** - No manual setup steps after `spuff up`

Without project-level configuration:

* Developers must manually install tools after VM creation
* Environments drift between team members
* Onboarding requires documentation and manual steps
* Environment setup is not reproducible or auditable

## Decision

We will implement a **project-level configuration specification** via `spuff.yaml` that:

### 1. Configuration File

* Single file: `spuff.yaml` in project root
* Optional secrets file: `spuff.secrets.yaml` (gitignored)
* YAML format for readability and compatibility
* Discovery: search upward from CWD to find config

### 2. Key Features

**Language Bundles**: Pre-configured toolchains that include compiler/runtime + LSPs + linters + formatters:

* rust, go, python, node, elixir, java, zig, cpp, ruby
* Each bundle is self-contained and tested

**Resource Overrides**: Project can specify VM size/region (CLI args take precedence)

**Docker Services Integration**: Uses existing `docker-compose.yaml` instead of duplicating configuration

**Repository Cloning**: Automatically clone related repositories with SSH agent forwarding

**Environment Variables**: Support for `$VAR`, `${VAR}`, and `${VAR:-default}` resolution from host

**Setup Scripts**: Ordered list of commands executed after packages/bundles install

**Port Tunneling**: Declare ports for automatic SSH tunnel setup via `spuff ssh`

### 3. Implementation Architecture

```mermaid
flowchart TB
    subgraph cli["CLI (spuff up)"]
        load["Load spuff.yaml from CWD"]
        merge["Merge with global config"]
        embed["Embed as project.json in cloud-init"]
        load --> merge --> embed
    end

    subgraph vm["VM boots → agent starts"]
        read["Agent reads /opt/spuff/project.json"]
        bundles["Install bundles (async)"]
        packages["Install packages"]
        repos["Clone repositories"]
        services["Start services (docker-compose)"]
        scripts["Run setup scripts"]
        hooks["Execute hooks"]

        read --> bundles
        read --> packages
        read --> repos
        read --> services
        packages --> scripts
        scripts --> hooks
    end

    embed --> vm
```

### 4. What We Will NOT Do

* **No config inheritance/extends** - Simplicity first; can add later
* **No IDE extension management** - Environment is terminal-focused
* **No lock file** - Consider for future version
* **No secrets management service** - Use local files + env vars

## Consequences

### Positive

* **Reproducible environments** - `git clone` + `spuff up` = working environment
* **Self-documenting** - Configuration shows what the project needs
* **Version controlled** - Changes are tracked and auditable
* **Team alignment** - Everyone uses the same tooling
* **Faster onboarding** - No manual setup steps
* **Composable** - Can share configs between similar projects

### Negative

* **Initial setup cost** - Projects need to create spuff.yaml
* **Increased complexity** - More configuration to understand
* **Potential conflicts** - Project config vs global config can confuse users
* **Bundle maintenance** - Need to keep bundle scripts updated

### Neutral

* Moves setup responsibility from developers to the configuration file
* Requires agent to handle async installation tasks

## Alternatives Considered

### Alternative 1: Nix Flakes

Use Nix for environment definition (like devenv.sh).

**Pros:**

* Extremely reproducible
* Large package ecosystem
* Declarative and functional

**Cons:**

* Steep learning curve
* Nix-specific syntax
* Slow initial builds
* Not familiar to most developers

**Why rejected:** Nix is powerful but adds significant complexity. Our target users want simple YAML, not a new language.

### Alternative 2: Devcontainers

Use VS Code's devcontainer.json specification.

**Pros:**

* Industry standard
* VS Code integration
* Container-based isolation

**Cons:**

* VS Code-centric
* Docker dependency
* Doesn't fit our VM-based model

**Why rejected:** We provision VMs, not containers. Different paradigm.

### Alternative 3: Terraform/Pulumi

Full IaC tools for environment definition.

**Pros:**

* Extremely powerful
* Multi-cloud support
* State management

**Cons:**

* Overkill for dev environments
* Slow iteration
* Complex for simple use cases

**Why rejected:** These tools are designed for production infrastructure, not ephemeral dev environments.

### Alternative 4: Shell Scripts Only

Use a `setup.sh` script in each project.

**Pros:**

* No new concepts
* Full flexibility
* Easy to understand

**Cons:**

* Not declarative
* Hard to track progress
* No structure or validation
* Can't show nice status output

**Why rejected:** We want declarative configuration with structured output and progress tracking.

## References

* [docs/project-config.md](/project-config.md) - User documentation
* [src/project\_config.rs](https://github.com/avelino/spuff/blob/main/src/project_config.rs) - CLI parsing implementation
* [src/agent/project\_setup.rs](https://github.com/avelino/spuff/blob/main/src/agent/project_setup.rs) - Agent setup handler
* [Devcontainers specification](https://containers.dev/) - Inspiration (but different approach)
* [devenv.sh](https://devenv.sh/) - Nix-based alternative


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://spuff.avelino.run/adr/0006-project-config-spec.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
