Skip to content

Commit b6b5f20

Browse files
Merge branch 'main' into feat/osep-developer-console
Resolve conflicts in lifecycle routes (keep OSEP auth/async handlers) and sandbox-lifecycle spec (pool mode + dual-auth docs). Signed-off-by: divyamagrawal06 <ludicrouslytrue@gmail.com>
2 parents 9917e6e + f4a000d commit b6b5f20

128 files changed

Lines changed: 6740 additions & 3354 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/publish-components.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ on:
2222
- egress
2323
- controller
2424
- task-executor
25+
- image-committer
2526
default: 'execd'
2627
image_tag:
2728
description: 'Docker image tag'
@@ -35,6 +36,7 @@ on:
3536
- 'docker/egress/**'
3637
- 'k8s/controller/**'
3738
- 'k8s/task-executor/**'
39+
- 'k8s/image-committer/**'
3840

3941
jobs:
4042
publish:
@@ -117,6 +119,8 @@ jobs:
117119
cd kubernetes
118120
elif [ "$COMPONENT" == "task-executor" ]; then
119121
cd kubernetes
122+
elif [ "$COMPONENT" == "image-committer" ]; then
123+
cd kubernetes
120124
else
121125
cd sandboxes/$COMPONENT
122126
fi

.github/workflows/publish-js-sdks.yml

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,13 @@ jobs:
4242
- name: Set up pnpm
4343
uses: pnpm/action-setup@v4
4444
with:
45-
version: latest
46-
47-
- name: Enable corepack
48-
run: corepack enable
45+
version: 9.15.0
46+
run_install: false
4947

5048
- name: Get pnpm store path
5149
id: pnpm-store
52-
run: echo "STORE_PATH=$(corepack pnpm store path)" >> "$GITHUB_OUTPUT"
50+
working-directory: sdks
51+
run: echo "STORE_PATH=$(pnpm store path)" >> "$GITHUB_OUTPUT"
5352

5453
- name: Cache pnpm store
5554
uses: actions/cache@v5
@@ -60,11 +59,11 @@ jobs:
6059

6160
- name: Install workspace dependencies
6261
working-directory: sdks
63-
run: corepack pnpm install --frozen-lockfile
62+
run: pnpm install --frozen-lockfile
6463

6564
- name: Build SDK
6665
working-directory: sdks
67-
run: corepack pnpm --filter ${{ matrix.sdk.packageName }}... --sort run build
66+
run: pnpm --filter ${{ matrix.sdk.packageName }}... --sort run build
6867

6968
- name: Pack SDK
7069
if: startsWith(github.ref, format('refs/tags/js/{0}/v', matrix.sdk.tagPrefix))
@@ -74,7 +73,7 @@ jobs:
7473
set -euo pipefail
7574
PACK_DIR="${GITHUB_WORKSPACE}/dist/npm/${{ matrix.sdk.name }}"
7675
mkdir -p "$PACK_DIR"
77-
corepack pnpm pack --pack-destination "$PACK_DIR"
76+
pnpm pack --pack-destination "$PACK_DIR"
7877
PACKAGE_TARBALL="$(find "$PACK_DIR" -maxdepth 1 -name '*.tgz' -print -quit)"
7978
if [[ -z "$PACKAGE_TARBALL" ]]; then
8079
echo "No package tarball was produced in $PACK_DIR" >&2
@@ -93,4 +92,4 @@ jobs:
9392
env:
9493
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
9594
run: |
96-
corepack pnpm publish "${{ steps.pack.outputs.tarball }}" --access public --no-git-checks
95+
pnpm publish "${{ steps.pack.outputs.tarball }}" --access public --no-git-checks

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ Quick start:
122122
osb config init
123123
osb config set connection.domain localhost:8080
124124
osb config set connection.protocol http
125+
osb config set connection.api_key <your-api-key>
125126
osb sandbox create --image python:3.12 --timeout 30m -o json
126127
osb command run <sandbox-id> -o raw -- python -c "print(1 + 1)"
127128
```

cli/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ opensandbox-server
5050
osb config init
5151
osb config set connection.domain localhost:8080
5252
osb config set connection.protocol http
53+
osb config set connection.api_key <your-api-key>
5354
osb config show -o json
5455
```
5556

