Skip to content

feat(networks): add portable sandbox port mappings#291

Draft
fuchencong wants to merge 16 commits into
mainfrom
feat/runtime-network-config
Draft

feat(networks): add portable sandbox port mappings#291
fuchencong wants to merge 16 commits into
mainfrom
feat/runtime-network-config

Conversation

@fuchencong

Copy link
Copy Markdown
Contributor

Summary

  • add Compose-style project networks, agent network attachments, internal expose, and external ports configuration
  • add a focused network manager that converts persisted network intent into runtime-neutral endpoint and TCP binding state
  • apply the same normalized bindings through Docker, BoxLite, and Microsandbox publishers while keeping runtime-specific behavior behind adapter/driver boundaries
  • preserve allocated listener ports across stop/resume and expose actual bindings through sandbox inspection APIs
  • support explicit Docker/runtime internal publish addresses, with Docker's default bridge IPv4 gateway as the fallback
  • keep port_mapping networks logical: they do not create project Docker networks or install firewall policy
  • allow expose long syntax to fix an internal listener port with host_port; omitted listener ports remain dynamically allocated
  • document the configuration, deployment requirements, and current network-policy boundary in English and Chinese

Example:

networks:
  frontend: {}
  backend:
    driver: port_mapping

agents:
  api:
    networks: [frontend, backend]
    expose:
      - "8080"
      - target: 9090
        host_port: 19090
        protocol: tcp
    ports:
      - "127.0.0.1:18080:8080"

Named networks currently record logical membership and annotate bindings. The driver does not provide DNS, transparent api:8080 routing, or enforced cross-network isolation.

Testing

  • task lint
    • passed with 0 issues.
  • clean-environment coverage gate (scripts/test-coverage.sh, with deployment/runtime/LLM override variables removed)
    • Unit: 75.78%
    • Integration: 62.25%
    • E2E: 61.12%
    • Combined: 78.12%
  • deployment gates
    • deploy/install_test.sh passed in a non-root temporary checkout
    • scripts/test-compose-kvm-config.sh passed
    • scripts/test-installer-assets.sh passed
  • task build
    • passed; full Linux daemon binary, protobuf packages, runtime SDK, and SDK packaging completed
  • focused network packages
    • go test ./pkg/compose ./pkg/runs ./pkg/networks ./pkg/agentcompose/api
    • passed
  • local runtime smoke matrix
    • native daemon + Docker: 36 PASS, 1 reachability observation, 0 failures
    • container-host/container-bridge daemon + Docker: 30 PASS, 2 reachability observations, 0 failures
    • container-host/container-bridge daemon + Microsandbox: same-network and multi-network data paths passed; cross-network reachability was recorded as an observation

The literal root-run task test command stops in the installer incomplete-rollback case because that test uses chmod a-w to force restoration failure and root can bypass the directory permission. The branch does not modify deploy/. Running the same deployment gate as a non-root user exercised the intended failure path and passed; the remaining coverage gate passed under the normal build user.

Microsandbox real-runtime resume also exposed an existing lifecycle failure (disk image ... is already attached with an incompatible disk mode). It is outside the network data path and was not changed in this PR.

Checklist

  • Documentation updated when behavior or configuration changed.
  • Tests added or updated for user-visible behavior.
  • No secrets, private endpoints, internal certificates, or local runtime state included.

@monkeyscan

monkeyscan Bot commented Jul 14, 2026

Copy link
Copy Markdown

PR Title: feat(networks): add portable sandbox port mappings

Commit: f04a778

本PR引入了沙箱网络(sandbox networks)功能,核心变更包括:

  1. 配置层:新增 NETWORK_DOCKER_PUBLISH_ADDRESSNETWORK_RUNTIME_PUBLISH_ADDRESS 两个 IPv4 环境变量,用于指定内部网络端口绑定的发布地址。

  2. 模型层:新增 SandboxNetworkIntentSandboxNetworkState,支持网络附件(attachments)、暴露端口(expose)和发布端口(ports)的声明与运行时状态。

  3. Compose 规范:扩展了 YAML/JSON 规范,支持顶层 networks 定义和每个 agent 的 networks/expose/ports 字段。未显式声明网络但使用 expose 时,会自动注入 default 网络。

  4. 网络管理器pkg/networks/manager.go):负责根据 intent 创建 binding,支持动态/固定主机端口分配,并能在重新准备时保留已分配的端口。

  5. 运行时适配:Docker 运行时通过 dockerSandboxPortConfig 将网络 binding 转换为 Docker 的 PortMap/PortSet;microsandbox 运行时通过 WithPortBindings 传递端口绑定。

  6. 会话存储sessionstore 新增 AllocateHostPort 机制,通过 net.Listen("tcp", "0.0.0.0:0") 分配临时端口,并持久化扫描已用端口,避免重启后复用。

  7. API 与 Proto:扩展了 v2 proto,支持网络相关的 spec 和 state 序列化。

整体架构清晰,测试覆盖较全。但在 Compose 规范的 published port 校验逻辑中存在一个漏判:当两条 ports 条目具有相同的 host IP 和固定 published 端口、但 target 端口不同时,未能识别为冲突。这会导致运行时 Docker 创建容器失败。

Comment thread pkg/compose/network.go
result := make([]PublishedPortSpec, 0, len(values))
seen := make(map[string]struct{}, len(values))
for index, value := range values {
protocol, err := normalizeTCPProtocol(value.Protocol)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Compose 规范中固定 published port 的重复冲突未被检测

在 normalizePublishedPorts 中,published port 的去重键包含了 target 端口:key := fmt.Sprintf("%s/%d/%d", hostIP, value.Published, value.Target)。这导致当两条 ports 条目具有相同的 host IP 和相同的固定 published 端口、但 target 端口不同时,不会被识别为重复。例如 "127.0.0.1:8080:80" 与 "127.0.0.1:8080:81" 在 Compose 规范化阶段不会报错,但在运行时 Docker 会尝试将同一个 host IP:port 绑定到两个不同的容器端口,导致容器创建失败。

Problem code:

Changed code at pkg/compose/network.go:176-185

Recommendation:
修改 normalizePublishedPorts 中的去重逻辑:仅当 published != 0(固定端口)时,使用 hostIP + published 作为去重键;动态端口可保持原有逻辑。并补充对应的单元测试。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant