Compare commits

..

43 Commits

Author SHA1 Message Date
Chris Farhood e860499757 fix: mount memory-backed emptyDir at /dev/shm for Electron apps
Instead of disabling shared memory usage, mount a proper tmpfs at
/dev/shm so Antigravity (and Chrome) have real shared memory available.
Removes --disable-dev-shm-usage; keeps --no-sandbox (separate issue).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 15:24:15 -05:00
Chris Farhood e90a2fe553 fix: add --no-sandbox to antigravity launch command
Electron apps crash in Docker without --no-sandbox and
--disable-dev-shm-usage, same as Chrome. VSCode handles this
internally; Antigravity does not.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 15:22:19 -05:00
github-actions[bot] 897f1409b5 chore: bump chart version to 0.1.7 [skip ci] 2026-02-20 19:21:23 +00:00
Chris Farhood 32d4fe4944 Merge pull request #20 from cpfarhood/feat/ide-choice
feat: add IDE choice (VSCode, Google Antigravity, SSH)
2026-02-20 14:21:11 -05:00
Chris Farhood e8c263a045 docs: add SSH_AUTHORIZED_KEYS to secret keys reference table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 14:02:15 -05:00
Chris Farhood 927c9f1051 refactor: make SSH additive boolean, add ide=none
SSH is now a standalone `ssh: true/false` value that starts sshd on
port 22 *in addition to* whatever IDE is running, rather than replacing
it. The `ide` value loses the `ssh` option and gains `none` (keep
container alive with no GUI IDE, useful when ssh: true is the only
access method).

- chart/values.yaml: replace `ide: ssh` with `ssh: false` boolean
- chart/templates/deployment.yaml: expose port 22 when ssh=true,
  port 5800 when ide!=none; probes use HTTP (VNC) or TCP socket (SSH-only)
- chart/templates/service.yaml: include both ports when both enabled
- scripts/cont-init-sshd.sh: check SSH=true instead of IDE=ssh
- scripts/startapp.sh: add ide=none case (sleep infinity), drop ssh case
- chart/Chart.yaml: bump to 0.1.6
- README.md: update IDE choice and SSH access docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 14:01:05 -05:00
Chris Farhood 298a1ce6ec feat: add IDE choice — VSCode (default), Google Antigravity, SSH
- Add `ide` Helm value with options: vscode, antigravity, ssh
- Dockerfile: install Google Antigravity via apt and openssh-server
- scripts/startapp.sh: branch on IDE env var to launch the right app
- scripts/cont-init-sshd.sh: start sshd as root in SSH mode, set up
  authorized_keys from SSH_AUTHORIZED_KEYS env var
- chart/templates/deployment.yaml: pass IDE env var, conditional ports
  and probes (HTTP for VNC modes, TCP socket for SSH mode)
- chart/templates/service.yaml: expose port 5800 (VNC) or 22 (SSH)
- chart/values.yaml: add ide field with documentation
- README.md: document IDE choice, fix stale happyHomeDir references
- chart/Chart.yaml: bump to 0.1.5

Closes #10

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 13:49:31 -05:00
github-actions[bot] f33c7e1ae8 chore: bump chart version to 0.1.4 [skip ci] 2026-02-20 18:22:44 +00:00
Chris Farhood b0d4b98bb4 fix: restore HAPPY_HOME_DIR to PVC to preserve settings across restarts (#19)
Moving it to /workspace (emptyDir) wiped Happy Coder's auth, config,
and state on every pod restart. The daemon also failed to start on boot
because the settings were gone.

Keep HAPPY_HOME_DIR on the home PVC (/home/user/.happy) for persistence.
The stale lock cleanup in init-repo.sh already handles the daemon.state.json.lock
problem that motivated the workspace move.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 13:22:33 -05:00
github-actions[bot] b5820cfc7f chore: bump chart version to 0.1.3 [skip ci] 2026-02-20 16:31:45 +00:00
Chris Farhood bace308394 Merge pull request #18 from cpfarhood/feat/cluster-access-rbac
feat: add clusterAccess option for Kubernetes RBAC
2026-02-20 11:31:35 -05:00
Chris Farhood 9c964e7069 docs: rewrite README with current architecture and clusterAccess docs
- Replace all stale references (claude user, k8s/ dir, StatefulSet,
  old Happy paths) with current state (app user, Helm chart, Deployment)
- Add full Helm chart reference table including new clusterAccess option
  with usage examples for all five access levels
- Document Happy Coder daemon setup and startup flow accurately
- Update troubleshooting commands to match actual resource names
- Add Claude authentication notes (browser OAuth + API key fallback)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 11:26:45 -05:00
Chris Farhood d7210fb4e5 feat: add clusterAccess option for Kubernetes RBAC in Helm chart
Adds a clusterAccess value with five levels:
  none        — no cluster access (default, no RBAC resources created)
  readonlyns  — Role + RoleBinding: get/list/watch in release namespace
  readwritens — Role + RoleBinding: full access in release namespace
  readonly    — ClusterRole + ClusterRoleBinding: get/list/watch cluster-wide
  readwrite   — ClusterRole + ClusterRoleBinding: full access cluster-wide

A ServiceAccount is created for the pod whenever access != none and
referenced in the Deployment's serviceAccountName.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 11:25:29 -05:00
github-actions[bot] 7a96f5156c chore: bump chart version to 0.1.2 [skip ci] 2026-02-20 15:22:03 +00:00
Chris Farhood 8df46d6b6f Merge pull request #17 from cpfarhood/fix/happy-daemon-stale-lock
fix: remove stale Happy daemon lock file on startup
2026-02-20 10:21:53 -05:00
Chris Farhood 5d8b1369c3 fix: move HAPPY_HOME_DIR to /workspace so lock never persists across restarts
The daemon.state.json.lock lives on the home PVC and survives pod
restarts, causing happy daemon start to fail on every reboot. Moving
HAPPY_HOME_DIR to /workspace (emptyDir) means the entire happy state
directory is ephemeral and always clean on startup.

The rm -f in init-repo.sh is kept as a safety net for the within-run
case but is now a no-op on fresh starts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 10:20:49 -05:00
Chris Farhood 751402be44 fix: remove stale happy daemon lock file before starting
daemon.state.json.lock is left behind when the daemon crashes or is
killed (e.g. pod restart). On next startup happy daemon start sees the
lock and exits with "Failed to start daemon" without further detail.

Remove the lock file unconditionally at startup — if no daemon is
running, the lock is stale by definition.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 10:18:12 -05:00
Chris Farhood 66e0d1f406 Merge pull request #16 from cpfarhood/fix/happy-daemon-no-sudo
fix: remove sudo from happy daemon start
2026-02-20 10:07:24 -05:00
Chris Farhood e89c3040b7 fix: remove sudo from happy daemon start — startapp.sh already runs as app user
sudo -u failed because the app user is not in sudoers. It was unnecessary
anyway: startapp.sh (and init-repo.sh) run as the app user (UID 1000) so
happy daemon start already executes as the correct user with the right HOME.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 10:05:41 -05:00
Chris Farhood 8d7b39f1b5 Merge pull request #15 from cpfarhood/fix/happy-daemon-user
fix: run Happy daemon as app user so it can find credentials
2026-02-20 10:01:06 -05:00
Chris Farhood 32e87254d2 fix: run Happy daemon as app user, not root
When init-repo.sh starts the happy daemon as root, HOME=/root so
Happy and Claude Code can't find credentials stored in /home/user.
The mobile app works when the user manually runs happy from a VSCode
terminal (as the user user) because the right home dir is in scope.

Use sudo -u with -E (preserve environment) so the daemon runs as the
correct user while still inheriting the pod's env vars (HAPPY_SERVER_URL,
HAPPY_WEBAPP_URL, ANTHROPIC_API_KEY, etc.).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 09:59:59 -05:00
Chris Farhood 66ccee1202 Merge pull request #14 from cpfarhood/fix/chrome-in-docker
fix: make Chrome work inside Docker for Claude Max OAuth login
2026-02-20 09:31:59 -05:00
Chris Farhood 1909c2a3aa fix: make Chrome work inside Docker for Claude OAuth browser login
Chrome requires --no-sandbox and --disable-dev-shm-usage when running
inside a Docker container, otherwise it crashes silently and the OAuth
popup never completes.

