README.md

# DevCon 4

> **DEFCON** (Defense Readiness Condition) is the US military alert system.
> DEFCON 5 is peacetime. DEFCON 1 is nuclear war.
>
> **DevCon 4** is where we operate: heightened awareness, not full lockdown.
> Your development container is secured with a restrictive firewall, but you
> still have access to everything you need -- GitHub, Hex, Anthropic. Vigilant,
> but productive.

Devcontainer setup for Elixir projects. A Mix archive installer that generates
a complete `.devcontainer/` configuration with Claude Code, a restrictive
firewall, and development tools.

## What it generates

Three files are created in your project's `.devcontainer/` directory:

| File | Purpose |
|------|---------|
| `devcontainer.json` | Container settings, volume mounts, VS Code extensions, and lifecycle commands |
| `Dockerfile` | Elixir + Node.js image with Claude Code, zsh, git, and firewall tools |
| `init-firewall.sh` | Restrictive outbound firewall allowing only essential domains |

### Dockerfile details

The generated Dockerfile builds an image with:

- **Elixir & Erlang** -- from the official `hexpm/elixir` image (version configurable)
- **Node.js 20** -- required for Claude Code
- **Claude Code** -- installed globally via npm
- **Shell** -- zsh with git and fzf plugins
- **Dev tools** -- git, vim, nano, jq, gh (GitHub CLI), fzf, unzip
- **Firewall tools** -- iptables, ipset, iproute2, dnsutils, aggregate
- **Non-root user** -- `developer` with passwordless sudo
- **Hex & Rebar** -- pre-installed for Elixir development

### Firewall details

The `init-firewall.sh` script applies a default-deny outbound policy, allowing
only these domains:

| Domain | Reason |
|--------|--------|
| `api.github.com`, GitHub IP ranges | Git operations, GitHub CLI |
| `registry.npmjs.org` | Claude Code installation/updates |
| `api.anthropic.com` | Claude Code API access |
| `sentry.io` | Claude Code error reporting |
| `statsig.anthropic.com`, `statsig.com` | Claude Code telemetry |
| `repo.hex.pm`, `builds.hex.pm`, `hex.pm` | Elixir package management |

DNS (port 53), SSH (port 22), and localhost are always allowed.

## Installation

```bash
mix archive.install hex devcon4
```

## Usage

Run in any Elixir project:

```bash
mix devcon4.install
```

## Options

All options have sensible defaults. Override them as needed:

| Option | Default | Description |
|--------|---------|-------------|
| `--elixir-version` | `1.18.3` | Elixir version for the base Docker image |
| `--erlang-version` | `27.3.3` | Erlang/OTP version for the base Docker image |
| `--ubuntu-version` | `noble` | Ubuntu release codename |
| `--ubuntu-date-tag` | `20260217` | Ubuntu image date tag from `hexpm/elixir` |
| `--timezone` | `America/Sao_Paulo` | Default timezone fallback (host `$TZ` takes priority) |
| `--force` | `false` | Overwrite existing files without prompting |

### Examples

Install with defaults:

```bash
mix devcon4.install
```

Install with custom Elixir/Erlang versions:

```bash
mix devcon4.install \
  --elixir-version 1.17.0 \
  --erlang-version 26.2.1
```

## Starting the container

### Via terminal (devcontainer CLI)

```bash
# Build and start the container
devcontainer up --workspace-folder .

# Execute commands inside the container
devcontainer exec --workspace-folder . mix test
devcontainer exec --workspace-folder . claude

# Enter an interactive shell
docker exec -it <container_id> zsh

# Rebuild after changing devcontainer files
devcontainer up --workspace-folder . --rebuild-if-exists
```

### Via VS Code

1. Install the [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension
2. Open the project folder
3. Press `Ctrl+Shift+P` and select **Dev Containers: Reopen in Container**
4. The ElixirLS extension is automatically installed

### Using Claude Code inside the container

Once inside the container, Claude Code is available globally:

```bash
claude                    # Start Claude Code
claude --dangerously-skip-permissions  # Unattended mode (use with caution)
```

The Claude Code configuration is persisted across container rebuilds via a
Docker volume mounted at `/home/developer/.claude`.

## Customization

After installation, the generated files are yours to modify. Common
customizations:

### Adding firewall domains

Edit `.devcontainer/init-firewall.sh` and add domains to the `for domain in`
loop:

```bash
for domain in \
    "registry.npmjs.org" \
    "api.anthropic.com" \
    ...
    "your-domain.example.com"; do   # <-- add here
```

### Adding system packages

Edit `.devcontainer/Dockerfile` and add packages to the `apt-get install` line:

```dockerfile
RUN apt-get update && apt-get install -y --no-install-recommends \
  ...
  postgresql-client \   # <-- add here
  && apt-get clean && rm -rf /var/lib/apt/lists/*
```

### Adding VS Code extensions

Edit `.devcontainer/devcontainer.json`:

```json
"customizations": {
  "vscode": {
    "extensions": [
      "JakeBecker.elixir-ls",
      "your.extension-id"
    ]
  }
}
```

### Changing container settings

Edit `.devcontainer/devcontainer.json`. Common changes:

- `postCreateCommand` -- add project-specific setup steps
- `forwardPorts` -- expose ports to the host (e.g., `[4000]` for Phoenix)
- `containerEnv` -- add environment variables

## Security considerations

The firewall restricts outbound network access to a whitelist of domains. This
provides meaningful isolation but is **not a complete sandbox**:

- A malicious project could still access any whitelisted domain
- Claude Code credentials are accessible inside the container
- The `developer` user has passwordless sudo

**Only use devcontainers with repositories you trust.**

The `--dangerously-skip-permissions` flag for Claude Code is designed for use
within this restricted environment. It allows Claude Code to operate without
interactive permission prompts, which is useful for automated workflows. The
firewall limits the blast radius of any unintended actions.

## Finding available image tags

The base image tag follows the pattern
`hexpm/elixir:{elixir}-erlang-{erlang}-ubuntu-{ubuntu}-{date}`. To find
available tags:

```bash
# Search for tags matching your desired versions
curl -s "https://hub.docker.com/v2/repositories/hexpm/elixir/tags?page_size=100&name=1.18" \
  | jq -r '.results[].name' | grep noble | sort -V
```

## License

Apache-2.0