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
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ compose.yaml

# References directory (don't need in image)
references/
reference/

# Claude trace logs
.claude-trace/
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,12 @@ jobs:
set -euo pipefail
./scripts/test-install-agent-tooling.sh

- name: Smoke image/tag precedence
shell: bash
run: |
set -euo pipefail
./scripts/test-image-precedence.sh

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Invoke the non-executable smoke script through Bash

The new script is committed with mode 100644, so this direct ./scripts/test-image-precedence.sh invocation fails with Permission denied before running any assertions, causing every CI run through this job to fail. Either mark the script executable or invoke it as bash ./scripts/test-image-precedence.sh.

Useful? React with 👍 / 👎.


- name: Smoke kimi auth wiring
shell: bash
run: |
Expand Down
54 changes: 54 additions & 0 deletions .github/workflows/cloak-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Cloak Image

# Paths-filtered on purpose: the cloak build bakes a ~200MB Chromium binary,
# too heavy for every PR. Nightly also skips cloak deliberately — it only
# refreshes agent CLI versions, none of which live in the cloak layer.
# Release builds and pushes the real :cloak tag.
on:
pull_request:
branches: [ main ]
paths:
- "Dockerfile.cloak"
- "scripts/cloak-entrypoint.sh"
- "versions.env"
- ".github/workflows/cloak-image.yml"
Comment on lines +10 to +14

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Trigger the cloak workflow for daemon changes

A PR or push that changes only scripts/cloak-browserd.mjs skips this workflow even though Dockerfile.cloak copies that file into the image and test-cloak is intended to validate it. This allows a broken daemon change to merge without building or testing the cloak image; add the daemon path to both path-filter lists.

Useful? React with 👍 / 👎.

push:
branches: [ main ]
paths:
- "Dockerfile.cloak"
- "scripts/cloak-entrypoint.sh"
- "versions.env"
- ".github/workflows/cloak-image.yml"

jobs:
build-and-test:
name: Build and Test Cloak Image
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Load pinned versions
id: pins
shell: bash
run: |
set -euo pipefail
source ./scripts/version-pins.sh
load_version_pins
emit_github_outputs "$GITHUB_OUTPUT"

# Build on top of the published rust image: this validates the cloak
# layer itself without rebuilding the whole base/rust stack in CI.
- name: Build cloak image
run: |
make build-cloak-image \
IMAGE_NAME=deva-smoke \
CLOAK_TAG=ci-cloak \
RUST_IMAGE=ghcr.io/thevibeworks/deva:rust \
CLOAKBROWSER_WRAPPER_VERSION="${{ steps.pins.outputs.cloakbrowser_wrapper_version }}"

- name: Test cloak image
run: |
make test-cloak \
IMAGE_NAME=deva-smoke \
CLOAK_TAG=ci-cloak
62 changes: 62 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ jobs:
ccx_version: ${{ steps.pins.outputs.ccx_version }}
copilot_api_version: ${{ steps.pins.outputs.copilot_api_version }}
playwright_version: ${{ steps.pins.outputs.playwright_version }}
cloakbrowser_wrapper_version: ${{ steps.pins.outputs.cloakbrowser_wrapper_version }}
rust_toolchains: ${{ steps.pins.outputs.rust_toolchains }}
rust_default_toolchain: ${{ steps.pins.outputs.rust_default_toolchain }}
rust_targets: ${{ steps.pins.outputs.rust_targets }}
Expand Down Expand Up @@ -216,6 +217,63 @@ jobs:
RUST_DEFAULT_TOOLCHAIN=${{ needs.load-version-pins.outputs.rust_default_toolchain }}
RUST_TARGETS=${{ needs.load-version-pins.outputs.rust_targets }}

# Optional, non-gating: the cloak layer bakes a ~200MB Chromium per arch
# (arm64 under QEMU), holds no agent CLIs, and changes only when
# Dockerfile.cloak / cloak-entrypoint.sh / CLOAKBROWSER_WRAPPER_VERSION move
# -- not on a deva.sh version bump. The GitHub Release must not be hostage to
# it, so `release` does not wait on this job and a failure here does not fail
# the release. Re-run this job alone to publish a missing -cloak tag.
build-and-push-cloak:
name: Build and Push Cloak Profile Image
runs-on: ubuntu-latest
needs: [prepare, load-version-pins, build-and-push-rust]
continue-on-error: true
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ needs.prepare.outputs.release_tag }}

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata for cloak profile
id: meta-cloak
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=${{ needs.prepare.outputs.release_tag }}-cloak
type=raw,value=cloak

- name: Build and push cloak image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.cloak
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta-cloak.outputs.tags }}
labels: ${{ steps.meta-cloak.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BASE_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.prepare.outputs.release_tag }}-rust
CLOAKBROWSER_WRAPPER_VERSION=${{ needs.load-version-pins.outputs.cloakbrowser_wrapper_version }}

