mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-02 07:34:45 +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
192 lines
2.7 KiB
Plaintext
192 lines
2.7 KiB
Plaintext
# =============================================================================
|
|
# Rust
|
|
# =============================================================================
|
|
|
|
# Build output
|
|
/target/
|
|
debug/
|
|
release/
|
|
|
|
# Cargo lock for libraries (keep for binaries/workspace)
|
|
# Cargo.lock # We keep this since this is a binary/workspace project
|
|
|
|
# Generated code
|
|
*.rs.bk
|
|
|
|
# Profiling
|
|
*.profraw
|
|
*.profdata
|
|
|
|
# =============================================================================
|
|
# Python
|
|
# =============================================================================
|
|
|
|
# Byte-compiled / optimized / DLL files
|
|
__pycache__/
|
|
*.py[cod]
|
|
*$py.class
|
|
|
|
# C extensions
|
|
*.so
|
|
|
|
# Distribution / packaging
|
|
.Python
|
|
develop-eggs/
|
|
dist/
|
|
downloads/
|
|
eggs/
|
|
.eggs/
|
|
lib/
|
|
lib64/
|
|
parts/
|
|
sdist/
|
|
var/
|
|
wheels/
|
|
share/python-wheels/
|
|
*.egg-info/
|
|
.installed.cfg
|
|
*.egg
|
|
MANIFEST
|
|
|
|
python/*.data
|
|
|
|
# Virtual environments
|
|
.venv/
|
|
venv/
|
|
ENV/
|
|
env/
|
|
.python-version
|
|
|
|
# Installer logs
|
|
pip-log.txt
|
|
pip-delete-this-directory.txt
|
|
|
|
# Unit test / coverage reports
|
|
htmlcov/
|
|
.tox/
|
|
.nox/
|
|
.coverage
|
|
.coverage.*
|
|
.cache
|
|
nosetests.xml
|
|
coverage.xml
|
|
*.cover
|
|
*.py,cover
|
|
.hypothesis/
|
|
.pytest_cache/
|
|
pytestdebug.log
|
|
|
|
# Translations
|
|
*.mo
|
|
*.pot
|
|
|
|
# mypy
|
|
.mypy_cache/
|
|
.dmypy.json
|
|
dmypy.json
|
|
|
|
# Ruff
|
|
.ruff_cache/
|
|
|
|
# Type stubs
|
|
.pytype/
|
|
|
|
# Cython debug symbols
|
|
cython_debug/
|
|
|
|
# Generated Python protobuf stubs (keep package marker)
|
|
python/navigator/_proto/*
|
|
!python/navigator/_proto/__init__.py
|
|
|
|
# =============================================================================
|
|
# IDE / Editor
|
|
# =============================================================================
|
|
|
|
# VSCode
|
|
.vscode/
|
|
!.vscode/settings.json
|
|
!.vscode/tasks.json
|
|
!.vscode/launch.json
|
|
!.vscode/extensions.json
|
|
|
|
# JetBrains
|
|
.idea/
|
|
*.iml
|
|
*.ipr
|
|
*.iws
|
|
out/
|
|
|
|
# Vim
|
|
*.swp
|
|
*.swo
|
|
*~
|
|
|
|
# Emacs
|
|
*~
|
|
\#*\#
|
|
/.emacs.desktop
|
|
/.emacs.desktop.lock
|
|
*.elc
|
|
auto-save-list
|
|
tramp
|
|
.\#*
|
|
|
|
# =============================================================================
|
|
# OS
|
|
# =============================================================================
|
|
|
|
# macOS
|
|
.DS_Store
|
|
.AppleDouble
|
|
.LSOverride
|
|
._*
|
|
|
|
# Windows
|
|
Thumbs.db
|
|
ehthumbs.db
|
|
Desktop.ini
|
|
|
|
# Linux
|
|
*~
|
|
|
|
# =============================================================================
|
|
# Project-specific
|
|
# =============================================================================
|
|
|
|
# Local configuration
|
|
*.local
|
|
.env
|
|
.env.*
|
|
!.env.example
|
|
|
|
# Logs
|
|
*.log
|
|
logs/
|
|
|
|
# Temporary files
|
|
tmp/
|
|
temp/
|
|
*.tmp
|
|
|
|
# Secrets/credentials (should never be committed)
|
|
*.pem
|
|
*.key
|
|
*.crt
|
|
secrets/
|
|
credentials/
|
|
|
|
kubeconfig
|
|
|
|
# Docker build artifacts (image tarballs, packaged helm charts)
|
|
deploy/docker/.build/
|
|
|
|
# Local mise settings
|
|
mise.local.toml
|
|
|
|
# Ignore plans for now
|
|
architecture/plans
|
|
|
|
# Claude
|
|
.claude/settings.local.json.claude/worktrees/
|
|
.claude/worktrees/
|