Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/reference/network-policies.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,24 @@ $ ./scripts/walkthrough.sh

This opens a split tmux session with the TUI on the left and the agent on the right.

## Policy Presets

Presets extend the baseline network policy with additional endpoints for common services.
Apply a preset with `nemoclaw <name> policy-add` and list available presets with `nemoclaw <name> policy-list`.

| Preset | Endpoints | Use case |
|---|---|---|
| `discord` | `discord.com`, `discordapp.com` | Discord bot and webhook access |
| `docker` | `registry-1.docker.io`, `auth.docker.io`, `index.docker.io` | Docker Hub image pulls |
| `github` | `api.github.com`, `github.com`, `raw.githubusercontent.com`, `uploads.github.com` | GitHub REST API, raw file content, and asset uploads |
| `huggingface` | `huggingface.co`, `cdn-lfs.huggingface.co`, `api-inference.huggingface.co` | Hugging Face Hub and Inference API |
| `jira` | `*.atlassian.net` | Jira issue tracking |
| `npm` | `registry.npmjs.org` | npm package registry |
| `outlook` | `graph.microsoft.com`, `login.microsoftonline.com`, `outlook.office365.com`, `outlook.office.com` | Microsoft Graph and Outlook |
| `pypi` | `pypi.org`, `files.pythonhosted.org` | Python package index |
| `slack` | `slack.com`, `api.slack.com`, `hooks.slack.com` | Slack API and webhooks |
| `telegram` | `api.telegram.org` | Telegram Bot API |

## Modifying the Policy

### Static Changes
Expand Down
44 changes: 44 additions & 0 deletions nemoclaw-blueprint/policies/presets/github.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

preset:
name: github
description: "GitHub REST API, raw content, and asset upload access"

network_policies:
github:
name: github
endpoints:
- host: api.github.com
port: 443
protocol: rest
enforcement: enforce
tls: terminate
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: POST, path: "/**" }
- allow: { method: PATCH, path: "/**" }
- allow: { method: PUT, path: "/**" }
- allow: { method: DELETE, path: "/**" }
- host: github.com
port: 443
protocol: rest
enforcement: enforce
tls: terminate
rules:
- allow: { method: GET, path: "/**" }
- allow: { method: POST, path: "/**" }
Comment thread
Ryuketsukami marked this conversation as resolved.
- host: raw.githubusercontent.com
port: 443
protocol: rest
enforcement: enforce
tls: terminate
rules:
- allow: { method: GET, path: "/**" }
- host: uploads.github.com
port: 443
protocol: rest
enforcement: enforce
tls: terminate
rules:
- allow: { method: POST, path: "/**" }
17 changes: 13 additions & 4 deletions test/policies.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import policies from "../bin/lib/policies";

describe("policies", () => {
describe("listPresets", () => {
it("returns all 9 presets", () => {
it("returns all 10 presets", () => {
const presets = policies.listPresets();
expect(presets.length).toBe(9);
assert.equal(presets.length, 10);
Comment thread
Ryuketsukami marked this conversation as resolved.
});

it("each preset has name and description", () => {
Expand All @@ -21,8 +21,8 @@ describe("policies", () => {

it("returns expected preset names", () => {
const names = policies.listPresets().map((p) => p.name).sort();
const expected = ["discord", "docker", "huggingface", "jira", "npm", "outlook", "pypi", "slack", "telegram"];
expect(names).toEqual(expected);
const expected = ["discord", "docker", "github", "huggingface", "jira", "npm", "outlook", "pypi", "slack", "telegram"];
assert.deepEqual(names, expected);
Comment thread
Ryuketsukami marked this conversation as resolved.
});
});

Expand Down Expand Up @@ -59,6 +59,15 @@ describe("policies", () => {
expect(hosts).toEqual(["api.telegram.org"]);
});

it("extracts hosts from github preset", () => {
const content = policies.loadPreset("github");
const hosts = policies.getPresetEndpoints(content);
assert.ok(hosts.includes("api.github.com"));
assert.ok(hosts.includes("github.com"));
assert.ok(hosts.includes("raw.githubusercontent.com"));
assert.ok(hosts.includes("uploads.github.com"));
});
Comment thread
Ryuketsukami marked this conversation as resolved.

it("every preset has at least one endpoint", () => {
for (const p of policies.listPresets()) {
const content = policies.loadPreset(p.name);
Expand Down