diff --git a/docs/changelog/index.md b/docs/changelog/index.md index 058a753e1..ffa825917 100644 --- a/docs/changelog/index.md +++ b/docs/changelog/index.md @@ -7,6 +7,7 @@ sidebar: false | Version | Date | |---------|------| +| [v0.5.0](./v0.5.0.md) | 2026.07.03 | | [v0.4.0](./v0.4.0.md) | 2026.06.14 | | [v0.3.1](./v0.3.1.md) | 2026.06.04 | | [v0.3.0](./v0.3.0.md) | 2026.06.02 | diff --git a/docs/changelog/v0.5.0.md b/docs/changelog/v0.5.0.md new file mode 100644 index 000000000..a9a0ded7f --- /dev/null +++ b/docs/changelog/v0.5.0.md @@ -0,0 +1,277 @@ +--- +title: v0.5.0 — 2026.07.03 +--- + +## 2026.07.03 Release v0.5.0 + +**Lifecycle**: `lifecycle` kwarg wire shape matches e2b (`onTimeout` / `autoResume`); the platform handles auto-pause/auto-resume state transitions. `kill-on-timeout` is the new default. The previously-stubbed `set_timeout` / `refresh` APIs are implemented, with a stable `end_at` timestamp. + +**Network**: per-sandbox traffic access tokens (auth for public-facing sandboxes); CubeEgress fail-closed during bootstrap; CubeVS allows sandbox-to-host-service access. A network hardening guide for production deployments is included. + +**Image and template**: pure-Go native rootfs export, TUI template management with pull progress tracking, real envd version probing. Fixes a uid/gid squashing issue that was preventing some non-root images from starting. + +**Deployment**: `--mode upgrade` flow with three-way `.env` merge, external MySQL/Redis support, Terraform-based Tencent Cloud cluster deployment, configurable TKE replica counts. + +**Control-plane stability**: rework of the snapshot runtime binding model, fixing MySQL 1213 deadlocks observed under concurrent rollback; fix for the cross-replica node metadata sync; rework of the template-deletion resource cleanup paths. + +85 commits from 22 contributors. + +### 🎯 Major Features + +#### e2b-Compatible Sandbox Lifecycle + +`lifecycle` is now a first-class kwarg on `Sandbox.create`, and the platform handles the auto-pause/auto-resume state transitions internally. The opt-in is per-sandbox (not per-cluster), so existing deployments keep their behavior. + +- **Auto-pause / auto-resume stack** (#613): The control plane publishes sandbox state into a shared Redis HSet snapshot plus an append-only event stream that the new `cube-proxy-sidecar` (bundled in the cube-proxy image, statically built for the musl/Alpine runtime) consumes via `XREADGROUP`. On a request, the data-plane `rewrite_phase` consults a state dict and either forwards traffic, returns 503 + `Retry-After`, or fires an internal sub-request to the sidecar's `/internal/resume`. Failure paths are explicit: not-found (130483) evicts the registry; already-in-state (130490) reconciles and treats as success; generic RPC failure rolls back and lets the sweeper retry. The sweeper has a bootstrap-warmup window so a fresh sidecar doesn't pause everything before its first `last_active` poll lands. +- **Kill-on-timeout and `set_timeout` / `refresh`** (#624): The new default timeout path is `kill`; the sidecar `sweeper` now dispatches to `tryPause` or `tryKill` based on `auto_pause`. `sandbox_state.lua` recognizes `killing` / `killed` and returns `410 Gone` (310410) so SDK clients stop retrying immediately. `DestroyReq` carries `KillReason` written into the `cube.master.instance.kill_reason` annotation. New endpoints `POST /cube/sandbox/timeout` and `POST /cube/sandbox/refresh` set `TimeoutSeconds` and refresh `EndAt`. +- **Stable `end_at` and SDK error mapping** (#624, #553): `GetSandboxResponse.end_at` is no longer always `None`. The CubeAPI `parse_response` path now remaps business codes: 130409 → HTTP 409 (capacity rejection carries the backend `ret_msg` verbatim, e.g. `"resume rejected by paused_resource_release_ratio policy: need X > quota Y"`), 130404 → HTTP 404. The WebUI surfaces pause/resume/kill failures (previously silent) on the sandbox list and detail pages, formatting 409 capacity rejections with the reported quota numbers. + +#### Paused Resource Release Ratio + +Pausing a sandbox snapshots memory to disk and shuts the MicroVM down — the host CPU/RAM it held is already reclaimed. The node resource accounting, however, still counted paused/pausing sandboxes against the node quota, so the scheduler kept rejecting new sandboxes even when capacity was physically free. This adds a node-local dial that trades resume headroom for scheduling density. + +- **Configurable release ratio** (#553): `host.quota.paused_resource_release_ratio` (in `[0, 1]`, default 0 = no behavior change) controls how much of a paused/pausing sandbox's CPU/memory quota is released back to the scheduler, with the rest reserved as resume headroom. 1.0 = release everything, maximum density with best-effort resume; 0 < r < 1 = partial release. Because the reserved quota still shows up in `QuotaCpuUsage` / `QuotaMemUsage`, a pause-heavy node is naturally deprioritised by the cpu/mem scoring factors. +- **Best-effort resume admission** (#553): `UpdateWithResume` now does a local, real-time admission check and rejects the resume with 130409 → HTTP 409 when the node can no longer fit the released fraction of the sandbox, preventing host memory overcommit. The accounting and admission logic are extracted into pure functions (`aggregateSandboxResources`, `clampRatio`, `resumeQuotaRejection`) covered by unit tests for zero/one/partial ratios, the pausing transient, quota boundaries, ratio clamping (incl. non-finite inputs), and the policy-disabled no-op. +- **Targets high-density, mostly-idle agent fleets**: the ratio trades resume headroom for higher scheduling density. Both the accounting and the admission logic are node-local — resume does not traverse the scheduler, so cubemaster needs no changes. + +#### Per-Sandbox Traffic Access Tokens + +Sandboxes created with `network.allow_public_traffic=false` now receive a per-sandbox `traffic_access_token`; CubeProxy rejects inbound requests that don't carry it in either `e2b-traffic-access-token` (E2B-compatible) or `cube-traffic-access-token` (native alias). Defaults unchanged. + +- **Token mint and propagation** (#639): `SandboxProxyMap` gains `AllowPublicTraffic` / `TrafficAccessToken`; `setProxyToRedis()` mints a UUID token after Redis acks the write. `mergeCubeNetworkConfigs` propagates the flag so the per-create override wins over the template's default. CubeAPI threads `allowPublicTraffic` into the wire request and surfaces `traffic_access_token` on the create response. +- **Data-plane enforcement** (#639): `enforce_traffic_token()` in `sandbox_backend.lua` gates both the cold path and the cache-hit path. 403 has empty body; token values are never logged. +- **SDK surface** (#639): Python SDK `Sandbox.traffic_access_token` property is `None` for unrestricted sandboxes and delivered exactly once at create time. + +#### CubeEgress Hardening + +- **Fail-closed bootstrap** (#670): Returns 403 while `bootstrap_status` is pending/unknown instead of allowing traffic through, so the data plane does not serve requests before policies are loaded. +- **Multiarch support** (#669): CubeEgress images build for `amd64` and `arm64`. + +#### Sandbox-to-Host-Service Access + +- **Allow host service access from inside sandboxes** (#681): Previously impossible even when the host service IP was in `allow_out`. The default TPROXY interception on host TCP 80/443 can be bypassed for specific host IPs with iptables rules of the form `iptables -t mangle -I TRANSPROXY 1 -d $HOST_IP/32 -p tcp --dport 80 -j RETURN`. + +#### Pure-Go Native Image Rootfs Export + +A new opt-in export mode that bypasses `docker` / `skopeo` / `umoci` and writes the rootfs natively in Go. Enabled by `CUBEMASTER_NATIVE_ROOTFS_EXPORT_ENABLED=true`. + +- **Concurrent prefetch pipeline** (#558): Uses `errgroup` for immediate context cancellation; if any layer fetch fails, the rest of the pipeline aborts and propagates the error. +- **Loop-mount streaming extraction** (#558): Bypasses intermediate host directories and decompressed streams directly into the target `ext4.img` block device. +- **Layer-by-layer cleanup** (#558): "Decompress-and-delete" removes compressed temporary files layer by layer, minimising peak disk usage. Peak memory 22 MiB (vs 36 MiB for dockerless, ~39% lower); build duration 31.30s (vs 37.26s, ~16% faster) on the same image. +- **Cross-image layer caching** (#558, follow-up): Enabled by the new concurrency control — drop downloaded layer blobs into a local OCI-layout cache for shared layer reuse. + +#### Online Upgrade and External Database Support + +- **`--mode upgrade` flow** (#538): Auto-detects existing installations via `.one-click.env`; performs a **three-way `.env` merge** (user customizations preserved, new defaults adopted for untouched keys, explicit new-bundle overrides applied; falls back to two-way when baseline is unavailable); backs up old `.one-click.env`, component configs, and DB/key directories to a timestamped directory; runs preflight checks (disk space, semver compatibility, service health, CIDR conflict with skip option). New flags: `-y` / `--yes`, `--allow-downgrade`, `--allow-role-change`. Mode resolution: `install | upgrade | auto`. +- **External MySQL/Redis** (#514, #673): Opt-in path to point CubeSandbox at an existing MySQL and/or Redis server via `.env`. The installer wires every service and helper to the external endpoint and stops managing the corresponding local container, while deployments that leave it unset keep the bundled behavior. #673 hardens the path: shell-sensitive password characters (e.g. `$`, backticks, quotes, spaces) are persisted in a source-safe form, and the Redis preflight probes `--connect-timeout` / `--timeout` support before using them with a compatible fallback for older `redis-cli` builds. +- **Static builds for `cube-{api,master}`** (#583): Aligns the cubemaster / cube-api binaries with the project's static binary convention, simplifying packaging and one-click release bundles. + +#### Terraform Tencent Cloud Cluster Deployment + +- **Clustered Tencent Cloud deployer** (#629): A managed **TKE** control plane runs `cubemaster` / `cube-api` / `cube-proxy` / `cube-webui`, backed by cloud **MySQL + Redis**, with one or more **CVM PVM** compute nodes inside a private VPC reached through an SSH jumpserver (port 443). `create.sh` is the single entry point and `destroy.sh` tears everything down; both run directly from an extracted release bundle and can work fully offline. Provisioning is phased, fail-fast, and re-runnable: network → TCR → CVMs → image build/push → MySQL/Redis → TKE + addons → health checks → compute setup. +- **Configurable TKE replica counts** (#658): `cubemaster_replicas` / `cube_api_replicas` / `cube_proxy_replicas` / `cube_webui_replicas` are now Terraform variables (default 2). `cubemaster_replicas` remains the single source of truth for both `spec.replicas` and the conf's `default_headless_service_nodes_num`, so the master concurrency apportionment stays consistent. Wired through `TENCENTCLOUD_*_REPLICAS` → `TF_VAR_*` and persisted in `.env`. `terraform-validate` CI workflow (fmt + validate + shellcheck) covers `deploy/one-click/terraform/**`. + +#### Template Center TUI and Progress Tracking + +- **Pull progress tracking** (#580): Template image pull progress persists in Redis (new migration `template_image_pull_progress`) with a structured image engine abstraction; progress events flow back to operator tooling in real time. +- **TUI-driven template management** (#580): Interactive `template_tui` provides a navigable view of templates, their state, and active operations. +- **Template watch mode** (#580): Real-time monitoring of in-progress template jobs, with output suitable for both interactive and CI/operator use. +- **Latest job id surfaced across clients** (#546): `cubemastercli tpl ls` adds a `JOB_ID` column; `tpl info` prints `job_id`; CubeAPI `GET /templates` and `GET /templates/:id` return `jobID`; WebUI template cards show it and the detail page uses it to auto-poll in-progress builds; Python/Go SDK `TemplateInfo.job_id` parses it. Returns the **latest** job id (including READY/FAILED), not only in-progress ones; legacy templates with no job history omit the field. + +#### Real envd Version Probing + +- **Per-template version detection** (#650): Collected once on low-frequency template creation paths via `envd --version` executed inside the guest (containerd `task.Exec`, timeout + bounded output). The semver is stored as the template annotation `cube.master.components.envd.version` and propagated to sandboxes via the existing template-annotation path. Multi-node template creation collects per replica, converges to a template-level single value, then writes the template definition once. +- **Wire surface** (#650): CubeAPI reads `ext_info[components.envd.version]` on create, and `annotations[components.envd.version]` on get/list/connect/resume. Missing or invalid falls back to `0.2.0` to preserve legacy behavior. e2b SDKs that consume `envdVersion` for client-side feature gating now see the actual version. + +#### Node Label Management API + +- **Two new internal endpoints** (#633): `POST /internal/meta/nodes/{node_id}/labels` (merge) and `DELETE /internal/meta/nodes/{node_id}/labels?key=...` (remove). +- **Multi-master data sync** (#633): Node labels, registration, and status live in MySQL and converge across replicas via the existing `syncAllFromDB` (≤ 30s). The receiving replica wraps the read-merge-write in a DB transaction with `SELECT ... FOR UPDATE` to prevent concurrent label updates from silently overwriting each other. Admin-set labels are preserved across heartbeats; cubelet-reported labels take priority on conflict (system labels always reflect actual hardware state). +- **Label validation** (#633): Key format follows K8s `IsQualifiedName` (`[prefix/]name`); value is a qualified name. Reserved namespaces (`kubernetes.io` / `beta.kubernetes.io` / `cube.cloud.tencentcloud.com`, including subdomains) cannot be created or deleted via the admin API. Covers typical scenarios: environment segregation, planned maintenance, tenant / team isolation, custom topology, gradual canary rollout. + +#### AgentHub Security and Unification + +- **Drop LLM env-var fallback, DB-only encrypted config** (#602): Master encryption key auto-generated via CSPRNG on first startup, persisted in `t_agenthub_setting.secret_master_key`, cached in process memory (`OnceLock`). All `env_llm_*` fallback functions are removed; LLM provider / base_url / model / credential_mode / api_key resolve exclusively from DB. Encrypted values that can't be decrypted (e.g. under a previous key) fail closed by returning empty string ("unset") instead of leaking ciphertext. `--mode upgrade` actively deletes 13 obsolete env keys (including `AGENTHUB_DEEPSEEK_API_KEY`, `AGENTHUB_SECRET_KEY`) to prevent stale plaintext secrets from lingering. Settings dialog: removed the "environment" source type; only `database | none` remain. +- **Persist assistant state and auth settings** (#582): AgentHub assistant auth/settings and OpenClaw runtime state are persisted; new auth guard/login flow and settings UI; snapshot/template persistence metadata and recovery-related API handling; one-click WebUI reverse proxy config updated for the new auth/API paths. +- **Unify OpenClaw runtime apply path** (#616): The three-branch dispatch (`configure_openclaw` / `configure_openclaw_model` / `configure_ds_openclaw`) collapses into a single `apply_openclaw_runtime` entry point. New types `LlmRuntimePlan` (model-to-namespace mapping), `OpenClawApplyOptions` / `OpenClawApplyMode` (declarative init mode selection) make the intent explicit. `update_agent_model` returns 501 — model is fixed at provision time; clone or recreate to switch models. Net −70 lines. +- **Migrations owned by CubeMaster** (#599, #590): AgentHub OpenClaw schema changes move into a new CubeMaster goose migration; CubeAPI no longer runs an ad-hoc AgentHub migration on database connect. The 0006 / 0008 migrations are made idempotent for partially migrated databases. + +#### Migration Identity Integrity + +- **Immutable migration identities** (#620): Three-layer defence against rebase-time silent migration skips: + 1. **Naming convention**: Historical block 0001-0010 frozen; all new migrations MUST use a 14-digit UTC timestamp prefix (`YYYYMMDDhhmmss`) that is globally unique and never needs renaming on rebase. + 2. **Allow out-of-order application**: `goose.WithAllowOutofOrder` enabled so timestamped migrations authored earlier but merged later do not hard-fail startup. Safe because every migration uses idempotent `*_if_missing` helpers. + 3. **Content fingerprinting**: `t_cubemaster_migration_fingerprint` records the SHA-256 of every applied migration; on each startup, the on-disk content is verified against the recorded fingerprint for every applied version. A mismatch fails loudly. Escape hatch: `CUBEMASTER_MIGRATION_SKIP_FINGERPRINT_CHECK=1`. + 4. **CI guard**: `migration-check.yml` workflow runs `TestMigrationFilenames` (no Docker) and rejects M/D/R changes to already-merged migration files via `git diff`. `scripts/new-migration.sh` enforces the timestamp convention at creation time. + +#### SDK Filesystem API Alignment + +- **Complete Python and Go filesystem API** (#678): All methods wrap envd's native endpoints — no new server-side changes required. + + | Method | Go (`Files`) | Python (`Filesystem`) | envd endpoint | + |--------|-------------|----------------------|---------------| + | `List` | `List(ctx, path)` | `list(path)` | `POST /filesystem.Filesystem/ListDir` | + | `Stat` | `Stat(ctx, path)` | `stat(path)` | `POST /filesystem.Filesystem/Stat` | + | `Exists` | `Exists(ctx, path)` | `exists(path)` | Stat + 404 check | + | `Remove` | `Remove(ctx, path)` | `remove(path)` | `POST /filesystem.Filesystem/Remove` | + | `Rename` | `Rename(ctx, old, new)` | `rename(old, new)` | `POST /filesystem.Filesystem/Move` | + | `MakeDir` | `MakeDir(ctx, path)` | `make_dir(path)` | `POST /filesystem.Filesystem/MakeDir` | + | `WriteFiles` | `WriteFiles(ctx, entries)` | `write_files(files)` | Batch `POST /files` | + | `WatchDir` | `WatchDir(ctx, path)` | `watch_dir(path)` | `POST /filesystem.Filesystem/WatchDir` (Connect streaming) | + +- **Per-host request transforms** (#568): Python SDK accepts E2B's `{host: [{transform: {headers: {...}}}]}` shape in `Sandbox.create(network={"rules": ...})` and translates it into CubeEgress L7 rules with `action.inject` entries. Each transform entry becomes one Rule (`e2b-transform-[-]`); each header pair becomes one Inject. Malformed inputs raise `ValueError` instead of being silently dropped. +- **Create-time env vars propagated to envd** (#566): `envs` is accepted as an alias of `envVars` in `NewSandbox` (E2B SDK compatible). Create-time envs are forwarded to CubeMaster as `create_time_env_vars` and serialized into an internal annotation; Cubelet triggers envd `/init` after sandbox startup and probe success, and uses the propagated `cube.master.components.envd.version` annotation when present. Falls back to probing the default envd init endpoint with bounded retry when the annotation is missing. Sandbox creation fails explicitly if envd init still can't complete. +- **Cube-bench `--host-mount` flag** (#651): New `--host-mount ''` flag for benchmarking create-time overhead with host-mount sandboxes. Validated and compacted once at config parse, sent as-is on every request. +- **JSON roundtrip fix** (#572): `Execution.to_json()` no longer double-encodes `logs` and `error` — new `to_dict()` helpers return nested objects while `Logs.to_json()` and `ExecutionError.to_json()` keep returning JSON strings (matching their method names). + +### ✨ Enhancements + +#### Scheduling and Control Plane + +- **Configurable HTTP listen address for CubeMaster** (#662): `common.http_bind` (default `0.0.0.0`) decouples the one-click and TKE deployment paths — one-click renders `__CUBEMASTER_HTTP_BIND__` from `CUBEMASTER_HTTP_BIND`; TKE uses `yamlencode` directly. Cubelet / CubeAPI / cube-proxy are intentionally not tightened because binding them to 127.0.0.1 would break multi-node (Cubelet gRPC) or WebUI (CubeAPI via `host.docker.internal`) reachability. +- **Multi-node scheduling scoring guidance** (#672): Documents that multi-node CubeMaster deployments should configure `scheduler.score` to avoid deterministic placement on the first sorted node; includes a baseline scoring example based on the workaround in #573. +- **Multi-replica node metadata sync** (#542): `loopReload` goroutine now started in `Init()`, fixing the bug where node metadata registered on one CubeMaster replica was never visible to other replicas. `reload()` is replaced with `applyReloadResult()` that does field-level merge: registration fields always take DB value; heartbeat/status fields keep the in-memory value when it is fresher than the DB snapshot; new nodes are added directly. Prevents the race where a slow DB scan could overwrite a healthy node's heartbeat that arrived on the current replica during the scan. + +#### Template Management + +- **Image uid/gid preservation in rootfs build** (#608, #671): `umoci unpack --rootless` only used when `euid != 0`; as root, unpack without it so the image's original ownership is preserved. The docker-export fallback now uses `tar -xf` with `--same-owner --numeric-owner`. Verified: python-slim template `/home/user` is back to 1000:1000 and the in-sandbox default user account works; browser template's Chromium starts and serves CDP. +- **Image layer squash fix** (#671): Removes the unconditional `WithNoSameOwner()` call so `archive.Apply` preserves the original uid/gid from image layers when running as root. +- **Template preview route registered** (#570): `POST /cube/sandbox/preview` is now wired into the CubeMaster HTTP mux. `cubemastercli tpl render` returns the existing three-layer preview (`api_request` / `merged_request` / `cubelet_request`) instead of 404. +- **`tpl info` field completeness** (#592, #594): Server `templateResponse` and CLI `templateResponse` add `display_name` / `created_at` / `image_info`. `GetTemplateInfo()` populates `CreatedAt` from the persisted template definition and `ImageInfo` from the stored request and latest image job, matching `ListTemplates()`. +- **`with_cube_ca` forwarded by CubeAPI** (#652): `CreateTemplateRequest` and `CreateTemplateFromImageReq` carry `with_cube_ca`; `TemplateService::create_template` forwards it. CubeMaster default `true` is preserved when omitted or `null`. + +#### Image and Build + +- **Native rootfs export** (#558, see Major Features): Pure-Go export path; concurrent prefetch + loop-mount streaming + decompress-and-delete. +- **Template image pull progress + TUI** (#580, see Major Features). +- **Template delete resource cleanup** (#631): Three-phase `cleanupArtifactFully` (lock + drop refs + placement snapshot → physical delete → re-check refs + delete placement/artifact) plus `startArtifactGC` (single-instance `GET_LOCK`) for `CLEANUP_PENDING` convergence. New migration `20260623145000_artifact_node_placement.sql` (new table + backfill from replica; legacy rows with only `node_ip` use synthetic key `ip:`). Cubelet gets `ext4image.DestroyPmemArtifact`, `DeleteByKind` opposite-kind retry, aggregated cleanup errors. CubeAPI cascades AgentHub deletes (market templates do not cascade `tpl-*`; non-market templates branch on `snapshot_kind` → OpenClaw dir cleanup or `snapshots.delete`) and best-effort soft-deletes AgentHub rows after infrastructure `DELETE /templates`. Out of scope: sandbox rollback old rootfs cleanup (follow-up), runtemplate bolt deregistration, AgentHub IDOR auth, pre-upgrade disk orphans. +- **Snapshot delete cleans job rows** (#559): `runSnapshotDeleteJob` now calls `cleanupTemplateJobs` after metadata cleanup, matching template delete behavior. Fixes the bug where deleted snapshots reappeared in `tpl ls` until a second delete. + +#### Deployment + +- **Configurable CubeMaster HTTP bind** (#662, see Scheduling). +- **External MySQL/Redis** (#514, #673, see Major Features). +- **Static cube-{api,master} builds** (#583). +- **TKE replica counts configurable** (#658, see Major Features). +- **CIDR preflight distinguishes same-CIDR reinstall** (#586): `cube-dev`'s own residual interface and `z*` TAP devices are skipped in both interface and route checks. Same network+mask reuses (logs only); overlapping different network dies with guidance; non-overlapping allows. Stop/start commands swapped to `systemctl stop/start 'cube-sandbox-*.target'` (verified: `PartOf=` propagates to all 14 `cube-sandbox-*` services; the legacy `down-with-deps.sh` pidfile path is a no-op against `Restart=on-failure` systemd services). +- **Compute control-plane preflight** (#657): `check_compute_control_plane_preflight()` validates `ONE_CLICK_CONTROL_PLANE_IP` or `ONE_CLICK_CONTROL_PLANE_CUBEMASTER_ADDR` before any package installation or extraction. Port derivation decoupled from `CUBEMASTER_ADDR` (fixed constant `8089` in all four resolution paths). Conflicting dual-variable config dies with a clear error. +- **Compute quickcheck retry** (#637): Wraps quickcheck in a bounded, configurable retry loop for systemd post-start races; covers install.sh, smoke.sh, deploy-manual.sh, create.sh and the systemd install path. +- **Proxy port split** (#588): `CUBE_PROXY_HOST_PORT` is deprecated; split into `CUBE_PROXY_HTTP_PORT` (default 80) and `CUBE_PROXY_HTTPS_PORT` (default 443). systemd post-start TCP check uses the new variables. +- **Fixed install root** (#649): `ONE_CLICK_INSTALL_PREFIX` / `ONE_CLICK_TOOLBOX_ROOT` removed; `/usr/local/services/cubetoolbox` is the single fixed install root. `CUBE_PROXY_CERT_DIR` defaults migrated accordingly. +- **Manual SQL seed removed** (#628): CubeMaster now owns single-node seed rows (`host_info` / `sub_host_info`) via embedded goose migrations; `sql/002_seed_single_node.sql` and the host-side mysql client seed logic are removed. +- **`DATABASE_URL` persisted** (#611): Local MySQL path writes `DATABASE_URL` into `RUNTIME_ENV_FILE`; `cube-api-start.sh` constructs it from `CUBE_SANDBOX_MYSQL_*` at service start when not set. +- **`MIRROR` persisted to `.one-click.env`** (#622): `install.sh` validates and persists `MIRROR` (empty or `cn`); `online-install.sh` exports it before launching the child installer. Fixes cube-egress defaulting to `-int` image when `MIRROR=cn` was set at install time. +- **semver comparison utilities** (#587): Full semver comparison in bash — `_version_trim_leading_zeroes`, `_version_compare_numbers`, `_version_split_semver`, `_version_compare_prerelease`, `semver_compare`, `version_lt` public API. Handles prerelease, build metadata, optional v-prefix. +- **Bundle includes `scripts/common`** (#597): `build-release-bundle.sh` was missing `scripts/common/validation.sh` from both the inner `sandbox-package.tar.gz` and the outer release tarball; install.sh and runtime scripts that source it were failing. +- **`resolvectl default-route` is best-effort** (#703, Closes #401): The systemd-v240 subcommand is no longer called unconditionally; older systemd (e.g. RHEL 8.3 with systemd 239) logs a note and continues. The `~cube.app` routing domain configured just above already scopes queries to the dummy link, so skipping the explicit default-route flag is harmless. Applied to both one-click and bundled systemd copies. + +#### Network and Data Plane + +- **CubeProxy disables buffering for envd streaming endpoints** (#647): `process.Process/Start`, `process.Process/Connect`, `filesystem.Filesystem/WatchDir` are unbuffered in both host-routed and path-routed server blocks. Global default buffering remains. Fixes two regressions: `commands.run(..., background=True)` blocking until the command exits, and `WatchDir`'s `start` event arriving only near the request timeout. +- **CubeProxy removes `cube_retcode` and faulty-backend dead code** (#653, #593): The `X-Cube-Retcode` response header and access-log `$cube_retcode` field exposed 6-digit error codes that an attacker could enumerate to determine sandbox existence, lifecycle state, and backend weak points. HTTP status codes collapse: existence / config / token errors → 404, infrastructure failures → 503, malformed request → 400. Uniform JSON error bodies (`{"error": "..."}`). The `faulty_backend` shared dict, `is_faulty_backend`, `assert_backend_healthy`, and the 340xxx branch in `header_filter_phase` are removed (they were never actually populated). `local_cache` shrinks from 500m to 100m (enough for ~100k sandboxes' metadata per single CubeProxy). +- **CubeVS: don't attach `from_world` to `lo`** (#656): It never worked. +- **CubeVS: BTF key/value for inner maps** (#595): Works around a kernel restriction on Ubuntu 22.04 + kernel v5.15 (`map.Put failed: update: invalid argument, name: allow_out_v2`). +- **CubeVS: regenerate BPF objects** (#623, follow-up to #617). +- **CubeVS: fix invalid TCP checksum on port-mapped sandbox replies** (#617): Cross-node port-mapped reply path was corrupting TCP checksums in the eBPF DNAT path, so TKE pods silently dropped the SYN-ACK and the handshake timed out (504 at /execute). Same-node and other NAT paths were unaffected. + +#### Security and Hardening + +- **MSI-X table/PBA OOB hardening** (#619): `pci/src/msix.rs` `read_table` / `write_table` / `read_pba` now bounds-check the index and return graceful error on mis-sized reads/writes (3 commits ported from cloud-hypervisor). Closes a guest-triggerable self-DoS of the VMM/shim process. +- **`X-Request-Method` forwarded to auth callback** (#315): Callback previously only received `X-Request-Path`, allowing a caller with read-only credentials to escalate to delete/rebuild on the same path. Callback can now enforce fine-grained (path + method) authz. +- **CubeEgress compute-node CA refresh** (#614): Compute nodes fetch CubeEgress CA materials from the master's `/cube/ca/{filename}` route on every prepare run, validate the downloaded cert/key pair, then install. Control nodes keep the local generate-if-missing behavior. + +#### AgentHub and Auth + +- **AgentHub state persistence** (#582, see Major Features). +- **OpenClaw runtime unification** (#616, see Major Features). +- **AgentHub migrations owned by CubeMaster** (#599, see Major Features). + +#### Virtualization + +- **virtio-blk exposes `VIRTIO_BLK_F_SEG_MAX` and `VIRTIO_RING_F_INDIRECT_DESC`** (#575): Allows the guest to put more than one segment per request, improving block I/O throughput. + +#### Observability + +- **Cubelet `image_forward_latency` histogram registered** (#515): `chi_cubebox_image_forward_latency_milliseconds` was created via `prometheus.NewHistogram` but never added to the `docker/go-metrics` namespace, so `metrics.Register(ns)` never exposed it on `/metrics`. +- **WebUI ignores `tsconfig.tsbuildinfo`** (#625): Auto-generated on each rebuild; caused noisy diffs. + +#### One-Click Installer Internals + +- **Redis single pool** (#604, #609): `redis_read` / `redis_write` / `redis_metadata` config sections and `RedisRead` / `RedisWrite` / `RedisMetaData` constants are removed. `GetRedis()` is now a parameterless single-pool API backed by the single `redis` config block. Centralized key definitions in `pkg/base/rediskey`; CubeProxy Lua scripts use the new key scheme; Redis data migration script ships; bilingual Redis key specification docs added. + +### 🐛 Bug Fixes + +These fixes address issues present in v0.4.0: + +- **CubeProxy streaming buffer regression** (#647, see Enhancements). +- **CubeVS cross-node port-mapped reply TCP checksum** (#617, see Enhancements). +- **MSI-X guest-triggerable panic** (#619, see Enhancements). +- **MySQL 1213 deadlock in concurrent snapshot rollback** (#693, see Engineering Improvements). +- **Cross-replica node metadata sync missing** (#542, see Scheduling). +- **Cubelet hostdir mount can hang indefinitely** (#691): An unhealthy host mount (e.g. geesefs with revoked remote access) blocked the hostdir bind step until upstream API timeout. The bind is moved out of the create goroutine into a bounded helper subprocess: read-write hostdir ≤ 3s per volume, read-only ≤ 6s per volume (bind + remount bounded separately). Hostdir backend metadata is recorded before the bind attempt so cleanup still knows the sandbox-scoped hostdir paths after a failed create. The top-level error category stays `CreateStorageFailed`; the message now makes the hostdir timeout explicit (e.g. `prepareHostDirVolume: bind mount /mnt/aime-oss/test -> /data/cubelet/hostdir//rw/hostdir-0 timed out after 3s: context deadline exceeded`). On the CubeAPI side, `parse_response` turns non-zero `ret_code` into an error before inspecting the response envelope, so the pause/resume/connect paths remap 130409 → HTTP 409 (forwarding the backend `ret_msg` verbatim). +- **Rootfs squashes uid/gid to root** (#608, #671, see Enhancements). +- **Snapshot delete leaves job rows** (#559, see Enhancements). +- **`tpl render` 404s** (#570, see Enhancements). +- **Template delete leaks node ext4 / cubecow / AgentHub resources** (#631, see Enhancements). +- **Python SDK `to_json` double-encoding** (#572, see SDK). +- **Python SDK `envs` silently dropped** (#566, see SDK). +- **Egress CA stale on compute nodes** (#614, see Enhancements). +- **CubeProxy `cube_retcode` leaks sandbox existence / lifecycle** (#653, see Enhancements). +- **CubeVS `from_world` attached to `lo`** (#656, see Enhancements). +- **CubeVS inner map kernel restriction** (#595, see Enhancements). +- **MSI-X OOB panic from guest** (#619, see Enhancements). +- **X-Request-Method missing in auth callback** (#315, see Enhancements). +- **External MySQL/Redis shell-sensitive password** (#673, see Major Features). +- **`MIRROR` not persisted** (#622, see Deployment). +- **`CUBE_PROXY_HOST_PORT` deprecation** (#588, see Deployment). +- **CIDR preflight false-positive on same-CIDR reinstall** (#586, see Deployment). +- **Compute node missing control-plane address fails late** (#657, see Deployment). +- **Compute quickcheck post-start race** (#637, see Deployment). +- **`resolvectl default-route` aborts install on systemd < v240** (#703, see Deployment). +- **`scripts/common` missing from release bundle** (#597, see Deployment). +- **MySQL 1213 deadlock mapping to wrong error code** (#693): `snapshotErrorCode()` was mis-reporting database errors as `MasterParamsError(130400)`; now correctly maps to `DBError`. +- **Tpl info missing fields** (#592, #594, see Enhancements). +- **`with_cube_ca` not forwarded** (#652, see Enhancements). +- **Cubelet `image_forward_latency` histogram not exposed** (#515, see Observability). +- **WebUI `tsconfig.tsbuildinfo` causes noisy diffs** (#625, see Observability). +- **CubeProxy `local_cache` over-sized** (#593, see Enhancements). + +### 📚 Documentation + +- **Network hardening guide (new, EN + ZH)** (#663): Default listening surface table (bind address, port, config method, auth/TLS status per service); per-service bind-address configuration with caveats (binding to 127.0.0.1 can break multi-node, WebUI, or health checks); hardening strategies: bind to a private NIC, firewall source-IP whitelisting (`iptables` / `ufw`); multi-node port reachability requirements; default credentials to rotate; `AUTH_CALLBACK_URL` mechanism; TLS notes. One-click deploy README (EN/ZH) adds a security callout linking to the guide. **Recommended reading before any public deployment.** +- **Snapshot/Clone/Rollback deep-dive blog** (#680): 11 architecture diagrams (EN + ZH) explaining the three kernel mechanisms behind snapshot, clone, and rollback: XFS reflink, `/proc/pagemap` anonymous page detection, soft-dirty bit. +- **Sandbox logs guide (new, EN + ZH)** (#692): `cubecli cubebox logs` reads the container init-process stdout/stderr; must be run on the node (enters the Cubelet mount namespace). `--tpl` flag for template build logs (host filesystem, no ns entry). Flag reference: `--stderr`, `--all`, `--tail N`, `--head N`. Log file paths for sandbox vs template. Scope: init-process only; envd sub-task logs retrieved via E2B SDK callbacks. Limitations: no `--follow`; logs deleted with sandbox. Nav entries under "Operations". +- **Lifecycle guide (new, EN + ZH)** (#613): State machine, every lifecycle method, auto-pause/auto-resume semantics, and operational notes. `examples/code-sandbox-quickstart/auto-resume.py` TUI demo verifies state survives the cycle across kernel memory and the filesystem. +- **Multi-node scheduling scoring guidance** (#672): Documents that multi-node CubeMaster deployments should configure `scheduler.score`; explains the deterministic-placement risk; provides a baseline scoring example. +- **Host mount permission fixes troubleshooting (new, EN + ZH)** (#560, Closes #239): `metadata["host-mount"]` maps an existing path on the Cubelet node and does not copy files, create a workspace snapshot, or rewrite ownership. Documents safe read-only workspace mounts, read-write ownership alignment, ACLs, and root-command tradeoffs. +- **Network deep-dive blog (EN + ZH)** (#627): 5 diagrams covering CubeVS (eBPF in-kernel L4 forwarding + LPM Trie IP/domain filtering), CubeProxy (OpenResty ingress gateway), and CubeEgress (L7 egress proxy with HTTPS interception, credential injection, and access auditing). +- **v0.4.0 release blog + agent-friendly-service use case blog (EN + ZH)** (#585): v0.4.0 release recap; Neon-style "fast spawn / clone / rollback" generalised to any traditional software service with hands-on Redis examples. +- **README enhancement** (#640): 6 product-highlight cards (boot speed, isolation, E2B migration, web console, credential vault, egress control); restructured changelog section with clearer v0.4 messaging; guide link consolidation. EN + ZH. +- **README benchmark links + arch diagram** (#646): Core Operations Performance Benchmark Report (bare metal) and PVM Cloud Server Benchmark Report links directly in README; updated architecture diagram. +- **Documentation optimization pass (EN + ZH)** (#665): Production-use warning callout at the top of Quick Start / Bare-Metal / PVM / Multi-Node guides (links to Network Hardening); architecture overview expanded with design principles, control-plane/data-plane split, core components (incl. CubeCoW, CubeEgress, WebUI), request lifecycle, storage / network / security sections; introduction repositioned as infrastructure for AI Agents with key advantages and a comparison table; "connect existing cluster" reframed as a four-option decision tree (Cube native SDK + `CUBE_PROXY_NODE_IP`, path mode for any HTTP client, wildcard DNS, dev-sidecar). +- **Doc sidebar + link fixes** (#664): ZH homepage architecture link, `Snapshot, Rollback & Clone` in Core Concepts sidebar, site-absolute network-hardening paths replaced with full URLs. +- **Internal links localised** (#668): Hard-coded `.html` / old English doc paths replaced with relative `.md` links so the Chinese and English language trees stay intact. +- **GitHub `main` → `master` link fixes** (#660): `docs/guide/restrict-public-access.md`, `docs/zh/...`, `docs/guide/security-proxy.md`, `docs/zh/...` and similar. Lifecycle anchor fix (`#platform-managed-auto-pause--auto-resume` → `#platform-managed-auto-pause-auto-resume`). +- **CODEOWNERS**: maintainer entries refreshed. + +### ⚙️ Engineering Improvements + +- **Snapshot runtime binding table + lock hardening** (#693): The new `t_cube_snapshot_runtime_active` table (PK `(sandbox_id, binding_type)`) is the authoritative current binding state; `INSERT ... ON DUPLICATE KEY UPDATE` provides InnoDB row locks as the concurrency control point, serialising even across multiple CubeMaster instances. The old `t_cube_snapshot_runtime_ref` is degraded to an audit/history log (INSERT-only). `SnapshotRuntimeBindingService` is the single write entry point (`AttachSnapshotRuntimeBinding` / `DetachSnapshotRuntimeBinding` / `RefreshSnapshotRuntimeBindingsFromNode`). Read paths (`ListActiveSnapshotRuntimeRefs` / `GetActiveSnapshotRuntimeRefBySandbox` / `CountActiveSnapshotRuntimeRefs` / `populateSnapshotRuntimeRefSummary` / `DeleteSnapshot` active check / `allocateNextRollbackGen`) switch from `WHERE status='ACTIVE'` to the active table. `snapshot_ops.go` introduces sandbox-level (`snapshot-sandbox:`), resource-level (`snapshot-resource:`), and request-level (`snapshot-request:`) distributed locks; `withSnapshotWriteLocks` recursively acquires multiple locks. Stress test `bench_rollback_concurrency.py -c 5 -n 20` and `-c 10`: no 1213 deadlocks; `SHOW ENGINE INNODB STATUS` shows no runtime-ref-related deadlocks. Migration retains the old table; all writers converge on the binding service; no application-level lock dependency for correctness. +- **Redis unification** (#604, #609, see One-Click Installer Internals). +- **WebUI `tsconfig.tsbuildinfo` gitignore** (#625, see Observability). +- **Build system: `cube-{api,master}` static builds** (#583, see Major Features). +- **Migration `scripts/new-migration.sh` helper** (#620, see Major Features). +- **Format check CI** (carried from v0.4.0, expanded): `migration-check.yml` workflow added on top of the existing `fmt-check.yml` (the agent's `fmt` target still auto-generates `version.rs` and protocol `.rs` before formatting). +- **Pause/resume accounting as pure functions** (#553): `aggregateSandboxResources`, `clampRatio`, `resumeQuotaRejection` extracted for unit-testability. +- **CubeProxy dead-code cleanup** (#593, see Enhancements). +- **WebUI TS incremental build ignore** (#625). + +--- + +**Upgrade notes** + +- `--mode upgrade` (#538) deletes 13 obsolete AgentHub env keys (#602) to prevent stale plaintext secrets from lingering; if you hand-configured any of `AGENTHUB_DEEPSEEK_API_KEY` / `AGENTHUB_SECRET_KEY` / etc., re-enter them through the WebUI Settings page after upgrade. +- External MySQL/Redis (#514) stops managing the corresponding local container once enabled; reverting to bundled mode requires manual reinstall. +- MySQL 1213 deadlock fix (#693) is schema-compatible at the table level — `t_cube_snapshot_runtime_active` is added and backfilled, `t_cube_snapshot_runtime_ref` is retained as a history/audit log. +- CubeEgress fail-closed (#670) means the data plane returns 403 during policy bootstrap; if you see "startup-time traffic denied", wait for the cube-egress CA and policy load to complete. +- Per-sandbox traffic tokens (#639) only apply when `network.allow_public_traffic=false`; clients that previously distinguished "token mismatch" by 403 should expect 404 (the security status-code convergence in #653). diff --git a/docs/guide/digital-assistant.md b/docs/guide/digital-assistant.md index 67d875899..a46517624 100644 --- a/docs/guide/digital-assistant.md +++ b/docs/guide/digital-assistant.md @@ -10,7 +10,7 @@ The Digital Assistant is a preview feature intended for demos and early validati AgentHub creates assistants from a CubeSandbox template. Before deployment, build the Digital Assistant template (see the command below), then copy the auto-generated `tpl-` prefixed template ID into `.env`: -```env +```bash AGENTHUB_DS_OPENCLAW_TEMPLATE= ``` diff --git a/docs/zh/changelog/index.md b/docs/zh/changelog/index.md index 315c72bab..29087bf6e 100644 --- a/docs/zh/changelog/index.md +++ b/docs/zh/changelog/index.md @@ -7,6 +7,7 @@ sidebar: false | 版本 | 发布日期 | |------|----------| +| [v0.5.0](./v0.5.0.md) | 2026.07.03 | | [v0.4.0](./v0.4.0.md) | 2026.06.14 | | [v0.3.1](./v0.3.1.md) | 2026.06.04 | | [v0.3.0](./v0.3.0.md) | 2026.06.02 | diff --git a/docs/zh/changelog/v0.5.0.md b/docs/zh/changelog/v0.5.0.md new file mode 100644 index 000000000..bf8fd350d --- /dev/null +++ b/docs/zh/changelog/v0.5.0.md @@ -0,0 +1,277 @@ +--- +title: v0.5.0 — 2026.07.03 +--- + +## 2026.07.03 发布 v0.5.0 + +**生命周期**:`lifecycle` kwarg 的 wire 形态与 e2b 对齐(`onTimeout` / `autoResume`),平台承担 auto-pause/auto-resume 状态机切换;`kill-on-timeout` 成为新默认行为;之前标注为 stub 的 `set_timeout` / `refresh` API 落地,`end_at` 时间戳稳定可用。 + +**网络**:per-sandbox 流量访问令牌(公网沙箱鉴权)、CubeEgress 启动期 fail-closed、CubeVS 允许沙箱访问宿主服务;附一份面向生产部署的网络安全硬化指南。 + +**镜像与模板**:纯 Go 原生 rootfs 导出、TUI 模板管理 + 拉取进度追踪、真实 envd 版本探测;修复镜像层 uid/gid 被压缩为 root 的问题(部分非 root 镜像因此启动失败)。 + +**部署**:`--mode upgrade` 升级流(三路 .env 合并)、外部 MySQL/Redis 支持、腾讯云 Terraform 集群化部署、TKE 副本数可配。 + +**控制面稳定性**:重构 snapshot 运行时绑定模型,修复高并发回滚下观察到的 MySQL 1213 死锁;修复跨副本节点元数据同步;重写模板删除的资源清理路径。 + +85 个 commits,22 位贡献者。 + +### 🎯 主要特性 + +#### 与 e2b 兼容的沙箱生命周期 + +`lifecycle` 现在是 `Sandbox.create` 的一等参数,平台承担 auto-pause/auto-resume 的状态机切换。开关是 per-sandbox 的(不是 per-cluster),因此已有部署行为保持不变。 + +- **auto-pause / auto-resume 全栈**(#613):控制面将沙箱状态发布到共享的 Redis HSet 快照 + 追加式事件流,由新增的 `cube-proxy-sidecar`(随 cube-proxy 镜像一起发布,针对 musl/Alpine 运行时静态构建)通过 `XREADGROUP` 消费。请求到达时,数据面 `rewrite_phase` 查询状态字典,要么转发流量、要么返回 503 + `Retry-After`、要么触发到 sidecar `/internal/resume` 的内部子请求。失败路径明确:not-found(130483)驱逐注册项;already-in-state(130490)调和后视为成功;通用 RPC 失败回滚,由 sweeper 重试。sweeper 设有 bootstrap-warmup 窗口,避免新启动的 sidecar 在首次 `last_active` 轮询落地前就误杀所有沙箱。 +- **kill-on-timeout 与 `set_timeout` / `refresh`**(#624):新的默认超时路径是 `kill`;sidecar `sweeper` 现在按 `auto_pause` 分派到 `tryPause` 或 `tryKill`。`sandbox_state.lua` 识别 `killing` / `killed`,返回 `410 Gone`(310410),让 SDK 立即停止重试。`DestroyReq` 携带 `KillReason`,写入 `cube.master.instance.kill_reason` 注解。新增端点 `POST /cube/sandbox/timeout` 和 `POST /cube/sandbox/refresh` 用于设置 `TimeoutSeconds` 与刷新 `EndAt`。 +- **稳定的 `end_at` 与 SDK 错误映射**(#624, #553):`GetSandboxResponse.end_at` 不再恒为 `None`。CubeAPI `parse_response` 路径现在做业务码映射:130409 → HTTP 409(容量拒绝场景透传后端 `ret_msg`,例如 `"resume rejected by paused_resource_release_ratio policy: need X > quota Y"`),130404 → HTTP 404。WebUI 在沙箱列表与详情页上提示 pause/resume/kill 失败(之前是静默的),将 409 容量拒绝格式化为含具体配额数字的形式。 + +#### 暂停资源按比例释放 + +暂停沙箱会把内存快照到磁盘并关停 MicroVM —— 它原本占用的宿主机 CPU/内存其实已经释放。但节点的资源账本仍然把 paused/pausing 沙箱计入节点配额,导致调度器在物理容量空闲时仍然拒绝新沙箱。本版本提供一个节点级旋钮,在"恢复余量"与"调度密度"之间做权衡。 + +- **可配置的释放比例**(#553):`host.quota.paused_resource_release_ratio`(取值 `[0, 1]`,默认 0 = 行为与旧版完全一致)控制将 paused/pausing 沙箱的 CPU/内存配额按多大比例释放回调度器,剩余部分作为恢复余量。1.0 = 全部释放,调度密度最大,恢复为 best-effort;0 < r < 1 = 部分释放。由于保留的配额仍会出现在 `QuotaCpuUsage` / `QuotaMemUsage` 中,暂停密集的节点在 cpu/mem 评分因子上会被自然降权。 +- **best-effort 恢复的入队控制**(#553):`UpdateWithResume` 现在做本地、实时的入队检查,当节点无法再装下"被释放的那部分"沙箱时,以 130409 → HTTP 409 拒绝恢复请求,避免宿主机内存超用。账本与入队控制逻辑被抽取为纯函数(`aggregateSandboxResources`、`clampRatio`、`resumeQuotaRejection`),配单测覆盖 zero/one/部分 ratio、pausing 瞬态、配额边界、ratio 钳制(含非有限输入)、policy-disabled no-op。 +- **目标场景:高密度、多数闲置的 agent 集群**:比例旋钮在"恢复余量"与"调度密度"之间做权衡。账本与入队控制都是节点本地的 —— 恢复不经过调度器,cubemaster 不需要任何改动。 + +#### Per-sandbox 流量访问令牌 + +以 `network.allow_public_traffic=false` 创建的沙箱现在会收到 per-sandbox 的 `traffic_access_token`;CubeProxy 拒绝任何未携带此令牌的入站请求(兼容头 `e2b-traffic-access-token`,本地别名 `cube-traffic-access-token`)。默认行为不变。 + +- **令牌签发与传递**(#639):`SandboxProxyMap` 新增 `AllowPublicTraffic` / `TrafficAccessToken`;`setProxyToRedis()` 在 Redis 确认写入后签发 UUID。`mergeCubeNetworkConfigs` 透传该标志,使 per-create 覆盖优先于模板默认。CubeAPI 把 `allowPublicTraffic` 写入线协议请求,并在 create 响应中暴露 `traffic_access_token`。 +- **数据面强制**(#639):`sandbox_backend.lua` 中的 `enforce_traffic_token()` 同时守住冷路径与缓存命中路径。403 响应体为空;令牌值永不落日志。 +- **SDK 暴露**(#639):Python SDK `Sandbox.traffic_access_token` 属性对未受限沙箱返回 `None`,仅在 create 时返回一次。 + +#### CubeEgress 加固 + +- **启动期 fail-closed**(#670):`bootstrap_status` 为 pending/unknown 时返回 403,不再放行任何流量,确保策略加载完成前数据面不服务任何请求。 +- **多架构支持**(#669):CubeEgress 镜像新增 arm64 构建。 + +#### 沙箱到宿主服务的访问 + +- **沙箱内可访问宿主服务**(#681):此前即使宿主服务 IP 在 `allow_out` 中也无法访问。默认情况下宿主机 TCP 80/443 会被 TPROXY 拦截;要为特定宿主 IP 放行,可加 iptables 规则 `iptables -t mangle -I TRANSPROXY 1 -d $HOST_IP/32 -p tcp --dport 80 -j RETURN`。 + +#### 纯 Go 原生镜像 rootfs 导出 + +新的可选导出模式,绕过 `docker` / `skopeo` / `umoci`,由 Go 端以原生方式构建 rootfs。通过 `CUBEMASTER_NATIVE_ROOTFS_EXPORT_ENABLED=true` 启用。 + +- **并发预取管道**(#558):使用 `errgroup` 实现即时上下文取消;任一 layer 抓取失败,其余流水线立即中止并向上传播错误。 +- **Loop-mount 流式提取**(#558):跳过中间宿主机目录,解压流直接写入目标 `ext4.img` 块设备。 +- **逐层资源清理**(#558):"decompress-and-delete"逐层移除压缩临时文件,最小化峰值磁盘占用。同一镜像对比:峰值内存 22 MiB(vs dockerless 36 MiB,低约 39%),构建耗时 31.30s(vs 37.26s,快约 16%)。 +- **跨镜像 layer 缓存**(#558,后续):由新的并发控制能力支撑 —— 将抓取的 layer blob 落入本地 OCI-layout 缓存,实现跨镜像 layer 共享。 + +#### 在线升级与外部数据库支持 + +- **`--mode upgrade` 升级流**(#538):通过 `.one-click.env` 自动检测既有安装;执行**三路 `.env` 合并**(用户自定义保留、未触碰的键采用新默认值、显式新 bundle 覆盖生效;基线不可用时回退两路);将旧的 `.one-click.env`、组件配置、DB/密钥目录备份到时间戳目录;运行预检(磁盘空间、semver 兼容性、服务健康、CIDR 冲突,可跳过)。新增标志:`-y` / `--yes`、`--allow-downgrade`、`--allow-role-change`。模式解析:`install | upgrade | auto`。 +- **外部 MySQL/Redis**(#514, #673):通过 `.env` 将 CubeSandbox 指向既有的 MySQL 和/或 Redis 服务器的可选路径。Installer 把所有服务与辅助工具都接到外部端点,并停止管理对应的本地容器;未设置时保留 bundled 行为。#673 进一步加固:携带 shell 敏感字符的密码(例如 `$`、反引号、引号、空格)以 source-safe 形式持久化;Redis 预检在使用 `--connect-timeout` / `--timeout` 前先探测支持情况,针对老版本 `redis-cli` 提供兼容回退。 +- **`cube-{api,master}` 静态构建**(#583):将 cubemaster / cube-api 二进制与项目既有的静态二进制约定对齐,简化打包与 one-click release bundle。 + +#### 腾讯云 Terraform 集群化部署 + +- **腾讯云集群化 deployer**(#629):受管的 **TKE** 控制面运行 `cubemaster` / `cube-api` / `cube-proxy` / `cube-webui`,后端使用云 **MySQL + Redis**,一台或多台 **CVM PVM** 计算节点位于私有 VPC 内,通过 SSH 跳板机(443)接入。`create.sh` 是唯一入口,`destroy.sh` 负责销毁;两者都直接从解压后的 release bundle 启动,支持完全离线运行。预置分阶段、fail-fast、可重入:网络 → TCR → CVMs → 镜像构建/推送 → MySQL/Redis → TKE + addons → 健康检查 → 计算节点。 +- **TKE 副本数可配**(#658):`cubemaster_replicas` / `cube_api_replicas` / `cube_proxy_replicas` / `cube_webui_replicas` 改为 Terraform 变量(默认 2)。`cubemaster_replicas` 仍然是 `spec.replicas` 与 conf 中 `default_headless_service_nodes_num` 的单一来源,保证 master 并发分配的一致性。通过 `TENCENTCLOUD_*_REPLICAS` → `TF_VAR_*` 接入并持久化到 `.env`。`terraform-validate` CI workflow(fmt + validate + shellcheck)覆盖 `deploy/one-click/terraform/**`。 + +#### 模板中心 TUI 与拉取进度追踪 + +- **拉取进度追踪**(#558,#580):模板镜像拉取进度持久化到 Redis(新增迁移 `template_image_pull_progress`),配合结构化的 image engine 抽象,进度事件实时回流到运维工具。 +- **TUI 驱动的模板管理**(#580):交互式 `template_tui` 提供可导航的模板视图,呈现状态与进行中的操作。 +- **模板 watch 模式**(#580):对进行中的模板任务做实时监控,输出既适合交互场景也适合 CI/运维消费。 +- **最新 job id 在所有客户端暴露**(#546):`cubemastercli tpl ls` 增加 `JOB_ID` 列;`tpl info` 打印 `job_id`;CubeAPI `GET /templates` 与 `GET /templates/:id` 返回 `jobID`;WebUI 模板卡片显示该 ID,详情页据此自动轮询进行中的构建;Python/Go SDK 在 `TemplateInfo.job_id` 中解析。返回**最新** job id(含 READY/FAILED 状态),不仅仅是进行中;没有任务历史的老模板省略该字段。 + +#### 真实 envd 版本探测 + +- **per-template 版本探测**(#650):在低频的模板创建路径上执行 `envd --version`(在沙箱内通过 containerd `task.Exec` 调用,带超时与有界输出),仅收集一次。semver 写入模板注解 `cube.master.components.envd.version`,由既有的模板-注解传播路径带到沙箱。多节点模板创建时按副本分别采集,收敛成模板级单一值后写入模板定义一次。 +- **线协议暴露**(#650):CubeAPI 在 create 时读 `ext_info[components.envd.version]`,在 get/list/connect/resume 时读 `annotations[components.envd.version]`。缺失或非法时回退到 `0.2.0` 以保持向后兼容。客户端根据 `envdVersion` 做特性开关的 e2b SDK 现在能看到真实版本。 + +#### 节点标签管理 API + +- **两个新的内部端点**(#633):`POST /internal/meta/nodes/{node_id}/labels`(merge)与 `DELETE /internal/meta/nodes/{node_id}/labels?key=...`(remove)。 +- **多 master 数据同步**(#633):节点标签、注册、状态存放在 MySQL 中,通过既有的 `syncAllFromDB`(≤ 30s)跨副本收敛。接收写请求的副本用 DB 事务 + `SELECT ... FOR UPDATE` 包裹读-合并-写,防止并发标签更新互相静默覆盖。管理员设置的标签在心跳过程中保留;与 cubelet 上报的标签冲突时,**cubelet 优先**(系统标签始终反映真实硬件状态)。 +- **标签校验**(#633):key 格式遵循 K8s `IsQualifiedName`(`[prefix/]name`);value 是 qualified name。保留命名空间(`kubernetes.io` / `beta.kubernetes.io` / `cube.cloud.tencentcloud.com`,含子域)不能通过管理面 API 创建或删除。覆盖典型场景:环境隔离、计划维护、租户/团队隔离、自定义拓扑、灰度发布。 + +#### AgentHub 安全与统一 + +- **删除 LLM 环境变量回退,仅 DB 加密配置**(#602):主加密密钥首次启动时通过 CSPRNG 生成,持久化到 `t_agenthub_setting.secret_master_key`,进程内通过 `OnceLock` 缓存。所有 `env_llm_*` 回退函数被移除;LLM provider / base_url / model / credential_mode / api_key **仅**从 DB 解析。无法解密的密文(例如使用旧密钥加密的)按 fail-closed 返回空串("unset"),绝不泄露密文。`--mode upgrade` 主动删除 13 个废弃 env key(含 `AGENTHUB_DEEPSEEK_API_KEY`、`AGENTHUB_SECRET_KEY`),防止陈旧明文密钥残留。设置对话框:移除 "environment" 数据源类型,仅保留 `database | none`。 +- **助手状态与鉴权设置持久化**(#582):AgentHub 助手鉴权/设置与 OpenClaw 运行期状态被持久化;新增鉴权守卫/登录流程与设置 UI;快照/模板持久化元数据与恢复相关 API 处理;one-click WebUI 反向代理配置随新鉴权/API 路径同步更新。 +- **OpenClaw 运行期统一应用路径**(#616):三路分发(`configure_openclaw` / `configure_openclaw_model` / `configure_ds_openclaw`)合并为单一的 `apply_openclaw_runtime` 入口。新增类型 `LlmRuntimePlan`(模型到命名空间的映射)、`OpenClawApplyOptions` / `OpenClawApplyMode`(声明式初始化模式选择)使意图显式。`update_agent_model` 返回 501 —— 模型在 provision 时固定,切换模型需 clone 或重建。净减少 70 行。 +- **迁移由 CubeMaster 拥有**(#599, #590):AgentHub OpenClaw 的 schema 变更迁入新的 CubeMaster goose 迁移;CubeAPI 不再在 DB 连接上运行 ad-hoc 的 AgentHub 迁移。0006 / 0008 迁移对部分迁移过的 DB 变得幂等。 + +#### 迁移身份完整性 + +- **不可变的迁移身份**(#620):三层防御,应对 rebase 期间迁移被静默跳过的风险: + 1. **命名规约**:历史段 0001-0010 冻结;所有新迁移必须使用 14 位 UTC 时间戳前缀(`YYYYMMDDhhmmss`),全局唯一,rebase 时无需改名。 + 2. **允许乱序应用**:开启 `goose.WithAllowOutofOrder`,使更早编写但更晚合并的带时间戳迁移不会让启动硬失败。安全性来源于所有迁移都使用幂等的 `*_if_missing` 助手。 + 3. **内容指纹**:`t_cubemaster_migration_fingerprint` 记录每次已应用迁移的 SHA-256;启动时对所有已应用版本校验磁盘内容与已记录指纹是否一致。不匹配即 fail-fast。逃逸口:`CUBEMASTER_MIGRATION_SKIP_FINGERPRINT_CHECK=1`。 + 4. **CI 兜底**:`migration-check.yml` workflow 运行 `TestMigrationFilenames`(无需 Docker),通过 `git diff` 拒绝已合并迁移文件的 M/D/R 修改。`scripts/new-migration.sh` 在创建时强制时间戳规约。 + +#### SDK 文件系统 API 对齐 + +- **Python / Go 完整文件系统 API**(#678):所有方法包装 envd 的原生端点 —— 无需 server-side 改动。 + + | Method | Go (`Files`) | Python (`Filesystem`) | envd 端点 | + |--------|-------------|----------------------|---------------| + | `List` | `List(ctx, path)` | `list(path)` | `POST /filesystem.Filesystem/ListDir` | + | `Stat` | `Stat(ctx, path)` | `stat(path)` | `POST /filesystem.Filesystem/Stat` | + | `Exists` | `Exists(ctx, path)` | `exists(path)` | Stat + 404 检查 | + | `Remove` | `Remove(ctx, path)` | `remove(path)` | `POST /filesystem.Filesystem/Remove` | + | `Rename` | `Rename(ctx, old, new)` | `rename(old, new)` | `POST /filesystem.Filesystem/Move` | + | `MakeDir` | `MakeDir(ctx, path)` | `make_dir(path)` | `POST /filesystem.Filesystem/MakeDir` | + | `WriteFiles` | `WriteFiles(ctx, entries)` | `write_files(files)` | 批量 `POST /files` | + | `WatchDir` | `WatchDir(ctx, path)` | `watch_dir(path)` | `POST /filesystem.Filesystem/WatchDir`(Connect 流式) | + +- **per-host 请求转换**(#568):Python SDK 接受 E2B 的 `{host: [{transform: {headers: {...}}}]}` 形态,写入 `Sandbox.create(network={"rules": ...})`,并将其翻译为带 `action.inject` 的 CubeEgress L7 规则。每个 transform 项变成一条 Rule(`e2b-transform-[-]`);每个 header 对变成一条 Inject。非法输入抛 `ValueError`,不再静默丢弃。 +- **create-time 环境变量传递到 envd**(#566):`NewSandbox` 接受 `envs` 作为 `envVars` 的别名(兼容 E2B SDK)。create-time env 转发到 CubeMaster 作为 `create_time_env_vars`,并序列化到内部注解;Cubelet 在沙箱启动与探活成功后触发 envd `/init`,存在 `cube.master.components.envd.version` 注解时优先使用。注解缺失时回退到默认 init 端点带超时重试。envd 初始化仍无法完成时,沙箱创建显式失败。 +- **Cube-bench `--host-mount` 标志**(#651):新增 `--host-mount ''` 标志,用于压测带 host-mount 的沙箱创建开销。在配置解析时校验并压缩一次,请求时按原样发送。 +- **JSON 往返修复**(#572):`Execution.to_json()` 不再对 `logs` 和 `error` 做二次 JSON 编码;新增 `to_dict()` 助手返回嵌套对象,而 `Logs.to_json()` 与 `ExecutionError.to_json()` 保持返回 JSON 字符串(与命名一致)。 + +### ✨ 增强 + +#### 调度与控制面 + +- **CubeMaster HTTP 监听地址可配**(#662):`common.http_bind`(默认 `0.0.0.0`)解耦 one-click 与 TKE 部署路径 —— one-click 从 `CUBEMASTER_HTTP_BIND` 渲染 `__CUBEMASTER_HTTP_BIND__`;TKE 直接使用 `yamlencode`。Cubelet / CubeAPI / cube-proxy 故意不做收紧,因为绑到 127.0.0.1 会破坏多节点(Cubelet gRPC)或 WebUI(CubeAPI 通过 `host.docker.internal`)可达性。 +- **多节点调度评分指南**(#672):文档说明多节点 CubeMaster 部署应配置 `scheduler.score` 以避免"在第一排序节点确定性放置"的现象;提供基于 #573 的 baseline 评分示例。 +- **多副本节点元数据同步**(#542):`loopReload` 协程现在在 `Init()` 中启动,修复了"一个 CubeMaster 副本注册的节点元数据对其他副本不可见"的 bug。`reload()` 被替换为做字段级合并的 `applyReloadResult()`:注册字段始终以 DB 为准;心跳/状态字段在内存值比 DB 快照新时保留内存值;新节点直接加入内存 map。从而避免了一次慢的 DB 扫描覆盖"扫描期间到达本副本的"健康节点心跳。 + +#### 模板管理 + +- **rootfs 构建保留镜像层 uid/gid**(#608, #671):`umoci unpack --rootless` 仅在 `euid != 0` 时使用;以 root 身份运行时,去掉 `--rootless` 以保留镜像原始 ownership。docker-export 回退路径现在使用带 `--same-owner --numeric-owner` 的 `tar -xf`。已验证:python-slim 模板的 `/home/user` 恢复为 1000:1000,沙箱内默认用户账户正常工作;browser 模板的 Chromium 正常启动并提供 CDP。 +- **修复镜像层 squash**(#671):移除无条件的 `WithNoSameOwner()` 调用,使 `archive.Apply` 在以 root 运行时保留镜像层的原始 uid/gid。 +- **注册模板预览路由**(#570):`POST /cube/sandbox/preview` 现已接入 CubeMaster HTTP mux。`cubemastercli tpl render` 返回既有的三层预览(`api_request` / `merged_request` / `cubelet_request`),不再返回 404。 +- **`tpl info` 字段补齐**(#592, #594):服务端 `templateResponse` 与 CLI 端 `templateResponse` 增加 `display_name` / `created_at` / `image_info`。`GetTemplateInfo()` 从持久化的模板定义回填 `CreatedAt`,从已存储的请求与最近的 image job 解析 `ImageInfo`,与 `ListTemplates()` 行为对齐。 +- **CubeAPI 透传 `with_cube_ca`**(#652):`CreateTemplateRequest` 与 `CreateTemplateFromImageReq` 携带 `with_cube_ca`;`TemplateService::create_template` 透传。缺省或 null 时保留 CubeMaster 默认 `true`。 + +#### 镜像与构建 + +- **Native rootfs 导出**(#558,见主要特性):纯 Go 导出路径;并发预取 + loop-mount 流式 + 解压即删除。 +- **模板镜像拉取进度 + TUI**(#580,见主要特性)。 +- **模板删除资源清理**(#631):三阶段 `cleanupArtifactFully`(加锁 + 降引用 + 放置快照 → 物理删除 → 再校验引用 + 删除放置/artifact)配合 `startArtifactGC`(`GET_LOCK` 单实例)兜底 `CLEANUP_PENDING` 状态。新增迁移 `20260623145000_artifact_node_placement.sql`(新表 + 从 replica 回填;只有 `node_ip` 的旧记录使用合成 key `ip:`)。Cubelet 新增 `ext4image.DestroyPmemArtifact`、`DeleteByKind` 对端 kind 重试、聚合的清理错误处理。CubeAPI 级联 AgentHub 删除(market 模板不级联 `tpl-*`;非 market 模板按 `snapshot_kind` 分支 → OpenClaw 目录清理或 `snapshots.delete`),并在基础设施 `DELETE /templates` 之后 best-effort 软删除 AgentHub 行。范围之外:沙箱回滚旧 rootfs 清理(后续)、runtemplate bolt 注销、AgentHub IDOR 鉴权、升级前无 DB 元数据的磁盘孤儿。 +- **快照删除清理任务行**(#559):`runSnapshotDeleteJob` 现在在元数据清理完成后调用 `cleanupTemplateJobs`,与模板删除行为对齐。修复了"已删除的快照在 `tpl ls` 中复活,需再删一次才彻底消失"的 bug。 + +#### 部署 + +- **CubeMaster HTTP bind 可配**(#662,见调度)。 +- **外部 MySQL/Redis**(#514, #673,见主要特性)。 +- **`cube-{api,master}` 静态构建**(#583)。 +- **TKE 副本数可配**(#658,见主要特性)。 +- **CIDR 预检区分同 CIDR 重装**(#586):`cube-dev` 自身的残存接口和 `z*` TAP 设备在接口检查与路由检查中都被跳过。同网络+掩码直接复用(仅日志);重叠但不同网络则给出指引后退出;不重叠则放行。停止/启动命令替换为 `systemctl stop/start 'cube-sandbox-*.target'`(已验证:`PartOf=` 会传播到全部 14 个 `cube-sandbox-*` 服务;老路径 `down-with-deps.sh` 的 pidfile 方式对 `Restart=on-failure` 的 systemd 服务是 no-op)。 +- **compute 控制面预检**(#657):`check_compute_control_plane_preflight()` 在任何包安装或解压前校验 `ONE_CLICK_CONTROL_PLANE_IP` 或 `ONE_CLICK_CONTROL_PLANE_CUBEMASTER_ADDR`。端口推导从 `CUBEMASTER_ADDR` 中解耦(在 4 处解析逻辑中统一为常量 `8089`)。两个变量同时设置但解析为不同地址时,配置冲突显式失败。 +- **compute quickcheck 重试**(#637):在 systemd post-start 竞态下,对 quickcheck 加有界、可配的重试循环;覆盖 install.sh、smoke.sh、deploy-manual.sh、create.sh 与 systemd 安装路径。 +- **代理端口拆分**(#588):`CUBE_PROXY_HOST_PORT` 已弃用;拆分为 `CUBE_PROXY_HTTP_PORT`(默认 80)与 `CUBE_PROXY_HTTPS_PORT`(默认 443)。systemd post-start TCP 检查使用新变量。 +- **固定安装根**(#649):移除 `ONE_CLICK_INSTALL_PREFIX` / `ONE_CLICK_TOOLBOX_ROOT`;`/usr/local/services/cubetoolbox` 成为唯一固定的安装根。`CUBE_PROXY_CERT_DIR` 默认值相应迁移。 +- **移除手工 SQL seed**(#628):CubeMaster 现在通过嵌入式 goose 迁移拥有单节点 seed 行(`host_info` / `sub_host_info`);`sql/002_seed_single_node.sql` 与宿主机侧 mysql 客户端的 seed 逻辑被移除。 +- **`DATABASE_URL` 持久化**(#611):本地 MySQL 路径将 `DATABASE_URL` 写入 `RUNTIME_ENV_FILE`;`cube-api-start.sh` 在未设置时从 `CUBE_SANDBOX_MYSQL_*` 构造。 +- **`MIRROR` 持久化到 `.one-click.env`**(#622):`install.sh` 校验并持久化 `MIRROR`(空或 `cn`);`online-install.sh` 在启动子 installer 前 export 该变量。修复了"安装时设置 `MIRROR=cn` 但 cube-egress 仍走 `-int` 镜像"的问题。 +- **semver 比较工具**(#587):bash 内的完整 semver 比较 —— `_version_trim_leading_zeroes`、`_version_compare_numbers`、`_version_split_semver`、`_version_compare_prerelease`,公开 API `semver_compare`、`version_lt`。处理 prerelease、build metadata、可选 v-prefix。 +- **Release bundle 包含 `scripts/common`**(#597):`build-release-bundle.sh` 此前在内层 `sandbox-package.tar.gz` 和外层 release tarball 中都没有包含 `scripts/common/validation.sh`;依赖该文件的 install.sh 与运行时脚本会失败。 +- **`resolvectl default-route` 改为 best-effort**(#703,Closes #401):systemd v240 引入的子命令不再无条件调用;在老版本 systemd(例如 RHEL 8.3 自带的 systemd 239)上只打一条 note 就继续。`~cube.app` 路由域已经把查询限定到 dummy 链路上,所以跳过显式的 default-route 标志是安全的。一键安装与随包 systemd 两份脚本都做了对应修改。 + +#### 网络与数据面 + +- **CubeProxy 关闭 envd 流式端点缓冲**(#647):`process.Process/Start`、`process.Process/Connect`、`filesystem.Filesystem/WatchDir` 在 host-routed 与 path-routed 两种 server block 中都不再缓冲。全局默认缓冲保持不变。修复两处回归:`commands.run(..., background=True)` 阻塞到命令结束才返回、`WatchDir` 的 `start` 事件在请求超时边界才到达。 +- **CubeProxy 移除 `cube_retcode` 与 faulty-backend 死代码**(#653, #593):响应头 `X-Cube-Retcode` 与访问日志字段 `$cube_retcode` 会暴露 6 位错误码(token 错误、沙箱暂停、Redis 失败、均衡器失败等),攻击者可据此枚举沙箱存在性、生命周期状态与后端弱点。HTTP 状态码收敛:存在性 / 配置 / token 错误 → 404,基础设施失败 → 503,畸形请求 → 400。统一 JSON 错误体(`{"error": "..."}`)。`faulty_backend` 共享字典、`is_faulty_backend`、`assert_backend_healthy` 与 `header_filter_phase` 中的 340xxx 分支被移除(这些分支从未被填充过)。`local_cache` 从 500m 缩到 100m(足以容纳单 CubeProxy 约 10w 沙箱的元数据)。 +- **CubeVS:`from_world` 不再 attach 到 `lo`**(#656):该路径从来就走不通。 +- **CubeVS:内部 map 使用 BTF key/value**(#595):规避 Ubuntu 22.04 + kernel 5.15 上的内核限制(`map.Put failed: update: invalid argument, name: allow_out_v2`)。 +- **CubeVS:重新生成 BPF 对象**(#617 后续,#623)。 +- **CubeVS:修复跨节点 port-mapped 回包 TCP 校验和错误**(#617):跨节点 port-mapped 回包路径在 eBPF DNAT 路径上会破坏 TCP 校验和,导致 TKE pod 静默丢弃 SYN-ACK,握手超时(/execute 返回 504)。同节点与其他 NAT 路径不受影响。 + +#### 安全与加固 + +- **MSI-X 表/PBA 越界访问加固**(#619):`pci/src/msix.rs` 中的 `read_table` / `write_table` / `read_pba` 现在对索引做边界检查,对尺寸错误的读/写返回 graceful error(从 cloud-hypervisor 移植 3 个 commits)。修复一处客户机可触发的 VMM/shim 进程自 DoS。 +- **`X-Request-Method` 透传到 auth callback**(#315):此前 callback 只收到 `X-Request-Path`,无法在同路径不同方法上做区分,持有只读凭证的调用方可以借此提权到删除/重建。callback 现在可基于"路径 + 方法"做精细鉴权。 +- **CubeEgress 计算节点 CA 刷新**(#614):计算节点在每次 prepare 时从 master 的 `/cube/ca/{filename}` 拉取 CubeEgress CA,校验下载的 cert/key 配对后再安装。控制节点保留本地 generate-if-missing 行为。 + +#### AgentHub 与鉴权 + +- **AgentHub 状态持久化**(#582,见主要特性)。 +- **OpenClaw 运行期统一**(#616,见主要特性)。 +- **AgentHub 迁移由 CubeMaster 拥有**(#599,见主要特性)。 + +#### 虚拟化 + +- **virtio-blk 暴露 `VIRTIO_BLK_F_SEG_MAX` 与 `VIRTIO_RING_F_INDIRECT_DESC`**(#575):允许客户机在单个请求中携带多个 segment,提升块 I/O 吞吐。 + +#### 可观测性 + +- **Cubelet `image_forward_latency` 直方图注册**(#515):`chi_cubebox_image_forward_latency_milliseconds` 通过 `prometheus.NewHistogram` 创建后从未被加入 `docker/go-metrics` 命名空间,因此 `metrics.Register(ns)` 没有把它暴露在 `/metrics` 上。 +- **WebUI 忽略 `tsconfig.tsbuildinfo`**(#625):每次构建都会自动生成,会产生大量噪声 diff。 + +#### One-Click Installer 内部 + +- **Redis 单池**(#604, #609):`redis_read` / `redis_write` / `redis_metadata` 三个 config section 与 `RedisRead` / `RedisWrite` / `RedisMetaData` 常量被移除。`GetRedis()` 现在是无参的单池 API,只使用单一的 `redis` 配置块。在 `pkg/base/rediskey` 中集中管理 key 定义;CubeProxy Lua 脚本使用新 key 方案;随包发布 Redis 数据迁移脚本;新增中英双语 Redis key 规范文档。 + +### 🐛 缺陷修复 + +以下修复针对 v0.4.0 中存在的问题: + +- **CubeProxy 流式缓冲回归**(#647,见增强)。 +- **CubeVS 跨节点 port-mapped 回包 TCP 校验和**(#617,见增强)。 +- **MSI-X 客户机可触发 panic**(#619,见增强)。 +- **高并发 snapshot 回滚时的 MySQL 1213 死锁**(#693,见工程改进)。 +- **跨副本节点元数据同步缺失**(#542,见调度)。 +- **Cubelet hostdir 挂载可能无限挂起**(#691):不健康的宿主挂载(例如 geesefs 远端访问被吊销)会一直阻塞 hostdir bind 步骤,直到上层 API 超时。bind 操作从 create 协程移到有界辅助子进程:读-写 hostdir 每卷 ≤ 3s,只读 hostdir 每卷 ≤ 6s(bind + remount 各自有界)。hostdir 后端元数据在 bind 尝试前记录,确保 create 失败后清理路径仍可定位沙箱作用域内的 hostdir 路径。顶层错误类别保持 `CreateStorageFailed`;错误消息现在会显式指出 hostdir 超时(例如 `prepareHostDirVolume: bind mount /mnt/aime-oss/test -> /data/cubelet/hostdir//rw/hostdir-0 timed out after 3s: context deadline exceeded`)。在 CubeAPI 侧,`parse_response` 在解析响应包络之前先把非零 `ret_code` 转为错误,因此 pause/resume/connect 路径会把 130409 映射为 HTTP 409(透传后端 `ret_msg` 原文)。 +- **rootfs 把 uid/gid squash 为 root**(#608, #671,见增强)。 +- **快照删除残留任务行**(#559,见增强)。 +- **`tpl render` 返回 404**(#570,见增强)。 +- **模板删除泄漏节点 ext4 / cubecow / AgentHub 资源**(#631,见增强)。 +- **Python SDK `to_json` 二次编码**(#572,见 SDK)。 +- **Python SDK `envs` 被静默丢弃**(#566,见 SDK)。 +- **Egress CA 在计算节点上过期**(#614,见增强)。 +- **CubeProxy `cube_retcode` 泄露沙箱存在性/生命周期**(#653,见增强)。 +- **CubeVS `from_world` 被 attach 到 `lo`**(#656,见增强)。 +- **CubeVS 内部 map 触发的内核限制**(#595,见增强)。 +- **MSI-X 越界 panic(来自客户机)**(#619,见增强)。 +- **auth callback 缺失 `X-Request-Method`**(#315,见增强)。 +- **外部 MySQL/Redis shell 敏感字符密码**(#673,见主要特性)。 +- **`MIRROR` 未持久化**(#622,见部署)。 +- **`CUBE_PROXY_HOST_PORT` 弃用**(#588,见部署)。 +- **同 CIDR 重装时 CIDR 预检误报冲突**(#586,见部署)。 +- **compute 节点缺控制面地址导致延迟失败**(#657,见部署)。 +- **compute quickcheck 启动竞态**(#637,见部署)。 +- **systemd < v240 上 `resolvectl default-route` 中断安装**(#703,见部署)。 +- **`scripts/common` 缺失于 release bundle**(#597,见部署)。 +- **MySQL 1213 死锁被错误归类**(#693):`snapshotErrorCode()` 之前把数据库错误误报为 `MasterParamsError(130400)`;现在正确映射为 `DBError`。 +- **`tpl info` 缺字段**(#592, #594,见增强)。 +- **`with_cube_ca` 未透传**(#652,见增强)。 +- **Cubelet `image_forward_latency` 直方图未暴露**(#515,见可观测性)。 +- **WebUI `tsconfig.tsbuildinfo` 产生噪声 diff**(#625,见可观测性)。 +- **CubeProxy `local_cache` 过度分配**(#593,见增强)。 + +### 📚 文档 + +- **网络安全硬化指南(新增,中英双语)**(#663):默认监听面表(按服务列出 bind 地址、端口、配置方式、鉴权/TLS 状态);各服务 bind 地址配置说明与注意事项(绑到 127.0.0.1 可能破坏多节点 / WebUI / 健康检查);加固策略:绑到私网 NIC、防火墙源 IP 白名单(`iptables` / `ufw`);多节点必须保留的端口;需要轮换的默认凭据;`AUTH_CALLBACK_URL` 鉴权回调机制;TLS 说明。One-click 部署 README(EN/ZH)增加指向该指南的安全提示。**任何打算公网部署的团队上线前必读。** +- **Snapshot/Clone/Rollback 深度博客**(#680):11 张架构图(中英双语),解释快照、克隆、回滚背后的三种内核机制:XFS reflink、`/proc/pagemap` 匿名页检测、soft-dirty bit。 +- **沙箱日志指南(新增,中英双语)**(#692):`cubecli cubebox logs` 读取容器 init 进程的 stdout/stderr;必须在节点上运行(需进入 Cubelet 挂载命名空间)。`--tpl` 标志读取模板构建日志(宿主机文件系统,无 ns entry)。flag 参考: `--stderr` / `--all` / `--tail N` / `--head N`。沙箱与模板的日志文件路径。范围:仅 init 进程;envd 子任务日志通过 E2B SDK 回调获取。限制:无 `--follow`;日志随沙箱删除。导航栏 "Operations" 下新增。 +- **生命周期指南(新增,中英双语)**(#613):状态机、每个生命周期方法、auto-pause/auto-resume 语义与运维注意。`examples/code-sandbox-quickstart/auto-resume.py` TUI 演示,验证内核内存与文件系统状态在自动暂停/恢复后保持一致。 +- **多节点调度评分指南**(#672):文档说明多节点 CubeMaster 部署应配置 `scheduler.score`;解释确定性放置的风险;提供 baseline 评分示例。 +- **Host mount 权限排错指南(新增,中英双语)**(#560,Closes #239):`metadata["host-mount"]` 映射到 Cubelet 节点上的已有路径,不复制文件、不创建 workspace 快照、不重写 ownership。文档说明安全的只读 workspace 挂载、读-写 ownership 对齐、ACL 以及 root 命令的取舍。 +- **网络深度博客(中英双语)**(#627):5 张图,覆盖 CubeVS(eBPF 内核态 L4 转发 + LPM Trie IP/域名过滤)、CubeProxy(OpenResty 入口网关)与 CubeEgress(L7 出站代理,支持 HTTPS 拦截、凭据注入与访问审计)。 +- **v0.4.0 发布博客 + agent-friendly-service 用例博客(中英双语)**(#585):v0.4.0 发布回顾;将 Neon 式 "fast spawn / clone / rollback" 推广到任意传统软件服务,配 Redis 实操示例。 +- **README 增强**(#640):6 张产品亮点卡片(启动速度、隔离、E2B 迁移、Web 控制台、凭据保险箱、Egress 控制);changelog 段落重写,v0.4 信息更醒目;指南链接归并。中英双语。 +- **README 基准链接 + 架构图**(#646):在 README 中直接放裸金属《核心操作性能基准报告》与 PVM 云服务器基准报告链接;架构图更新。 +- **文档优化 pass(中英双语)**(#665):在 Quick Start / Bare-Metal / PVM / Multi-Node 指南顶部增加"生产用途"提示(链到 Network Hardening);架构总览扩展设计原则、控制面/数据面拆分、核心组件(含 CubeCoW、CubeEgress、WebUI)、请求生命周期、存储 / 网络 / 安全小节;产品介绍重新定位为 AI Agents 的基础设施,并补充核心优势与对比表;"连接已有集群"重写为四选一决策树(Cube 原生 SDK + `CUBE_PROXY_NODE_IP`、任意 HTTP 客户端的 path 模式、通配 DNS、`dev-sidecar`)。 +- **文档侧边栏与链接修复**(#664):中文首页架构链接、Core Concepts 侧边栏增加 `Snapshot, Rollback & Clone`、site-absolute 的网络硬化路径替换为完整 URL。 +- **内部链接本地化**(#668):硬编码的 `.html` / 老英文文档路径替换为相对 `.md` 链接,让中英文语言树保持自洽。 +- **GitHub `main` → `master` 链接修复**(#660):`docs/guide/restrict-public-access.md` 与中文版、`docs/guide/security-proxy.md` 与中文版等。修复 lifecycle 锚点(`#platform-managed-auto-pause--auto-resume` → `#platform-managed-auto-pause-auto-resume`)。 +- **CODEOWNERS**:维护者条目刷新。 + +### ⚙️ 工程改进 + +- **Snapshot 运行时绑定表 + 锁硬化**(#693):新增 `t_cube_snapshot_runtime_active` 表(主键 `(sandbox_id, binding_type)`)作为权威的当前绑定状态;`INSERT ... ON DUPLICATE KEY UPDATE` 借助 InnoDB 行锁作为并发控制点,跨多个 CubeMaster 实例也能串行化。旧表 `t_cube_snapshot_runtime_ref` 降级为审计/历史日志(仅追加)。`SnapshotRuntimeBindingService` 是唯一写入口(`AttachSnapshotRuntimeBinding` / `DetachSnapshotRuntimeBinding` / `RefreshSnapshotRuntimeBindingsFromNode`)。读路径(`ListActiveSnapshotRuntimeRefs` / `GetActiveSnapshotRuntimeRefBySandbox` / `CountActiveSnapshotRuntimeRefs` / `populateSnapshotRuntimeRefSummary` / `DeleteSnapshot` 中的 active 检查 / `allocateNextRollbackGen`)从 `WHERE status='ACTIVE'` 切换到 active 表。`snapshot_ops.go` 引入沙箱级(`snapshot-sandbox:`)、资源级(`snapshot-resource:`)与请求级(`snapshot-request:`)分布式锁;`withSnapshotWriteLocks` 递归获取多把锁。压测 `bench_rollback_concurrency.py -c 5 -n 20` 与 `-c 10`:无 1213 死锁;`SHOW ENGINE INNODB STATUS` 中无 runtime-ref 相关死锁。迁移保留旧表;所有写者收敛到 binding service;正确性不依赖应用层锁。 +- **Redis 统一**(#604, #609,见 One-Click Installer 内部)。 +- **WebUI `tsconfig.tsbuildinfo` gitignore**(#625,见可观测性)。 +- **构建系统:`cube-{api,master}` 静态构建**(#583,见主要特性)。 +- **迁移辅助脚本 `scripts/new-migration.sh`**(#620,见主要特性)。 +- **格式检查 CI**(从 v0.4.0 沿用并扩展):在既有 `fmt-check.yml` 之上增加 `migration-check.yml` workflow(agent 的 `fmt` 目标仍然在格式化前自动生成 `version.rs` 与协议 `.rs`)。 +- **暂停/恢复账本抽为纯函数**(#553):`aggregateSandboxResources`、`clampRatio`、`resumeQuotaRejection` 抽出以方便单测。 +- **CubeProxy 死代码清理**(#593,见增强)。 +- **WebUI TypeScript 增量构建忽略**(#625)。 + +--- + +**升级提示** + +- `--mode upgrade`(#538)会删除 13 个废弃的 AgentHub env key(#602),以防陈旧明文密钥残留;如果之前手工配置过 `AGENTHUB_DEEPSEEK_API_KEY` / `AGENTHUB_SECRET_KEY` 等,升级后请通过 WebUI Settings 页面重新输入。 +- 启用外部 MySQL/Redis(#514)后,installer 不再管理对应的本地容器;如要退回 bundled 模式需手动重装。 +- MySQL 1213 死锁修复(#693)在表结构上向后兼容 —— `t_cube_snapshot_runtime_active` 新增并回填,`t_cube_snapshot_runtime_ref` 保留为历史/审计日志。 +- CubeEgress fail-closed(#670)意味着数据面在策略 bootstrap 期间返回 403;如果出现"启动期流量被拒",请等待 cube-egress 的 CA 与策略加载完成。 +- Per-sandbox 流量令牌(#639)只在 `network.allow_public_traffic=false` 时下发;原先以 403 区分 "token mismatch" 的客户端应改为识别 404(#653 的安全收敛)。 diff --git a/docs/zh/guide/digital-assistant.md b/docs/zh/guide/digital-assistant.md index 5f0297554..25e2719db 100644 --- a/docs/zh/guide/digital-assistant.md +++ b/docs/zh/guide/digital-assistant.md @@ -10,7 +10,7 @@ AgentHub 会基于 CubeSandbox 模板创建数字助手。部署前需要先制作数字助手模板(见下方命令),然后将自动生成的 `tpl-` 前缀模板 ID 写入 `.env`: -```env +```bash AGENTHUB_DS_OPENCLAW_TEMPLATE= ```