release:
name: Create GitHub Release
runs-on: ubuntu-latest
Expand Down Expand Up @@ -258,6 +316,10 @@ jobs:
echo "- \`ghcr.io/${{ env.IMAGE_NAME }}:${RELEASE_TAG}-rust\`" >> release_notes.md
echo "- \`ghcr.io/${{ env.IMAGE_NAME }}:rust\`" >> release_notes.md
echo "" >> release_notes.md
echo "**Cloak Profile (CloakBrowser stealth Chromium, headed) - optional:**" >> release_notes.md
echo "- \`ghcr.io/${{ env.IMAGE_NAME }}:cloak\`" >> release_notes.md
echo "- \`ghcr.io/${{ env.IMAGE_NAME }}:${RELEASE_TAG}-cloak\` (published when the non-gating cloak build finishes; it may lag this release)" >> release_notes.md
echo "" >> release_notes.md
echo "## Supported Architectures" >> release_notes.md
echo "" >> release_notes.md
echo "- linux/amd64" >> release_notes.md
Expand Down
3 changes: 1 addition & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ runtime is Linux.
- $HOME is /home/deva (not /root). sudo works without password.
- Pre-installed: Node.js, Python (use `uv`, not pip), Go, git,
gh, make, curl. pip is NOT in PATH.
- Docker is available (socket mounted from host).
- System packages and build caches persist across sessions.
- Ephemeral container. Installed packages will not persist.
- Container details are in DEVA_* environment variables.
<!-- /deva:container-context -->
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
Makefile, Dockerfile(+rust), version-pins/upgrade/update scripts,
ci/nightly/release workflows; tests added (release-utils registry,
version-upgrade mock, install-tooling, and `test-kimi-auth.sh`).
- cloak authenticated-session support (#456): composable pieces for logging
into sites inside the stealth browser. The session persists via a host
profile dir (`~/.config/deva/cloak-profile` -> Chromium `userDataDir`,
auto-mounted for either path below) so a login done once survives container
removal.
- Interactive login over VNC: on OrbStack/native Linux the host reaches the
container directly, so the agent starts `x11vnc` on the `:99` display on
demand and you connect at the container IP -- no flag, no port publish.
- `--cloak-vnc` (`DEVA_CLOAK_VNC=1`): Docker-Desktop fallback that publishes
the VNC display on a host-loopback port (there the container IP is not
routable). Auto-generates a VNC password (printed once) unless
`DEVA_CLOAK_VNC_PASSWORD` is set; the published port is fixed at container
create. Independent of `--cloak-browser`.
- `--cloak-browser` (`DEVA_CLOAK_BROWSER=1`): an always-on CloakBrowser
daemon (`cloak-browserd`) holds one headed, persistent browser on the
cloak display with a loopback CDP endpoint. The agent drives it via
`connectOverCDP(CLOAK_CDP_ENDPOINT)` -- the same live browser, not a
throwaway. Starts post-UID-remap in docker-entrypoint so profile files
match the mounted host owner.
Documented in the `deva-cloak` skill.
- cloak profile (#456): `deva.sh -p cloak <agent>` runs agents in a new
image (`Dockerfile.cloak`, extends `:rust`) with CloakBrowser stealth
Chromium, headed on Xvfb `:99` + openbox. The Chromium binary is baked
at build time (hermetic, auto-update off); `CLOAKBROWSER_WRAPPER_VERSION`
in versions.env pins the wrapper and thereby Chromium. Ships the
`deva-cloak` skill (keyed off `DEVA_CLOAK=1`), `make build-cloak` /
`test-cloak` targets, a paths-filtered `cloak-image.yml` CI workflow,
and release pushes `:cloak` / `:vX.Y.Z-cloak` tags. cloak is an auxiliary
image on its own cadence: nightly skips it (that layer holds no agent
CLIs) and the release cloak job is non-gating (`continue-on-error`, not
in the `release` job's `needs`) — a ~200MB Chromium bake per arch must
never hold the GitHub Release hostage.

### Fixed
- `--trace` launch killed by `cp: cannot create regular file
Expand All @@ -37,6 +69,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
test became the subshell's exit status and `set -e` killed every
non-verbose traced launch right after the banner; the subshell now
exits 0 explicitly.
- image/tag precedence: a `.deva` config file setting `DEVA_DOCKER_TAG`
(or `DEVA_DOCKER_IMAGE`) silently overrode the real environment and made
CLI `-p` a no-op — `deva.sh -p cloak` with a config `DEVA_DOCKER_TAG=rust`
launched rust, and even `DEVA_DOCKER_TAG=cloak deva.sh ...` was clobbered
back to the config value. Precedence is now environment > config files >
`-p` profile > default, with explicit CLI `-p` beating a config-file tag.
Guarded by `scripts/test-image-precedence.sh` in CI.

## [0.16.0] - 2026-07-14

Expand Down
Loading
Loading