components/egress/docs/mitmproxy-transparent.md

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ By default, mitmproxy listens on `18081` and transparent redirect rules are set
3030
# Optional: change listening port (default: 18081)
3131
export OPENSANDBOX_EGRESS_MITMPROXY_PORT=18081
3232

33-
# Optional: enable mitm addon script (e.g., inject request headers)
34-
export OPENSANDBOX_EGRESS_MITMPROXY_SCRIPT=/opt/opensandbox/mitmscripts/add_header.py
33+
# Optional: load an additional user-defined mitm addon (loaded after the system addon)
34+
export OPENSANDBOX_EGRESS_MITMPROXY_SCRIPT=/path/to/your/addon.py
3535

3636
# Optional: bypass decryption for selected domains (semicolon-separated regex list)
3737
export OPENSANDBOX_EGRESS_MITMPROXY_IGNORE_HOSTS='.*\.log\.aliyuncs\.com;.*\.example\.internal'
@@ -43,7 +43,7 @@ export OPENSANDBOX_EGRESS_MITMPROXY_IGNORE_HOSTS='.*\.log\.aliyuncs\.com;.*\.exa
4343
|------|----------|------|--------|
4444
| `OPENSANDBOX_EGRESS_MITMPROXY_TRANSPARENT` | Yes | Enable transparent mitmproxy (`1/true/on`, etc.) | Disabled |
4545
| `OPENSANDBOX_EGRESS_MITMPROXY_PORT` | No | mitmdump listen port; `iptables` redirects `80/443` here | `18081` |
46-
| `OPENSANDBOX_EGRESS_MITMPROXY_SCRIPT` | No | mitm addon script path (`-s`) | Empty |
46+
| `OPENSANDBOX_EGRESS_MITMPROXY_SCRIPT` | No | Additional user mitm addon script path (`-s`); loaded after the system addon | Empty |
4747
| `OPENSANDBOX_EGRESS_MITMPROXY_IGNORE_HOSTS` | No | Host/IP regex list for TLS pass-through (`;` separated) | Empty |
4848
| `OPENSANDBOX_EGRESS_MITMPROXY_CONFDIR` | No | mitm config and CA directory (passed as `--set confdir=`, also used as `HOME`) | Default directory under `/var/lib/mitmproxy` |
4949
| `OPENSANDBOX_EGRESS_MITMPROXY_UPSTREAM_TRUST_DIR` | No | Trust directory for upstream TLS verification (OpenSSL style) | `/etc/ssl/certs` |
@@ -62,23 +62,31 @@ Notes:
6262
export OPENSANDBOX_EGRESS_MITMPROXY_TRANSPARENT=true
6363
```
6464

65-
### 2) Enable with Header Injection
65+
### 2) System Addon (Always On)
66+
67+
The bundled system addon at `/var/egress/mitmscripts/system.py` is shipped in the egress image and loaded automatically whenever transparent mode is enabled. It stays wire-transparent (no headers added or altered) and currently provides:
68+
69+
- Forces streaming (`flow.response.stream = True`) for SSE (`text/event-stream`) and chunked responses, so each chunk is forwarded immediately instead of being buffered up to the `stream_large_bodies=1m` threshold (critical for LLM streaming UX).
70+
71+
The system addon is always loaded and cannot be disabled via configuration. To override its behavior, supply a user addon via `OPENSANDBOX_EGRESS_MITMPROXY_SCRIPT`; user addons are loaded after the system addon and may observe or override its hooks.
72+
73+
### 3) Add a User Addon Alongside the System Addon
6674

6775
```bash
6876
export OPENSANDBOX_EGRESS_MITMPROXY_TRANSPARENT=true
69-
export OPENSANDBOX_EGRESS_MITMPROXY_SCRIPT=/opt/opensandbox/mitmscripts/add_header.py
77+
export OPENSANDBOX_EGRESS_MITMPROXY_SCRIPT=/path/to/your/addon.py
7078
```
7179

72-
Built-in example script: `/opt/opensandbox/mitmscripts/add_header.py` (adds `X-OpenSandbox-Egress: 1`).
80+
The user addon is loaded after the system addon (`-s system.py -s user.py`), so user hooks observe and may override system behavior.
7381

74-
### 3) Bypass Decryption for Specific Domains (e.g. log upload)
82+
### 4) Bypass Decryption for Specific Domains (e.g. log upload)
7583

7684
```bash
7785
export OPENSANDBOX_EGRESS_MITMPROXY_TRANSPARENT=true
7886
export OPENSANDBOX_EGRESS_MITMPROXY_IGNORE_HOSTS='.*\.log\.aliyuncs\.com'
7987
```
8088

81-
### 4) Use a Fixed CA (consistent fingerprint across replicas)
89+
### 5) Use a Fixed CA (consistent fingerprint across replicas)
8290

8391
If CA files already exist in `confdir`, mitmproxy reuses them instead of regenerating on each startup. Typical paths:
8492

components/egress/mitmscripts/add_header.py

Lines changed: 0 additions & 12 deletions
This file was deleted.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright 2026 Alibaba Group Holding Ltd.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# OpenSandbox egress system addon.
16+
#
17+
# Always loaded by the egress mitmproxy launcher. Stays transparent on the
18+
# wire (does not add or alter headers that would reveal the proxy to peers).
19+
#
20+
# Behavior:
21+
# Forces streaming for SSE / chunked responses so each chunk is forwarded
22+
# immediately, bypassing the stream_large_bodies=1m buffer set in launch.go
23+
# (which otherwise stalls LLM-style small-chunk streams).
24+
#
25+
# User-defined addons can be loaded alongside this script via
26+
# OPENSANDBOX_EGRESS_MITMPROXY_SCRIPT.
27+
from mitmproxy import http
28+
29+
30+
def responseheaders(flow: http.HTTPFlow) -> None:
31+
if flow.response is None:
32+
return
33+
content_type = flow.response.headers.get("content-type", "").lower()
34+
transfer_encoding = flow.response.headers.get("transfer-encoding", "").lower()
35+
if "text/event-stream" in content_type or "chunked" in transfer_encoding:
36+
flow.response.stream = True

components/egress/pkg/mitmproxy/launch.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,16 @@ const RunAsUser = "mitmproxy"
3434
// Loopback: transparent mode receives via REDIRECT; do not listen on 0.0.0.0 in the netns.
3535
const listenHostLoopback = "127.0.0.1"
3636

37+
// systemScriptPath: bundled system addon shipped via the egress Dockerfile
38+
// (COPY components/egress/mitmscripts /var/egress/mitmscripts). Always loaded.
39+
const systemScriptPath = "/var/egress/mitmscripts/system.py"
40+
3741
// Config: mitmdump --mode transparent; UserName must match iptables ! --uid-owner, ConfDir is mitm state/CA.
3842
type Config struct {
3943
ListenPort int
4044
UserName string
4145
ConfDir string
46+
// ScriptPath is an optional user-supplied addon, loaded after the system addon.
4247
ScriptPath string
4348
// OnExit is called (if non-nil) when mitmdump exits. Called from a background goroutine.
4449
OnExit func(error)
@@ -120,8 +125,10 @@ func Launch(cfg Config) (*Running, error) {
120125
args = append(args, "--set", "confdir="+cd)
121126
homeEnv = cd
122127
}
123-
if strings.TrimSpace(cfg.ScriptPath) != "" {
124-
args = append(args, "-s", strings.TrimSpace(cfg.ScriptPath))
128+
// Load the system addon first so user addons can observe / override its hooks.
129+
args = append(args, "-s", systemScriptPath)
130+
if user := strings.TrimSpace(cfg.ScriptPath); user != "" {
131+
args = append(args, "-s", user)
125132
}
126133

127134
// Upstream passthrough: each pattern becomes --set ignore_hosts= (regex; IP ranges are practical in transparent mode).

components/execd/bootstrap.sh

Lines changed: 75 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,27 +42,54 @@ _sudo() {
4242
fi
4343
}
4444

45-
# Install mitm egress CA into the system trust store (no extra env vars).
46-
# - Debian/Ubuntu/Alpine: update-ca-certificates + /usr/local/share/ca-certificates/
47-
# - RHEL/CentOS/Fedora/Alma/Rocky: update-ca-trust + /etc/pki/ca-trust/source/anchors/
45+
# Install mitm CA into the system trust store (for non-Python programs)
46+
# and set OPENSANDBOX_MERGED_CA to a PEM bundle containing a full root
47+
# set + mitm CA (for env vars like REQUESTS_CA_BUNDLE that *replace*
48+
# rather than append to the default roots).
49+
OPENSANDBOX_MERGED_CA=""
4850
trust_mitm_ca() {
4951
cert="$1"
52+
merged="/opt/opensandbox/merged-ca-certificates.pem"
53+
54+
# 1) Try to install into the system trust store (best-effort).
5055
if command -v update-ca-certificates >/dev/null 2>&1; then
51-
_sudo mkdir -p /usr/local/share/ca-certificates
52-
_sudo cp "$cert" /usr/local/share/ca-certificates/opensandbox-mitmproxy-ca.crt
53-
_sudo update-ca-certificates
54-
return 0
56+
_sudo mkdir -p /usr/local/share/ca-certificates \
57+
&& _sudo cp "$cert" /usr/local/share/ca-certificates/opensandbox-mitmproxy-ca.crt \
58+
&& _sudo update-ca-certificates \
59+
|| echo "warning: update-ca-certificates failed; system trust store may not include mitm CA" >&2
60+
elif command -v update-ca-trust >/dev/null 2>&1; then
61+
_sudo mkdir -p /etc/pki/ca-trust/source/anchors \
62+
&& _sudo cp "$cert" /etc/pki/ca-trust/source/anchors/opensandbox-mitmproxy-ca.pem \
63+
&& { _sudo update-ca-trust extract || _sudo update-ca-trust; } \
64+
|| echo "warning: update-ca-trust failed; system trust store may not include mitm CA" >&2
65+
else
66+
echo "warning: no system trust-store tooling found (need update-ca-certificates or update-ca-trust)" >&2
5567
fi
56-
if command -v update-ca-trust >/dev/null 2>&1; then
57-
_sudo mkdir -p /etc/pki/ca-trust/source/anchors
58-
_sudo cp "$cert" /etc/pki/ca-trust/source/anchors/opensandbox-mitmproxy-ca.pem
59-
if ! _sudo update-ca-trust extract; then
60-
_sudo update-ca-trust
61-
fi
62-
return 0
68+
69+
# 2) Build a merged bundle (complete root set + mitm CA).
70+
# Prefer certifi (full Mozilla root set) over system bundles which
71+
# may be incomplete in minimal Docker images.
72+
certifi_ca=""
73+
if command -v python3 >/dev/null 2>&1; then
74+
certifi_ca="$(python3 -c 'import certifi; print(certifi.where())' 2>/dev/null)" || certifi_ca=""
75+
elif command -v python >/dev/null 2>&1; then
76+
certifi_ca="$(python -c 'import certifi; print(certifi.where())' 2>/dev/null)" || certifi_ca=""
6377
fi
6478

65-
echo "warning: cannot install mitm CA (need update-ca-certificates or update-ca-trust)" >&2
79+
for candidate in \
80+
"$certifi_ca" \
81+
/etc/ssl/certs/ca-certificates.crt \
82+
/etc/pki/tls/certs/ca-bundle.crt \
83+
/etc/ssl/cert.pem \
84+
/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem; do
85+
if [ -n "$candidate" ] && [ -f "$candidate" ] && [ -s "$candidate" ]; then
86+
cat "$candidate" "$cert" > "$merged"
87+
OPENSANDBOX_MERGED_CA="$merged"
88+
return 0
89+
fi
90+
done
91+
92+
echo "warning: could not locate any CA bundle to merge with mitm CA" >&2
6693
return 0
6794
}
6895

@@ -117,8 +144,18 @@ if is_truthy "${OPENSANDBOX_EGRESS_MITMPROXY_TRANSPARENT:-}"; then
117144

118145
if [ -f "$MITM_CA" ] && [ -s "$MITM_CA" ]; then
119146
trust_mitm_ca_nss "$MITM_CA" || true
120-
export NODE_EXTRA_CA_CERTS="$MITM_CA"
121-
export REQUESTS_CA_BUNDLE="$MITM_CA"
147+
export NODE_EXTRA_CA_CERTS="$MITM_CA" # additive — Node appends to built-in roots
148+
149+
# REQUESTS_CA_BUNDLE and SSL_CERT_FILE replace the default bundle,
150+
# so use merged roots (certifi/system CA + mitm CA).
151+
if [ -n "$OPENSANDBOX_MERGED_CA" ] && [ -f "$OPENSANDBOX_MERGED_CA" ]; then
152+
export REQUESTS_CA_BUNDLE="$OPENSANDBOX_MERGED_CA"
153+
export SSL_CERT_FILE="$OPENSANDBOX_MERGED_CA"
154+
else
155+
echo "warning: merged CA bundle not available; REQUESTS_CA_BUNDLE/SSL_CERT_FILE will only contain the mitm CA" >&2
156+
export REQUESTS_CA_BUNDLE="$MITM_CA"
157+
export SSL_CERT_FILE="$MITM_CA"
158+
fi
122159
fi
123160
fi
124161

@@ -135,6 +172,27 @@ if ! touch "$EXECD_ENVS" 2>/dev/null; then
135172
fi
136173
export EXECD_ENVS
137174

175+
# Run a user-defined pre-script before launching execd. The script is sourced
176+
# with POSIX `.` (not executed as a child process) so any variables it
177+
# `export`s propagate to execd and the chained command below — a subprocess
178+
# would lose those exports the moment it exits.
179+
if [ -n "${EXECD_BOOTSTRAP_PRE_SCRIPT:-}" ]; then
180+
if [ -f "$EXECD_BOOTSTRAP_PRE_SCRIPT" ] && [ -r "$EXECD_BOOTSTRAP_PRE_SCRIPT" ]; then
181+
# Force `.` to read the literal path; without a slash it would fall
182+
# back to a PATH search and could load the wrong file.
183+
case "$EXECD_BOOTSTRAP_PRE_SCRIPT" in
184+
*/*) _pre_script="$EXECD_BOOTSTRAP_PRE_SCRIPT" ;;
185+
*) _pre_script="./$EXECD_BOOTSTRAP_PRE_SCRIPT" ;;
186+
esac
187+
echo "sourcing pre-script $EXECD_BOOTSTRAP_PRE_SCRIPT"
188+
# shellcheck disable=SC1090
189+
. "$_pre_script"
190+
unset _pre_script
191+
else
192+
echo "warning: EXECD_BOOTSTRAP_PRE_SCRIPT=$EXECD_BOOTSTRAP_PRE_SCRIPT not found or not readable" >&2
193+
fi
194+
fi
195+
138196
echo "starting OpenSandbox Execd daemon at $EXECD."
139197
$EXECD &
140198

0 commit comments

Comments
 (0)