From 726ab2928ebbd35a65464f6dabec35efce0d8232 Mon Sep 17 00:00:00 2001 From: John Myers Date: Tue, 3 Mar 2026 18:42:15 -0800 Subject: [PATCH 1/3] feat(skills): create nemoclaw-cli agent skill and fix generate-sandbox-policy schema drift Closes #83 Create a new nemoclaw-cli agent skill with tiered workflow guidance (basic through advanced) and a supplementary CLI reference. The skill covers provider management, sandbox lifecycle, policy iteration loops, BYOC pipelines, and agent-assisted sandbox sessions. It cross-references generate-sandbox-policy for policy content authoring and includes --help self-teaching as a fallback. Fix critical schema drift in generate-sandbox-policy: rename allowed_routing_hints to allowed_routes (matching the actual proto/serde field name). Update default read_only paths to match dev-sandbox-policy.yaml. Fix the same field name error in architecture/security-policy.md. --- .../skills/generate-sandbox-policy/SKILL.md | 5 +- .../generate-sandbox-policy/examples.md | 5 +- .agents/skills/nemoclaw-cli/SKILL.md | 584 ++++++++++++++++++ .agents/skills/nemoclaw-cli/cli-reference.md | 365 +++++++++++ architecture/security-policy.md | 10 +- 5 files changed, 960 insertions(+), 9 deletions(-) create mode 100644 .agents/skills/nemoclaw-cli/SKILL.md create mode 100644 .agents/skills/nemoclaw-cli/cli-reference.md diff --git a/.agents/skills/generate-sandbox-policy/SKILL.md b/.agents/skills/generate-sandbox-policy/SKILL.md index e60f66d7..8782886f 100644 --- a/.agents/skills/generate-sandbox-policy/SKILL.md +++ b/.agents/skills/generate-sandbox-policy/SKILL.md @@ -391,10 +391,11 @@ filesystem_policy: read_only: - /usr - /lib - - /proc/self + - /proc - /dev/urandom - /app - /etc + - /var/log read_write: - /sandbox - /tmp @@ -411,7 +412,7 @@ network_policies: # inference: - allowed_routing_hints: + allowed_routes: - local ``` diff --git a/.agents/skills/generate-sandbox-policy/examples.md b/.agents/skills/generate-sandbox-policy/examples.md index ee97cf45..c9611e2a 100644 --- a/.agents/skills/generate-sandbox-policy/examples.md +++ b/.agents/skills/generate-sandbox-policy/examples.md @@ -825,10 +825,11 @@ filesystem_policy: read_only: - /usr - /lib - - /proc/self + - /proc - /dev/urandom - /app - /etc + - /var/log read_write: - /sandbox - /tmp @@ -867,7 +868,7 @@ network_policies: - { path: /usr/local/bin/claude } inference: - allowed_routing_hints: + allowed_routes: - local ``` diff --git a/.agents/skills/nemoclaw-cli/SKILL.md b/.agents/skills/nemoclaw-cli/SKILL.md new file mode 100644 index 00000000..fbbd1e94 --- /dev/null +++ b/.agents/skills/nemoclaw-cli/SKILL.md @@ -0,0 +1,584 @@ +--- +name: nemoclaw-cli +description: Guide agents through using the NemoClaw CLI (nemoclaw/ncl) for sandbox management, provider configuration, policy iteration, BYOC workflows, and inference routing. Covers basic through advanced multi-step workflows. Trigger keywords - nemoclaw, ncl, sandbox create, sandbox connect, sandbox logs, provider create, policy set, policy get, image push, port forward, BYOC, bring your own container, use nemoclaw, run nemoclaw, CLI usage, manage sandbox, manage provider. +--- + +# NemoClaw CLI + +Guide agents through using the `nemoclaw` CLI (`ncl`) for sandbox and platform management -- from basic operations to advanced multi-step workflows. + +## Overview + +The NemoClaw CLI (`nemoclaw`, commonly aliased as `ncl`) is the primary interface for managing sandboxes, providers, policies, inference routes, and clusters. This skill teaches agents how to orchestrate CLI commands for common and complex workflows. + +**Companion skill**: For creating or modifying sandbox policy YAML content (network rules, L7 inspection, access presets), use the `generate-sandbox-policy` skill. This skill covers the CLI *commands* for the policy lifecycle; `generate-sandbox-policy` covers policy *content authoring*. + +**Self-teaching**: The CLI has comprehensive built-in help. When you encounter a command or option not covered in this skill, walk the help tree: + +```bash +ncl --help # Top-level commands +ncl --help # Subcommands in a group +ncl --help # Flags for a specific command +``` + +This is your primary fallback. Use it freely -- the CLI's help output is authoritative and always up-to-date. + +## Prerequisites + +- `ncl` or `nemoclaw` is on the PATH (install via `cargo install --path crates/navigator-cli` or use the `ncl` wrapper script) +- Docker is running (required for cluster operations and BYOC) +- For remote clusters: SSH access to the target host + +## Command Reference + +See [cli-reference.md](cli-reference.md) for the full command tree with all flags and options. Use it as a quick-reference to avoid round-tripping through `--help` for common commands. + +--- + +## Workflow 1: Getting Started + +Use this workflow when no cluster exists yet and the user wants to get a sandbox running for the first time. + +### Step 1: Bootstrap a cluster + +```bash +ncl cluster admin deploy +``` + +This provisions a local k3s cluster in Docker. The CLI will prompt interactively if a cluster already exists. The cluster is automatically set as the active cluster. + +For remote deployment: + +```bash +ncl cluster admin deploy --remote user@host --ssh-key ~/.ssh/id_rsa +``` + +### Step 2: Verify the cluster + +```bash +ncl cluster status +``` + +Confirm the cluster is reachable and shows a version. + +### Step 3: Create a sandbox + +The simplest way to get a sandbox running: + +```bash +ncl sandbox create +``` + +This creates a sandbox with defaults and drops you into an interactive shell. The CLI auto-bootstraps a cluster if none exists. + +**Shortcut for known tools**: When the trailing command is a recognized tool, the CLI auto-creates the required provider from local credentials: + +```bash +ncl sandbox create -- claude # Auto-creates claude provider +ncl sandbox create -- codex # Auto-creates codex provider +``` + +The agent will be prompted interactively if credentials are missing. + +### Step 4: Exit and clean up + +Exit the sandbox shell (`exit` or Ctrl-D), then: + +```bash +ncl sandbox delete +``` + +--- + +## Workflow 2: Provider Management + +Providers supply credentials to sandboxes (API keys, tokens, etc.). Manage them before creating sandboxes that need them. + +Supported types: `claude`, `opencode`, `codex`, `generic`, `nvidia`, `gitlab`, `github`, `outlook`. + +### Create a provider from local credentials + +```bash +ncl provider create --name my-github --type github --from-existing +``` + +The `--from-existing` flag discovers credentials from local state (e.g., `gh auth` tokens, Claude config files). + +### Create a provider with explicit credentials + +```bash +ncl provider create --name my-api --type generic \ + --credential API_KEY=sk-abc123 \ + --config base_url=https://api.example.com +``` + +Bare `KEY` (without `=VALUE`) reads the value from the environment variable of that name: + +```bash +ncl provider create --name my-api --type generic --credential API_KEY +``` + +### List, inspect, update, delete + +```bash +ncl provider list +ncl provider get my-github +ncl provider update my-github --type github --from-existing +ncl provider delete my-github +``` + +--- + +## Workflow 3: Sandbox Lifecycle + +### Create with options + +```bash +ncl sandbox create \ + --name my-sandbox \ + --provider my-github \ + --provider my-claude \ + --policy ./my-policy.yaml \ + --sync \ + -- claude +``` + +Key flags: +- `--provider`: Attach one or more providers (repeatable) +- `--policy`: Custom policy YAML (otherwise uses built-in default or `NEMOCLAW_SANDBOX_POLICY` env var) +- `--sync`: Push local git-tracked files to `/sandbox` in the container +- `--keep`: Keep sandbox alive after the command exits (useful for non-interactive commands) +- `--forward `: Forward a local port (implies `--keep`) + +### List and inspect sandboxes + +```bash +ncl sandbox list +ncl sandbox get my-sandbox +``` + +### Connect to a running sandbox + +```bash +ncl sandbox connect my-sandbox +``` + +Opens an interactive SSH shell. To configure VS Code Remote-SSH: + +```bash +ncl sandbox ssh-config my-sandbox >> ~/.ssh/config +``` + +### Sync files + +```bash +# Push local files to sandbox +ncl sandbox sync my-sandbox --up ./src /sandbox/src + +# Pull files from sandbox +ncl sandbox sync my-sandbox --down /sandbox/output ./local-output +``` + +### View logs + +```bash +# Recent logs +ncl sandbox logs my-sandbox + +# Stream live logs +ncl sandbox logs my-sandbox --tail + +# Filter by source and level +ncl sandbox logs my-sandbox --tail --source sandbox --level warn + +# Logs from the last 5 minutes +ncl sandbox logs my-sandbox --since 5m +``` + +### Delete sandboxes + +```bash +ncl sandbox delete my-sandbox +ncl sandbox delete sandbox-1 sandbox-2 sandbox-3 # Multiple at once +``` + +--- + +## Workflow 4: Policy Iteration Loop + +This is the most important multi-step workflow. It enables a tight feedback cycle where sandbox policy is refined based on observed activity. + +**Key concept**: Policies have static fields (immutable after creation: `filesystem_policy`, `landlock`, `process`) and dynamic fields (hot-reloadable on a running sandbox: `network_policies`, `inference`). Only dynamic fields can be updated without recreating the sandbox. + +``` +Create sandbox with initial policy + │ + ▼ + Monitor logs ◄──────────────────┐ + │ │ + ▼ │ + Observe denied actions │ + │ │ + ▼ │ + Pull current policy │ + │ │ + ▼ │ + Modify policy YAML │ + (use generate-sandbox-policy) │ + │ │ + ▼ │ + Push updated policy │ + │ │ + ▼ │ + Verify reload succeeded ─────────┘ +``` + +### Step 1: Create sandbox with initial policy + +```bash +ncl sandbox create --name dev --policy ./initial-policy.yaml --keep -- claude +``` + +Use `--keep` so the sandbox stays alive for iteration. The user can work in the sandbox via a separate shell. + +### Step 2: Monitor logs for denied actions + +In a separate terminal or as the agent: + +```bash +ncl sandbox logs dev --tail --source sandbox +``` + +Look for log lines with `action: deny` -- these indicate blocked network requests. The logs include: +- **Destination host and port** (what was blocked) +- **Binary path** (which process attempted the connection) +- **Deny reason** (why it was blocked) + +### Step 3: Pull the current policy + +```bash +ncl sandbox policy get dev --full > current-policy.yaml +``` + +The `--full` flag outputs valid YAML that can be directly re-submitted. This is the round-trip format. + +### Step 4: Modify the policy + +Edit `current-policy.yaml` to allow the blocked actions. **For policy content authoring, delegate to the `generate-sandbox-policy` skill.** That skill handles: +- Network endpoint rule structure +- L4 vs L7 policy decisions +- Access presets (`read-only`, `read-write`, `full`) +- TLS termination configuration +- Enforcement modes (`audit` vs `enforce`) +- Binary matching patterns + +Only `network_policies` and `inference` sections can be modified at runtime. If `filesystem_policy`, `landlock`, or `process` need changes, the sandbox must be recreated. + +### Step 5: Push the updated policy + +```bash +ncl sandbox policy set dev --policy current-policy.yaml --wait +``` + +The `--wait` flag blocks until the sandbox confirms the policy is loaded (polls every second). Exit codes: +- **0**: Policy loaded successfully +- **1**: Policy load failed +- **124**: Timeout (default 60 seconds) + +### Step 6: Verify the update + +```bash +ncl sandbox policy list dev +``` + +Check that the latest revision shows status `loaded`. If `failed`, check the error column for details. + +### Step 7: Repeat + +Return to Step 2. Continue monitoring logs and refining the policy until all required actions are allowed and no unnecessary permissions exist. + +### Policy revision history + +View all revisions to understand how the policy evolved: + +```bash +ncl sandbox policy list dev --limit 50 +``` + +Fetch a specific historical revision: + +```bash +ncl sandbox policy get dev --rev 3 --full +``` + +--- + +## Workflow 5: BYOC (Bring Your Own Container) + +Build a custom container image and run it as a sandbox. + +### Step 1: Build and push the image + +```bash +ncl sandbox image push \ + --dockerfile ./Dockerfile \ + --tag my-app:latest \ + --context . +``` + +The image is built locally via Docker and imported directly into the cluster's containerd runtime. No external registry needed. + +Build arguments are supported: + +```bash +ncl sandbox image push \ + --dockerfile ./Dockerfile \ + --tag my-app:v2 \ + --build-arg PYTHON_VERSION=3.12 +``` + +### Step 2: Create a sandbox with the custom image + +```bash +ncl sandbox create --image my-app:latest --keep --name my-app +``` + +When `--image` is specified, the CLI: +- Clears default `run_as_user`/`run_as_group` (custom images may not have the `sandbox` user) +- Uses a supervisor bootstrap pattern (init container copies the sandbox supervisor into a shared volume) + +### Step 3: Forward ports (if the container runs a service) + +```bash +# Foreground (blocks) +ncl sandbox forward start 8080 my-app + +# Background (returns immediately) +ncl sandbox forward start 8080 my-app -d +``` + +The service is now reachable at `localhost:8080`. + +### Step 4: Manage port forwards + +```bash +# List active forwards +ncl sandbox forward list + +# Stop a forward +ncl sandbox forward stop 8080 my-app +``` + +### Step 5: Iterate + +To update the container: + +```bash +ncl sandbox delete my-app +ncl sandbox image push --dockerfile ./Dockerfile --tag my-app:v2 +ncl sandbox create --image my-app:v2 --keep --name my-app --forward 8080 +``` + +### Shortcut: Create with port forward in one command + +```bash +ncl sandbox create --image my-app:latest --forward 8080 --keep -- ./start-server.sh +``` + +The `--forward` flag starts a background port forward before the command runs, so the service is reachable immediately. + +### Limitations + +- Distroless / `FROM scratch` images are not supported (the supervisor needs glibc, `/proc`, and a shell) +- Missing `iproute2` or required capabilities blocks startup in proxy mode + +--- + +## Workflow 6: Agent-Assisted Sandbox Session + +This workflow supports a human working in a sandbox while an agent monitors activity and refines the policy in parallel. + +### Step 1: Create sandbox with providers and keep alive + +```bash +ncl sandbox create \ + --name work-session \ + --provider github \ + --provider claude \ + --policy ./dev-policy.yaml \ + --keep +``` + +### Step 2: User connects in a separate shell + +Tell the user to run: + +```bash +ncl sandbox connect work-session +``` + +Or for VS Code: + +```bash +ncl sandbox ssh-config work-session >> ~/.ssh/config +# Then connect via VS Code Remote-SSH to the host "work-session" +``` + +### Step 3: Agent monitors logs + +While the user works, monitor the sandbox logs: + +```bash +ncl sandbox logs work-session --tail --source sandbox --level warn +``` + +Watch for `deny` actions that indicate the user's work is being blocked by policy. + +### Step 4: Agent refines policy + +When denied actions are observed: + +1. Pull current policy: `ncl sandbox policy get work-session --full > policy.yaml` +2. Modify the policy to allow the blocked actions (use `generate-sandbox-policy` skill for content) +3. Push the update: `ncl sandbox policy set work-session --policy policy.yaml --wait` +4. Verify: `ncl sandbox policy list work-session` + +The user does not need to disconnect -- policy updates are hot-reloaded within ~30 seconds (or immediately when using `--wait`, which polls for confirmation). + +### Step 5: Clean up when done + +```bash +ncl sandbox delete work-session +``` + +--- + +## Workflow 7: Inference Routing + +Configure inference routes so sandboxes can access LLM endpoints. + +### Create an inference route + +```bash +ncl inference create \ + --routing-hint local \ + --base-url https://my-llm.example.com \ + --model-id my-model-v1 \ + --api-key sk-abc123 +``` + +If `--protocol` is omitted, the CLI auto-detects by probing the endpoint. + +### List and manage routes + +```bash +ncl inference list +ncl inference update my-route --routing-hint local --base-url https://new-url.example.com --model-id my-model-v2 +ncl inference delete my-route +``` + +### Connect sandbox to inference + +Ensure the sandbox policy allows the routing hint: + +```yaml +# In the policy YAML +inference: + allowed_routes: + - local +``` + +Then create the sandbox with the policy: + +```bash +ncl sandbox create --policy ./policy-with-inference.yaml -- claude +``` + +--- + +## Workflow 8: Cluster Management + +### List and switch clusters + +```bash +ncl cluster list # See all clusters +ncl cluster use my-cluster # Switch active cluster +ncl cluster status # Verify connectivity +``` + +### Lifecycle + +```bash +ncl cluster admin deploy # Start local cluster +ncl cluster admin stop # Stop (preserves state) +ncl cluster admin deploy # Restart (reuses state) +ncl cluster admin destroy # Destroy permanently +``` + +### Remote clusters + +```bash +# Deploy to remote host +ncl cluster admin deploy --remote user@host --ssh-key ~/.ssh/id_rsa --name remote-cluster + +# Set up kubectl access +ncl cluster admin tunnel --name remote-cluster + +# Get cluster info +ncl cluster admin info --name remote-cluster +``` + +--- + +## Self-Teaching via `--help` + +When you encounter a command or option not covered in this skill: + +1. **Start broad**: `ncl --help` to see all command groups. +2. **Narrow down**: `ncl --help` to see subcommands (e.g., `ncl sandbox --help`). +3. **Get specific**: `ncl --help` for flags and usage (e.g., `ncl sandbox create --help`). + +The CLI help is always authoritative. If the help output contradicts this skill, follow the help output -- the CLI may have been updated since this skill was written. + +### Example: discovering an unfamiliar command + +```bash +$ ncl sandbox --help +# Shows: create, get, list, delete, connect, sync, logs, ssh-config, forward, image, policy + +$ ncl sandbox sync --help +# Shows: --up, --down flags, positional arguments, usage examples +``` + +--- + +## Quick Reference + +| Task | Command | +|------|---------| +| Deploy local cluster | `ncl cluster admin deploy` | +| Check cluster health | `ncl cluster status` | +| Create sandbox (interactive) | `ncl sandbox create` | +| Create sandbox with tool | `ncl sandbox create -- claude` | +| Create with custom policy | `ncl sandbox create --policy ./p.yaml --keep` | +| Connect to sandbox | `ncl sandbox connect ` | +| Stream live logs | `ncl sandbox logs --tail` | +| Pull current policy | `ncl sandbox policy get --full > p.yaml` | +| Push updated policy | `ncl sandbox policy set --policy p.yaml --wait` | +| Policy revision history | `ncl sandbox policy list ` | +| Build & push custom image | `ncl sandbox image push --dockerfile ./Dockerfile` | +| Forward a port | `ncl sandbox forward start -d` | +| Create provider | `ncl provider create --name N --type T --from-existing` | +| List providers | `ncl provider list` | +| Create inference route | `ncl inference create --routing-hint H --base-url U --model-id M` | +| Delete sandbox | `ncl sandbox delete ` | +| Destroy cluster | `ncl cluster admin destroy` | +| Self-teach any command | `ncl --help` | + +## Companion Skills + +| Skill | When to use | +|-------|------------| +| `generate-sandbox-policy` | Creating or modifying policy YAML content (network rules, L7 inspection, access presets, endpoint configuration) | +| `debug-navigator-cluster` | Diagnosing cluster startup or health failures | +| `tui-development` | Developing features for the Gator TUI (`ncl gator`) | diff --git a/.agents/skills/nemoclaw-cli/cli-reference.md b/.agents/skills/nemoclaw-cli/cli-reference.md new file mode 100644 index 00000000..329f37bd --- /dev/null +++ b/.agents/skills/nemoclaw-cli/cli-reference.md @@ -0,0 +1,365 @@ +# NemoClaw CLI Reference + +Quick-reference for the `nemoclaw` (aliased as `ncl`) command-line interface. For workflow guidance, see [SKILL.md](SKILL.md). + +> **Self-teaching**: If a command or flag is not listed here, use `ncl --help` to discover it. The CLI has comprehensive built-in help at every level. + +## Global Options + +| Flag | Description | +|------|-------------| +| `-v`, `--verbose` | Increase verbosity (`-v` = info, `-vv` = debug, `-vvv` = trace) | +| `-c`, `--cluster ` | Cluster to operate on. Also settable via `NEMOCLAW_CLUSTER` env var. Falls back to active cluster in `~/.config/nemoclaw/active_cluster`. | + +## Environment Variables + +| Variable | Description | +|----------|-------------| +| `NEMOCLAW_CLUSTER` | Override active cluster name (same as `--cluster`) | +| `NEMOCLAW_SANDBOX_POLICY` | Path to default sandbox policy YAML (fallback when `--policy` is not provided) | + +--- + +## Complete Command Tree + +``` +nemoclaw (ncl) +├── cluster +│ ├── status +│ ├── use +│ ├── list +│ └── admin +│ ├── deploy [opts] +│ ├── stop [opts] +│ ├── destroy [opts] +│ ├── info [--name] +│ └── tunnel [opts] +├── sandbox +│ ├── create [opts] [-- CMD...] +│ ├── get +│ ├── list [opts] +│ ├── delete ... +│ ├── connect +│ ├── sync {--up|--down} [dest] +│ ├── logs [opts] +│ ├── ssh-config +│ ├── forward +│ │ ├── start [-d] +│ │ ├── stop +│ │ └── list +│ ├── image +│ │ └── push [opts] +│ └── policy +│ ├── set --policy [--wait] +│ ├── get [--full] +│ └── list +├── provider +│ ├── create --name --type [opts] +│ ├── get +│ ├── list [opts] +│ ├── update --type [opts] +│ └── delete ... +├── inference +│ ├── create [opts] +│ ├── update [opts] +│ ├── delete ... +│ └── list [opts] +├── gator +├── completions +└── ssh-proxy [opts] +``` + +--- + +## Cluster Commands + +### `ncl cluster status` + +Show server connectivity and version. + +### `ncl cluster use ` + +Set the active cluster. Writes to `~/.config/nemoclaw/active_cluster`. + +### `ncl cluster list` + +List all provisioned clusters. Active cluster marked with `*`. + +### `ncl cluster admin deploy` + +Provision or start a cluster (local or remote). + +| Flag | Default | Description | +|------|---------|-------------| +| `--name ` | `nemoclaw` | Cluster name | +| `--remote ` | none | SSH destination for remote deployment | +| `--ssh-key ` | none | SSH private key for remote deployment | +| `--port ` | 8080 | Host port mapped to gateway | +| `--gateway-host ` | none | Override gateway host in metadata | +| `--kube-port [PORT]` | none | Expose K8s control plane on host port | +| `--update-kube-config` | false | Write kubeconfig into `~/.kube/config` | +| `--get-kubeconfig` | false | Print kubeconfig to stdout | + +### `ncl cluster admin stop` + +Stop a cluster (preserves state for later restart). + +| Flag | Description | +|------|-------------| +| `--name ` | Cluster name (defaults to active) | +| `--remote ` | SSH destination | +| `--ssh-key ` | SSH private key | + +### `ncl cluster admin destroy` + +Destroy a cluster and all its state. Same flags as `stop`. + +### `ncl cluster admin info` + +Show deployment details: endpoint, kubeconfig path, kube port, remote host. + +| Flag | Description | +|------|-------------| +| `--name ` | Cluster name (defaults to active) | + +### `ncl cluster admin tunnel` + +Print or start an SSH tunnel for kubectl access to a remote cluster. + +| Flag | Description | +|------|-------------| +| `--name ` | Cluster name (defaults to active) | +| `--remote ` | SSH destination | +| `--ssh-key ` | SSH private key | +| `--print-command` | Only print the SSH command, don't execute | + +--- + +## Sandbox Commands + +### `ncl sandbox create [OPTIONS] [-- COMMAND...]` + +Create a sandbox, wait for readiness, then connect or execute the trailing command. Auto-bootstraps a cluster if none exists. + +| Flag | Description | +|------|-------------| +| `--name ` | Sandbox name (auto-generated if omitted) | +| `--image ` | Custom container image (BYOC) | +| `--sync` | Sync local git-tracked files into sandbox at `/sandbox` | +| `--keep` | Keep sandbox alive after non-interactive commands finish | +| `--provider ` | Provider to attach (repeatable) | +| `--policy ` | Path to custom policy YAML | +| `--forward ` | Forward local port to sandbox (implies `--keep`) | +| `--remote ` | SSH destination for auto-bootstrap | +| `--ssh-key ` | SSH private key for auto-bootstrap | +| `[-- COMMAND...]` | Command to execute (defaults to interactive shell) | + +### `ncl sandbox get ` + +Show sandbox details (id, name, namespace, phase, policy). + +### `ncl sandbox list` + +List sandboxes in a table. + +| Flag | Default | Description | +|------|---------|-------------| +| `--limit ` | 100 | Max sandboxes to return | +| `--offset ` | 0 | Pagination offset | +| `--ids` | false | Print only sandbox IDs | +| `--names` | false | Print only sandbox names | + +### `ncl sandbox delete ...` + +Delete one or more sandboxes by name. Stops any background port forwards. + +### `ncl sandbox connect ` + +Open an interactive SSH shell to a sandbox. + +### `ncl sandbox sync {--up | --down } [dest]` + +Sync files to/from a sandbox using tar-over-SSH. + +| Flag | Description | +|------|-------------| +| `--up ` | Push local files to sandbox | +| `--down ` | Pull sandbox files to local | +| `[DEST]` | Destination path (default: `/sandbox` for up, `.` for down) | + +### `ncl sandbox logs ` + +View sandbox logs. Supports one-shot and streaming. + +| Flag | Default | Description | +|------|---------|-------------| +| `-n ` | 200 | Number of log lines | +| `--tail` | false | Stream live logs | +| `--since ` | none | Only show logs from this duration ago (e.g., `5m`, `1h`) | +| `--source ` | `all` | Filter: `gateway`, `sandbox`, or `all` (repeatable) | +| `--level ` | none | Minimum level: `error`, `warn`, `info`, `debug`, `trace` | + +### `ncl sandbox ssh-config ` + +Print an SSH config `Host` block for a sandbox. Useful for VS Code Remote-SSH. + +--- + +## Port Forwarding Commands + +### `ncl sandbox forward start ` + +Start forwarding a local port to a sandbox. + +| Flag | Description | +|------|-------------| +| `` | Port number (used as both local and remote) | +| `` | Sandbox name | +| `-d`, `--background` | Run in background | + +### `ncl sandbox forward stop ` + +Stop a background port forward. + +### `ncl sandbox forward list` + +List all active port forwards (sandbox, port, PID, status). + +--- + +## Custom Image Commands (BYOC) + +### `ncl sandbox image push` + +Build a container image and push it into the cluster's internal registry. + +| Flag | Description | +|------|-------------| +| `--dockerfile ` | Path to Dockerfile (required) | +| `--tag ` | Image name and tag (default: `navigator/sandbox-custom:`) | +| `--context ` | Build context directory (default: Dockerfile parent) | +| `--build-arg KEY=VALUE` | Build argument (repeatable) | + +--- + +## Policy Commands + +### `ncl sandbox policy set --policy ` + +Update the policy on a live sandbox. Only dynamic fields (`network_policies`, `inference`) can be changed at runtime. + +| Flag | Default | Description | +|------|---------|-------------| +| `--policy ` | -- | Path to policy YAML (required) | +| `--wait` | false | Wait for sandbox to confirm policy is loaded | +| `--timeout ` | 60 | Timeout for `--wait` | + +Exit codes with `--wait`: 0 = loaded, 1 = failed, 124 = timeout. + +### `ncl sandbox policy get ` + +Show current active policy for a sandbox. + +| Flag | Default | Description | +|------|---------|-------------| +| `--rev ` | 0 (latest) | Show a specific revision | +| `--full` | false | Print the full policy as YAML (round-trips with `--policy` input) | + +### `ncl sandbox policy list ` + +List policy revision history (version, hash, status, created, error). + +| Flag | Default | Description | +|------|---------|-------------| +| `--limit ` | 20 | Max revisions to return | + +--- + +## Provider Commands + +Supported provider types: `claude`, `opencode`, `codex`, `generic`, `nvidia`, `gitlab`, `github`, `outlook`. + +### `ncl provider create --name --type ` + +Create a provider configuration. + +| Flag | Description | +|------|-------------| +| `--name ` | Provider name (required) | +| `--type ` | Provider type (required) | +| `--from-existing` | Load credentials from local state (mutually exclusive with `--credential`) | +| `--credential KEY[=VALUE]` | Credential pair. Bare `KEY` reads from env var. Repeatable. | +| `--config KEY=VALUE` | Config key/value pair. Repeatable. | + +### `ncl provider get ` + +Show provider details (id, name, type, credential keys, config keys). + +### `ncl provider list` + +List providers in a table. + +| Flag | Default | Description | +|------|---------|-------------| +| `--limit ` | 100 | Max providers | +| `--offset ` | 0 | Pagination offset | +| `--names` | false | Print only names | + +### `ncl provider update --type ` + +Update an existing provider. Same flags as `create`. + +### `ncl provider delete ...` + +Delete one or more providers by name. + +--- + +## Inference Commands + +### `ncl inference create` + +Create an inference route. Auto-detects supported protocols if `--protocol` is omitted. + +| Flag | Default | Description | +|------|---------|-------------| +| `--name ` | auto-generated | Route name | +| `--routing-hint ` | -- | Routing hint (required) | +| `--base-url ` | -- | Inference endpoint base URL (required) | +| `--protocol ` | auto-detected | Protocol(s): `openai_chat_completions`, `openai_completions`, `anthropic_messages`. Repeatable. | +| `--api-key ` | `""` | API key for the endpoint | +| `--model-id ` | -- | Model identifier (required) | +| `--disabled` | false | Create in disabled state | + +### `ncl inference update ` + +Update an existing inference route. Same flags as `create`. + +### `ncl inference delete ...` + +Delete inference routes by name. + +### `ncl inference list` + +List inference routes. + +| Flag | Default | Description | +|------|---------|-------------| +| `--limit ` | 100 | Max routes | +| `--offset ` | 0 | Pagination offset | + +--- + +## Other Commands + +### `ncl gator` + +Launch the Gator interactive TUI. + +### `ncl completions ` + +Generate shell completion scripts. Supported shells: `bash`, `fish`, `zsh`, `powershell`. + +### `ncl ssh-proxy` + +SSH proxy used as a `ProxyCommand`. Not typically invoked directly. diff --git a/architecture/security-policy.md b/architecture/security-policy.md index 08110471..dce09cd3 100644 --- a/architecture/security-policy.md +++ b/architecture/security-policy.md @@ -286,7 +286,7 @@ network_policies: # Inference routing policy (gRPC mode only) inference: - allowed_routing_hints: [] + allowed_routes: [] ``` --- @@ -472,11 +472,11 @@ Controls access to the platform's inference routing system (gRPC mode only, incl | Field | Type | Default | Description | | ----------------------- | ---------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `allowed_routing_hints` | `string[]` | `[]` | Which routing hints the sandbox may request. e.g., `["local"]` for private-only, `["local", "frontier"]` for full access. Empty means no inference allowed. | +| `allowed_routes` | `string[]` | `[]` | Which routing hints the sandbox may request. e.g., `["local"]` for private-only, `["local", "frontier"]` for full access. Empty means no inference allowed. | ```yaml inference: - allowed_routing_hints: + allowed_routes: - local ``` @@ -987,7 +987,7 @@ network_policies: - { path: /usr/bin/curl } inference: - allowed_routing_hints: + allowed_routes: - local ``` @@ -1016,7 +1016,7 @@ When the gateway delivers policy via gRPC, the protobuf `SandboxPolicy` message | `NetworkEndpoint` | `host`, `port`, `protocol`, `tls`, `enforcement`, `access`, `rules`, `allowed_ips` | Same field names | | `L7Rule` | `allow` | `rules[].allow` | | `L7Allow` | `method`, `path`, `command` | `rules[].allow.method`, `.path`, `.command` | -| `InferencePolicy` | `allowed_routing_hints` | `inference.allowed_routing_hints` | +| `InferencePolicy` | `allowed_routes` | `inference.allowed_routes` | The conversion is performed in `crates/navigator-sandbox/src/opa.rs` -- `proto_to_opa_data_json()`. From 9b91fd26a439ce243514f1d8c684ef787c7cac1c Mon Sep 17 00:00:00 2001 From: John Myers Date: Wed, 4 Mar 2026 09:31:08 -0800 Subject: [PATCH 2/3] policy updates --- dev-sandbox-policy-empty.yaml | 36 +++++++++++++++ dev-sandbox-policy.yaml | 83 +++++++---------------------------- 2 files changed, 51 insertions(+), 68 deletions(-) create mode 100644 dev-sandbox-policy-empty.yaml diff --git a/dev-sandbox-policy-empty.yaml b/dev-sandbox-policy-empty.yaml new file mode 100644 index 00000000..f8a00760 --- /dev/null +++ b/dev-sandbox-policy-empty.yaml @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# Empty sandbox policy — no network access. +# Use this as a starting point for iterative policy development. +# Network policies and inference routes can be added at runtime +# via `ncl sandbox policy set`. + +version: 1 + +filesystem_policy: + include_workdir: true + read_only: + - /usr + - /lib + - /proc + - /dev/urandom + - /app + - /etc + - /var/log + read_write: + - /sandbox + - /tmp + - /dev/null + +landlock: + compatibility: best_effort + +process: + run_as_user: sandbox + run_as_group: sandbox + +# No network policies — all outbound connections will be denied. +# Add policies here or push updates at runtime with: +# ncl sandbox policy set --policy +network_policies: {} diff --git a/dev-sandbox-policy.yaml b/dev-sandbox-policy.yaml index 92d9a419..29cdd8f0 100644 --- a/dev-sandbox-policy.yaml +++ b/dev-sandbox-policy.yaml @@ -36,7 +36,7 @@ process: network_policies: claude_code: - name: claude_code + name: claude-code endpoints: - { host: api.anthropic.com, port: 443, protocol: rest, enforcement: enforce, access: full, tls: terminate } - { host: statsig.anthropic.com, port: 443 } @@ -46,15 +46,9 @@ network_policies: binaries: - { path: /usr/local/bin/claude } - { path: /usr/bin/node } - gitlab: - name: gitlab - endpoints: - - { host: gitlab.com, port: 443 } - - { host: gitlab.mycorp.com, port: 443 } - binaries: - - { path: /usr/bin/glab } - github: - name: github + + github_ssh_over_https: + name: github-ssh-over-https endpoints: - host: github.com port: 443 @@ -71,66 +65,32 @@ network_policies: - allow: method: POST path: "/**/git-upload-pack" - # Data transfer for writes (alpha-claw only) - - allow: - method: POST - path: "/johntmyers/alpha-claw*/git-receive-pack" + # Data transfer for writes + # - allow: + # method: POST + # path: "/**/git-receive-pack" binaries: - { path: /usr/bin/git } - nvidia: - name: nvidia + + nvidia_inference: + name: nvidia-inference endpoints: - { host: integrate.api.nvidia.com, port: 443 } binaries: - { path: /usr/bin/curl } - { path: /bin/bash } - { path: /usr/local/bin/opencode } - nvidia_web: - name: nvidia_web - endpoints: - - { host: nvidia.com, port: 443 } - - { host: www.nvidia.com, port: 443 } - binaries: - - { path: /usr/bin/curl } - # --- GitHub repo-scoped policy (L7, TLS-terminated) --- - # Combined policy for api.github.com to avoid OPA complete-rule conflict. - # - johntmyers/alpha-claw: full access (all methods including DELETE) - # - johntmyers/bravo-claw: read-only + create/edit issues - github_repos: - name: github_repos + # --- GitHub REST API (read-only) --- + github_rest_api: + name: github-rest-api endpoints: - host: api.github.com port: 443 protocol: rest tls: terminate enforcement: enforce - rules: - # Read-only access to all GitHub API paths - - allow: - method: GET - path: "/**" - - allow: - method: HEAD - path: "/**" - - allow: - method: OPTIONS - path: "/**" - # GraphQL API (used by gh CLI for most operations) - # - allow: - # method: POST - # path: "/graphql" - # alpha-claw: full write access - - allow: - method: "*" - path: "/repos/johntmyers/alpha-claw/**" - # bravo-claw: create + edit issues - - allow: - method: POST - path: "/repos/johntmyers/bravo-claw/issues" - - allow: - method: PATCH - path: "/repos/johntmyers/bravo-claw/issues/*" + access: read-only binaries: - { path: /usr/local/bin/claude } - { path: /usr/bin/gh } @@ -149,19 +109,6 @@ network_policies: - { path: "/sandbox/.vscode-server/**" } - { path: "/sandbox/.vscode-remote-containers/**" } - # --- Private network access (allowed_ips demo) --- - # Allows any binary to reach services on the k3s cluster pod network - # (10.42.0.0/16). Without allowed_ips, the proxy's SSRF check blocks - # all connections to private RFC 1918 addresses. - cluster_pods: - name: cluster_pods - endpoints: - - port: 8080 - allowed_ips: - - "10.42.0.0/16" - binaries: - - { path: "/**" } - inference: allowed_routes: - local From bc78d445a0a2ec5b752b3442452cf442e564f665 Mon Sep 17 00:00:00 2001 From: John Myers Date: Wed, 4 Mar 2026 09:46:27 -0800 Subject: [PATCH 3/3] fix(test): update OPA tests for renamed github_ssh_over_https policy key --- crates/navigator-sandbox/src/opa.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/navigator-sandbox/src/opa.rs b/crates/navigator-sandbox/src/opa.rs index fba3f1ad..07b178e3 100644 --- a/crates/navigator-sandbox/src/opa.rs +++ b/crates/navigator-sandbox/src/opa.rs @@ -829,7 +829,10 @@ mod tests { "Expected allow, got deny: {}", decision.reason ); - assert_eq!(decision.matched_policy.as_deref(), Some("github")); + assert_eq!( + decision.matched_policy.as_deref(), + Some("github_ssh_over_https") + ); } #[test] @@ -961,7 +964,10 @@ network_policies: {} "Expected allow via ancestor match, got deny: {}", decision.reason ); - assert_eq!(decision.matched_policy.as_deref(), Some("github")); + assert_eq!( + decision.matched_policy.as_deref(), + Some("github_ssh_over_https") + ); } #[test] @@ -1778,7 +1784,7 @@ process: assert_eq!( action, NetworkAction::Allow { - matched_policy: Some("github".to_string()) + matched_policy: Some("github_ssh_over_https".to_string()) }, ); }