## 1. Project Overview & Quickstart (devcontainers/spec)

## File: README.md

# Development Containers

This repository is for the Development Container Specification. You can check out the spec on our website https://containers.dev/ too.

A development container allows you to use a container as a full-featured development environment. It can be used to run an application, to separate tools, libraries, or runtimes needed for working with a codebase, and to aid in continuous integration and testing.

The Development Container Specification seeks to find ways to enrich existing formats with common development specific settings, tools, and configuration while still providing a simplified, un-orchestrated single container option – so that they can be used as coding environments or for continuous integration and testing.



The first format in the specification, `devcontainer.json`, was born out of necessity. It is a structured JSON with Comments (jsonc) metadata format that tools can use to store any needed configuration required to develop inside of local or cloud-based containerized coding. 

We envision that this same structured data can be embedded in images and other formats – all while retaining a common object model for consistent processing. For example, some of this same metadata can be added to a `devcontainer.metadata` image label to tie settings directly to a container image.

Beyond repeatable setup, these same development containers provide consistency to avoid environment specific problems across developers and centralized build and test automation services. You can use the [open-source CLI reference implementation](https://github.com/devcontainers/cli) either directly or integrated into product experiences to use the structured metadata to deliver these benefits. It currently supports integrating with Docker Compose and a simplified, un-orchestrated single container option – so that they can be used as coding environments or for continuous integration and testing.

A GitHub Action and an Azure DevOps Task are available in [devcontainers/ci](https://github.com/devcontainers/ci) for running a repository's dev container in continuous integration (CI) builds. This allows you to reuse the same setup that you are using for local development to also build and test your code in CI.

### Spec content

You may review the specification in the [docs/specs folder](https://github.com/devcontainers/spec/tree/main/docs/specs) of this repo.

You may also review active proposals in the [proposals folder](https://github.com/devcontainers/spec/tree/main/proposals).

Images used in this repo will be contained in the [images folder](/images). The icon for the [dev container GitHub org](https://github.com/devcontainers) is from the [Fluent icon library](https://github.com/microsoft/fluentui-system-icons/blob/master/assets/Cube/SVG/ic_fluent_cube_32_filled.svg).

## Contributing and Feedback

If you are interested in contributing, please check out the [How to Contribute](contributing.md) document, open an issue, or [join our community Slack channel](https://aka.ms/dev-container-community).

Please report issues in the following repositories:

- Spec-maintained Features and Templates: [devcontainers/features](https://github.com/devcontainers/features), [devcontainers/templates](https://github.com/devcontainers/templates)
- CLI reference implementation and non-spec related feature requests: [devcontainers/cli](https://github.com/devcontainers/cli)
- GitHub Action and Azure DevOps Task: [devcontainers/ci](https://github.com/devcontainers/ci)

# License

License for this repository:

Copyright © Microsoft Corporation All rights reserved.
Creative Commons Attribution 4.0 License (International): https://creativecommons.org/licenses/by/4.0/legalcode

---

## File: docs/specs/declarative-secrets.md

# Declarative Secrets

This has now been implemented:
* Discussion issue: https://github.com/devcontainers/spec/issues/198, https://github.com/devcontainers/spec/issues/216
* Schema PR: https://github.com/devcontainers/spec/pull/303

Below is the original proposal.

## Motivation

Various projects exist in the wild that require various secrets for them to run properly. Examples include:

- https://github.com/lostintangent/codespaces-langchain
- https://github.com/openai/openai-quickstart-python
- https://github.com/openai/openai-quickstart-node

Today these projects have to include specific instructions (e.g. in their README) telling users where to procure what they need to run and then how to set it up as a secret or add it as an environment variable. This currently acts as an impediment to adoption and promotion of dev containers for these projects.

## Goal

Simplify using dev containers for these kinds of projects by supporting secrets as a first-class part of the dev container creation flow.

## Proposal

Add an optional `secrets` property to the `devContainer.base.schema.json`. This will be used to declare the secrets needed within the dev container.

Property | Type | Description
--- | --- | ---
`secrets` | `object` | The keys of this object are the names of the secrets that are in use. Keys should be [valid Linux environment variable names](https://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html).

The `secrets` property contains a map of secret names and details about those secrets. These `secrets` are recommended to the user but are not _required_ for creation. All properties are optional.

Property | Type | Description
--- | --- | ---
`description` | `string` | A brief description of the secret.
`documentationUrl` | `string` | A URL pointing to where the user can obtain more information about the secret. For example where to find docs on provisioning an API key for a service.

## Example

```json
{
    "image": "mcr.microsoft.com/devcontainers/base:bullseye",
    "secrets": {
        "CHAT_GPT_API_KEY": {
            "description": "I'm your super cool API key for ChatGPT.",
            "documentationUrl": "https://openai.com/api/"
        },
        "STABLE_DIFFUSION_API_KEY": {}
    }
}
```

This example would declare that this dev container wants the user to provide two secrets. The `CHAT_GPT_API_KEY` secret also provides some additional metadata that clients can use in displaying a user experience to provide that secret. The `STABLE_DIFFUSION_API_KEY` skips that metadata.

Implementations _may_ inject these secrets into the container as environment variables.

## Possible Future Extensions

These are **out of scope** for this proposal but the following has been discussed in relation:

1. Inclusion of a `type` field per secret. The default, which we are assuming and leveraging here, would be `"environmentVariable"`. This would include an option to specify `"reference"` and then reference the provided secrets in other parts of the dev container.
2. Prescriptions of _how_ the secrets are injected into the dev container.

---

## File: docs/specs/devcontainer-features.md

# Dev Container Features reference

**Development Container Features** are self-contained, shareable units of installation code and development container configuration. The name comes from the idea that referencing one of them allows you to quickly and easily add more tooling, runtime, or library "Features" into your development container for you or your collaborators to use.

> **Note:** While Features may be installed on top of any base image, the implementation of a Feature might restrict it to a subset of possible base images.  
> 
> For example, some Features may be authored to work with a certain linux distro (e.g. debian-based images that use the `apt` package manager).

Feature metadata is captured by a `devcontainer-feature.json` file in the root folder of the Feature.

> **Tip:** This section covers details on the Features specification. If you are looking for summarized information on creating your own Features, see the [template](https://github.com/devcontainers/feature-template) and [core Features](https://github.com/devcontainers/features) repositories.

## Folder structure 

A Feature is a self contained entity in a folder with at least a `devcontainer-feature.json` and `install.sh` entrypoint script. Additional files are permitted and are packaged along side the required files.

```
+-- feature
|    +-- devcontainer-feature.json
|    +-- install.sh
|    +-- (other files)
```

## `devcontainer-feature.json` properties

The `devcontainer-feature.json` file defines metadata about a given Feature.

All properties are optional **except for `id`, `version`, and `name`**. 

| Property | Type | Description |
| :--- | :--- | :--- |
| `id` | string | **Required**: Identifier of the Feature.  Must be unique in the context of the repository where the Feature exists and must match the name of the directory where the `devcontainer-feature.json` resides. ID should be provided lowercase. |
| `version` | string | **Required**: The semantic version of the Feature (e.g. `1.0.0`). |
| `name` | string | **Required**: A "human-friendly" display name for the Feature. |
| `description` | string | Description of the Feature. |
| `documentationURL` | string | Url that points to the documentation of the Feature. |
| `licenseURL` | string | Url that points to the license of the Feature. |
| `keywords` | array | List of strings relevant to a user that would search for this Feature. |
| `options` | object | A map of options that will be passed as environment variables to the execution of the script. |
| `containerEnv` | object | A set of name value pairs that sets or overrides environment variables. |
| `privileged` | boolean | Sets [privileged mode](https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities) for the container (required by things like docker-in-docker) when the Feature is used. |
| `init` | boolean | Adds the [tini init](https://github.com/krallin/tini) process to the container (`--init`) when the Feature is used. |
| `capAdd` | array | Adds container [capabilities](https://docs.docker.com/engine/security/#linux-kernel-capabilities) when the Feature is used. |
| `securityOpt` | array | Sets container security options like updating the [seccomp profile](https://docs.docker.com/engine/security/seccomp/) when the Feature is used. |
| `entrypoint` | string | Set if the Feature requires an "entrypoint" script that should fire at container start up. |
| `customizations` | object | Product specific properties, each namespace under `customizations` is treated as a separate set of properties. For each of this sets the object is parsed, values are replaced while arrays are set as a union. |
| `dependsOn` | `object` | An object (\**)  of Feature dependencies that **must** be satisified before this Feature is installed. Elements follow the same semantics of the `features` object in `devcontainer.json`. [See *Installation Order* for further information](#installation-order).  |
| `installsAfter` | array | Array of ID's of Features (omitting a version tag) that should execute before this one. Allows control for Feature authors on soft dependencies between different Features. [See *Installation Order* for further information](#installation-order). |
| `legacyIds` | array | Array of old IDs used to publish this Feature. The property is useful for renaming a currently published Feature within a single namespace. |
| `deprecated` | boolean | Indicates that the Feature is deprecated, and will not receive any further updates/support. This property is intended to be used by the supporting tools for highlighting Feature deprecation. |
| `mounts` | object | Defaults to unset. Cross-orchestrator way to add additional mounts to a container. Each value is an object that accepts the same values as the [Docker CLI `--mount` flag](https://docs.docker.com/engine/reference/commandline/run/#mount). The Pre-defined [devcontainerId](./devcontainerjson-reference.md/#variables-in-devcontainerjson) variable may be referenced in the value. For example:`"mounts": [{ "source": "dind-var-lib-docker", "target": "/var/lib/docker", "type": "volume" }]` |

(**) The ID must refer to either a Feature (1) published to an OCI registry, (2) a Feature Tgz URI, or (3) a Feature in the local file tree. Deprecated Feature identifiers (i.e. GitHub Release) are not supported and the presence of this property may be considered a fatal error or ignored. For [local Features (i.e. during development)](https://containers.dev/implementors/features-distribution/#addendum-locally-referenced), you may also depend on other local Features by providing a relative path to the Feature, relative to folder containing the active `devcontainer.json`. This behavior of Features within this property again mirror the `features` object in `devcontainer.json`.

### Lifecycle Hooks

The following lifecycle hooks may be declared as properties of `devcontainer-feature.json`. 

| Property | Type|
| :--- | :--- |
| `onCreateCommand` | [string, array, object](/docs/specs/devcontainerjson-reference.md#formatting-string-vs-array-properties)|
| `updateContentCommand` | [string, array, object](/docs/specs/devcontainerjson-reference.md#formatting-string-vs-array-properties)|
| `postCreateCommand` | [string, array, object](/docs/specs/devcontainerjson-reference.md#formatting-string-vs-array-properties)|
| `postStartCommand` | [string, array, object](/docs/specs/devcontainerjson-reference.md#formatting-string-vs-array-properties) |
| `postAttachCommand` | [string, array, object](/docs/specs/devcontainerjson-reference.md#formatting-string-vs-array-properties) |

#### Behavior

Each property mirrors the behavior of the matching property in [`devcontainer.json`](/docs/specs/devcontainerjson-reference.md#Lifecycle-scripts), including the behavior that commands are executed from the context of the [project workspace folder](https://containers.dev/implementors/spec/#project-workspace-folder).

For each lifecycle hook (in [Feature installation order](https://containers.dev/implementors/features/#installation-order)), each command contributed by a Feature is executed in sequence (blocking the next command from executing).   Commands provided by Features are always executed _before_ any user-provided lifecycle commands (i.e. in the `devcontainer.json`).

If a Feature provides a given command with the [object syntax](/docs/specs/devcontainerjson-reference.md#formatting-string-vs-array-properties), all commands within that group are executed in parallel, but still blocking commands from subsequent Features and/or the `devcontainer.json`.

> **Note:** These properties are stored within [image metadata](https://github.com/devcontainers/spec/blob/joshspicer/lifecycle_hook_feature_spec/docs/specs/devcontainer-reference.md#merge-logic).

### The `options` property

The options property is contains a map of option IDs and their related configuration settings. The ID becomes the name of the environment variable in all caps. See [option resolution](#option-resolution) for more details. For example:

```json
{
  "options": {
    "optionIdGoesHere": {
      "type": "string",
      "description": "Description of the option",
      "proposals": ["value1", "value2"],
      "default": "value1"
    }
  }
}
```

| Property | Type | Description |
| :--- | :--- | :--- |
| `optionId` | string | ID of the option that is converted into an all-caps environment variable with the selected value in it. |
| `optionId.type` | string | Type of the option. Valid types are currently: `boolean`, `string` |
| `optionId.proposals` | array | A list of suggested string values. Free-form values **are** allowed. Omit when using `optionId.enum`. |
| `optionId.enum` | array | A strict list of allowed string values. Free-form values are **not** allowed. Omit when using `optionId.proposals`. |
| `optionId.default` | string or boolean | Default value for the option. |
| `optionId.description` | string | Description for the option. |

### User environment variables

Feature scripts run as the `root` user and sometimes need to know which user account the dev container will be used with.

`_REMOTE_USER` and `_CONTAINER_USER` environment variables are passed to the Features scripts with `_CONTAINER_USER` being the container's user and `_REMOTE_USER` being the configured `remoteUser`. If no `remoteUser` is configured, `_REMOTE_USER` is set to the same value as `_CONTAINER_USER`.

Additionally, the home folders of the two users are passed to the Feature scripts as `_REMOTE_USER_HOME` and `_CONTAINER_USER_HOME` environment variables.

The container user can be set with `containerUser` in the devcontainer.json and image metadata, `user` in the docker-compose.yml, `USER` in the Dockerfile, and can be passed down from the base image.

### Dev Container ID

An identifier will be referred to as `${devcontainerId}` in the `devcontainer.json` and the Feature metadata and that will be replaced with the dev container's id. It should only be used in parts of the configuration and metadata that is not used for building the image because that would otherwise prevent pre-building the image at a time when the dev container's id is not known yet. Excluding boolean, numbers and enum properties the properties supporting `${devcontainerId}` in the Feature metadata are: `entrypoint`, `mounts`, `customizations`.

Implementations can choose how to compute this identifier. They must ensure that it is unique among other dev containers on the same Docker host and that it is stable across rebuilds of dev containers. The identifier must only contain alphanumeric characters. We describe a way to do this below.

#### Label-based Implementation 

The following assumes that a dev container can be identified among other dev containers on the same Docker host by a set of labels on the container. Implementations may choose to follow this approach.

The identifier is derived from the set of container labels uniquely identifying the dev container. It is up to the implementation to choose these labels. For example, if the dev container is based on a local folder, the label could be named `devcontainer.local_folder` and have the local folder's path as its value.

For example, the [`ghcr.io/devcontainers/features/docker-in-docker` Feature](https://github.com/devcontainers/features/blob/main/src/docker-in-docker/devcontainer-feature.json) could use the dev container id with:

```jsonc
{
    "id": "docker-in-docker",
    "version": "1.0.4",
    // ...
    "mounts": [
        {
            "source": "dind-var-lib-docker-${devcontainerId}",
            "target": "/var/lib/docker",
            "type": "volume"
        }
    ]
}
```

#### Label-based Computation

- Input the labels as a JSON object with the object's keys being the label names and the object's values being the labels' values.
	- To ensure implementations get to the same result, the object keys must be sorted and any optional whitespace outside of the keys and values must be removed.
- Compute a SHA-256 hash from the UTF-8 encoded input string.
- Use a base-32 encoded representation left-padded with '0' to 52 characters as the result.

JavaScript implementation taking an object with the labels as argument and returning a string as the result:

```js
const crypto = require('crypto');
function uniqueIdForLabels(idLabels) {
	const stringInput = JSON.stringify(idLabels, Object.keys(idLabels).sort()); // sort properties
	const bufferInput = Buffer.from(stringInput, 'utf-8');
	const hash = crypto.createHash('sha256')
		.update(bufferInput)
		.digest();
	const uniqueId = BigInt(`0x${hash.toString('hex')}`)
		.toString(32)
		.padStart(52, '0');
	return uniqueId;
}
```

## devcontainer.json properties

Features are referenced in a user's [`devcontainer.json`](/docs/specs/devcontainer-reference.md#devcontainerjson) under the top level `features` object. 

A user can specify an arbitrary number of Features. At build time, these Features will be installed in an order defined by a combination of the [installation order rules and implementation](#Installation-Order). 

A single Feature is provided as a key/value pair, where the key is the Feature identifier, and the value is an object containing "options" (or empty for "default"). Each key in the Feature object must be unique.

These options are sourced as environment variables at build-time, as specified in [Option Resolution](#Option-Resolution).

Below is a valid `features` object provided as an example.
```jsonc
"features": {
  "ghcr.io/user/repo/go": {},
  "ghcr.io/user/repo1/go:1": {},
  "ghcr.io/user/repo2/go:latest": {},
  "https://github.com/user/repo/releases/devcontainer-feature-go.tgz": { 
        "optionA": "value" 
  },
  "./myGoFeature": { 
        "optionA": true,
        "optionB": "hello",
        "version" : "1.0.0"
  }
}
```

> **Note:** The `:latest` version annotation is added implicitly if omitted. To pin to a specific package version ([example](https://github.com/devcontainers/features/pkgs/container/features/go/versions)), append it to the end of the Feature.

An option's value can be provided as either a `string` or `boolean`, and should match what is expected by the Feature in the `devcontainer-feature.json` file.

As a shorthand, the value of a `feature` can be provided as a single string. This string is mapped to an option called `version`.  In the example below, both examples are equivalent. 

```jsonc
"features": {
  "ghcr.io/owner/repo/go": "1.18"
}
```
```jsonc
"features": {
  "ghcr.io/owner/repo/go": {
    "version": "1.18"
  }
}
```

### Referencing a feature 

The `id` format specified dictates how a supporting tool will locate and download a given Feature. `id` is one of the following:

| Type | Description | Example |
| :--- | :--- | :--- |
| `<oci-registry>/<namespace>/<feature>[:<semantic-version>]` | Reference to feature in OCI registry(*) | `ghcr.io/user/repo/go`  `ghcr.io/user/repo/go:1`  `ghcr.io/user/repo/go:latest`|
| `https://` | Direct HTTPS URI to a tarball. | `https://github.com/user/repo/releases/devcontainer-feature-go.tgz` |
| `./`| A relative directory(**) to folder containing a devcontainer-feature.json. | `./myGoFeature` |

Feature identifiers should be assumed to be case-insensitive and should be normalized to lowercase.

(*) OCI registry must implement the [OCI Artifact Distribution Specification](https://github.com/opencontainers/distribution-spec).  Some implementors can be [found here](https://oras.land/implementors/).

(**) The provided path is always relative to the folder containing the `devcontainer.json`.  Further requirements are outlined in the [Locally Referenced Addendum](./devcontainer-features-distribution.md#locally-referenced-features)

## Versioning

Each Feature is individually [versioned according to the semver specification](https://semver.org/).  The `version` property in the respective `devcontainer-feature.json` file is updated to increment the Feature's version.

Tooling that handles releasing Features will not republish Features if that exact version has already been published; however, tooling must republish major and minor versions in accordance with the semver specification.

## Authoring

Features can be authored in a number of languages, the most straightforward being bash scripts. If a Feature is authored in a different language information about it should be included in the metadata so that users can make an informed choice about it.

Reference information about the application required to execute the Feature should be included in `devcontainer-feature.json` in the metadata section.

Applications should default to `/bin/sh` for Features that do not include this information.

If the Feature is included in a folder as part of the repository that contains `devcontainer.json`, no other steps are necessary.

## Release

_For information on distributing Features, see [devcontainer-features-distribution.md](./devcontainer-features-distribution.md)._

## Execution

### Invoking `install.sh`

The `install.sh` script for each Feature should be executed as `root` during a container image build. This allows the script to add needed OS dependencies or settings that could not otherwise be modified. This also allows the script to switch into another user's context using the `su` command (e.g. `su ${USERNAME} -c "command-goes-here"`). In combination, this allows both root and non-root image modifications to occur even if `sudo` is not present in the base image for security reasons.

To ensure that the appropriate shell is used, the execute bit should be set on `install.sh` and the file invoked directly (e.g. `chmod +x install.sh && ./install.sh`). 

> **Note:** It is recommended that Feature authors write `install.sh` using a shell available by default in their supported distributions (e.g. `bash` in Debian/Ubuntu or Fedora, `sh` in Alpine). In the event a different shell is required (e.g. `fish`), `install.sh` can be used to boostrap by checking for the presence of the desired shell, installing it if needed, and then invoking a secondary script using the shell.
>
> The `install.sh` file can similarly be used to bootstrap something written in a compiled language like Go. Given the increasing likelihood that a Feature needs to work on both x86_64 and arm64-based devices (e.g. Apple Silicon Macs), `install.sh` can detect the current architecture (e.g. using something like `uname -m` or `dpkg --print-architecture`), and then invoke the right executable for that architecture.

### Installation order

By default, Features are installed on top of a base image in an order determined as optimal by the implementing tool.

If any of the following properties are provided in the Feature's `devcontainer-feature.json`, or the user's `devcontainer.json`, the order indicated by these propert(ies) are respected.

* The `dependsOn` property defined as a part of a Feature's `devcontainer-feature.json`.
* The `installsAfter` property defined as part of a Feature's `devcontainer-feature.json`.
* The `overrideFeatureInstallOrder` property in user's `devcontainer.json`. Allows users to control the order of execution of their Features.

#### The `dependsOn` property

The optional `dependsOn` property indicates a set of required, "hard" dependencies for a given Feature.  

The `dependsOn` property is declared in a Feature's `devcontainer-feature.json` metadata file. Elements of this property mirror the semantics of the `features` object in `devcontainer.json`.  Therefore, all dependencies may provide the relevant options, or an empty object (e.g. `"bar:123": {}`) if the Feature's default options are sufficient.  Identical Features that provide different options are treated as _different_ Features (see [Feature equality](#definition-feature-equality) for more info).

All Features indicated in the `dependsOn` property **must** be satisfied (a Feature [equal](#definition-feature-equality) to each dependency is present in the installation order) _before_ the given Feature is set to be installed.  If any of the Features indicated in the `dependsOn` property cannot be installed (e.g. due to circular dependency, failure to resolve the Feature, etc) the entire dev container creation should fail.

The `dependsOn` property must be evaluated recursively.  Therefore, if a Feature dependency has its own `dependsOn` property, that Feature's dependencies must also be satisfied before the given Feature is installed.

```json
{
    "name": "My Feature",
    "id": "myFeature",
    "version": "1.0.0",
    "dependsOn": {
        "foo:1": {
            "flag": true
        },
        "bar:1.2.3": {},
        "baz@sha256:a4cdc44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" {},
    }
}
```

In the snippet above, `myfeature` MUST be installed after `foo`, `bar`, and `baz`.  If the Features provided via the `dependsOn` property declare their own dependencies, those must also be satisfied before the Feature is installed.

#### The `installsAfter` Property

The `installsAfter` property indicates a "soft dependency" that influences the installation order of Features that are already queued to be installed. The effective behavior of this property is the same as `dependsOn`, with the following differences:

- `installsAfter` is **not** evaluated recursively.
- `installsAfter` only influences the installation order of Features that are **already set to be installed**.  Any Feature not set to be installed after (1) resolving the `dependsOn` dependency tree or (2) indicated by the user's `devcontainer.json` should not be added to the installation list.
- The Feature indicated by `installsAfter` can **not** provide options, nor are they able to be pinned to a specific version tag or digest.  Resolution to the canonical name should still be performed (e.g. if the Feature has been [renamed](#steps-to-rename-a-feature)).

```
{
    "name": "My Feature",
    "id": "myFeature",
    "version": "1.0.0",
    "installsAfter": [
        "foo",
        "bar"
    ]
}
```

In the snippet above, `myfeature` must be installed after `foo` and `bar` **if** the Feature is already queued to be installed. If `foo` and `bar` are not already queued to be installed, this dependency relationship should be ignored.

#### The 'overrideFeatureInstallOrder' property

The `overrideFeatureInstallOrder` property of `devcontainer.json` is an array of Feature IDs that are to be installed in descending priority order as soon as its dependencies outlined above are installed.

> This property may not indicate an installation order that is inconsistent with the resolved dependency graph (see [dependency algorithm](#dependency-installation-order-algorithm)).  If the `overrideFeatureInstallOrder` property is inconsistent with the dependency graph, the implementing tool should fail the dependency resolution step.

This evaluation is performed by assigning a [`roundPriority`](#2-assigning-round-priority) to all nodes that match the Feature identifier (version omitted) present in the property. 

For example, given `n` Features in the `overrideFeatureInstallOrder` array, the orchestrating tool should assign a `roundPriority` of `n - idx` to each Feature, where `idx` is the zero-based index of the Feature in the array.

For example:

```javascript
overrideFeatureInstallOrder = [
  "foo",
  "bar",
  "baz"
]
```

would result in the following `roundPriority` assignments:

```javascript
const roundPriority = {
  "foo": 3,
  "bar": 2,
  "baz": 1
}
```

This property must not influence the dependency relationship as defined by the dependency graph (see [dependency graph](#1-build-a-dependency-graph)) and shall only be evaulated at the round-based sorting step (see [round sort](#3-round-based-sorting)).  Put another way, this property cannot "pull forward" a Feature until all of its dependencies (both soft and hard) have been installed. After a Feature's dependencies have been installed in other rounds, this property should "pull forward" each Feature as early as possible (given the order of identifiers in the array).

Similar to `installsAfter`, this property's members may not provide options, nor are they able to be pinned to a specific version tag or digest.

If a Feature is indicated in `overrideFeatureInstallOrder` but not a member of the dependency graph (i.e. it is not queued to be installed), the orchestrating tool may fail the dependency resolution step.

> ## Definitions
> ### Definition: Feature Equality
>
> This specification defines two Features as equal if both Features point to the same exact contents and are executed with the same options.
> 
> **For Features published to an OCI registry**, two Feature are identical if their manifest digests are equal, and the options executed against the Feature are equal (compared value by value).  Identical manifest digests implies that the tgz  contents of the Feature and its entire `devcontainer-feature.json` are identical.  If any of these conditions are not met,  the Features are considered not equal.
> 
> **For Features fetched by HTTPS URI**, two Features are identical if the contents of the tgz are identical (hash to the same value), and the options executed against the Feature are equal (compared value by value).  If any of these conditions  are not met, the Features are considered not equal.
> 
> **For local Features**, each Feature is considered unique and not equal to any other local Feature.
> 
> ### Definition: Round Stable Sort
> 
> To prevent non-deterministic behavior, the algorithm will sort each **round** according to the following rules:
> 
> - Compare and sort each Feature lexiographically by their fully qualified resource name (For OCI-published Features, that  means the ID without version or digest.).  If the comparison is equal:
> - Compare and sort each Feature from oldest to newest tag (`latest` being the "most new").  If the comparision is equal:
> - Compare and sort each Feature by their options by:
>   - Greatest number of user-defined options (note omitting an option will default that value to the Feature's default value  and is not considered a user-defined option). If the comparison is equal:
>   - Sort the provided option keys lexicographically.  If the comparison is equal:
>   - Sort the provided option values lexicographically. If the comparision is equal:
> - Sort Features by their canonical name (For OCI-published Features, the Feature ID resolved to the digest hash).
> 
> If there is no difference based on these comparator rules, the Features are considered equal.
>
> 

> ## Dependency installation order algorithm
>
> An implementing tool is responsible for calculating the Feature installation order (or providing an error if no valid installation order can be resolved). The set of Features to be installed is the union of user-defined Features (those directly indicated in the user's `devcontainer.json` and their dependencies (those indicated by the `dependsOn` or `installsAfter` property, taking into account the user dev container's `overrideFeatureInstallOrder` property).  The implmenting tool will perform the following steps:
> ### (1) Build a dependency graph
> 
> From the user-defined Features, the orchestrating tool will build a dependency graph.  The graph will be built by traversing the `dependsOn` and `installsAfter` properties of each Feature.  The metadata for each dependency is then fetched and the node added as an edge to to the dependent Feature.  For `dependsOn`  dependencies, the dependency will be fed back into the worklist to be recursively resolved. 
> 
> An accumulator is maintained with all uniquely discovered and user-provided Features, each with a reference to its dependencies.  If the exact Feature (see **Feature Equality**) has already been added to the accumulator, it will not be added again.  The accumulator will be fed into (B3) after the Feature tree has  been resolved.
> 
> The graph may be stored as an adjacency list with two kinds of edges (1) `dependsOn` edges or "hard dependencies" and (2) `installsAfter` edges or "soft dependencies".
> 
> ### (2) Assigning round priority
> 
> Each node in the graph has an implicit, default `roundPriority` of 0.
> 
> To influence installation order globally while still honoring the dependency graph of built in **(1)**, `roundPriority` values may be tweaks for each Feature.  When each round is calculated in **(3)**, only the Features equal to the max `roundPriority` of that set will be committed (the remaining will be uncommitted and reevaulated in subsequent rounds).
> 
> The `roundPriority` is set to a non-zero value in the following instances:
> 
> - If the [`devcontainer.json` contains an `overrideFeatureInstallOrder`](#the-overridefeatureinstallorder-property).
> 
> ### (3) Round-based sorting
> 
> Perform a sort on the result of **(1)** in rounds. This sort will rearrange Features, producing a sorted list of Features to install.  The sort will be performed as follows: 
> 
> Start with all the elements from **(2)** in a `worklist` and an empty list `installationOrder`.  While the `worklist` is not empty, iterate through each element in the `worklist` and check if all its dependencies (if any) are already members of `installationOrder`.  If the check is true, add it to an intermediate  list `round` If not, skip it.  Equality is determined in **Feature Equality**.
> 
> Then for each intermediate `round` list, commit to `installationOrder` only those nodes who share the maximum `roundPriority`.  Return all nodes in `round` with a strictly lower `roundPriority` to the `worklist` to be reprocessed in subsequent iterations.  If there are multiple nodes with the same `roundPriority`,  commit them to `installationOrder` with a final sort according to **Round Stable Sort**.
> 
> Repeat for as many rounds as necessary until `worklist` is empty.  If there is ever a round where no elements are added to `installationOrder`, the algorithm should terminate and return an error.  This indicates a circular dependency or other fatal error in the dependency graph.  Implementations should attempt to  provide the user with information about the error and possible mitigation strategies.
>
> ### Notes
>
> From an implementation point of view, `installsAfter` nodes may be added as a separate set of directed edges, just as `dependsOn` nodes are added as directed edges (see **(1)**).  Before round-based installation and sorting **(3)**, an orchestrating tool should remove all `installsAfter` directed edges that do not correspond with a Feature in the `worklist` that is set to be installed.  In each round, a Feature can then be installed if all its requirements (both `dependsOn` and `installsAfter` dependencies) have been fulfilled in previous rounds.
> 
> An implemention should fail the dependency resolution step if the evaluation of the `installsAfter` property results in an inconsistent state (e.g. a circular dependency).
>


### Option Resolution

A Feature's 'options' - specified as the value of a single Feature key/value pair in the user's `devcontainer.json` - are passed to the Feature as environment variables.

A supporting tool will parse the `options` object provided by the user.  If a value is provided for a Feature, it will be emitted to a file named `devcontainer-features.env` following the format `<OPTION_NAME>=<value>`.  

To ensure a option that is valid as an environment variable, the follow substitutions are performed.

```javascript
(str: string) => str
	.replace(/[^\w_]/g, '_')
	.replace(/^[\d_]+/g, '_')
	.toUpperCase();
```

This file is sourced at build-time for the Feature `install.sh` entrypoint script to handle.

Any options defined by a Feature's `devcontainer-feature.json` that are omitted in the user's `devcontainer.json` will be implicitly exported as its default value.

### Option resolution example

Suppose a `python` Feature has the following `options` parameters declared in the `devcontainer-feature.json` file:

```jsonc
// ...
"options": {
    "version": {
        "type": "string",
        "enum": ["latest", "3.10", "3.9", "3.8", "3.7", "3.6"],
        "default": "latest",
        "description": "Select a Python version to install."
    },
    "pip": {
        "type": "boolean",
        "default": true,
        "description": "Installs pip"
    },
    "optimize": {
        "type": "boolean",
        "default": true,
        "description": "Optimize python installation"
    }
}
```

The user's `devcontainer.json` declared the python Feature like so

```jsonc

"features": {
    "ghcr.io/devcontainers/features/python:1": {
        "version": "3.10",
        "pip": false
    }
}
```
The emitted environment variables will be:

```env
VERSION="3.10"
PIP="false"
OPTIMIZE="true"
```

These will be sourced and visible to the `install.sh` entrypoint script.  The following `install.sh`...


```bash
#!/usr/bin/env bash

echo "Version is $VERSION"
echo "Pip? $PIP"
echo "Optimize? $OPTIMIZE"
```

... outputs the following:
```
Version is 3.10
Pip? false
Optimize? true
```

### Steps to rename a Feature

1. Update the Feature [source code](./devcontainer-features-distribution.md#source-code) folder and the `id` property in the [devcontainer-feature.json properties](#devcontainer-featurejson-properties) to reflect the new `id`. Other properties (`name`, `documentationUrl`, etc.) can optionally be updated during this step.
2. Add or update the `legacyIds` property to the Feature, including the previously used `id`.
3. Bump the semantic version of the Feature.  
4. Rerun the `devcontainer features publish` command, or equivalent tool that implements the [Features distribution specification](./features-distribution/#distribution).

#### Example: Renaming a Feature

Let's say we currently have a `docker-from-docker` Feature 👇 

Current `devcontainer-feature.json` : 

```jsonc
{
    "id": "docker-from-docker",
    "version": "2.0.1",
    "name": "Docker (Docker-from-Docker)",
    "documentationURL": "https://github.com/devcontainers/features/tree/main/src/docker-from-docker",
    ....
}
```

We'd want to rename this Feature to `docker-outside-of-docker`. The source code folder of the Feature will be updated to `docker-outside-of-docker` and the updated `devcontainer-feature.json` will look like 👇 

```jsonc
{
    "id": "docker-outside-of-docker",
    "version": "2.0.2",
    "name": "Docker (Docker-outside-of-Docker)",
    "documentationURL": "https://github.com/devcontainers/features/tree/main/src/docker-outside-of-docker",
    "legacyIds": [
        "docker-from-docker"
    ]
    ....
}
```

**Note** - The semantic version of the Feature defined by the `version` property should be **continued** and should not be restarted at `1.0.0`.

### Implementation notes

There are several things to keep in mind for an application that implements Features:

- The order of execution of Features is determined by the application, based on the `installsAfter` property used by feature authors. It can be overridden by users if necessary with the `overrideFeatureInstallOrder` in `devcontainer.json`.
- Features are used to create an image that can be used to create a container or not.
- Parameters like `privileged`, `init` are included if just 1 Feature requires them.
- Parameters like `capAdd`, `securityOp`  are concatenated.
- `containerEnv` is added before the Feature is executed as `ENV` commands in the Dockerfile.
- Each Feature script executes as its own layer to aid in caching and rebuilding.

---

## File: docs/specs/devcontainer-features-distribution.md

# Distribution and Discovery of Dev Container Features

**TL;DR Check out the [quick start repository](https://github.com/devcontainers/feature-template) to get started on distributing your own Dev Container Features.**

This specification defines a pattern where community members and organizations can author and self-publish [Dev Container Features](./devcontainer-features.md). 

Goals include:

- For Feature authors, create a "self-service" way to publish a Feature, either publicly or privately, that is not centrally controlled.
- For users, provide the ability to validate the integrity of fetched Feature assets. 
- For users, provide the ability to pin to a particular version (absolute, or semantic version) of a Feature to allow for consistent, repeatable environments.
- Provide the ability to standardize publishing such that [supporting tools](../docs/specs/supporting-tools.md) may implement their own mechanism to aid Feature discoverability as they see fit.

> **Tip:** This section covers details on the Features specification. If you are looking for summarized information on creating your own Features, see the [template](https://github.com/devcontainers/feature-template) and [core Features](https://github.com/devcontainers/features) repositories.

## Source code

Features source code is stored in a git repository.

For ease of authorship and maintenance, [1..n] features can share a single git repository. This set of features is referred to as a "collection," and will share the same [`devcontainer-collection.json`](#devcontainer-collection.json) file and "namespace" (eg. `<owner>/<repo>`).

Source code for the set follows the example file structure below:

```
.
├── README.md
├── src
│   ├── dotnet
│   │   ├── devcontainer-feature.json
│   │   ├── install.sh
│   │   └── ...
|   ├
│   ├── go
│   │   ├── devcontainer-feature.json
│   │   └── install.sh
|   ├── ...
│   │   ├── devcontainer-feature.json
│   │   └── install.sh
├── test
│   ├── dotnet
│   │   ├── test.sh
│   │   └── ...
│   └── go
│   |   └── test.sh
|   ├── ...
│   │   └── test.sh
├── ...
```

...where `src` is a directory containing a sub-folder with the name of the Feature (e.g. `src/dotnet` or `src/go`) with at least a file named `devcontainer-feature.json` that contains the Feature metadata, and an `install.sh` script that implementing tools will use as the entrypoint to install the Feature.

Each sub-directory should be named such that it matches the `id` field of the `devcontainer-feature.json`.  Other files can also be included in the Feature's sub-directory, and will be included during the [packaging step](#packaging) alongside the two required files.  Any files that are not part of the Feature's sub-directory (e.g. outside of `src/dotnet`) will not be included in the [packaging step](#packaging).

Optionally, a mirrored `test` directory can be included with an accompanying `test.sh` script. Implementing tools may use this to run tests against the given Feature.

## Versioning

Each Feature is individually [versioned according to the semver specification](https://semver.org/).  The `version` property in the respective `devcontainer-feature.json` file is parsed to determine if the Feature should be republished.

Tooling that handles publishing Features will not republish features if that exact version has already been published; however, tooling must republish major and minor versions in accordance with the semver specification.

## Packaging

Features are distributed as tarballs.  The tarball contains the entire contents of the Feature sub-directory, including the `devcontainer-feature.json`, `install.sh`, and any other files in the directory.

The tarball is named `devcontainer-feature-.tgz`, where `` is the Feature's `id` field.

A reference implementation for packaging and distributing Features is provided as a GitHub Action (https://github.com/devcontainers/action).

### devcontainer-collection.json

The `devcontainer-collection.json` is an auto-generated metadata file.

| Property | Type | Description |
| :--- | :--- | :--- |
| `sourceInformation` | object | Metadata from the implementing packaging tool. |
| `features` | array | The list of features that are contained in this collection.|

Each Features's `devcontainer-feature.json` metadata file is appended into the `features` top-level array.

## Distribution

There are several supported ways to distribute Features. Distribution is handled by the implementing packaging tool such as the [Dev Container CLI](https://github.com/devcontainers/cli) or [Dev Container Publish GitHub Action](https://github.com/marketplace/actions/dev-container-publish). See the [quick start repository](https://github.com/devcontainers/feature-template) for a full working example.

> Feature identifiers should be provided lowercase by an author. If not, an implementing packaging tool should normalize the identifier into lowercase for any internal representation.

A user references a distributed Feature in a `devcontainer.json` as defined in ['referencing a feature'](./devcontainer-features.md#Referencing-a-feature).

### OCI Registry

An OCI registry that implements the [OCI Artifact Distribution Specification](https://github.com/opencontainers/distribution-spec) serves as the primary distribution mechanism for Features.

Each packaged feature is pushed to the registry following the naming convention `<registry>/<namespace>/[:version]`, where version is the major, minor, and patch version of the Feature, according to the semver specification.

> **Note:** `namespace` is a unique identifier for the collection of Features.  There are no strict rules for `namespace`; however, one pattern is to set `namespace` equal to source repository's `<owner>/<repo>`.  The `namespace` should be lowercase, following [the regex provided in the OCI specification](https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pulling-manifests).

A custom media type `application/vnd.devcontainers` and `application/vnd.devcontainers.layer.v1+tar` are used as demonstrated below.

For example, the `go` Feature in the `devcontainers/features` namespace at version `1.2.3` would be pushed to the ghcr.io OCI registry.  

> **Note:** The example below uses [`oras`](https://oras.land/) for demonstration purposes.  A supporting tool should directly implement the required functionality from the aforementioned OCI artifact distribution specification.

```bash
# ghcr.io/devcontainers/features/go:1 
REGISTRY=ghcr.io
NAMESPACE=devcontainers/features
FEATURE=go

ARTIFACT_PATH=devcontainer-feature-go.tgz

for VERSION in 1  1.2  1.2.3  latest
do
    oras push ${REGISTRY}/${NAMESPACE}/${FEATURE}:${VERSION} \
            --manifest-config /dev/null:application/vnd.devcontainers \
                             ./${ARTIFACT_PATH}:application/vnd.devcontainers.layer.v1+tar
done

```

The "namespace" is the globally identifiable name for the collection of Features (e.g. `owner/repo` for the source code's git repository).

The auto-generated `devcontainer-collection.json` is pushed to the registry with the same `namespace` as above and no accompanying `feature` name. The collection file is always tagged as `latest`.

```bash
# ghcr.io/devcontainers/features
REGISTRY=ghcr.io
NAMESPACE=devcontainers/features

oras push ${REGISTRY}/${NAMESPACE}:latest \
        --manifest-config /dev/null:application/vnd.devcontainers \
                            ./devcontainer-collection.json:application/vnd.devcontainers.collection.layer.v1+json
```

Additionally, an [annotation](https://github.com/opencontainers/image-spec/blob/main/annotations.md) named `dev.containers.metadata` should be populated on the manifest when published by an implementing tool.  This annotation is the escaped JSON object of the entire `devcontainer-feature.json` as it appears during the [packaging stage](https://containers.dev/implementors/features-distribution/#packaging).  

An example manifest with the `dev.containers.metadata` annotation:

```json
{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "config": {
    "mediaType": "application/vnd.devcontainers",
    "digest": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "size": 0
  },
  "layers": [
    {
      "mediaType": "application/vnd.devcontainers.layer.v1+tar",
      "digest": "sha256:738af5504b253dc6de51d2cb1556cdb7ce70ab18b2f32b0c2f12650ed6d2e4bc",
      "size": 3584,
      "annotations": {
        "org.opencontainers.image.title": "devcontainer-feature-myFeature.tgz"
      }
    }
  ],
  "annotations": {
    "dev.containers.metadata": "{\"name\": \"My Feature\",\"id\": \"myFeature\",\"version\": \"1.0.0\",\"dependsOn\": {\"ghcr.io/myotherFeature:1\": {\"flag\": true},\"features.azurecr.io/aThirdFeature:1\": {},\"features.azurecr.io/aFourthFeature:1.2.3\": {}}}"
  }
}
```

### Directly referencing a tarball

A Feature can be referenced directly in a user's [`devcontainer.json`](/docs/specs/devcontainer-reference.md#devcontainerjson) file by HTTPS URI that points to the tarball from the [package step](#packaging).

The `.tgz` archive file must be named `devcontainer-feature-<featureId>.tgz`.

### Locally referenced Features

Instead of publishing a Feature to an OCI registry, a Feature's source code may be referenced from a local folder. Locally referencing a Feature may be useful when first authoring a Feature.

A local Feature is referenced in the devcontainer's `feature` object **relative to the folder containing the project's `devcontainer.json`**.

Additional constraints exists when including local Features in a project:

* The project must have a `.devcontainer/` folder at the root of the [**project workspace folder**](/docs/specs/devcontainer-reference.md#project-workspace-folder).

* A local Feature's source code **must** be contained within a sub-folder of the `.devcontainer/ folder`.

* The sub-folder name **must** match the Feature's `id` field.

* A local Feature may **not** be referenced by absolute path.

* The local Feature's sub-folder **must** contain at least a `devcontainer-feature.json` file and `install.sh` entrypoint script, mirroring the [previously outlined file structure](#Source-code).

The relative path is provided using unix-style path syntax (e.g. `./myFeature`) regardless of the host operating system.

An example project is illustrated below:

```
.
├── .devcontainer/
│   ├── localFeatureA/
│   │   ├── devcontainer-feature.json
│   │   ├── install.sh
│   │   └── ...
│   ├── localFeatureB/
│   │   ├── devcontainer-feature.json
│   │   ├── install.sh
│   │   └── ...
│   ├── devcontainer.json
```

##### devcontainer.json
```jsonc
{
        // ...
        "features": {
                "./localFeatureA": {},
                "./localFeatureB": {}
        }

}
```

---

## File: docs/specs/devcontainer-id-variable.md

# Dev Container ID

This has now been implemented:
* Discussion issue: https://github.com/devcontainers/spec/issues/62
* CLI PR: https://github.com/devcontainers/cli/pull/242

Below is the original proposal.

## Goal

Allow features to refer to an identifier that is unique to the dev container they are installed into and that is stable across rebuilds.

E.g., the `docker-in-docker` feature needs a way to mount a volume per dev container.

## Proposal

The identifier will be referred to as `${devcontainerId}` in the devcontainer.json and the feature metadata and that will be replaced with the dev container's id. It should only be used in parts of the configuration and metadata that is not used for building the image because that would otherwise prevent pre-building the image at a time when the dev container's id is not known yet. Excluding boolean, numbers and enum properties the properties supporting `${devcontainerId}` in the devcontainer.json are: `name`, `runArgs`, `initializeCommand`, `onCreateCommand`, `updateContentCommand`, `postCreateCommand`, `postStartCommand`, `postAttachCommand`, `workspaceFolder`, `workspaceMount`, `mounts`, `containerEnv`, `remoteEnv`, `containerUser`, `remoteUser`, `customizations`. Excluding boolean, numbers and enum properties the properties supporting `${devcontainerId}` in the feature metadata are: `entrypoint`, `mounts`, `customizations`.

Implementations can choose how to compute this identifier. They must ensure that it is unique among other dev containers on the same Docker host and that it is stable across rebuilds of dev containers. The identifier must only contain alphanumeric characters. We describe a way to do this below.

### Label-based Implementation 

The following assumes that a dev container can be identified among other dev containers on the same Docker host by a set of labels on the container. Implementations may choose to follow this approach.

The identifier is derived from the set of container labels uniquely identifying the dev container. It is up to the implementation to choose these labels. For example, if the dev container is based on a local folder, the label could be named `devcontainer.local_folder` and have the local folder's path as its value.

For example, the [`ghcr.io/devcontainers/features/docker-in-docker` feature](https://github.com/devcontainers/features/blob/main/src/docker-in-docker/devcontainer-feature.json) could use the dev container id with:

```jsonc
{
    "id": "docker-in-docker",
    "version": "1.0.4",
    // ...
    "mounts": [
        {
            "source": "dind-var-lib-docker-${devcontainerId}",
            "target": "/var/lib/docker",
            "type": "volume"
        }
    ]
}
```

### Label-based Computation

- Input the labels as a JSON object with the object's keys being the label names and the object's values being the labels' values.
	- To ensure implementations get to the same result, the object keys must be sorted and any optional whitespace outside of the keys and values must be removed.
- Compute a SHA-256 hash from the UTF-8 encoded input string.
- Use a base-32 encoded representation left-padded with '0' to 52 characters as the result.

JavaScript implementation taking an object with the labels as argument and returning a string as the result:
```js
const crypto = require('crypto');

function uniqueIdForLabels(idLabels) {
	const stringInput = JSON.stringify(idLabels, Object.keys(idLabels).sort()); // sort properties
	const bufferInput = Buffer.from(stringInput, 'utf-8');

	const hash = crypto.createHash('sha256')
		.update(bufferInput)
		.digest();

	const uniqueId = BigInt(`0x${hash.toString('hex')}`)
		.toString(32)
		.padStart(52, '0');
	return uniqueId;
}
```

---

## File: docs/specs/devcontainer-lockfile.md

# Lockfiles

This has now been implemented:
* Discussion issue: https://github.com/devcontainers/spec/issues/236
* CLI PR: https://github.com/devcontainers/cli/issues/564

Below is the original proposal.

## Goal

Introduce a lockfile that records the exact version, download information and checksums for each feature listed in the devcontainer.json.

This will allow for:
- Improved reproducibility of image builds (installing "latest" of a tool will still have different outcomes as the tool publishes new releases).
- Improved cachability of image builds (image cache checksums will remain stable when the lockfile pins a feature to a particular version).
- Improved security by detecting when a feature's release artifact changes after its checksum was first recorded in the lockfile ("trust on first use").

## Proposal

(The following is inspired by NPM's `package-lock.json` and Yarn's `yarn.lock`.)

Each feature is recorded with the identifier and version it is referred to in the devcontainer.json as its key and the following properties as its values:
- `resolved`:
    - OCI feature: A qualified feature id with the sha256 hash (not the version number).
    - tarball feature: The `https:` URL used to download the feature.
- `version`: The full version number.
- `integrity`: `sha256:` followed by the hex encoded SHA-256 checksum of the download artifact.
- `dependsOn`: An array of feature identifiers equal to the feature's `dependsOn` keys in its `devcontainer-feature.json` including any version or checksum suffix. If the array is empty, this property can be omitted. For every feature listed here, the lockfile will also have a feature record.

The feature identifiers recorded as keys and in the `dependsOn` arrays are converted to lowercase to simplify lookups and comparisons. Note that the `devcontainer.json` and `devcontainer-features.json` may refer to the same features with different casing because these identifiers are not case-senstive.

Local features and the deprecated GitHub releases features are not recorded in the lockfile.

The lockfile is named `devcontainer-lock.json`, is located in the same folder as the `devcontainer.json` and contains a JSON object with a `"features"` property holding the above keys.

Example:

```jsonc
{
    "features": {
        "ghcr.io/devcontainers/features/node:1": {
            "version": "1.0.4",
            "resolved": "ghcr.io/devcontainers/features/node@sha256:567d704b3f4d3eca3acee51ded7c460a8395436d135d53d1175fb565daff42b8",
            "integrity": "sha256:567d704b3f4d3eca3acee51ded7c460a8395436d135d53d1175fb565daff42b8"
        },
        "https://mycustomdomain.com/devcontainer-feature-myfeature.tgz": {
            "version": "1.2.3",
            "resolved": "https://mycustomdomain.com/devcontainer-feature-myfeature.tgz",
            "integrity": "sha256:567d704b3f4d3eca3acee51ded7c460a8395436d135d53d1175fb565daff42b8"
        }
    }
}
```

Example with dependency:

```jsonc
{
  "features": {
    "ghcr.io/codspace/dependson/a": {
      "version": "1.2.1",
      "resolved": "ghcr.io/codspace/dependson/a@sha256:932027ef71da186210e6ceb3294c3459caaf6b548d2b547d5d26be3fc4b2264a",
      "integrity": "sha256:932027ef71da186210e6ceb3294c3459caaf6b548d2b547d5d26be3fc4b2264a",
      "dependsOn": [
        "ghcr.io/codspace/dependson/e:2"
      ]
    },
    "ghcr.io/codspace/dependson/e:2": {
      "version": "2.3.4",
      "resolved": "ghcr.io/codspace/dependson/e@sha256:9f36f159c70f8bebff57f341904b030733adb17ef12a5d58d4b3d89b2a6c7d5a",
      "integrity": "sha256:9f36f159c70f8bebff57f341904b030733adb17ef12a5d58d4b3d89b2a6c7d5a"
    }
  }
}
```

---

## File: docs/specs/devcontainer-reference.md

# Dev Container Specification

The purpose of the **Development Container Specification** is to provide a way to enrich containers with the content and metadata necessary to enable development inside them. These container **environments** should be easy to use, create, and recreate. 

A **development container** is a container in which a user can develop an application.  Tools that want to implement this specification should provide a set of features/commands that give more flexibility to users and allow **development containers** to scale to large development groups.

An **environment** is defined as a logical instance of one or more **development containers**, along with any needed side-car containers. An environment is based on one set of metadata that can be managed as a single unit. Users can create multiple **environments** from the same configuration metadata for different purposes.

# Metadata

Development containers allow one to define a repeatable development environment for a user or team of developers that includes the execution environment the application needs. A development container defines an environment in which you develop your application before you are ready to deploy. While deployment and development containers may resemble one another, you may not want to include tools in a deployment image that you use during development and you may need to use different secrets or other settings. 

Furthermore, working inside a development container can require additional **metadata** to drive tooling or service experiences than you would normally need with a production container. Providing a structured and consistent form for this metadata is a core part of this specification.

## devcontainer.json

While the structure of this metadata is critical, it is also important to call out how this data can be represented on disk where appropriate. While other representations may be added over time, metadata can be stored in a JSON with Comments file called `devcontainer.json` today. Products using it should expect to find a `devcontainer.json` file in one or more of the following locations (in order of precedence):

- .devcontainer/devcontainer.json
- .devcontainer.json
- .devcontainer/`<folder>`/devcontainer.json (where `<folder>` is a sub-folder, one level deep)

It is valid that these files may exist in more than one location, so consider providing a mechanism for users to select one when appropriate.

## Image Metadata

Certain dev container metadata properties can be stored in an image label as an array of metadata snippets. This allows them to be stored in prebuilt images, such that, the image and its related configuration are self-contained. These contents should then be merged with any local `devcontainer.json` file contents at the time the container is created. An array is used so subsequent image builds can simply append changes to the array rather than attempting to merge at that point - which improves compatibility with arbitrary image build systems.

Metadata should be representative of with the following structure, using one entry per [Dev Container Feature](../features) and `devcontainer.json` (see table below for the full list):

```json
[
	{
		"id"?: string,
		"init"?: boolean,
		"privileged"?: boolean,
		"capAdd"?: string[],
		"securityOpt"?: string[],
		"entrypoint"?: string,
		"mounts"?: [],
		...
		"customizations"?: {
			...
		}
	},
	...
]
```

To simplify adding this metadata for other tools, we also support having a single top-level object with the same properties.

The metadata is added to the image as a `devcontainer.metadata` label with a JSON string value representing the above array or single object.

### Merge Logic

To apply the metadata together with a user's `devcontainer.json` at runtime the following merge logic by property is used. The table also notes which properties are currently supported coming from the `devcontainer.json` and which from the Feature metadata, this will change over time as we add more properties.

| Property | Type/Format | Merge Logic | devcontainer.json | devcontainer-feature.json |
| -------- | ----------- | ----------- | :---------------: | :--------------: |
| `id` | E.g., `ghcr.io/devcontainers/features/node:1` | Not merged. |   | ✓ |
| `init` | `boolean` | `true` if at least one is `true`, `false` otherwise. | ✓ | ✓ |
| `privileged` | `boolean` | `true` if at least one is `true`, `false` otherwise. | ✓ | ✓ |
| `capAdd` | `string[]` | Union of all `capAdd` arrays without duplicates. | ✓ | ✓ |
| `securityOpt` | `string[]` | Union of all `securityOpt` arrays without duplicates. | ✓ | ✓ |
| `entrypoint` | `string` | Collected list of all entrypoints. |   | ✓ |
| `mounts` | `(string \| { type, src, dst })[]` | Collected list of all mountpoints. Conflicts: Last source wins. | ✓ | ✓ |
| `onCreateCommand` | `string \| string[] \| {[key: string]: string \| string[]}` | Collected list of all onCreateCommands. | ✓ | ✓ |
| `updateContentCommand` | `string \| string[] \| {[key: string]: string \| string[]}` | Collected list of all updateContentCommands. | ✓ | ✓ |
| `postCreateCommand` | `string \| string[] \| {[key: string]: string \| string[]}` | Collected list of all postCreateCommands. | ✓ | ✓ |
| `postStartCommand` | `string \| string[] \| {[key: string]: string \| string[]}` | Collected list of all postStartCommands. | ✓ | ✓ |
| `postAttachCommand` | `string \| string[] \| {[key: string]: string \| string[]}` | Collected list of all postAttachCommands. | ✓ | ✓ |
| `waitFor` | enum | Last value wins. | ✓ |   |
| `customizations` | Object of tool-specific customizations. | Merging is left to the tools. | ✓ | ✓ |
| `containerUser` | `string` | Last value wins. | ✓ |   |
| `remoteUser` | `string` | Last value wins. | ✓ |   |
| `userEnvProbe` | `string` (enum) | Last value wins. | ✓ |   |
| `remoteEnv` | Object of strings. | Per variable, last value wins. | ✓ |   |
| `containerEnv` | Object of strings. | Per variable, last value wins. | ✓ |   |
| `overrideCommand` | `boolean` | Last value wins. | ✓ |   |
| `portsAttributes` | Map of ports to attributes. | Per port (not per port attribute), last value wins. | ✓ |   |
| `otherPortsAttributes` | Port attributes. | Last value wins (not per port attribute). | ✓ |   |
| `forwardPorts` | `(number \| string)[]` | Union of all ports without duplicates. Last one wins (when mapping changes). | ✓ |   |
| `shutdownAction` | `string` (enum) | Last value wins. | ✓ |   |
| `updateRemoteUserUID` | `boolean` | Last value wins. | ✓ |   |
| `hostRequirements` | `cpus`, `memory`, `storage`, `gpu` | Max value wins. | ✓ |   |

Variables in string values will be substituted at the time the value is applied. When the order matters, the `devcontainer.json` is considered last.

### Notes

- Passing the label as a `LABEL` instruction in the Dockerfile:
	- The size limit on Dockerfiles is around 1.3MB. The line length is limited to 65k characters.
	- Using one line per Feature should allow for making full use of these limits.
- Passing the label as a command line argument:
	- There is no size limit documented for labels, but the daemon returns an error when the request header is >500kb.
	- The 500kb limit is shared, so we cannot use a second label in the same build to avoid it.
	- If/when this becomes an issue we could embed the metadata as a file in the image (e.g. with a label indicating it).

# Orchestration options

A core principle of this specification is to seek to enrich existing container orchestrator formats with development container metadata where appropriate rather than replacing them. As a result, the metadata schema includes a set of optional properties for interoperating with different orchestrators. Today, the specification includes scenario-specific properties for working without a container orchestrator (by directly referencing an image or Dockerfile) and for using Docker Compose as a simple multi-container orchestrator. At the same time, this specification leaves space for further development and implementation of other orchestrator mechanisms and file formats. 

The following section describes the differences between those that are supported now. 

## Image based

Image based configurations only reference an image that should be reachable and downloadable through `docker pull` commands. Logins and tokens required for these operations are execution environment specific. The only required parameter is `image`. The details are [here](devcontainerjson-reference.md#image-or-dockerfile-specific-properties).

## Dockerfile based

These configurations are defined as using a `Dockerfile` to define the starting point of the development containers. As with image based configurations, it is assumed that any base images are already reachable by **Docker** when performing a `docker build` command. The only required parameter in this case is the relative reference to the `Dockerfile` in `build.dockerfile`. The details are [here](devcontainerjson-reference.md#image-or-dockerfile-specific-properties).

There are multiple properties that allow users to control how `docker build` works:

- `build.context` 
- `build.args`
- `build.options`
- `build.target`
- `build.cacheFrom`

## Docker Compose based

Docker Compose configurations use `docker-compose` (which may be Docker Compose V1 or aliased Docker Compose V2) to create and manage a set of containers required for an application. As with the other configurations, any images required for this operation are assumed to be reachable. The required parameters are:

- `dockerComposeFile`: the reference to the Docker Compose file(s) to be used.
- `service`: declares the **main** container that will be used for all other operations. Tools are assumed to also use this parameter to connect to the development container, although they can provide facilities to connect to the other containers as required by the user.
- `runServices`: an optional property that indicates the set of services in the `docker-compose` configuration that should be started or stopped with the environment.

It is important to note that `image` and `dockerfile` properties are not needed since Docker Compose supports them natively in the format. 

# Other options

In addition to the configuration options explained above, there are other settings that apply when creating development containers to facilitate their use by developers. 

A complete list of available metadata properties and their purposes can be found in the [`devcontainer.json` reference](devcontainerjson-reference.md). However, we will describe the critical ones below in more detail.

## Environment Variables

Environment variables can be set at different points in the dev container lifecycle. With this in mind, **development containers** support two classes of environment variables:

* **Container**: These variables are part of the container when it is created and are available at all points in its lifecycle. This concept is native to containers and can be set in the container image itself, using `containerEnv` for **image** and **Dockerfile** scenarios or using orchestrator specific properties like `env` in **Docker Compose** files.
* **Remote**: These variables should be set by a **development container** supporting tool as part of configuring its runtime environment. Users can set these using the `remoteEnv` property and implementing tools or services may add their own for specific scenarios (e.g. secrets). These variables can change during the lifetime of the container, and are added after the container's `ENTRYPOINT` has fired.

The reason for this separation is it allows for the use of information not available at image build time and simplifies updating the environment for project/repository specific needs without modifying an image. With this in mind, it's important to note that implementing tools should also support the [dynamic variable syntax](devcontainerjson-reference.md#variables-in-devcontainerjson) described in the metadata reference document.

Another notable and important environment variable related property is **`userEnvProbe`**. Implementing tools should use this property to "probe" for expected environment variables using the specified type of shell. However, it does not specify that this type of shell needs to be used for all sub-processes (given the performance impact). Instead, "probed" environment variables should be merged with Remote environment variables for any processes the implementer injects after the container is created. This allows implementors to emulate developer expected behaviors around values added to their profile and rc files. 

## Mounts

Mounts allow containers to have access to the underlying machine, share data between containers and to persist information between development containers. 

A default mount should be included so that the source code is accessible from inside the container. Source code is stored outside of the container so that a developer's in-flight edits can be extracted, or a new container created in the event a container no longer starts.

While this "workspace mount" is often a "bind" mount, this is not a requirement of this specification. It is also important to note that these mounts may be "bind" mounts that connect to the underlying filesystem and thus cloud environments might not have access to the same data as a local machine.

Inside the container this mount defaults to `/workspace`.

## workspaceFolder and workspaceMount

The default mount point for the source code can be set with the `workspaceMount` property for **image** and **dockerfile** scenarios or using the built in `mounts` property in **Docker Compose** files. This folder should point to the root of a repository (where the `.git` folder is found) so that source control operations work correctly inside the container.

The `workspaceFolder` can then be set to the default folder inside the container that should used in the container. Typically this is either the mount point in the container, or a sub-folder under it. Allowing a sub-folder to be used is particularly important for monorepos given you need the `.git` folder to interact with source control but developers are typically interacting with a specific sub-project within the overall repository. 

See [`workspaceMount` and `workspaceFolder`](devcontainerjson-reference.md#image-or-dockerfile-specific-properties) for reference.

## Users

Users control the permissions of applications executed in the containers, allowing the developer to control them. The specification takes into account two types of user definitions:

* **Container User**: The user that will be used for all operations that run inside a container. This concept is native to containers. It may be set in the container image, using the `containerUser` property for  **image** and **dockerfile** scenarios, or using an orchestratric specific property like `user` property in Docker Compose files.
* **Remote User**: Used to run the [lifecycle](#lifecycle) scripts inside the container. This is also the user tools and editors that connect to the container should use to run their processes. This concept is not native to containers. Set using the `remoteUser` property in all cases and defaults to the container user.

This separation allows the ENTRYPOINT for the image to execute with different permissions than the developer and allows for developers to switch users without recreating their containers.

# Lifecycle

A development environment goes through different lifecycle events during its use in the outer and inner loop of development.

- Configuration Validation
- Environment Creation
- Environment Stop
- Environment Resume

## Configuration Validation

The exact steps required to validate configuration can vary based on exactly where the development container metadata is persisted. However, when considering a `devcontainer.json` file, the following validation should occur:

1. Validate that a workspace source folder has been provided. It is up to the implementing tool to determine what to do if no source folder is provided.
2. Search for a `devcontainer.json` file in one of the locations [above](#devcontainerjson) in the workspace source folder. 
3. If no `devcontainer.json` is found, it is up to the implementing tool or service to determine what to do. This specification does not dictate this behavior.
4. Validate that the metadata (for example `devcontainer.json`) contains all parameters required for the selected configuration type.

## Environment Creation

The creation process goes through the steps necesarry to go from the user configuration to a working **environment** that is ready to be used.

### Initialization

During this step, the following is executed:
- Validate access to the container orchestrator specified by the configuration.
- Execution of `initializeCommand`.

### Image creation

The first part of environment creation is generating the final image(s) that the development containers are going to use. This step is orchestrator dependent and can consist of just pulling a Docker image, running Docker build, or docker-compose build. Additionally, this step is useful on its own since it permits the creation of intermediate images that can be uploaded and used by other users, thus cutting down on creation time. It is encouraged that tools implementing this specification give access to a command that just executes this step.

This step executes the following tasks:

1. [Configuration Validation](#configuration-validation) 
2. Pull/build/execute of the defined container orchestration format to create images.
3. Validate the result of these operations.

### Container Creation

After image creation, containers are created based on that image and setup.

This step executes the following tasks:

1. [Optional] Perform any required user UID/GID sync'ing (more next)
2. Create the container(s) based on the properties specified above.
3. Validate the container(s) were created successfully.

Note that container [mounts](#mounts), [environment variables](#environment-variables), and [user](#users) configuration should be applied at this point. However, remote user and environment variable configuration should **not** be.

UID/GID sync'ing is an optional task for Linux (only) and that executes if the `updateRemoteUserUID` property is set to true and a `containerUser` or `remoteUser` is specified. In this case, an image update should be made prior to creating the container to set the specified user's UID and GID to match the current local user’s UID/GID to avoid permission problems with bind mounts. Implementations **may** skip this task if they do not use bind mounts on Linux, or use a container engine that does this translation automatically.

### Post Container Creation

At the end of the container creation step, a set of commands are executed inside the **main** container: 
- `onCreateCommand`, `updateContentCommand` and `postCreateCommand`. This set of commands is executed in sequence on a container the first time it's created and depending on the creation parameters received. You can learn more in the [documentation on lifecycle scripts](devcontainerjson-reference.md#lifecycle-scripts). By default, `postCreateCommand` is executed in the background after reporting the successful creation of the development environment.
- If the `waitFor` property is defined, then execution should block until all commands in the sequence up to the specified property have executed. This property defaults to `updateContentCommand`.

Remote [environment variables](#environment-variables) and [user](#users) configuration should be applied to all created processes in the container (inclusive of `userEnvProbe`).

### Implementation specific steps

After these steps have been executed, any implementation specific commands can safely execute. Specifically, any processes required by the implementation to support other properties in this specification should be started at this point. These may occur in parallel to any non-blocking, background post-container creation commands (as dictated by the `waitFor` property).

Any user facing processes should have remote [environment variables](#environment-variables) and [user](#users) configuration applied (inclusive of `userEnvProbe`).

For example, in the [CLI reference implementation](https://github.com/devcontainers/cli), this is the point in which anything executed with `devcontainer exec` would run.

Typically, this is also the step where implementors would apply config or settings from the `customizations` section of the dev container metadata (e.g. VS Code installs extensions based on the `customizations.vscode.extensions` property). Examples of these can be found in the [supporting tools section](supporting-tools.md) reference. However, applying these at this point is not strictly required or mandated by this specification.

Once these final steps have occurred, implementing tools or services may connect to the environment as they see fit.

## Environment Stop

The intention of this step is to ensure all containers are stopped correctly based on the appropriate orchestrator specific steps to ensure no data is lost. It is up to the implementing tool or service to determine when this event should happen.

## Environment Resume

While it is not a strict requirement to keep a **development container** after it has been stopped, this is the most common scenario.

To resume the environment from a stopped state:

1. Restart all related containers.
2. Follow the appropriate [implementation specific steps](#implementation-specific-steps).
3. Additionally, execute the `postStartCommand` and `postAttachCommand` in the container.

Like during the create process, remote [environment variables](#environment-variables) and [user](#users) configuration should be applied to all created processes in the container (inclusive of `userEnvProbe`).

## Parallel lifecycle script execution </a>

Dev containers support a single command for each of its lifecycle scripts. While serial execution of multiple commands can be achieved with `;`, `&&`, etc., parallel execution deserves first-class support.

All lifecycle scripts have been extended to support `object` types. The key of the `object` will be a unique name for the command and the value will be the `string` or `array` command. Each command must exit successfully for the stage to be considered successful.

Each entry in the `object` will be run in parallel during that lifecycle step.

### Example

```json
{
  "postCreateCommand": {
    "server": "npm start",
    "db": ["mysql", "-u", "root", "-p", "my database"]
  }
}
```

# Definitions

#### **Project Workspace Folder**

The **project workspace folder** is where an implementing tool should begin to search for `devcontainer.json` files. If the target project on disk is using git, the **project workspace folder** is typically the root of the git repository.

---

## File: docs/specs/devcontainer-templates.md

# Dev Container Templates reference

**Development Container Templates** are source files packaged together that encode configuration for a complete development environment. A Template can be used in a new or existing project, and a [supporting tool](https://containers.dev/supporting) will use the configuration from the Template to build a development container.

The configuration is placed in [`.devcontainer/devcontainer.json`](/docs/specs/devcontainer-reference.md#devcontainerjson) which can also reference other files within the Template. A Template can also provide additional source files (e.g. boilerplate code or a [lifecycle script](https://containers.dev/implementors/json_reference/#lifecycle-scripts).

Template metadata is captured by a `devcontainer-template.json` file in the root folder of the Template.

## Folder structure 

A single Template is a folder with at least a `devcontainer-template.json` and [`.devcontainer/devcontainer.json`](/docs/specs/devcontainer-reference.md#devcontainerjson).  Additional files are permitted and are packaged along side the required files.

```
+-- template
|   +-- devcontainer-template.json
|   +-- .devcontainer
|       +-- devcontainer.json
|       +-- (other files)
|   +-- (other files)
```

## devcontainer-template.json properties

The `devcontainer-template.json` file defines information about the Template to be used by any [supporting tools](../docs/specs/supporting-tools.md#supporting-tools-and-services).

The properties of the file are as follows:

| Property | Type | Description |
| :--- | :--- | :--- |
| `id` | string | ID of the Template. The `id` should be unique in the context of the repository/published package where the Template exists and must match the name of the directory where the `devcontainer-template.json` resides. |
| `version` | string | The semantic version of the Template. |
| `name` | string | Name of the Template. |
| `description` | string | Description of the Template. |
| `documentationURL` | string | Url that points to the documentation of the Template. |
| `licenseURL` | string | Url that points to the license of the Template. |
| [`options`](#the-options-property) | object | A map of options that the supporting tools should use to populate different configuration options for the Template. |
| `platforms` | array | Languages and platforms supported by the Template. |
| `publisher` | string | Name of the publisher/maintainer of the Template. |
| `keywords` | array | List of strings relevant to a user that would search for this Template. |
| [`optionalPaths`](#the-optionalpaths-property) | array | An array of files or directories that tooling may consider "optional" when applying a Template. Directories are indicated with a trailing `/*` (e.g. `.github/*`).

### The `options` property

The `options` property contains a map of option IDs and their related configuration settings. These `options` are used by the supporting tools to prompt the user to choose from different Template configuration options. The tools would replace the option ID with the selected value in all the files (within the sub-directory of the Template). This replacement would happen before dropping the `.devcontainer/devcontainer.json` and other files (within the sub-directory of the Template) required to containerize your project. See [option resolution](#option-resolution) for more details. For example:

```json
{
  "options": {
    "optionId": {
      "type": "string",
      "description": "Description of the option",
      "proposals": ["value1", "value2"],
      "default": "value1"
    }
  }
}
```

| Property | Type | Description |
| :--- | :--- | :--- |
| `optionId` | string | ID of the option used by the supporting tools to replace the selected value in the files within the sub-directory of the Template. |
| `optionId.type` | string | Type of the option. Valid types are currently: `boolean`, `string` |
| `optionId.description` | string | Description for the option. |
| `optionId.proposals` | array | A list of suggested string values. Free-form values **are** allowed. Omit when using `optionId.enum`. |
| `optionId.enum` | array | A strict list of allowed string values. Free-form values are **not** allowed. Omit when using `optionId.proposals`. |
| `optionId.default` | string | Default value for the option. |

> **Note:** The `options` must be unique for every `devcontainer-template.json`

### The `optionalPaths` property

Before applying a Template, tooling must inspect the `optionalPaths` property of a Template and prompt the user on whether each file or folder should be included in the resulting output workspace folder.  A path is relative to the root of the Template source directory.

- For a single file, provide the full relative path (without any leading or trailing path delimiters).  
- For a directory, provide the full relative path with a trailing slash and asterisk (`/*`) appended to the path.  The directory and its children will be recursively ignored.

Examples are shown below:

```jsonc
{
    "id": "cpp",
    "version": "3.0.0",
    "name": "C++",
    "description": "Develop C++ applications",
    "optionalPaths": [
         "GETTING-STARTED.md",                 // Single file
         "example-project-1/MyProject.csproj", // Single file in nested directory
         ".github/*"                           // Entire recursive contents of directory
     ]
}
```

### Referencing a Template 

The `id` format (`<oci-registry>/<namespace>/<template>[:<semantic-version>]`) dictates how a [supporting tool](https://containers.dev/supporting) will locate and download a given Template from an OCI registry. For example:

- `ghcr.io/user/repo/go`
- `ghcr.io/user/repo/go:1`
- `ghcr.io/user/repo/go:latest`

The registry must implement the [OCI Artifact Distribution Specification](https://github.com/opencontainers/distribution-spec). Some implementors can be [found here](https://oras.land/implementors/).

## Versioning

Each Template is individually [versioned according to the semver specification](https://semver.org/). The `version` property in the respective `devcontainer-template.json` file is updated to increment the Template's version.

Tooling that handles releasing Templates will not republish Templates if that exact version has already been published; however, tooling must republish major and minor versions in accordance with the semver specification.

## Release

_For information on distributing Templates, see [devcontainer-templates-distribution.md](./devcontainer-templates-distribution.md)._

### Option Resolution

A Template's `options` property is used by a supporting tool to prompt for different configuration options. A supporting tool will parse the `options` object provided by the user. If a value is selected for a Template, it will be replaced in the files (within the sub-directory of the Template).

### Option resolution example

Consider a `java` Template with the following folder structure:

```
+-- java
|   +-- devcontainer-template.json
|   +-- .devcontainer
|       +-- devcontainer.json
```

Suppose the `java` Template has the following `options` parameters declared in the `devcontainer-template.json` file:

```json
// ...
"options": {
    "imageVariant": {
        "type": "string",
        "description": "Specify version of java.",
        "proposals": [
          "17-bullseye",
          "17-buster",
          "11-bullseye",
          "11-buster",
          "17",
          "11"
        ],
			  "default": "17-bullseye"
    },
    "nodeVersion": {
        "type": "string", 
        "proposals": [
          "latest",
          "16",
          "14",
          "10",
          "none"
        ],
        "default": "latest",
        "description": "Specify version of node, or 'none' to skip node installation."
    },
    "installMaven": {
        "type": "boolean", 
        "description": "Install Maven, a management tool for Java.",
        "default": "false"
    },
}
```

...and it has the following `.devcontainer/devcontainer.json` file:

```json
{
	"name": "Java",
	"image": "mcr.microsoft.com/devcontainers/java:0-${templateOption:imageVariant}",
	"features": {
		"ghcr.io/devcontainers/features/node:1": {
			"version": "${templateOption:nodeVersion}",
      "installMaven": "${templateOption:installMaven}"
		}
	},
//	...
}
```

A user tries to add the `java` Template to their project using the [supporting tools](../docs/specs/supporting-tools.md#supporting-tools-and-services) and selects `17-bullseye` when prompted for `"Specify version of Go"` and the `default` values when prompted for `"Specify version of node, or 'none' to skip node installation"` and `"Install Maven, a management tool for Java"`.

The supporting tool could then use a string replacer for all the files within the sub-directory of the Template. In this example, `.devcontainer/devcontainer.json` needs to be modified and hence, the inputs can provided to it as follows:

```
{
  imageVariant:"17-bullseye",
  nodeVersion: "latest",
  installMaven: "false"
}
```

The modified `.devcontainer/devcontainer.json` will be as follows:

```json
{
	"name": "Go",
	"image": "mcr.microsoft.com/devcontainers/go:0-17-bullseye",
	"features": {
		"ghcr.io/devcontainers/features/node:1": {
			"version": "latest",
      "installMaven": "false"
		}
	},
	...
}
```

The modified `.devcontainer/devcontainer.json` would be dropped into any existing folder as a starting point for containerizing your project.

---

## File: docs/specs/devcontainer-templates-distribution.md

# Distribution and Discovery of Dev Container Templates

**TL;DR Check out the [quick start repository](https://github.com/devcontainers/template-starter) to get started on distributing your own Dev Container Templates.**

This specification defines a pattern where community members and organizations can author and self-publish [Dev Container Templates](./devcontainer-templates.md). 

Goals include:

- For Template authors, create a "self-service" way to publish a Template, either publicly or privately, that is not centrally controlled.
- Provide the ability to standardize publishing such that supporting tools may implement their own mechanism to aid Template discoverability as they see fit.

## Source code

A Template's source code is stored in a git repository.

For ease of authorship and maintenance, [1..n] Templates can share a single git repository. This set of Templates is referred to as a "collection," and will share the same [`devcontainer-collection.json`](#devcontainer-collection.json) file and "namespace" (eg. `<owner>/<repo>`).

> **Note:** Templates and [Features](./devcontainer-features.md) should be placed in different git repositories. 

Source code for a set of Templates follows the example file structure below:

```
.
├── README.md
├── src
│   ├── dotnet
│   │   ├── devcontainer-template.json
│   │   ├── .devcontainer
│   │       ├── devcontainer.json
│   │       └── ...
│   │   ├── ...
|   ├
│   ├── docker-from-docker
│   │   ├── devcontainer-template.json
│   │   ├── .devcontainer
│   │       ├── devcontainer.json
│   │       ├── Dockerfile
│   │       └── ...
│   │   ├── ...
|   ├
│   ├── go-postgres
│   │   ├── devcontainer-template.json
│   │   ├── .devcontainer
│   │       ├── devcontainer.json
│   │       ├── docker-compose.yml
│   │       ├── Dockerfile
│   │       └── ...
│   │   ├── ...
```

...where `src` is a directory containing a sub-folder with the name of the Template (e.g. `src/dotnet` or `src/docker-from-docker`) with at least a file named `devcontainer-template.json` that contains the Template metadata, and a `.devcontainer/devcontainer.json` that the supporting tools will drop into an existing project or folder.

Each sub-directory should be named such that it matches the `id` field of the `devcontainer-template.json`.  Other files can also be included in the Templates's sub-directory, and will be included during the [packaging step](#packaging) alongside the two required files.  Any files that are not part of the Templates's sub-directory (e.g. outside of `src/dotnet`) will not be included in the [packaging step](#packaging).

## Versioning

Each Template is individually [versioned according to the semver specification](https://semver.org/). The `version` property in the respective `devcontainer-template.json` file is parsed to determine if the Template should be republished.

Tooling that handles publishing Templates will not republish Templates if that exact version has already been published; however, tooling must republish major and minor versions in accordance with the semver specification.

## Packaging

Templates are distributed as tarballs. The tarball contains the entire contents of the Template sub-directory, including the `devcontainer-template.json`, `.devcontainer/devcontainer.json`, and any other files in the directory.

The tarball is named `devcontainer-template-.tgz`, where `` is the Templates's `id` field.

A reference implementation for packaging and distributing Templates is provided as a GitHub Action (https://github.com/devcontainers/action).

### devcontainer-collection.json

The `devcontainer-collection.json` is an auto-generated metadata file.

| Property | Type | Description |
| :--- | :--- | :--- |
| `sourceInformation` | object | Metadata from the implementing packaging tool. |
| `templates` | array | The list of Templates that are contained in this collection.|

Each Template's `devcontainer-template.json` metadata file is appended into the `templates` top-level array.

## Distribution

There are several supported ways to distribute Templates. Distribution is handled by the implementing packaging tool such as the **[Dev Container CLI](https://github.com/devcontainers/cli)** or **[Dev Container Publish GitHub Action](https://github.com/marketplace/actions/dev-container-publish)**.

A user can add a Template in to their projects as defined by the [supporting Tools](../docs/specs/supporting-tools.md#supporting-tools-and-services).

### OCI Registry

An OCI registry that implements the [OCI Artifact Distribution Specification](https://github.com/opencontainers/distribution-spec) serves as the primary distribution mechanism for Templates.

Each packaged Template is pushed to the registry following the naming convention `<registry>/<namespace>/[:version]`, where version is the major, minor, and patch version of the Template, according to the semver specification.

> **Note:** `namespace` is a unique identifier for the collection of Templates and must be different than the collection of [Features](./devcontainer-features.md). There are no strict rules for `namespace`; however, one pattern is to set `namespace` equal to source repository's `<owner>/<repo>`. 

A custom media type `application/vnd.devcontainers` and `application/vnd.devcontainers.layer.v1+tar` are used as demonstrated below.

For example, the `go` Template in the `devcontainers/templates` namespace at version `1.2.3` would be pushed to the ghcr.io OCI registry.

> **Note:** The example below uses [`oras`](https://oras.land/) for demonstration purposes.  A supporting tool should directly implement the required functionality from the aforementioned OCI artifact distribution specification.

```bash
# ghcr.io/devcontainers/templates/go:1
REGISTRY=ghcr.io
NAMESPACE=devcontainers/templates
TEMPLATE=go

ARTIFACT_PATH=devcontainer-template-go.tgz

for VERSION in 1  1.2  1.2.3  latest
do
        oras push ${REGISTRY}/${NAMESPACE}/${TEMPLATE}:${VERSION} \
                --config /dev/null:application/vnd.devcontainers \
                        ./${ARTIFACT_PATH}:application/vnd.devcontainers.layer.v1+tar
done

```

The "namespace" is the globally identifiable name for the collection of Templates (e.g. `owner/repo` for the source code's git repository).

The auto-generated `devcontainer-collection.json` is pushed to the registry with the same `namespace` as above and no accompanying `template` name. The collection file is always tagged as `latest`.

```bash
# ghcr.io/devcontainers/templates
REGISTRY=ghcr.io
NAMESPACE=devcontainers/templates

oras push ${REGISTRY}/${NAMESPACE}:latest \
        --config /dev/null:application/vnd.devcontainers \
                            ./devcontainer-collection.json:application/vnd.devcontainers.collection.layer.v1+json
```

## 2. Official Technical Reference & Guides (devcontainers/devcontainers.github.io)

# Development Containers Website

This repo holds the website for the [Development Containers Specification](https://github.com/devcontainers/spec).

You may view the site at [containers.dev](https://containers.dev).

## Building

If you'd like to build and preview the site yourself, we make it as smooth as possible through a dev container in this repo!

### Dev container

You may build GitHub Pages sites with [Jekyll](https://jekyllrb.com/), which is a Ruby gem. You could manually install these tools on your machine, or you can easily get started with the setup you already need through a dev container!

You may review this repo's dev container in the [`.devcontainer`](https://github.com/devcontainers/containers.dev/tree/gh-pages/.devcontainer) folder.

It is from this [Jekyll Dev Container Template](https://github.com/devcontainers/templates/tree/main/src/jekyll).

### Steps to build and run

* Clone or open this repo in the dev container-supporting editor of your choosing.
     * You may review supporting tools and services [here](https://containers.dev/supporting).
* Reopen this repo in the dev container, so that the container builds and you may develop inside it using the included tools. 
* Once the dev container finishes building, execute the following command in your dev container to start the site: `bundle exec jekyll serve`
* Check out the site! http://localhost:4000/containers.dev/

## Feedback and contributing

If you'd like to provide feedback on or contribute to the dev containers website, please feel free to open an issue or PR in this repo.

If you'd like to add your own Features or Templates collection to the [community index](https://containers.dev/collections), please read the [Authoring a Dev Container Feature](https://containers.dev/guide/author-a-feature) guide first — in particular the [Adding Features to the Index](https://containers.dev/guide/author-a-feature#add-to-index) section, which explains how to correctly format your `collection-index.yml` entry.

For issues on and contributions to the dev container specification itself, please visit the [Dev Containers Spec repo](https://github.com/devcontainers/spec).

## License

License for this repository: https://github.com/devcontainers/containers.dev/blob/gh-pages/LICENSE.