sha256:6ca2c5cafca7a509ed42aae866d0e7ac17cf992808c5b5229fa7d11d3dcc8fa4
Last pushed
6 days by dockerpublicbot
Type
Sandbox Kit
Manifest digest
sha256:6ca2c5cafca7a509ed42aae866d0e7ac17cf992808c5b5229fa7d11d3dcc8fa4
schemaVersion: "2"
kind: sandbox
name: devin-enterprise
version: 0.1.0
displayName: Devin (Enterprise)
description: Devin CLI by Cognition, pinned to a Devin Enterprise deployment (<org>.devinenterprise.com). The sibling `devin` kit targets Cognition's public SaaS; this fork repoints auth, API, and model backends at the enterprise endpoints and takes the org slug as a kit argument.
args:
org:
required: true
description: Enterprise org slug — "acme" for acme.devinenterprise.com
pattern: ^[a-z0-9][a-z0-9-]*$
sandbox:
image: docker.io/sbx/devin-image:latest
entrypoint:
- devin
command:
default:
- --permission-mode
- dangerous
- --respect-workspace-trust=false
interactive:
- --permission-mode
- dangerous
- --respect-workspace-trust=false
agentInstructions:
filename: AGENTS.md
permissions:
network:
allow:
- '*.devinenterprise.com'
- '*.enterprise.windsurf.com'
- windsurf.com
- server.codeium.com
- unleash.codeium.com
- cli.devin.ai
- static.devin.ai
- archive.ubuntu.com
- security.ubuntu.com
- ports.ubuntu.com
- download.docker.com
setup:
startup:
- command:
- sh
- -c
- '{ command -v apt-get && apt-get update -qq -y || true; } >/dev/null 2>&1 </dev/null &'
user: root
description: Update apt package cache in background
- command:
- sh
- -c
- |
mkdir -p /etc/devin
cat > /etc/devin/system.json <<'EOF'
{"enterprise_host": "${{ kit.args.org }}.devinenterprise.com"}
EOF
user: root
description: Pin Devin enterprise host via system.json
- command:
- sh
- -c
- |
set -e
[ -n "$MCP_GATEWAY_URL" ] || exit 0
mkdir -p "$HOME/.config/devin"
cat > "$HOME/.config/devin/mcp_config.json" <<EOF
{
"mcpServers": {
"mcp-gateway": {
"url": "$MCP_GATEWAY_URL",
"transport": "http",
"headers": {
"Authorization": "Bearer $MCP_SENTINEL_TOKEN_NAME"
}
}
}
}
EOF
user: agent
description: Register the sandbox MCP gateway
files:
- path: /home/agent/.config/devin/config.json
content: '{"auto_update": false}'
onlyIfMissing: true
description: Disable Devin background updates
- path: /home/agent/.local/bin/devin
content: |
#!/bin/bash
# Keep the base Devin wrapper's auth checks and login flow. Enterprise
# credentials stay on disk because proxy-managed auth is unavailable.
# The real CLI is installed alongside this wrapper as devin-cli.
#
# `set -e` is deliberately NOT set. Every failure below is checked explicitly,
# and several checks depend on reading a non-zero status rather than dying on
# it.
set -uo pipefail
credentials_file="${HOME}/.local/share/devin/credentials.toml"
# The four states the credential file can be in, as one word. They are
# exhaustive and mutually exclusive, which is what lets the dispatch below
# close with a plain `else`.
#
# absent — no file, or no windsurf_api_key line in it
# empty — the key is present but blank, which the CLI treats as
# logged-in-with-nothing and will not overwrite on its own
# sentinel — a proxy placeholder left by the managed wrapper
# credential — a real key sitting in the container
credential_state() {
if [[ ! -f "${credentials_file}" ]] ||
! grep -Eq '^[[:space:]]*windsurf_api_key[[:space:]]*=' "${credentials_file}"; then
echo absent
elif grep -Eq '^[[:space:]]*windsurf_api_key[[:space:]]*=[[:space:]]*""[[:space:]]*$' "${credentials_file}"; then
echo empty
elif grep -Fqx 'windsurf_api_key = "devin-proxy-managed"' "${credentials_file}"; then
echo sentinel
else
echo credential
fi
}
# `devin-cli auth status` exits 0 whether or not a session exists in some
# releases, so its message is checked as well as its status. It is captured
# into a variable rather than piped into grep: `grep -q` exits on first match,
# which can leave devin-cli killed by SIGPIPE, and under `pipefail` that
# failure becomes the pipeline's status and would mask the successful match.
state=$(credential_state)
login_required=false
if [[ "${state}" == absent || "${state}" == empty ]]; then
login_required=true
elif [[ "${state}" == sentinel ]]; then
# The placeholder is only as good as the key the host holds behind it. If
# that key has been revoked or rotated away, the file is worthless — drop
# it and log in again rather than starting an agent that will fail on its
# first request.
if ! status=$(devin-cli auth status 2>&1) ||
[[ "${status,,}" == *"failed to fetch"* ]] ||
[[ "${status,,}" == *"not logged in"* ]]; then
rm -f "${credentials_file}"
state=absent
login_required=true
fi
else
# state == credential. A real key is on disk; find out whether it works
# before deciding. Unlike the sentinel branch above, a transport failure
# here is fatal rather than a trigger for re-login: this key may be the
# user's own, and silently discarding it on a network blip would be worse
# than stopping.
if ! status=$(devin-cli auth status 2>&1); then
echo "Failed to read Devin authentication status." >&2
exit 1
fi
if [[ "${status,,}" == *"failed to fetch"* ]]; then
echo "Failed to validate Devin authentication." >&2
exit 1
fi
[[ "${status,,}" == *"not logged in"* ]] && login_required=true
fi
if [[ "${login_required}" == true ]]; then
echo "Not authenticated. Starting Devin login..."
# An empty key reads as logged-in to the CLI, so `auth login` would refuse
# to run. Clearing the session first is what makes the login below
# reachable from that state.
if [[ "${state}" == empty ]] && ! devin-cli auth logout >/dev/null 2>&1; then
echo "Failed to clear empty Devin credentials." >&2
exit 1
fi
# No browser runs in the sandbox, so the default localhost redirect never
# comes back. --force-manual-token-flow is Devin's own answer for that
# case, documented for remote and SSH sessions: it prints a URL to open on
# the host and reads the token back here.
if ! devin-cli auth login --force-manual-token-flow; then
echo "Login failed." >&2
exit 1
fi
# Login returns as soon as it has written the durable key. This status
# call is what puts that key on the wire to a Devin host, which is the
# moment the proxy can capture it — before secure_credentials below
# replaces the local copy.
if ! status=$(devin-cli auth status 2>&1) ||
[[ "${status,,}" == *"failed to fetch"* ]] ||
[[ "${status,,}" == *"not logged in"* ]]; then
echo "Failed to validate Devin login." >&2
exit 1
fi
fi
# exec so devin-cli replaces this wrapper rather than running underneath it:
# Ctrl-C and SIGTERM then reach the CLI directly instead of a shell that would
# have to forward them.
exec devin-cli "$@"
mode: "0755"
description: Entrypoint — validate credentials, login if needed, then exec devin-cli