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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ body:
id: version
attributes:
label: Devboxes version
placeholder: v0.1.0
placeholder: v0.1.1
validations:
required: true
- type: input
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
run: python -m pip install uv==0.11.26
run: python -m pip install uv==0.11.28
- name: Install dependencies
working-directory: controller
run: uv sync --frozen --extra dev
Expand Down
108 changes: 108 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,22 @@ permissions:
contents: read

jobs:
validate:
name: Release metadata
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Check tag and synchronized versions
run: |
scripts/check-version.sh
version="${GITHUB_REF_NAME#v}"
chart_version="$(awk '/^version:/ {print $2; exit}' charts/devboxes/Chart.yaml)"
test "$version" = "$chart_version"
grep -Fq "## [$version]" CHANGELOG.md

create-release:
name: Create GitHub release
needs: validate
runs-on: ubuntu-latest
permissions:
contents: write
Expand Down Expand Up @@ -56,6 +70,7 @@ jobs:

images:
name: ${{ matrix.component }} image
needs: validate
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -106,6 +121,7 @@ jobs:

helm:
name: Helm chart
needs: validate
runs-on: ubuntu-latest
permissions:
contents: read
Expand All @@ -126,3 +142,95 @@ jobs:
run: |
helm package charts/devboxes --destination /tmp
helm push "/tmp/devboxes-${GITHUB_REF_NAME#v}.tgz" oci://ghcr.io/${{ github.repository_owner }}/charts

published-images:
name: Published images
needs: images
runs-on: ubuntu-latest
permissions:
contents: read
packages: read
attestations: read
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Verify anonymous pulls, platforms, SBOMs, and provenance
env:
DEVBOXES_VERSION: ${{ github.ref_name }}
GH_TOKEN: ${{ github.token }}
run: |
DEVBOXES_VERSION="${DEVBOXES_VERSION#v}" scripts/verify-published-images.sh

published-chart:
name: Published chart (Helm ${{ matrix.major }})
needs: helm
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- major: 3
version: v3.21.3
- major: 4
version: v4.2.2
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
with:
version: ${{ matrix.version }}
- name: Pull and render the public OCI chart
env:
DEVBOXES_VERSION: ${{ github.ref_name }}
run: |
version="${DEVBOXES_VERSION#v}"
helm pull oci://ghcr.io/${{ github.repository_owner }}/charts/devboxes \
--version "$version" \
--destination /tmp
helm template devboxes "/tmp/devboxes-$version.tgz" \
--namespace devboxes \
--set workspace.sshService.type=NodePort \
--set workspace.sshService.host=192.0.2.10 \
--set ingress.enabled=true \
--set ingress.className=nginx \
--set ingress.tls.enabled=true \
--set ingress.tls.secretName=devboxes-tls \
--set controller.externalUrl=https://devboxes.example.com \
--set controller.cookieSecure=true \
--set podDisruptionBudget.enabled=true \
> /tmp/devboxes-published.yaml
grep -Fq "ghcr.io/${{ github.repository_owner }}/devboxes-controller:$version" /tmp/devboxes-published.yaml
grep -Fq "ghcr.io/${{ github.repository_owner }}/devboxes-workspace:$version" /tmp/devboxes-published.yaml
- name: Strictly validate published Kubernetes resources
run: >-
docker run --rm -i ghcr.io/yannh/kubeconform:v0.7.0
-strict -summary
< /tmp/devboxes-published.yaml

published-e2e:
name: Published clean-cluster lifecycle
needs: [cli, published-images, published-chart]
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
packages: read
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
with:
version: v4.2.2
- name: Install kind
run: |
go install sigs.k8s.io/kind@v0.31.0
echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
- name: Run lifecycle through released CLI, chart, and images
env:
DEVBOXES_VERSION: ${{ github.ref_name }}
run: |
DEVBOXES_VERSION="${DEVBOXES_VERSION#v}" scripts/published-e2e.sh