- Add a /usr/local/bin/google-chrome wrapper that injects these flags
- Install xdg-utils so xdg-open can resolve browser handlers in VNC
- Set BROWSER env var to the wrapper so Claude Code and xdg-open both
  use it when opening the Claude Max login URL

The OAuth callback (to localhost) works fine inside VNC because both
the browser and the Claude Code local auth server share the same
container network namespace.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 09:30:36 -05:00
Chris Farhood d078bb1c44 Merge pull request #12 from cpfarhood/fix/install-claude-code
fix: install Claude Code CLI so Happy Coder can find it
2026-02-20 09:15:36 -05:00
Chris Farhood 56c648187a fix: install Claude Code CLI so Happy Coder can find it
Happy Coder requires the `claude` CLI to be present but it was never
installed in the image. Add @anthropic-ai/claude-code to the npm
global install step alongside happy-coder.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 09:13:39 -05:00
Chris Farhood 8870d60ccc Merge pull request #9 from cpfarhood/fix/app-user-shell
fix: set app user shell to /bin/bash so VSCode terminals work
2026-02-20 07:51:35 -05:00
Chris Farhood d54515244c fix: set app user shell to /bin/bash so VSCode terminals work
baseimage-gui creates the app user (UID 1000) at runtime with
shell=/sbin/nologin and home=/dev/null. VSCode tries to spawn the
user's login shell for terminals, which fails with exit code 1.

Adds a cont-init script that runs as root after baseimage-gui's
adduser step and corrects both the shell and home directory.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-20 07:50:25 -05:00
Chris Farhood 2918cfde25 Merge pull request #7 from cpfarhood/fix/happy-daemon-nonblocking
fix: don't abort startup if happy daemon fails to start
2026-02-20 07:37:10 -05:00
Chris Farhood dd77cf6a48 fix: don't abort startup if happy daemon fails to start
happy daemon start failing should not crash-loop the container.
VSCode should still open regardless.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-20 07:36:21 -05:00
github-actions[bot] 961a0985b6 chore: bump chart version to 0.1.1 [skip ci] 2026-02-20 12:29:12 +00:00
Chris Farhood d3f5e9f185 Merge pull request #6 from cpfarhood/chore/rename-chart-cpfarhood
chore: rename Helm chart from antigravity to cpfarhood
2026-02-20 07:29:01 -05:00
Chris Farhood 9aab08b8e4 Merge pull request #2 from cpfarhood/dependabot/github_actions/docker/build-push-action-6
build(deps): Bump docker/build-push-action from 5 to 6
2026-02-20 07:28:37 -05:00
Chris Farhood 727487053d Merge pull request #1 from cpfarhood/dependabot/github_actions/actions/checkout-6
build(deps): Bump actions/checkout from 4 to 6
2026-02-20 07:28:17 -05:00
Chris Farhood 47a275d667 chore: auto-bump chart patch version on every merge to main
Workflow now increments the patch version in Chart.yaml, commits it
back with [skip ci], then packages and pushes to GHCR so Flux always
picks up a new version on chart changes.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-20 07:27:04 -05:00
Chris Farhood 7788352995 chore: rename Helm chart from antigravity to devcontainer
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-20 07:25:07 -05:00
Chris Farhood 81a7098c21 chore: rename Helm chart from antigravity to cpfarhood
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-20 07:24:25 -05:00
Chris Farhood 3832fd922b Merge pull request #5 from cpfarhood/chore/renovate
chore(deps): replace Dependabot with Renovate
2026-02-20 07:18:10 -05:00
Chris Farhood 4984e5200c chore(deps): replace Dependabot with Renovate
Adds renovate.json modelled after the kubernetes repo config.
Removes .github/dependabot.yml.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-20 07:17:18 -05:00
Chris Farhood c8f51beac6 Merge pull request #4 from cpfarhood/feat/helm-oci-publish
Publish Helm chart to GHCR OCI registry
2026-02-20 07:16:35 -05:00
Chris Farhood ee7a4a0be8 Merge pull request #3 from cpfarhood/fix/happy-daemon-start
Fix Happy Coder startup
2026-02-20 07:16:22 -05:00
Chris Farhood 23ba5c2e35 Fix Happy Coder startup: use happy daemon start, create home dir
The npm package installs a binary named 'happy', not 'happy-coder'.
Use 'happy daemon start' to run it as a background service.

