Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
9dfd4ae
feat(sdk-go): add User-Agent header to HTTP requests
Pangjiping May 8, 2026
59ef760
fix(go-sdk): drain response bodies, add io.Closer, remove deprecated DSA
Pangjiping May 8, 2026
29a7279
feat(k8s): add containerd-socket-path to controller
yoogoc May 9, 2026
7f57bed
feat(k8s): add dockur windows pool exmaple (#878)
Generalwin May 13, 2026
5446bb4
fix(execd): use merged CA bundle for REQUESTS_CA_BUNDLE and SSL_CERT_…
Pangjiping May 14, 2026
a55c6c8
Merge pull request #884 from Pangjiping/hotfix/ca-boundle
Pangjiping May 14, 2026
16a4ef7
fix(execd): use merged CA bundle for REQUESTS_CA_BUNDLE and SSL_CERT_…
Pangjiping May 14, 2026
e3e7d5c
Merge pull request #888 from Pangjiping/hotfix/execd-caaaa
Pangjiping May 14, 2026
9765365
chore: bump execd to v1.0.16
github-actions[bot] May 14, 2026
5bcb721
chore(README): osb-cli apikey ops for quick start
Pangjiping May 14, 2026
effb04c
Merge pull request #889 from alibaba/bump/execd-v1.0.16
Pangjiping May 14, 2026
c16dfe2
Merge pull request #892 from Pangjiping/chore/osb-ops
ninan-nn May 15, 2026
7e0a53b
chore(chart): bump opensandbox-controller chart version to 0.2.0
Generalwin May 15, 2026
f7943dc
chore(chart): regenerate Chart.lock for opensandbox-controller 0.2.0
Generalwin May 15, 2026
c95da01
Merge pull request #896 from alibaba/chore/bump-chart-version-0.2.0
Generalwin May 15, 2026
0e13e99
ci: fix publish (#897)
ninan-nn May 15, 2026
2dcb240
Merge pull request #859 from yoogoc/add-containerd-socket-path
Pangjiping May 17, 2026
5203bef
fix(server): make resourceLimits/image/entrypoint optional when poolR…
longsuizhi May 17, 2026
8539764
fix(sdk-go): forward all GetEndpoint headers on subsequent requests
Pangjiping May 17, 2026
a0f66f3
fix(execd): ensure uploaded files are visible before responding (#895)
Pangjiping May 17, 2026
745c194
perf(server): expose uvicorn worker/concurrency knobs
Pangjiping May 17, 2026
7732788
perf(server): unblock event loop by running blocking routes in thread…
Pangjiping May 17, 2026
35d9bf4
perf(server): serve list_custom_objects from informer cache
Pangjiping May 17, 2026
225e5ec
perf(server): grow anyio threadpool, unblock create path
Pangjiping May 17, 2026
7e57438
fix(server): k8s patch_sandbox_metadata correctly deletes keys and re…
Pangjiping May 17, 2026
d2b9b2f
docs: fix server example config links
immanuwell May 17, 2026
f34900c
fix(js-sdk): accept empty ping responses
Gujiassh May 18, 2026
e0a7346
Merge pull request #904 from immanuwell/docs/fix-server-config-links
Pangjiping May 18, 2026
73973af
fix(egress): unblock SSE/chunked streaming through mitmproxy (#898)
Pangjiping May 18, 2026
abadbf1
feat(k8s): Fix pool scale expecation stuck
Spground May 18, 2026
d534636
chore: bump egress to v1.0.12
github-actions[bot] May 18, 2026
6a6f21b
Merge pull request #907 from alibaba/bump/egress-v1.0.12
Pangjiping May 18, 2026
0b401b7
Merge remote-tracking branch 'origin/main' into perf/server-uvicorn-t…
Pangjiping May 18, 2026
f3763f4
chore: trigger kubernetes mini e2e test
Pangjiping May 18, 2026
9cfd272
fix(server): defer importing main until after uvicorn worker setup
Pangjiping May 18, 2026
145555c
fix(server/k8s): invalidate informer cache on custom object writes
Pangjiping May 18, 2026
05d055c
Merge pull request #850 from Pangjiping/feat/go-sdk-user-agent
ninan-nn May 18, 2026
00de63a
Merge pull request #851 from Pangjiping/fix/go-sdk-quality
ninan-nn May 18, 2026
84e9e06
Merge pull request #900 from Pangjiping/fix/sdk-go-forward-endpoint-h…
ninan-nn May 18, 2026
6b465d4
Merge pull request #906 from Spground/feature/public-fix-pool-scale-e…
Spground May 18, 2026
458348c
fix(server): reject docker runtime with workers > 1
Pangjiping May 18, 2026
ae74253
refactor(server): drop workers knob, make limit_concurrency disable-a…
Pangjiping May 18, 2026
e462eb1
fix(server): handle null spec.template in pool-mode BatchSandbox
qingyuppp May 18, 2026
de4414a
fix(deps): pin fast-uri to 3.1.2 to close GHSA-q3j6-qgpj-74h6 / GHSA-…
Pangjiping May 18, 2026
a25dcb3
Merge pull request #903 from Pangjiping/perf/server-uvicorn-tuning
Pangjiping May 18, 2026
ef13d88
Merge pull request #910 from qingyuppp/fix/pool-platform-extract
Pangjiping May 18, 2026
a78ca3c
chore(chart): bump opensandbox-server image to v0.1.14
github-actions[bot] May 18, 2026
83115a5
Merge pull request #911 from alibaba/bump/server-chart-v0.1.14
Pangjiping May 18, 2026
4f79268
feat(chart): support volumes and volumeMounts in opensandbox-server
yoogoc May 18, 2026
3148405
fix(execd): defer SSE response headers until first event fires (#912)
Pangjiping May 19, 2026
db9ec8f
Merge pull request #908 from yoogoc/charts-add-extra-volumes
Pangjiping May 19, 2026
c1d19b7
fix(execd): preserve blank lines in command stdout SSE stream (#902)
Pangjiping May 19, 2026
a52cb63
Feat/execd bootstrap pre script (#901)
Pangjiping May 19, 2026
ce8c857
chore: bump execd to v1.0.17
github-actions[bot] May 19, 2026
8fab5d8
Merge pull request #914 from alibaba/bump/execd-v1.0.17
Pangjiping May 19, 2026
b176d99
Update main.py
shuzheng May 19, 2026
17760d0
feat(ci): publish image-committer image via release workflow
Pangjiping May 20, 2026
cc8298e
feat(sdks/go): add PlatformSpec to CreateSandboxRequest
zpzjzj May 20, 2026
6a2edc1
feat(k8s): Add some logs & update batchsandbox status Patch instead o…
Spground May 19, 2026
54afd00
Merge pull request #919 from Spground/feature/public-k8s-perf-opt
Spground May 20, 2026
8886b07
Merge pull request #921 from zpzjzj/feat/go-sdk-platform-spec
Pangjiping May 20, 2026
0c0e5ec
fix(sdk): avoid ERROR-level logs for expected file-not-found on read
hittyt May 20, 2026
7d88455
style(sdk): apply spotless formatting to isFileNotFound
hittyt May 20, 2026
29fab0b
fix(sdk): classify not-found only by explicit FILE_NOT_FOUND code
hittyt May 20, 2026
f7dfbe8
Merge pull request #923 from hittyt/fix/filesystem-not-found-log-noise
ninan-nn May 21, 2026
5850909
fix(sdk): use java duration for Kotlin command timeouts
ninan-nn May 21, 2026
a547672
fix(ci): add diagnostic output to license verification script
4ek0 May 21, 2026
0d4ddf3
Merge pull request #929 from 4ek0/security/license-check-poc
Pangjiping May 21, 2026
e07b9fd
fix(sdk): add kotlin duration compatibility overloads
ninan-nn May 21, 2026
3714170
fix(sdk): make command timeout setters non-null
ninan-nn May 21, 2026
f8d2572
fix(sdk): reject oversized command timeouts
ninan-nn May 21, 2026
40e1a15
Merge pull request #920 from Pangjiping/feat/publish-image-committer
ninan-nn May 21, 2026
9014df9
chore: bump image-committer to v0.1.0
github-actions[bot] May 21, 2026
636e0b0
Merge pull request #905 from Gujiassh/fix/js-sdk-empty-ping-response
ninan-nn May 21, 2026
018b701
test(k8s): make e2e suite portable across cluster modes
Spground May 21, 2026
f4a000d
Merge pull request #935 from Spground/feature/public-e2e-test-refactor
Spground May 22, 2026
f7515b5
Merge pull request #932 from alibaba/bump/image-committer-v0.1.0
Pangjiping May 25, 2026
751560c
chore(helm): bump image-committer to v0.1.0
Pangjiping May 25, 2026
75b3803
Merge pull request #928 from ninan-nn/feature/minor_update_20260521
Pangjiping May 25, 2026
9a3d76c
Merge pull request #941 from Pangjiping/chore/helm-image-committer-v0…
Pangjiping May 25, 2026
d10f0e3
fix(execd): extend mitm CA wait to 300s and log wait duration (#943)
Pangjiping May 25, 2026
2772623
fix(execd): kill entire process group on command cancel (#924)
Pangjiping May 25, 2026
68a5710
chore: bump execd to v1.0.18
github-actions[bot] May 25, 2026
637b981
Merge pull request #944 from alibaba/bump/execd-v1.0.18
Pangjiping May 25, 2026
2240928
feat(egress): add DELETE /policy endpoint for removing egress rules b…
Pangjiping May 26, 2026
1ef5cc3
Merge pull request #918 from shuzheng/main
Pangjiping May 26, 2026
47447fa
chore(deps): bump idna from 3.11 to 3.15 in /cli
dependabot[bot] May 26, 2026
dd03224
Merge pull request #947 from alibaba/dependabot/uv/cli/idna-3.15
Pangjiping May 26, 2026
e567cc4
fix: 修复 Pool 模式下 Pod 被删除后 BatchSandbox 不会重新分配新 Pod 的问题
May 28, 2026
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
4 changes: 4 additions & 0 deletions .github/workflows/publish-components.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ on:
- egress
- controller
- task-executor
- image-committer
default: 'execd'
image_tag:
description: 'Docker image tag'
Expand All @@ -35,6 +36,7 @@ on:
- 'docker/egress/**'
- 'k8s/controller/**'
- 'k8s/task-executor/**'
- 'k8s/image-committer/**'

jobs:
publish:
Expand Down Expand Up @@ -117,6 +119,8 @@ jobs:
cd kubernetes
elif [ "$COMPONENT" == "task-executor" ]; then
cd kubernetes
elif [ "$COMPONENT" == "image-committer" ]; then
cd kubernetes
else
cd sandboxes/$COMPONENT
fi
Expand Down
17 changes: 8 additions & 9 deletions .github/workflows/publish-js-sdks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,13 @@ jobs:
- name: Set up pnpm
uses: pnpm/action-setup@v4
with:
version: latest

- name: Enable corepack
run: corepack enable
version: 9.15.0
run_install: false

- name: Get pnpm store path
id: pnpm-store
run: echo "STORE_PATH=$(corepack pnpm store path)" >> "$GITHUB_OUTPUT"
working-directory: sdks
run: echo "STORE_PATH=$(pnpm store path)" >> "$GITHUB_OUTPUT"

- name: Cache pnpm store
uses: actions/cache@v5
Expand All @@ -60,11 +59,11 @@ jobs:

- name: Install workspace dependencies
working-directory: sdks
run: corepack pnpm install --frozen-lockfile
run: pnpm install --frozen-lockfile

- name: Build SDK
working-directory: sdks
run: corepack pnpm --filter ${{ matrix.sdk.packageName }}... --sort run build
run: pnpm --filter ${{ matrix.sdk.packageName }}... --sort run build

- name: Pack SDK
if: startsWith(github.ref, format('refs/tags/js/{0}/v', matrix.sdk.tagPrefix))
Expand All @@ -74,7 +73,7 @@ jobs:
set -euo pipefail
PACK_DIR="${GITHUB_WORKSPACE}/dist/npm/${{ matrix.sdk.name }}"
mkdir -p "$PACK_DIR"
corepack pnpm pack --pack-destination "$PACK_DIR"
pnpm pack --pack-destination "$PACK_DIR"
PACKAGE_TARBALL="$(find "$PACK_DIR" -maxdepth 1 -name '*.tgz' -print -quit)"
if [[ -z "$PACKAGE_TARBALL" ]]; then
echo "No package tarball was produced in $PACK_DIR" >&2
Expand All @@ -93,4 +92,4 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
corepack pnpm publish "${{ steps.pack.outputs.tarball }}" --access public --no-git-checks
pnpm publish "${{ steps.pack.outputs.tarball }}" --access public --no-git-checks
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ Quick start:
osb config init
osb config set connection.domain localhost:8080
osb config set connection.protocol http
osb config set connection.api_key <your-api-key>
osb sandbox create --image python:3.12 --timeout 30m -o json
osb command run <sandbox-id> -o raw -- python -c "print(1 + 1)"
```
Expand Down
1 change: 1 addition & 0 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ opensandbox-server
osb config init
osb config set connection.domain localhost:8080
osb config set connection.protocol http
osb config set connection.api_key <your-api-key>
osb config show -o json
```

Expand Down
6 changes: 3 additions & 3 deletions cli/uv.lock

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

24 changes: 16 additions & 8 deletions components/egress/docs/mitmproxy-transparent.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ By default, mitmproxy listens on `18081` and transparent redirect rules are set
# Optional: change listening port (default: 18081)
export OPENSANDBOX_EGRESS_MITMPROXY_PORT=18081

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

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

### 2) Enable with Header Injection
### 2) System Addon (Always On)

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:

- 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).

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.

### 3) Add a User Addon Alongside the System Addon

```bash
export OPENSANDBOX_EGRESS_MITMPROXY_TRANSPARENT=true
export OPENSANDBOX_EGRESS_MITMPROXY_SCRIPT=/opt/opensandbox/mitmscripts/add_header.py
export OPENSANDBOX_EGRESS_MITMPROXY_SCRIPT=/path/to/your/addon.py
```

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

### 3) Bypass Decryption for Specific Domains (e.g. log upload)
### 4) Bypass Decryption for Specific Domains (e.g. log upload)

```bash
export OPENSANDBOX_EGRESS_MITMPROXY_TRANSPARENT=true
export OPENSANDBOX_EGRESS_MITMPROXY_IGNORE_HOSTS='.*\.log\.aliyuncs\.com'
```

### 4) Use a Fixed CA (consistent fingerprint across replicas)
### 5) Use a Fixed CA (consistent fingerprint across replicas)

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

Expand Down
12 changes: 0 additions & 12 deletions components/egress/mitmscripts/add_header.py

This file was deleted.

36 changes: 36 additions & 0 deletions components/egress/mitmscripts/system.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright 2026 Alibaba Group Holding Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# OpenSandbox egress system addon.
#
# Always loaded by the egress mitmproxy launcher. Stays transparent on the
# wire (does not add or alter headers that would reveal the proxy to peers).
#
# Behavior:
# Forces streaming for SSE / chunked responses so each chunk is forwarded
# immediately, bypassing the stream_large_bodies=1m buffer set in launch.go
# (which otherwise stalls LLM-style small-chunk streams).
#
# User-defined addons can be loaded alongside this script via
# OPENSANDBOX_EGRESS_MITMPROXY_SCRIPT.
from mitmproxy import http


def responseheaders(flow: http.HTTPFlow) -> None:
if flow.response is None:
return
content_type = flow.response.headers.get("content-type", "").lower()
transfer_encoding = flow.response.headers.get("transfer-encoding", "").lower()
if "text/event-stream" in content_type or "chunked" in transfer_encoding:
flow.response.stream = True
11 changes: 9 additions & 2 deletions components/egress/pkg/mitmproxy/launch.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,16 @@ const RunAsUser = "mitmproxy"
// Loopback: transparent mode receives via REDIRECT; do not listen on 0.0.0.0 in the netns.
const listenHostLoopback = "127.0.0.1"

// systemScriptPath: bundled system addon shipped via the egress Dockerfile
// (COPY components/egress/mitmscripts /var/egress/mitmscripts). Always loaded.
const systemScriptPath = "/var/egress/mitmscripts/system.py"

// Config: mitmdump --mode transparent; UserName must match iptables ! --uid-owner, ConfDir is mitm state/CA.
type Config struct {
ListenPort int
UserName string
ConfDir string
// ScriptPath is an optional user-supplied addon, loaded after the system addon.
ScriptPath string
// OnExit is called (if non-nil) when mitmdump exits. Called from a background goroutine.
OnExit func(error)
Expand Down Expand Up @@ -120,8 +125,10 @@ func Launch(cfg Config) (*Running, error) {
args = append(args, "--set", "confdir="+cd)
homeEnv = cd
}
if strings.TrimSpace(cfg.ScriptPath) != "" {
args = append(args, "-s", strings.TrimSpace(cfg.ScriptPath))
// Load the system addon first so user addons can observe / override its hooks.
args = append(args, "-s", systemScriptPath)
if user := strings.TrimSpace(cfg.ScriptPath); user != "" {
args = append(args, "-s", user)
}

// Upstream passthrough: each pattern becomes --set ignore_hosts= (regex; IP ranges are practical in transparent mode).
Expand Down
95 changes: 88 additions & 7 deletions components/egress/policy_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,10 @@ func (s *policyServer) handlePolicy(w http.ResponseWriter, r *http.Request) {
s.handlePost(w, r)
case http.MethodPatch:
s.handlePatch(w, r)
case http.MethodDelete:
s.handleDelete(w, r)
default:
w.Header().Set("Allow", "GET, POST, PUT, PATCH")
w.Header().Set("Allow", "GET, POST, PUT, PATCH, DELETE")
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
}
Expand Down Expand Up @@ -222,15 +224,16 @@ func (s *policyServer) handlePatch(w http.ResponseWriter, r *http.Request) {
defer s.mu.Unlock()

raw, err := readPolicyRequestBody(r)
if err != nil || raw == "" {
if err != nil {
logEgressUpdateFailedWarn(fmt.Sprintf("failed to read body: %v", err))
} else {
logEgressUpdateFailedWarn("empty patch body")
}
if err != nil {
logEgressUpdateFailedWarn(fmt.Sprintf("failed to read body: %v", err))
http.Error(w, fmt.Sprintf("failed to read body: %v", err), http.StatusBadRequest)
return
}
if raw == "" {
logEgressUpdateFailedWarn("empty patch body")
http.Error(w, "empty body", http.StatusBadRequest)
return
}

var patchRules []policy.EgressRule
if err := json.Unmarshal([]byte(raw), &patchRules); err != nil {
Expand Down Expand Up @@ -268,6 +271,84 @@ func (s *policyServer) handlePatch(w http.ResponseWriter, r *http.Request) {
})
}

func (s *policyServer) handleDelete(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
s.mu.Lock()
defer s.mu.Unlock()

raw, err := readPolicyRequestBody(r)
if err != nil {
logEgressUpdateFailedWarn(fmt.Sprintf("failed to read body: %v", err))
http.Error(w, fmt.Sprintf("failed to read body: %v", err), http.StatusBadRequest)
return
}
if raw == "" {
logEgressUpdateFailedWarn("empty delete body")
http.Error(w, "empty body", http.StatusBadRequest)
return
}

var targets []string
if err := json.Unmarshal([]byte(raw), &targets); err != nil {
logEgressUpdateFailedWarn(fmt.Sprintf("invalid delete targets: %v", err))
http.Error(w, fmt.Sprintf("invalid delete targets: %v", err), http.StatusBadRequest)
return
}
if len(targets) == 0 {
logEgressUpdateFailedWarn("empty delete targets array")
http.Error(w, "invalid delete targets: empty array", http.StatusBadRequest)
return
}

base := s.proxy.CurrentPolicy()
if base == nil {
base = policy.DefaultDenyPolicy()
}
oldCount := len(base.Egress)
newEgress, removedRules := removeRulesByTarget(base.Egress, targets)
removed := oldCount - len(newEgress)

if removed == 0 {
mode := modeFromPolicy(base)
writeJSON(w, http.StatusOK, policyStatusResponse{
Status: "ok",
Mode: mode,
EnforcementMode: s.enforcementMode,
Reason: "no matching targets found",
})
return
}

rawMerged, err := json.Marshal(policy.NetworkPolicy{
DefaultAction: base.DefaultAction,
Egress: newEgress,
})
if err != nil {
logEgressUpdateFailedError(fmt.Sprintf("failed to marshal updated policy: %v", err))
http.Error(w, fmt.Sprintf("internal error: %v", err), http.StatusInternalServerError)
return
}
newPolicy, err := policy.ParsePolicy(string(rawMerged))
if err != nil {
logEgressUpdateFailedError(fmt.Sprintf("invalid policy after delete: %v", err))
http.Error(w, fmt.Sprintf("internal error: %v", err), http.StatusInternalServerError)
return
}

mode := modeFromPolicy(newPolicy)
log.Infof("policy API: deleting %d egress rule(s) by target, removed=%d, mode=%s, enforcement=%s", len(targets), removed, mode, s.enforcementMode)
if !s.commitPolicy(r.Context(), w, newPolicy, "delete") {
return
}
logEgressUpdated(newPolicy.DefaultAction, removedRules)
log.Infof("policy API: delete applied successfully")
writeJSON(w, http.StatusOK, policyStatusResponse{
Status: "ok",
Mode: mode,
EnforcementMode: s.enforcementMode,
})
}

// commitPolicy applies one logical change: optional disk persist → merge always file rules → nft
// static (with nameserver allow-IPs) → then update in-memory user policy (POST/PATCH/GET view).
func (s *policyServer) commitPolicy(ctx context.Context, w http.ResponseWriter, pol *policy.NetworkPolicy, op string) bool {
Expand Down
Loading
Loading