mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-18 05:39:50 +08:00
Closes #78 ## What You Can Do ### Live policy updates Update a running sandbox's network policy without recreating it: ```bash nav sandbox policy set <sandbox> --policy new-policy.yaml --wait --timeout 60 ``` The sandbox hot-reloads the policy within 30s (configurable). On failure, the previous policy stays active (last-known-good). Idempotent — submitting the same policy twice returns the existing version: ``` ✓ Policy version 3 submitted (hash: a1b2c3d4e5f6) $ nav sandbox policy set test --policy same.yaml · Policy unchanged (version 3, hash: a1b2c3d4e5f6) ``` ### Policy history & inspection ```bash nav sandbox policy list <sandbox> # version history with status nav sandbox policy get <sandbox> # current policy metadata nav sandbox policy get <sandbox> --full # print full policy as YAML nav sandbox policy get <sandbox> --rev 2 --full # specific revision as YAML ``` ### Sandbox logs Stream logs from both the gateway and sandbox supervisor in one view: ```bash nav sandbox logs <sandbox> # one-shot, last 2000 lines nav sandbox logs <sandbox> --tail # live streaming nav sandbox logs <sandbox> --source sandbox # supervisor logs only nav sandbox logs <sandbox> --source gateway # gateway logs only nav sandbox logs <sandbox> --level warn # warnings and errors only nav sandbox logs <sandbox> --since 5m # last 5 minutes nav sandbox logs <sandbox> --tail --source sandbox --level info ``` Each log line is tagged with its source and includes structured fields: ``` [1772055394.673] [sandbox] [INFO ] [navigator_sandbox::proxy] CONNECT action=allow dst_host=api.anthropic.com dst_port=443 policy=claude_code [1772055061.005] [gateway] [INFO ] [navigator_server::grpc] GetSandboxPolicy served from policy history ``` --- ## Implementation ### Proto changes - 7 new RPCs: `UpdateSandboxPolicy`, `GetSandboxPolicyStatus`, `ListSandboxPolicies`, `ReportPolicyStatus`, `GetSandboxLogs`, `PushSandboxLogs` (client-streaming) - `SandboxLogLine`: added `source` (gateway/sandbox), `fields` (structured key-value map) - `WatchSandboxRequest`: added `log_since_ms`, `log_sources`, `log_min_level` - `GetSandboxLogsRequest`: added `sources`, `min_level` - `PolicyStatus` enum, `SandboxPolicyRevision` message - `Sandbox.current_policy_version` field ### Server - **Policy persistence**: New `sandbox_policies` table (SQLite + Postgres) with per-sandbox monotonic versions, status tracking, and policy hash - **UpdateSandboxPolicy**: Validates static field immutability (filesystem/landlock/process), network mode consistency (Block↔Proxy), deterministic hash comparison for idempotent updates - **Lazy backfill**: First `GetSandboxPolicy` call creates version 1 from `spec.policy` for existing sandboxes - **Log broker**: `TracingLogBus::publish_external()` injects sandbox-pushed logs into the same broadcast channel + tail buffer (2000 lines). Server forces `source="sandbox"` and `sandbox_id` on all pushed logs - **Source/level filtering**: Applied server-side in both `GetSandboxLogs` and `WatchSandbox` streams - **Version supersession**: When a new version is loaded, all older pending+loaded versions are marked superseded ### Sandbox - **`OpaEngine::reload_from_proto()`**: Full `from_proto()` pipeline (L7 validation, access preset expansion) with atomic engine swap. On failure, previous engine untouched (LKG) - **Policy poll loop**: Background task polls every 30s (configurable via `NAVIGATOR_POLICY_POLL_INTERVAL_SECS`), reports status via `ReportPolicyStatus` RPC - **`LogPushLayer`**: Tracing layer captures events at INFO+ (configurable via `NAVIGATOR_LOG_PUSH_LEVEL`), sends structured fields via `PushSandboxLogs` client-streaming RPC. Background task batches 50 lines / flushes every 500ms. Best-effort (drops on full channel, never blocks) - **`CachedNavigatorClient`**: Persistent mTLS channel for both policy polling and log push ### Database migration - `002_create_sandbox_policies.sql` (SQLite + Postgres) ## Tests - **Unit**: 8 policy persistence tests (put/get/list/status/supersede/isolation) - **Integration**: 4 test files updated with new RPC stubs - **E2E**: `test_live_policy_update_and_logs` — full lifecycle: create → set same (unchanged) → push new → wait for load → verify connectivity → push same (unchanged) → verify history → fetch logs ## Documentation - `architecture/sandbox.md`: Log streaming architecture, LogPushLayer, push task, server broker, source tagging, structured fields, CLI filtering, failure modes - `architecture/security-policy.md`: Live update semantics, deterministic hashing, CLI filter flags, policy inspection - `architecture/plans/issue-78-sandbox-log-streaming.md`: Design plan for log streaming ## Security - Trust boundary documented: shared mTLS cert model (per-sandbox auth tracked in #80) - Server forces `source="sandbox"` and `sandbox_id` on pushed logs (can't impersonate gateway or other sandboxes) - Per-batch line cap (100) prevents flooding