Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Takashi Nozawa

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
153 changes: 153 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Bare Dev Container Features

[![CI](https://github.com/bare-devcontainer/features/actions/workflows/ci.yml/badge.svg)](https://github.com/bare-devcontainer/features/actions/workflows/ci.yml)

[Dev Container Features](https://containers.dev/features) for Debian and Ubuntu based
dev containers, published to `ghcr.io/bare-devcontainer/features`. Each Feature installs
a single tool from the upstream project's own distribution channel, verified against
signing material committed to this repository, and requires no other Feature.

## Features

| Feature | What you get | Source and verification |
|---------|--------------|-------------------------|
| [claude-code](src/claude-code)<br>`ghcr.io/bare-devcontainer/features/claude-code` | Claude Code, Anthropic's agentic coding CLI, with the VS Code extension requested alongside it. Credentials, settings and history live on a volume, so a rebuild does not mean signing in again. | Anthropic's APT repository. The package signature is checked with a key committed to this repository. |
| [node](src/node)<br>`ghcr.io/bare-devcontainer/features/node` | Node.js, with Corepack in place of npm, so the package manager comes from the project's `packageManager` field rather than from the image. | [nodejs.org](https://nodejs.org/dist/). The release checksums are checked against the Node.js release keys committed to this repository. |

Each Feature's README documents its options, requirements, what it deliberately leaves
out, and how its supply chain works.

## Why these features

A dev container is part of the trusted development environment, and every Feature added
to one installs software from another upstream. These Features are built so that adding
one stays reviewable, and so that a container can be rebuilt often — which is how
security updates arrive — without that becoming expensive:

- **Minimal trusted upstreams** — software comes from the upstream project's own
distribution channel and nowhere else, verified with the method that upstream
recommends.
- **Trusted material reviewed in this repository** — signing keys are committed here and
changed only through a pull request, so nothing is fetched and trusted while the
container is built. See [Supply chain](#supply-chain).
- **Self-contained installs** — no other Feature is required, only the prerequisites a
base image turns out to be missing are installed, and nothing from the install is left
behind in the image.
- **Minimal images stay minimal** — every Feature is installed and exercised on a
minimal Debian image as well as on Microsoft's official base image on each change, so
a Feature neither depends on tooling a larger image happens to carry nor drags such
tooling in. See [Testing](#testing).
- **Fast, frequent rebuilds** — state a rebuild would otherwise discard, such as a
tool's credentials and history, is kept in a named volume declared by the Feature, so
rebuilding to pick up an update costs nothing but the build.

## Quick start

Add a Feature to `.devcontainer/devcontainer.json`, then reopen the project in a
container:

```json
{
"image": "mcr.microsoft.com/devcontainers/base:debian",
"features": {
"ghcr.io/bare-devcontainer/features/node:1": {}
}
}
```

Any [tool that supports dev containers](https://containers.dev/supporting) can install
them, onto any Debian or Ubuntu based image on `linux/amd64` or `linux/arm64`.
Requirements beyond that, such as glibc for a tool distributed as a prebuilt binary, are
stated in the Feature's own README.

Options are passed as the value of the reference. Each Feature's README lists the
options it accepts and their defaults:

```json
"features": {
"ghcr.io/bare-devcontainer/features/node:1": {
"version": "24",
"keepNpm": true
}
}
```

## Versions and pinning

Every release publishes four tags. Using version 1.1.1 as an example:

| Tag | Points at | Moves when |
|-----|-----------|------------|
| `1.1.1` | That exact release | Never |
| `1.1` | The newest 1.1.x release | A patch release is published |
| `1` | The newest 1.x release | A minor or patch release is published |
| `latest` | The newest release | Any release |

A reference is resolved when the container is built, so any tag shorter than the exact
version resolves to different content over time. That is how `:1` picks up fixes
automatically, and also why it is not a reproducible reference on its own — commit
`.devcontainer/devcontainer-lock.json` to record the digest it resolved to.

## Supply chain

**What you are trusting.** The upstream distribution channel listed for the Feature in
the table above, and the contents of this repository at the version you pin. Nothing
else takes part in the install: the signing material is read from the Feature itself, so
no key is fetched while the container is built, and no other Feature's install script
runs on its behalf.

**What you can check before pinning.** Every input is a file in this repository: one
install script per Feature, and the signing material sitting next to it. Each Feature's
README shows how to inspect its key.

**How the trusted material changes.** Only through a pull request. The
`Update Trusted Material` workflow
([`.github/workflows/update-material.yml`](.github/workflows/update-material.yml)) checks
upstream weekly and opens one when a key has changed, so every rotation is visible in
the history of this repository rather than picked up silently at build time.

**What is not covered.** The upstream projects themselves are still trusted: verification
proves a download came from them unaltered, not that what they published is sound. These
Features are published as plain OCI artifacts, with no SLSA provenance attestation and no
SBOM. And a floating tag still resolves to whatever is newest — see
[Versions and pinning](#versions-and-pinning).

## Testing

Every Feature ships a `smoke_test.sh` next to its `devcontainer-feature.json`. On every
change, the `CI` workflow ([`.github/workflows/ci.yml`](.github/workflows/ci.yml))
installs each Feature at its default options on
`mcr.microsoft.com/devcontainers/base:debian`, the official Dev Container base image
from Microsoft, and on `ghcr.io/bare-devcontainer/debian:trixie`, a minimal Debian image
with no `sudo`, no alternative shell and no CLI tooling beyond the basics, then runs
that script inside the running container as the remote user.

The scripts use no test helper library, so each one reads as a plain description of what
a successful install looks like.

## Related repositories

- [bare-devcontainer/images](https://github.com/bare-devcontainer/images) — minimal
Debian based dev container images, one per language stack. When the base image can be
chosen freely, an image that already carries the tool is simpler than adding a
Feature.
- [bare-devcontainer/templates](https://github.com/bare-devcontainer/templates) —
`devcontainer.json` templates for those images, with hardened defaults and cache
volumes. Features can be added to a template's configuration after it is applied.

## Contributing

Bug reports, Feature requests and pull requests are welcome. [AGENTS.md](AGENTS.md)
describes the repository layout and the conventions a change is expected to follow.

Note that `src/*/README.md` is generated by `devcontainer features generate-docs` from
`devcontainer-feature.json` and the Feature's `NOTES.md`. Put prose in `NOTES.md`; edits
made directly to a Feature's `README.md` are overwritten.

## Security

To report a vulnerability, see [SECURITY.md](SECURITY.md).

## License

[MIT](LICENSE)
12 changes: 12 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Security Policy

## Supported Versions

The newest published release of each feature under `src/` is supported. Fixes are
released as a new feature version and picked up by the floating tags (`1`, `1.1`,
`latest`); earlier versions are not patched in place.

## Reporting a Vulnerability

You can report a vulnerability privately via private vulnerability reporting on GitHub.
See [GitHub Docs](https://docs.github.com/en/code-security/how-tos/report-and-fix-vulnerabilities/report-privately) for more information.
85 changes: 63 additions & 22 deletions src/claude-code/NOTES.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,51 @@
## Details
## Requirements

- Requires a Debian or Ubuntu based image.
- The repository signing key is vendored with the feature and installed to
`/usr/share/keyrings/claude-code.asc`, so no key is downloaded at build time.
- The APT source is written to `/etc/apt/sources.list.d/claude-code.list`. Set
`keepAptSource` to `false` to drop the source and key once installation
finishes.
- A Debian or Ubuntu based image: the package is installed with `apt-get`, and the
install script stops early on an image without it.

### VS Code extension
## Usage

The feature also requests the Claude Code VS Code extension, so an editor that
attaches to the container gets the sidebar, inline diffs and the IDE
integration alongside the CLI:
The defaults install the newest package and leave the APT source in place. To drop
the source and its key once the package is installed:

```json
"features": {
"ghcr.io/bare-devcontainer/features/claude-code:1": {
"keepAptSource": false
}
}
```

`version` takes an APT package version rather than a release name, so read the exact
string off `apt-cache policy claude-code` in a container that has the source
configured before pinning to one.

## Installed software

- The `claude` CLI, from Anthropic's APT repository at
`https://downloads.claude.ai/claude-code/apt/stable`. The source is written to
`/etc/apt/sources.list.d/claude-code.list` and the signing key to
`/usr/share/keyrings/claude-code.asc`.
- The Claude Code VS Code extension (`anthropic.claude-code`), installed by clients
that read `customizations.vscode`.

To leave the extension out, list it with a minus sign in `devcontainer.json`:

```json
"customizations": {
"vscode": {
"extensions": [
"anthropic.claude-code"
"-anthropic.claude-code"
]
}
}
```

Extension installation is done by the attaching client, not by the install
script, so this applies to VS Code, Cursor and GitHub Codespaces, and is
ignored by clients that do not consume `customizations.vscode` (the
`devcontainer` CLI, or attaching from another editor). The CLI is installed
either way. Nothing in `devcontainer.json` can remove an extension a feature
asks for, so uninstall it in the container if it is not wanted.

### Persisting configuration and credentials
## Configuration and credentials

Claude Code keeps its credentials, settings and history in its configuration
directory. The feature declares a volume mount so those survive container
rebuilds, and points Claude Code at it:
directory. The feature declares a volume mount so those survive container rebuilds,
and points Claude Code at it:

```json
"containerEnv": {
Expand Down Expand Up @@ -83,3 +94,33 @@ The volume is per dev container (`${devcontainerId}`) and is not shared between
projects. Mounts declared by a feature cannot be disabled from
`devcontainer.json`, but overriding `CLAUDE_CONFIG_DIR` as shown above leaves the
volume mounted and unused.

## Supply chain

The package is installed by APT from Anthropic's official repository, and its
signature is checked against the repository signing key. That key is vendored with
the feature and installed from the feature directory to
`/usr/share/keyrings/claude-code.asc`, so nothing is trusted that was not reviewed
in this repository — no key is downloaded at build time. It is refreshed by this
repository's `Update Trusted Material` workflow, which opens a pull request when
upstream publishes a different key.

To see the key before pinning the feature:

```sh
gpg --show-keys src/claude-code/claude-code.asc
```

`keepAptSource` decides what is left behind. Keeping the source and key, the
default, means `apt-get upgrade` inside the container can pick up newer releases.
Setting it to `false` removes both once the install finishes, so the image carries
no additional APT source. Either way the apt lists downloaded during installation
are deleted.

## Tips

- For the tags this feature is published under, see
[Versions and pinning](https://github.com/bare-devcontainer/features#versions-and-pinning).
- The configuration volume is per dev container, so authentication is done once per
project. Point `CLAUDE_CONFIG_DIR` at a mount of your own to share credentials
between projects.
Loading
Loading