release-gate:
name: Release gate
needs: published-e2e
runs-on: ubuntu-latest
steps:
- run: echo "Published release artifacts passed every release gate."
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ All notable changes to Devboxes are documented here. The project follows [Keep a

## [Unreleased]

## [0.1.1] - 2026-07-13

### Added

- Public golden path, CLI and API references, operations runbook, troubleshooting guide, and documentation index.
Expand All @@ -13,10 +15,13 @@ All notable changes to Devboxes are documented here. The project follows [Keep a

- Tightened Rust and Python quality policy, regression tests, and contributor guidance.
- Removed internal product and design specifications from the public repository after preserving durable requirements in maintained documentation.
- Updated the workspace to Rust 1.97 and uv 0.11.28, the controller image to uv 0.11.28, the CLI TOML parser to 1.1, and pinned GitHub Actions to their current supported releases.
- Added release gates that install the checksum-verified public CLI, inspect multi-architecture image attestations, render the published OCI chart with Helm 3 and Helm 4, and run the full lifecycle against published artifacts.

### Fixed

- Updated pytest to a patched release and expanded CI auditing to cover development dependencies in the Python lockfile.
- Extended the workspace Deployment progress deadline for cold pulls of the prepared image on slower registry paths.

## [0.1.0] - 2026-07-12

Expand All @@ -29,5 +34,6 @@ All notable changes to Devboxes are documented here. The project follows [Keep a
- Portable Helm chart with values schema, namespace-scoped RBAC, configurable storage, ingress, LoadBalancer or NodePort SSH, ServiceMonitor, and disruption budget.
- macOS and Linux CLI releases, SHA-256 verification installer, GHCR images, OCI chart publishing, image provenance attestations, and clean Kind install CI.

[Unreleased]: https://github.com/vicotrbb/devboxes/compare/v0.1.0...HEAD
[Unreleased]: https://github.com/vicotrbb/devboxes/compare/v0.1.1...HEAD
[0.1.1]: https://github.com/vicotrbb/devboxes/compare/v0.1.0...v0.1.1
[0.1.0]: https://github.com/vicotrbb/devboxes/releases/tag/v0.1.0
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ kubectl -n devboxes create secret generic devboxes-workspace \
--from-file=SSH_AUTHORIZED_KEYS="$HOME/.ssh/id_ed25519.pub"

helm install devboxes oci://ghcr.io/vicotrbb/charts/devboxes \
--version 0.1.0 \
--version 0.1.1 \
--namespace devboxes
```

Expand Down
4 changes: 2 additions & 2 deletions charts/devboxes/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ apiVersion: v2
name: devboxes
description: Self-hosted, ephemeral development environments on Kubernetes
type: application
version: 0.1.0
appVersion: "0.1.0"
version: 0.1.1
appVersion: "0.1.1"
kubeVersion: ">=1.29.0-0"
home: https://github.com/vicotrbb/devboxes
icon: https://raw.githubusercontent.com/vicotrbb/devboxes/main/docs/assets/devboxes-mark.svg
Expand Down
2 changes: 1 addition & 1 deletion cli/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "devbox-cli"
version = "0.1.0"
version = "0.1.1"
edition = "2024"
rust-version = "1.96"
description = "Terminal client for self-hosted Kubernetes development environments"
Expand Down
2 changes: 1 addition & 1 deletion controller/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "devboxes-controller"
version = "0.1.0"
version = "0.1.1"
description = "Controller and dashboard for self-hosted Kubernetes development environments"
readme = "README.md"
requires-python = ">=3.12"
Expand Down
2 changes: 1 addition & 1 deletion controller/src/devboxes_controller/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Devboxes controller package."""

__version__ = "0.1.0"
__version__ = "0.1.1"
4 changes: 4 additions & 0 deletions controller/src/devboxes_controller/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ def build_deployment(
},
"spec": {
"replicas": 1,
# The prepared workspace image is intentionally comprehensive and
# can take longer than Kubernetes' ten-minute default to pull on a
# cold node or slower registry path.
"progressDeadlineSeconds": 1800,
"strategy": {"type": "Recreate"},
"selector": {"matchLabels": {LABEL_NAME: request.name}},
"template": {
Expand Down
6 changes: 3 additions & 3 deletions controller/src/devboxes_controller/templates/docs.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light">
<title>Documentation · Devboxes</title>
<link rel="icon" href="/static/favicon.svg?v=0.1.0" type="image/svg+xml">
<link rel="stylesheet" href="/static/styles.css?v=0.1.0">
<script src="/static/docs.js?v=0.1.0" defer></script>
<link rel="icon" href="/static/favicon.svg?v=0.1.1" type="image/svg+xml">
<link rel="stylesheet" href="/static/styles.css?v=0.1.1">
<script src="/static/docs.js?v=0.1.1" defer></script>
</head>
<body class="docs-page">
<a class="skip-link" href="#main-content">Skip to documentation</a>
Expand Down
6 changes: 3 additions & 3 deletions controller/src/devboxes_controller/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light">
<title>Devboxes</title>
<link rel="icon" href="/static/favicon.svg?v=0.1.0" type="image/svg+xml">
<link rel="stylesheet" href="/static/styles.css?v=0.1.0">
<script src="/static/app.js?v=0.1.0" defer></script>
<link rel="icon" href="/static/favicon.svg?v=0.1.1" type="image/svg+xml">
<link rel="stylesheet" href="/static/styles.css?v=0.1.1">
<script src="/static/app.js?v=0.1.1" defer></script>
</head>
<body>
<a class="skip-link" href="#main-content">Skip to content</a>
Expand Down
4 changes: 2 additions & 2 deletions controller/src/devboxes_controller/templates/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light">
<title>Sign in · Devboxes</title>
<link rel="icon" href="/static/favicon.svg?v=0.1.0" type="image/svg+xml">
<link rel="stylesheet" href="/static/styles.css?v=0.1.0">
<link rel="icon" href="/static/favicon.svg?v=0.1.1" type="image/svg+xml">
<link rel="stylesheet" href="/static/styles.css?v=0.1.1">
</head>
<body class="login-page">
<main class="login-shell">
Expand Down
2 changes: 1 addition & 1 deletion controller/tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def test_browser_login_and_dashboard_session() -> None:
assert dashboard.headers["x-content-type-options"] == "nosniff"
assert "Kubernetes connected" in dashboard.text
assert "cluster default storage" in dashboard.text
styles = client.get("/static/styles.css?v=0.1.0")
styles = client.get("/static/styles.css?v=0.1.1")
assert "[hidden]" in styles.text
assert "display: none !important" in styles.text
payload = client.get("/api/v1/devboxes").json()
Expand Down
1 change: 1 addition & 0 deletions controller/tests/test_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def test_deployment_is_hardened_and_ready_for_tmux_workspace() -> None:
assert pod["automountServiceAccountToken"] is False
assert pod["securityContext"]["fsGroup"] == 1000
assert pod["securityContext"]["fsGroupChangePolicy"] == "OnRootMismatch"
assert deployment["spec"]["progressDeadlineSeconds"] == 1800
assert deployment["spec"]["strategy"]["type"] == "Recreate"
assert deployment["metadata"]["annotations"][ANNOTATION_TTL_HOURS] == "24"
assert container["resources"]["limits"] == {"memory": "8Gi"}
Expand Down
2 changes: 1 addition & 1 deletion controller/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
Use a values file for durable installations:

```bash
helm show values oci://ghcr.io/vicotrbb/charts/devboxes --version 0.1.0 > values.yaml
helm show values oci://ghcr.io/vicotrbb/charts/devboxes --version 0.1.1 > values.yaml
helm upgrade --install devboxes oci://ghcr.io/vicotrbb/charts/devboxes \
--version 0.1.0 \
--version 0.1.1 \
--namespace devboxes \
--create-namespace \
--values values.yaml
```

`values.schema.json` rejects unknown top-level and component fields, invalid service types, incomplete NodePort configuration, invalid ports, and out-of-range TTLs before Kubernetes resources are rendered.

When `scripts/install.sh` runs from a source checkout, it uses the local chart by default. Set `DEVBOXES_CHART_SOURCE=oci` and `DEVBOXES_VERSION=X.Y.Z` to force a released OCI chart, or set `DEVBOXES_CHART_SOURCE=local` to require the checkout chart. This prevents release verification from silently falling back to local templates.

## Controller

| Value | Default | Meaning |
Expand Down
6 changes: 4 additions & 2 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,10 @@ helm template devboxes charts/devboxes --namespace devboxes
kind create cluster --name devboxes
```

The CI Kind job builds the controller image, loads it into a clean cluster, creates placeholder Secrets, installs the chart, waits for rollout, and exercises both the health and authenticated API endpoints.
The CI Kind job builds both images, loads them into a clean cluster, creates placeholder Secrets, installs the local chart, waits for rollout, and exercises the authenticated API, SSH, stop, retention, recreation, host identity, and purge lifecycle.

## Release contract

The CLI crate version, controller project version, chart `version`, and chart `appVersion` must match the release tag without the leading `v`. A `vX.Y.Z` tag creates a GitHub Release, publishes checksummed CLI archives, pushes multi-architecture images, attests image provenance, and publishes the chart to GHCR.
The CLI manifest and lockfile, controller package and lockfile, chart `version` and `appVersion`, repository package metadata, installer default, public examples, and static asset cache keys must match. `scripts/check-version.sh` enforces that contract and verifies the current changelog section and comparison link.

A `vX.Y.Z` tag on an already-green `main` commit creates a GitHub Release, publishes four checksummed CLI archives, pushes multi-architecture images with SPDX SBOM and SLSA provenance attestations, and publishes the chart to GHCR. The workflow then verifies anonymous image pulls, both Linux architectures, signed provenance, public chart rendering under Helm 3 and Helm 4, strict Kubernetes schemas, the public checksum installer, and the complete clean-cluster lifecycle using only released chart and image artifacts. Run the same final lifecycle manually with `DEVBOXES_VERSION=X.Y.Z scripts/published-e2e.sh`.
2 changes: 1 addition & 1 deletion docs/golden-path.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ kubectl create namespace devboxes
# Create devboxes-auth and devboxes-workspace here, as described below.

helm upgrade --install devboxes oci://ghcr.io/vicotrbb/charts/devboxes \
--version 0.1.0 \
--version 0.1.1 \
--namespace devboxes \
--values values.yaml
```
Expand Down
6 changes: 6 additions & 0 deletions docs/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,9 @@ Back up required PVCs, delete boxes through the CLI, and use `--purge` only for
Recreate the namespace, controller Secret, workspace Secret, and Helm release from versioned values. Restore home PVCs with their expected `devbox-NAME-home` names before recreating corresponding boxes. Reusing the name reconnects compute to the retained home and its SSH host identity.

If the original controller token is unavailable, issue a new token and authenticate clients again. Kubernetes resources and PVC contents do not depend on browser or CLI sessions.

## Repository protection recovery

The public repository has no standing ruleset bypass. Normal recovery uses a short-lived branch, a pull request, and the complete required check set. Before changing protection, capture the active rulesets and their identifiers with `gh api repos/vicotrbb/devboxes/rulesets` so the original state is auditable.

If GitHub Actions or the pull request service is unavailable and a critical security or release-integrity fix cannot wait, the repository owner may temporarily disable only the `devboxes-main` ruleset in **Settings → Rules → Rulesets**. Keep the release-tag ruleset active, make the smallest reviewed and signed recovery change, then immediately reactivate the unchanged main ruleset. Verify its `active` enforcement and full rule list through the API, run the omitted checks as soon as the service recovers, and record the incident, commit, reason, operator, timestamps, and validation in the relevant security advisory or issue. Never add a permanent administrator or organization-wide bypass for this procedure.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "devboxes-repository-tooling",
"version": "0.1.0",
"version": "0.1.1",
"private": true,
"type": "module",
"description": "JavaScript and documentation quality gates for Devboxes",
Expand Down
Loading