Also create $HOME on the PVC if it doesn't exist yet, which was
causing git config failures on fresh volumes.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-20 06:33:40 -05:00
dependabot[bot] 17c2d04d70 build(deps): Bump docker/build-push-action from 5 to 6
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-15 13:34:38 +00:00
dependabot[bot] 636b68d263 build(deps): Bump actions/checkout from 4 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-15 13:34:35 +00:00
16 changed files with 615 additions and 399 deletions
-19
View File
@@ -1,19 +0,0 @@
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
- "github-actions"
# Maintain dependencies for Docker
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
- "docker"
+2 -2
View File
@@ -25,7 +25,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
@@ -53,7 +53,7 @@ jobs:
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
+23 -3
View File
@@ -9,7 +9,7 @@ on:
workflow_dispatch:
permissions:
contents: read
contents: write
packages: write
jobs:
@@ -18,10 +18,31 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Helm
uses: azure/setup-helm@v4
- name: Bump patch version
id: bump
run: |
CURRENT=$(grep '^version:' chart/Chart.yaml | awk '{print $2}')
MAJOR=$(echo $CURRENT | cut -d. -f1)
MINOR=$(echo $CURRENT | cut -d. -f2)
PATCH=$(echo $CURRENT | cut -d. -f3)
NEW_VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))"
sed -i "s/^version: .*/version: ${NEW_VERSION}/" chart/Chart.yaml
echo "version=${NEW_VERSION}" >> $GITHUB_OUTPUT
- name: Commit version bump
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add chart/Chart.yaml
git commit -m "chore: bump chart version to ${{ steps.bump.outputs.version }} [skip ci]"
git push
- name: Log in to GHCR
run: |
helm registry login ghcr.io \
@@ -33,5 +54,4 @@ jobs:
- name: Push chart to GHCR
run: |
CHART_VERSION=$(helm show chart chart/ | grep '^version:' | awk '{print $2}')
helm push antigravity-${CHART_VERSION}.tgz oci://ghcr.io/cpfarhood/charts
helm push devcontainer-${{ steps.bump.outputs.version }}.tgz oci://ghcr.io/cpfarhood/charts
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
+37 -9
View File
@@ -25,30 +25,54 @@ RUN apt-get update && apt-get install -y \
sudo \
&& rm -rf /var/lib/apt/lists/*
# Install Chrome
# Install Chrome and xdg-utils (needed for xdg-open to work in VNC)
RUN wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /usr/share/keyrings/google-chrome-keyring.gpg && \
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome-keyring.gpg] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list && \
apt-get update && \
apt-get install -y google-chrome-stable && \
apt-get install -y google-chrome-stable xdg-utils && \
rm -rf /var/lib/apt/lists/*
# Chrome wrapper: adds flags required for running inside a Docker container.
# xdg-open (used by Claude Code on Linux) respects $BROWSER, so pointing it
# here ensures the OAuth popup works without manual --no-sandbox invocations.
RUN printf '#!/bin/bash\nexec /usr/bin/google-chrome-stable \\\n --no-sandbox \\\n --disable-dev-shm-usage \\\n --disable-gpu \\\n "$@"\n' > /usr/local/bin/google-chrome && \
chmod +x /usr/local/bin/google-chrome
# Install Node.js (LTS version for Happy Coder)
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - && \
apt-get install -y nodejs && \
rm -rf /var/lib/apt/lists/*
# Install Happy Coder globally
RUN npm install -g happy-coder
# Install Happy Coder and Claude Code globally
RUN npm install -g happy-coder @anthropic-ai/claude-code
# Install Antigravity (Google's Project IDX / Cloud Code alternative)
# Note: Antigravity might be packaged differently - adjust as needed
# For now, we'll use VSCode with Project IDX extensions as a placeholder
# Install VSCode
RUN wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/packages.microsoft.gpg && \
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" > /etc/apt/sources.list.d/vscode.list && \
apt-get update && \
apt-get install -y code && \
rm -rf /var/lib/apt/lists/*
# Install Google Antigravity IDE
RUN mkdir -p /etc/apt/keyrings && \
curl -fsSL https://us-central1-apt.pkg.dev/doc/repo-signing-key.gpg | \
gpg --dearmor --yes -o /etc/apt/keyrings/antigravity-repo-key.gpg && \
echo "deb [signed-by=/etc/apt/keyrings/antigravity-repo-key.gpg] https://us-central1-apt.pkg.dev/projects/antigravity-auto-updater-dev/ antigravity-debian main" \
> /etc/apt/sources.list.d/antigravity.list && \
apt-get update && \
apt-get install -y antigravity && \
rm -rf /var/lib/apt/lists/*
# Install OpenSSH server (for SSH IDE mode)
RUN apt-get update && \
apt-get install -y openssh-server && \
rm -rf /var/lib/apt/lists/* && \
mkdir -p /var/run/sshd && \
sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config && \
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config && \
sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config && \
echo "PermitRootLogin no" >> /etc/ssh/sshd_config
# Create user user with specific UID/GID
RUN groupadd -g 1000 user && \
useradd -u 1000 -g 1000 -m -s /bin/bash user && \
@@ -58,16 +82,20 @@ RUN groupadd -g 1000 user && \
RUN mkdir -p /workspace && \
chown -R user:user /workspace
# Copy startup script
# Copy startup scripts
COPY --chmod=755 scripts/startapp.sh /startapp.sh
COPY --chmod=755 scripts/init-repo.sh /usr/local/bin/init-repo
# Fix app user shell after baseimage-gui creates it at runtime
COPY --chmod=755 scripts/cont-init-user.sh /etc/cont-init.d/20-fix-user-shell.sh
COPY --chmod=755 scripts/cont-init-sshd.sh /etc/cont-init.d/25-start-sshd.sh
# Set working directory
WORKDIR /workspace
# Configure container to run as user user
ENV HOME=/home/user \
USER=user
USER=user \
BROWSER=/usr/local/bin/google-chrome
# Expose VNC port (baseimage-gui default)
EXPOSE 5800
+251 -345
View File
@@ -2,366 +2,272 @@
![Build and Push](https://github.com/cpfarhood/devcontainer/actions/workflows/build-and-push.yaml/badge.svg)
A containerized development environment with GUI access, featuring:
- **Antigravity** (VSCode/Cloud IDE) via web browser
- **Happy Coder** - AI-powered development assistant
- **Automatic GitHub repo cloning**
- **Persistent user home directory**
- **Secure non-root execution**
## Features
### GUI Access
- Web-based VNC interface (port 5800)
- Full desktop environment in your browser
- Secure connections with optional password protection
### Development Tools
- Antigravity IDE (VSCode-based)
- Happy Coder AI assistant
- Git integration
- Node.js and npm
- Python 3
- Chrome browser
### Security
- Runs as non-root user `claude` (UID 1000, GID 1000)
- Secure VNC connections
- Token-based GitHub authentication
- Isolated workspace
### Persistence
- ReadWriteMany PVC for `/home` (user data persists)
- Workspace mounted at `/workspace`
- Repository cloned on first startup
## Documentation
- **[DEPLOYMENT.md](DEPLOYMENT.md)** - Complete deployment guide with step-by-step instructions
- **[VARIABLES.md](VARIABLES.md)** - Reference for all configuration variables
- **[README.md](README.md)** - This file (overview and quick start)
A containerized cloud development environment with web-based GUI access, featuring:
- **VSCode** via browser-based VNC (port 5800)
- **Happy Coder** AI assistant backed by Claude
- **Automatic GitHub repo cloning** on startup
- **Persistent home directory** via ReadWriteMany PVC
- **Kubernetes-native** Helm chart deployment
## Quick Start
**👉 For detailed deployment instructions, see [DEPLOYMENT.md](DEPLOYMENT.md)**
### 1. Create a secret
### 1. Get the Image
The secret is picked up automatically via `envFrom`. Keys recognised:
The image is automatically built and published to GitHub Container Registry on every push to main.
| Key | Purpose |
|-----|---------|
| `GITHUB_TOKEN` | PAT for private repo access (`repo` scope) |
| `VNC_PASSWORD` | Password for the VNC web UI |
| `ANTHROPIC_API_KEY` | API key — alternative to browser-based Claude login |
| `SSH_AUTHORIZED_KEYS` | Public key(s) for SSH access (required when `ssh: true`) |
```bash
# Pull the latest image
docker pull ghcr.io/cpfarhood/devcontainer:latest
# Or pull a specific version
docker pull ghcr.io/cpfarhood/devcontainer:v1.0.0
kubectl create secret generic devcontainer-mydev-secrets-env \
--from-literal=GITHUB_TOKEN='ghp_...' \
--from-literal=VNC_PASSWORD='changeme'
```
**Building locally (optional):**
Or use SealedSecrets:
```bash
kubectl create secret generic devcontainer-mydev-secrets-env \
--from-literal=GITHUB_TOKEN='ghp_...' \
--from-literal=VNC_PASSWORD='changeme' \
--dry-run=client -o yaml | \
kubeseal --format=yaml | kubectl apply -f -
```
### 2. Deploy with Helm
```bash
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo
```
### 3. Access
```bash
# Local port-forward
kubectl port-forward deployment/devcontainer-mydev 5800:5800
open http://localhost:5800
```
Or configure an ingress / Gateway API HTTPRoute pointing at port 5800.
### 4. Authenticate Claude
On first launch, open a terminal in the VSCode GUI and run:
```bash
claude
```
A Chrome browser window will open inside VNC for the Claude Max OAuth login. Credentials are stored on the home PVC and persist across pod restarts.
---
## Helm Chart Reference
### Core values
| Value | Default | Description |
|-------|---------|-------------|
| `name` | `""` | Instance name — used in all resource names (`devcontainer-{name}`) |
| `githubRepo` | `""` | Repository to clone into `/workspace` on startup |
| `ide` | `vscode` | IDE to launch — `vscode`, `antigravity`, or `none` (see below) |
| `ssh` | `false` | Also start an OpenSSH server on port 22 (additive, any `ide`) |
| `image.repository` | `ghcr.io/cpfarhood/devcontainer` | Container image |
| `image.tag` | `latest` | Image tag |
### IDE choice
`ide` controls what GUI is launched in the VNC session:
| Value | Port | Description |
|-------|------|-------------|
| `vscode` (default) | 5800 (VNC) | VSCode desktop via browser-based VNC |
| `antigravity` | 5800 (VNC) | Google Antigravity (VSCode fork with AI) via VNC |
| `none` | — | No IDE; container stays alive (useful when `ssh: true`) |
### SSH access
`ssh: true` starts OpenSSH on port 22 **in addition to** the IDE. It works with any `ide` value:
```bash
# SSH-only (no VNC)
helm install mydev ./chart --set name=mydev --set ide=none --set ssh=true
# VSCode in VNC + SSH access at the same time
helm install mydev ./chart --set name=mydev --set ssh=true
```
Add your public key to the env secret:
```bash
kubectl create secret generic devcontainer-mydev-secrets-env \
--from-literal=GITHUB_TOKEN='ghp_...' \
--from-literal=SSH_AUTHORIZED_KEYS='ssh-ed25519 AAAA...'
```
Then connect:
```bash
kubectl port-forward deployment/devcontainer-mydev 2222:22
ssh -p 2222 user@localhost
```
### Happy Coder
| Value | Default | Description |
|-------|---------|-------------|
| `happyServerUrl` | `https://happy.farh.net` | Happy Coder server endpoint |
| `happyWebappUrl` | `https://happy-coder.farh.net` | Happy Coder webapp URL |
| `happyHomeDir` | `/home/user/.happy` | Happy runtime state directory (persists on the home PVC) |
| `happyExperimental` | `true` | Enable experimental Happy features |
### Kubernetes cluster access
The `clusterAccess` value provisions a ServiceAccount, Role/ClusterRole, and binding so the devcontainer pod can interact with the Kubernetes API. The default is `none` — no RBAC resources are created.
| Value | Scope | Verbs |
|-------|-------|-------|
| `none` (default) | — | no access |
| `readonlyns` | release namespace | `get`, `list`, `watch` |
| `readwritens` | release namespace | `*` |
| `readonly` | cluster-wide | `get`, `list`, `watch` |
| `readwrite` | cluster-wide | `*` |
```bash
# Give the pod read-only access to its own namespace
helm install mydev ./chart \
--set name=mydev \
--set githubRepo=https://github.com/youruser/yourrepo \
--set clusterAccess=readonlyns
```
With any non-`none` value, a `ServiceAccount` named `devcontainer-{name}` is created and set as the pod's `serviceAccountName`, so `kubectl` and any in-cluster API calls use it automatically.
### Display and resources
| Value | Default | Description |
|-------|---------|-------------|
| `display.width` | `1920` | VNC width (px) |
| `display.height` | `1080` | VNC height (px) |
| `secureConnection` | `0` | Set to `1` if TLS is not terminated upstream |
| `userId` | `1000` | UID for the app user |
| `groupId` | `1000` | GID for the app user |
| `storage.size` | `32Gi` | Home PVC size |
| `storage.className` | `ceph-filesystem` | StorageClass (must be ReadWriteMany) |
| `resources.requests.memory` | `2Gi` | |
| `resources.requests.cpu` | `1000m` | |
| `resources.limits.memory` | `8Gi` | |
| `resources.limits.cpu` | `4000m` | |
| `envSecretName` | `devcontainer-{name}-secrets-env` | Override the secret name |
---
## Architecture
### Startup flow
```
Container start
→ cont-init.d/20-fix-user-shell.sh — fix shell/home on baseimage-gui app user
→ cont-init.d/25-start-sshd.sh — start sshd if SSH=true
→ /startapp.sh (runs as app user, UID 1000)
→ init-repo.sh
→ clone / pull GITHUB_REPO into /workspace/{repo}
→ rm daemon.state.json.lock — clear stale Happy lock
→ happy daemon start — starts Happy Coder background daemon
→ IDE=vscode: code --new-window --wait /workspace/{repo}
IDE=antigravity: antigravity --new-window --wait /workspace/{repo}
IDE=none: sleep infinity
(SSH=true: sshd also running as root on port 22)
```
### Storage
| Mount | Source | Persistence |
|-------|--------|-------------|
| `/home` | ReadWriteMany PVC (`userhome-{name}`) | Survives pod restarts — stores Claude credentials, dotfiles, git config |
| `/workspace` | `emptyDir` | Ephemeral — repo is re-cloned on each pod start |
Happy Coder's runtime state (`HAPPY_HOME_DIR`) is kept in `/home/user/.happy` on the persistent home PVC, so auth credentials and settings survive pod restarts. A stale lock file (`daemon.state.json.lock`) is removed automatically on each startup.
---
## Troubleshooting
### Happy Coder daemon not starting
```bash
# Check daemon status
happy daemon status
# Start manually (also clears any stale lock)
happy daemon start
# View daemon logs
ls ~/.happy/logs/
```
### Claude not authenticated
Browser-based OAuth login is the primary method (works inside VNC via the Chrome wrapper). If you prefer API key auth:
```bash
kubectl patch secret devcontainer-mydev-secrets-env \
--type='json' \
-p='[{"op":"add","path":"/data/ANTHROPIC_API_KEY","value":"'$(echo -n "sk-ant-..." | base64)'"}]'
```
Then restart the pod to pick up the new env var.
### VNC not loading
```bash
kubectl port-forward deployment/devcontainer-mydev 5800:5800
kubectl logs deployment/devcontainer-mydev
kubectl describe pod -l instance=mydev
```
### Repository not cloning
```bash
kubectl logs deployment/devcontainer-mydev | grep "Repository Initialization"
kubectl exec deployment/devcontainer-mydev -- env | grep GITHUB
```
---
## Local Docker run
```bash
docker run -d \
-p 5800:5800 \
-e GITHUB_REPO="https://github.com/youruser/yourrepo" \
-e GITHUB_TOKEN="ghp_..." \
-e VNC_PASSWORD="changeme" \
-v $(pwd)/home:/home \
ghcr.io/cpfarhood/devcontainer:latest
```
---
## Building
```bash
docker build -t ghcr.io/cpfarhood/devcontainer:latest .
docker push ghcr.io/cpfarhood/devcontainer:latest
```
### 2. Configure Secrets
The image is also built and pushed automatically by CI on every push to `main` and on version tags (`v*`).
Edit `k8s/secrets-example.yaml` and create a sealed secret:
```bash
kubectl create secret generic antigravity-secrets \
--from-literal=github-token='ghp_your_token' \
--from-literal=vnc-password='your_password' \
--dry-run=client -o yaml | \
kubeseal --format=yaml > k8s/sealedsecrets.yaml
```
### 3. Configure Repository
Edit `k8s/configmap.yaml`:
```yaml
data:
github-repo: "https://github.com/yourusername/yourrepo"
```
### 4. Deploy to Kubernetes
```bash
kubectl apply -k k8s/
```
### 5. Access the Interface
```bash
# Port forward for local access
kubectl port-forward statefulset/antigravity 5800:5800
# Open in browser
open http://localhost:5800
```
Or configure HTTPRoute (Gateway API) for external access via your domain.
## Environment Variables
### Required
- `GITHUB_REPO` - GitHub repository URL to clone
### Optional
- `GITHUB_TOKEN` - GitHub Personal Access Token (for private repos)
- `VNC_PASSWORD` - Password for VNC access
- `USER_ID` - UID for claude user (default: 1000)
- `GROUP_ID` - GID for claude user (default: 1000)
- `DISPLAY_WIDTH` - VNC display width (default: 1920)
- `DISPLAY_HEIGHT` - VNC display height (default: 1080)
### Happy Coder Configuration (Optional)
- `HAPPY_SERVER_URL` - Custom Happy server URL (default: https://api.cluster-fluster.com)
- `HAPPY_WEBAPP_URL` - Custom Happy webapp URL (default: https://app.happy.engineering)
- `HAPPY_HOME_DIR` - Happy data directory (default: /home/claude/.happy)
- `HAPPY_EXPERIMENTAL` - Enable experimental features (default: true in container)
## Architecture
```
┌─────────────────────────────────────┐
│ Web Browser (Port 5800) │
└──────────────┬──────────────────────┘
┌─────────────────────────────────────┐
│ VNC Web Interface │
│ (jlesage/baseimage-gui) │
└──────────────┬──────────────────────┘
┌─────────────────────────────────────┐
│ Antigravity IDE │
│ (VSCode + Extensions) │
│ Running as user: claude (1000) │
└──────────────┬──────────────────────┘
┌─────────────────────────────────────┐
│ Happy Coder (Background Process) │
│ AI Development Assistant │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Workspace: /workspace/{repo} │
│ Home: /home/claude (RWX PVC) │
└─────────────────────────────────────┘
```
## Startup Flow
1. **Container starts** - baseimage-gui initializes
2. **init-repo.sh runs**:
- Checks for `GITHUB_REPO` environment variable
- Clones repository to `/workspace/{repo-name}` if not exists
- Configures git credentials with `GITHUB_TOKEN`
- Starts Happy Coder in background
3. **startapp.sh runs**:
- Opens Antigravity IDE in the cloned repository
- Happy Coder is already running and accessible
## Happy Coder Integration
Happy Coder runs as a background service and is accessible within the IDE:
```bash
# Check Happy Coder status
ps aux | grep happy-coder
# View logs
cat /tmp/happy-coder.log
# Restart Happy Coder
sudo -u claude bash -c "cd /workspace/your-repo && happy-coder &"
```
## Local Development
### Run with Docker Compose
```yaml
version: '3.8'
services:
antigravity:
build: .
ports:
- "5800:5800"
environment:
- GITHUB_REPO=https://github.com/yourusername/yourrepo
- GITHUB_TOKEN=ghp_your_token
- VNC_PASSWORD=yourpassword
- HAPPY_EXPERIMENTAL=true
volumes:
- ./home:/home
- ./workspace:/workspace
```
```bash
docker-compose up
```
### Run with Docker
```bash
docker run -d \
-p 5800:5800 \
-e GITHUB_REPO="https://github.com/yourusername/yourrepo" \
-e GITHUB_TOKEN="ghp_your_token" \
-e VNC_PASSWORD="yourpassword" \
-e HAPPY_EXPERIMENTAL="true" \
-v $(pwd)/home:/home \
-v $(pwd)/workspace:/workspace \
ghcr.io/cpfarhood/antigravity:latest
```
## Kubernetes Deployment
### With Flux
See the animaniacs cluster configuration for GitOps deployment patterns.
### Standalone
```bash
# Apply manifests
kubectl apply -k k8s/
# Check status
kubectl get statefulset antigravity
kubectl get pods -l app=antigravity
# Access logs
kubectl logs antigravity-0
# Access shell
kubectl exec -it antigravity-0 -- bash
```
## Troubleshooting
### Repository not cloning
```bash
# Check logs
kubectl logs antigravity-0 | grep "Repository Initialization"
# Verify GITHUB_REPO is set
kubectl exec antigravity-0 -- env | grep GITHUB
# Check git credentials
kubectl exec antigravity-0 -- cat /home/claude/.git-credentials
```
### Happy Coder not starting
```bash
# Check Happy Coder logs
kubectl exec antigravity-0 -- cat /tmp/happy-coder.log
# Verify API key
kubectl exec antigravity-0 -- env | grep HAPPY_CODER
# Restart Happy Coder
kubectl exec antigravity-0 -- sudo -u claude bash -c "cd /workspace/repo && happy-coder &"
```
### VNC not accessible
```bash
# Check port forwarding
kubectl port-forward antigravity-0 5800:5800
# Verify service
kubectl get svc antigravity
# Check pod status
kubectl describe pod antigravity-0
```
### Permission issues
```bash
# Check ownership
kubectl exec antigravity-0 -- ls -la /home/claude
kubectl exec antigravity-0 -- ls -la /workspace
# Fix ownership
kubectl exec antigravity-0 -- chown -R claude:claude /home/claude
kubectl exec antigravity-0 -- chown -R claude:claude /workspace
```
## Security Considerations
1. **Secrets Management**: Use SealedSecrets or external secret managers
2. **Network Policies**: Restrict ingress/egress as needed
3. **RBAC**: Limit who can access the namespace
4. **VNC Password**: Always set a strong VNC password
5. **GitHub Token**: Use fine-grained tokens with minimal permissions
6. **Container Security**: Runs as non-root user (claude:1000)
## Storage
### Home Directory (`/home`)
- Mounted from ReadWriteMany PVC (`userhome`)
- Persists user settings, credentials, history
- Survives pod restarts
### Workspace (`/workspace`)
- ephemeral emptyDir (can be changed to PVC)
- Contains cloned repository
- Rebuild on pod restart
To persist workspace:
1. Create a PVC for workspace
2. Update `statefulset.yaml` to use PVC instead of emptyDir
## Customization
### Add More Tools
Edit `Dockerfile`:
```dockerfile
RUN apt-get update && apt-get install -y \
your-package-here \
&& rm -rf /var/lib/apt/lists/*
```
### Change Display Resolution
Set environment variables:
```yaml
env:
- name: DISPLAY_WIDTH
value: "2560"
- name: DISPLAY_HEIGHT
value: "1440"
```
### Auto-clone Multiple Repos
Modify `init-repo.sh` to support `GITHUB_REPOS` (comma-separated):
```bash
IFS=',' read -ra REPOS <<< "$GITHUB_REPOS"
for repo in "${REPOS[@]}"; do
# Clone each repo
done
```
## License
MIT
---
## Credits
- Built on [jlesage/baseimage-gui](https://github.com/jlesage/docker-baseimage-gui)
- Uses [Happy Coder](https://happy.engineering)
- Inspired by Google's Project IDX
- Base image: [jlesage/docker-baseimage-gui](https://github.com/jlesage/docker-baseimage-gui)
- AI assistant: [Happy Coder](https://happy.engineering) + [Claude](https://claude.ai)
+2 -2
View File
@@ -1,6 +1,6 @@
apiVersion: v2
name: antigravity
name: devcontainer
description: Antigravity Dev Container with Happy Coder AI assistant
type: application
version: 0.1.0
version: 0.1.9
appVersion: "latest"
+33
View File
@@ -14,6 +14,9 @@ spec:
labels:
{{- include "antigravity.labels" . | nindent 8 }}
spec:
{{- if ne (.Values.clusterAccess | default "none") "none" }}
serviceAccountName: {{ include "antigravity.fullname" . }}
{{- end }}
securityContext:
fsGroup: 1000
fsGroupChangePolicy: "OnRootMismatch"
@@ -22,10 +25,21 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
{{- if ne (.Values.ide | default "vscode") "none" }}
- containerPort: 5800
name: vnc-web
protocol: TCP
{{- end }}
{{- if .Values.ssh }}
- containerPort: 22
name: ssh
protocol: TCP
{{- end }}
env:
- name: IDE
value: {{ .Values.ide | default "vscode" | quote }}
- name: SSH
value: {{ .Values.ssh | toString | quote }}
- name: USER_ID
value: {{ .Values.userId | quote }}
- name: GROUP_ID
@@ -57,6 +71,9 @@ spec:
mountPath: /home
- name: workspace
mountPath: /workspace
- name: shm
mountPath: /dev/shm
{{- if ne (.Values.ide | default "vscode") "none" }}
livenessProbe:
httpGet:
path: /
@@ -69,9 +86,25 @@ spec:
port: 5800
initialDelaySeconds: 10
periodSeconds: 5
{{- else if .Values.ssh }}
livenessProbe:
tcpSocket:
port: 22
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe:
tcpSocket:
port: 22
initialDelaySeconds: 5
periodSeconds: 5
{{- end }}
volumes:
- name: workspace
emptyDir: {}
- name: shm
emptyDir:
medium: Memory
sizeLimit: {{ .Values.shm.sizeLimit }}
- name: userhome
persistentVolumeClaim:
claimName: {{ include "antigravity.pvcName" . }}
+97
View File
@@ -0,0 +1,97 @@
{{- $access := .Values.clusterAccess | default "none" }}
{{- $name := include "antigravity.fullname" . }}
{{- $ns := .Release.Namespace }}
{{- $labels := include "antigravity.labels" . }}
{{- if ne $access "none" }}
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ $name }}
namespace: {{ $ns }}
labels:
{{- $labels | nindent 4 }}
{{- if or (eq $access "readonlyns") (eq $access "readwritens") }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ $name }}
namespace: {{ $ns }}
labels:
{{- $labels | nindent 4 }}
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs:
{{- if eq $access "readonlyns" }}
- get
- list
- watch
{{- else }}
- "*"
{{- end }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ $name }}
namespace: {{ $ns }}
labels:
{{- $labels | nindent 4 }}
subjects:
- kind: ServiceAccount
name: {{ $name }}
namespace: {{ $ns }}
roleRef:
kind: Role
name: {{ $name }}
apiGroup: rbac.authorization.k8s.io
{{- end }}
{{- if or (eq $access "readonly") (eq $access "readwrite") }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ $name }}
labels:
{{- $labels | nindent 4 }}
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs:
{{- if eq $access "readonly" }}
- get
- list
- watch
{{- else }}
- "*"
{{- end }}
- nonResourceURLs: ["*"]
verbs:
{{- if eq $access "readonly" }}
- get
{{- else }}
- "*"
{{- end }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ $name }}
labels:
{{- $labels | nindent 4 }}
subjects:
- kind: ServiceAccount
name: {{ $name }}
namespace: {{ $ns }}
roleRef:
kind: ClusterRole
name: {{ $name }}
apiGroup: rbac.authorization.k8s.io
{{- end }}
{{- end }}
+8
View File
@@ -6,9 +6,17 @@ metadata:
{{- include "antigravity.labels" . | nindent 4 }}
spec:
ports:
{{- if ne (.Values.ide | default "vscode") "none" }}
- port: 5800
name: vnc-web
protocol: TCP
targetPort: vnc-web
{{- end }}
{{- if .Values.ssh }}
- port: 22
name: ssh
protocol: TCP
targetPort: ssh
{{- end }}
selector:
{{- include "antigravity.labels" . | nindent 4 }}
+25
View File
@@ -9,6 +9,17 @@ image:
# GitHub repository to clone into /workspace
githubRepo: ""
# IDE to launch inside the container.
# Options:
# vscode — VSCode via VNC browser UI on port 5800 (default)
# antigravity — Google Antigravity (VSCode fork) via VNC on port 5800
# none — no IDE; useful when ssh: true is the sole access method
ide: vscode
# Start an OpenSSH server on port 22 in addition to the IDE.
# Set SSH_AUTHORIZED_KEYS in the env secret to allow key-based login.
ssh: false
# Happy Coder endpoints
happyServerUrl: "https://happy.farh.net"
happyWebappUrl: "https://happy-coder.farh.net"
@@ -30,6 +41,11 @@ storage:
size: 32Gi
className: ceph-filesystem
# Shared memory size — mounted at /dev/shm as a memory-backed emptyDir.
# Electron apps (Antigravity, Chrome) use /dev/shm for GPU/IPC buffers.
shm:
sizeLimit: 2Gi
resources:
requests:
memory: "2Gi"
@@ -38,6 +54,15 @@ resources:
memory: "8Gi"
cpu: "4000m"
# Kubernetes cluster access granted to the devcontainer pod via RBAC.
# Options:
# none — no cluster access (default)
# readonlyns — get/list/watch all resources in the release namespace
# readwritens — full access to all resources in the release namespace
# readonly — get/list/watch all resources cluster-wide
# readwrite — full access to all resources cluster-wide
clusterAccess: none
# Name of existing Secret containing env vars (GITHUB_TOKEN, VNC_PASSWORD, etc.)
# Defaults to: devcontainer-{name}-secrets-env
envSecretName: ""
+72
View File
@@ -0,0 +1,72 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":gitSignOff"
],
"semanticCommits": "enabled",
"dependencyDashboard": true,
"suppressNotifications": [
"prEditedNotification"
],
"rebaseWhen": "conflicted",
"commitMessagePrefix": "chore(deps):",
"commitMessageAction": "update",
"commitMessageTopic": "{{depName}}",
"prConcurrentLimit": 5,
"prHourlyLimit": 2,
"schedule": [
"before 6am on monday"
],
"packageRules": [
{
"description": "GitHub Actions",
"matchManagers": [
"github-actions"
],
"groupName": "github-actions",
"additionalBranchPrefix": "github-actions-",
"semanticCommitScope": "github-actions",
"pinDigests": true
},
{
"description": "Docker base image",
"matchManagers": [
"dockerfile"
],
"groupName": "docker",
"additionalBranchPrefix": "docker-",
"semanticCommitScope": "docker"
},
{
"description": "Automerge patch updates",
"matchUpdateTypes": [
"patch"
],
"automerge": true,
"automergeType": "pr",
"platformAutomerge": true
},
{
"description": "Automerge minor updates for stable packages",
"matchUpdateTypes": [
"minor"
],
"matchCurrentVersion": "!/^0/",
"automerge": true,
"automergeType": "pr",
"platformAutomerge": true
},
{
"description": "Separate major updates - require manual review",
"matchUpdateTypes": [
"major"
],
"automerge": false,
"additionalBranchPrefix": "major-"
}
],
"ignorePaths": [
"**/node_modules/**"
]
}
+27
View File
@@ -0,0 +1,27 @@
#!/bin/sh
# Start OpenSSH server when SSH=true.
# Runs as root during container initialisation (cont-init.d).
[ "${SSH:-false}" = "true" ] || exit 0
echo "=== SSH enabled: starting sshd ==="
# Generate host keys if missing (first boot or ephemeral /etc/ssh)
ssh-keygen -A 2>/dev/null || true
# Populate authorized_keys from env var (injected via Kubernetes secret)
if [ -n "$SSH_AUTHORIZED_KEYS" ]; then
HOME_DIR="/home/user"
mkdir -p "$HOME_DIR/.ssh"
chmod 700 "$HOME_DIR/.ssh"
printf '%s\n' "$SSH_AUTHORIZED_KEYS" > "$HOME_DIR/.ssh/authorized_keys"
chmod 600 "$HOME_DIR/.ssh/authorized_keys"
chown -R 1000:1000 "$HOME_DIR/.ssh"
echo "SSH authorized keys configured."
else
echo "WARNING: SSH_AUTHORIZED_KEYS not set — you will not be able to log in."
fi
# Start sshd in background (root required to bind :22 and fork sessions)
/usr/sbin/sshd -D &
echo "sshd started (PID $!)"
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
# Fix the app user (UID 1000) created by baseimage-gui at runtime.
# baseimage-gui sets shell=/sbin/nologin and home=/dev/null, which
# prevents VSCode from opening terminals.
usermod -s /bin/bash app
usermod -d /home/user app
+13 -14
View File
@@ -55,23 +55,22 @@ RUN_UID="${USER_ID:-1000}"
RUN_GID="${GROUP_ID:-1000}"
chown -R "$RUN_UID:$RUN_GID" "$WORKSPACE_DIR"
# Start Happy Coder in background as the app user
# Ensure home directory exists on the PVC (may be absent on a fresh volume)
mkdir -p "$HOME"
chown "$RUN_UID:$RUN_GID" "$HOME"
# Start Happy Coder daemon. startapp.sh already runs as the app user (UID 1000),
# so no sudo needed — Happy/Claude Code will find credentials in the correct home dir.
echo "Starting Happy Coder..."
# Remove stale lock file. HAPPY_HOME_DIR lives on the home PVC so it survives
# pod restarts — without this cleanup the daemon refuses to start after a crash.
rm -f "${HAPPY_HOME_DIR:-$HOME/.happy}/daemon.state.json.lock"
cd "$WORKSPACE_DIR"
happy daemon start || echo "Happy Coder daemon failed to start, continuing anyway..."
# Create Happy Coder log file
HAPPY_LOG="/tmp/happy-coder.log"
touch "$HAPPY_LOG"
chown "$RUN_UID:$RUN_GID" "$HAPPY_LOG"
# Start Happy Coder (already running as the correct user via baseimage-gui)
bash -c "cd '$WORKSPACE_DIR' && happy-coder > '$HAPPY_LOG' 2>&1 &"
# Save PID for monitoring
echo $! > /tmp/happy-coder.pid
echo "Happy Coder started (PID: $(cat /tmp/happy-coder.pid))"
echo "Logs available at: $HAPPY_LOG"
echo "Happy Coder daemon started"
# Export workspace directory for startapp.sh
echo "$WORKSPACE_DIR" > /tmp/workspace-dir
+18 -4
View File
@@ -14,8 +14,22 @@ else
WORKSPACE_DIR="/workspace/default"
fi
echo "Opening Antigravity in: $WORKSPACE_DIR"
IDE="${IDE:-vscode}"
echo "IDE mode: $IDE"
echo "Workspace: $WORKSPACE_DIR"
# Start Antigravity (VSCode) in the workspace directory as claude user
# The baseimage-gui will handle the GUI display
exec code --new-window --wait "$WORKSPACE_DIR"
case "$IDE" in
antigravity)
echo "Opening Google Antigravity in: $WORKSPACE_DIR"
# --no-sandbox is required for Electron apps in Docker (no kernel sandbox available)
exec antigravity --no-sandbox --new-window --wait "$WORKSPACE_DIR"
;;
none)
echo "IDE=none: no IDE launched, keeping container alive."
exec sleep infinity
;;
*)
echo "Opening VSCode in: $WORKSPACE_DIR"
exec code --new-window --wait "$WORKSPACE_DIR"
;;
esac