Create, edit, validate, and test nono.sh sandbox profiles (JSON/JSONC) that scope a command's filesystem, network, and credential access. Use when the user wants to sandbox a tool/command with nono, write or update a nono profile, check a profile is valid, or test what a profile actually allows/denies.
Note the directory of this SKILL.md when loaded — that is $SKILL_ROOT. Substitute the real
absolute path when running the commands below; it is not a shell variable that already exists.
Authors nono.sh sandbox profiles: scaffold one for a given command, fill in the exact filesystem/
network/credential access it needs, explain the result in plain language, then validate and test it.
The profile format is not documented in this skill.nono profile guide and nono profile schema ship with the binary and are always version-matched, so read the format from there via
scripts/profile-docs.sh rather than from any summary. A summary would drift; the binary cannot.
Workflow
Step 1: Confirm nono is available — run $SKILL_ROOT/scripts/check-nono.sh.
If it reports nono is not installed, tell the user exactly that — "nono is not installed" —
and stop. Don't add install instructions or any other detail about the absence.
Step 2: Work out what the target command actually needs: which paths it reads/writes, which
network domains it calls, which credentials/env vars it needs, and whether an existing security
group already covers it ($SKILL_ROOT/scripts/profile-docs.sh --groups). Inspect the project
(dependencies, config, source) for evidence rather than guessing, and ask the user
(AskUserQuestion) to confirm anything that isn't derivable from the code.
Step 3: Scaffold with nono profile init <name> [--extends <base>] [--groups <g1>,<g2>] --output <path>,
then hand-edit the JSON/JSONC to add the specific filesystem/network/credential entries from
Step 2. Look up every field you touch before writing it:
$SKILL_ROOT/scripts/profile-docs.sh --sections — what the guide covers
$SKILL_ROOT/scripts/profile-docs.sh --section filesystem — that section in full
$SKILL_ROOT/scripts/profile-docs.sh --schema FilesystemConfig — the exact schema entry
$SKILL_ROOT/scripts/profile-docs.sh --search '<regex>' — when you don't know the section name
Step 4: Explain the profile in plain language, using the Output format below. This step is
required for every profile created or edited — the point is letting the user check the claims
against the JSON themselves, not just trusting the tool.
Step 5: Validate — run $SKILL_ROOT/scripts/validate-profile.sh <path> [name]. Fix anything
it flags and re-run until it's clean.
Step 6: Test — run $SKILL_ROOT/scripts/test-profile.sh --profile <name> --command "<cmd>", plus:
--path <p> --op read|write|readwrite — authoritative verdict on filesystem access
--host <h> [--port <n>] — authoritative verdict on network access
--scope signal|abstract-unix-socket — Landlock scope only, not general policy inspection
--live — actually execute the command under the profile
--deny-command "<cmd>" — confirm something that should be blocked really is
Done when the intended command runs clean and every expected denial reports PASS.
INCONCLUSIVE (exit 2) means the command failed without proving a denial — treat it as untested
and fix the cause, never as a pass.
Output format (Step 4)
## Profile: <name> (<path>)
Extends: <base profile chain, or "none">
Groups: <included groups> | excludes: <excluded groups, if any>
Filesystem
- Read+write: <paths>
- Read-only: <paths>
- Write-only: <paths>
- Denied: <paths, including anything a group denies that this profile doesn't override>
Workdir: <access level>
Network
- Blocked entirely: <yes/no>
- Allowed domains: <list, or "none">
- Ports — outbound: <list>, inbound: <list>
- Credentials proxied: <named services, or "none">
Other
- Security/process isolation: <signal_mode / process_info_mode / anything non-default>
- Anything else non-default worth flagging
Base every line on the actual resolved JSON (nono profile show), not just the file you wrote —
inheritance can add or override things the file itself doesn't show.
Gotchas
Verified against the shipped guide; everything else, look up in Step 3 rather than assuming.
Linux rejects deny inside a grant. Landlock is strictly allow-list and has no kernel deny
primitive, so a deny path overlapping an allow/read/write path makes nono refuse to
start — a hard error, not an override. macOS Seatbelt enforces the same profile fine. Write the
grants narrowly enough that they never match what you want denied, instead of granting broadly
and subtracting.
bypass_protection does not grant access. It only lifts a group's deny rule; the path still
needs its own entry under allow/read/write (or a *_file variant) to be reachable.
groups.include: [] does not mean "no deny rules" — groups marked required (deny_credentials,
deny_shell_history, …) resolve in regardless. groups.exclude is what removes them, and it
weakens the sandbox.
workdir.access defaults to none, so a profile grants no working-directory access unless you
set it — and a child setting "none"inherits the base instead of overriding it.
open_urls replaces the parent's value on inheritance instead of merging, unlike every other
array/map field. To clear it, pass an explicit empty object, not null (null inherits).
There is no mechanism to remove an inherited filesystem path. Only network_profile supports
null-clearing.
network.block: true cannot be combined with proxy settings.
The pre-#594 policy.* keys still load but are deprecated and removed in v1.0.0. Write canonical
keys (filesystem.*, groups.include, commands.allow); nono profile validate --strict fails
on the legacy ones.
nono profile show and nono run --profile both accept a name or a file path, but a name only
resolves once the file is discoverable (typically ~/.config/nono/profiles/<name>.json). Pass the
path while drafting.
Reference
scripts/profile-docs.sh — all of nono's own profile documentation: --sections, --section <query>, --search <regex>, --guide, --schema [name], --groups [name].
1---2name: nono-profile3description: Create, edit, validate, and test nono.sh sandbox profiles (JSON/JSONC) that scope a command's filesystem, network, and credential access. Use when the user wants to sandbox a tool/command with nono, write or update a nono profile, check a profile is valid, or test what a profile actually allows/denies.4---56# nono Profile Authoring78Note the directory of this SKILL.md when loaded — that is `$SKILL_ROOT`. Substitute the real9absolute path when running the commands below; it is not a shell variable that already exists.1011Authors nono.sh sandbox profiles: scaffold one for a given command, fill in the exact filesystem/12network/credential access it needs, explain the result in plain language, then validate and test it.1314**The profile format is not documented in this skill.** `nono profile guide` and `nono profile15schema` ship with the binary and are always version-matched, so read the format from there via16`scripts/profile-docs.sh` rather than from any summary. A summary would drift; the binary cannot.1718## Workflow1920- [ ] Step 1: Confirm nono is available — run `$SKILL_ROOT/scripts/check-nono.sh`.21 - If it reports `nono is not installed`, tell the user exactly that — "nono is not installed" —22 and stop. Don't add install instructions or any other detail about the absence.23- [ ] Step 2: Work out what the target command actually needs: which paths it reads/writes, which24 network domains it calls, which credentials/env vars it needs, and whether an existing security25 group already covers it (`$SKILL_ROOT/scripts/profile-docs.sh --groups`). Inspect the project26 (dependencies, config, source) for evidence rather than guessing, and ask the user27 (`AskUserQuestion`) to confirm anything that isn't derivable from the code.28- [ ] Step 3: Scaffold with `nono profile init <name> [--extends <base>] [--groups <g1>,<g2>] --output <path>`,29 then hand-edit the JSON/JSONC to add the specific filesystem/network/credential entries from30 Step 2. Look up every field you touch before writing it:31 - `$SKILL_ROOT/scripts/profile-docs.sh --sections` — what the guide covers32 - `$SKILL_ROOT/scripts/profile-docs.sh --section filesystem` — that section in full33 - `$SKILL_ROOT/scripts/profile-docs.sh --schema FilesystemConfig` — the exact schema entry34 - `$SKILL_ROOT/scripts/profile-docs.sh --search '<regex>'` — when you don't know the section name35- [ ] Step 4: Explain the profile in plain language, using the Output format below. This step is36 required for every profile created or edited — the point is letting the user check the claims37 against the JSON themselves, not just trusting the tool.38- [ ] Step 5: Validate — run `$SKILL_ROOT/scripts/validate-profile.sh <path> [name]`. Fix anything39 it flags and re-run until it's clean.40- [ ] Step 6: Test — run `$SKILL_ROOT/scripts/test-profile.sh --profile <name> --command "<cmd>"`, plus:41 - `--path <p> --op read|write|readwrite` — authoritative verdict on filesystem access42 - `--host <h> [--port <n>]` — authoritative verdict on network access43 - `--scope signal|abstract-unix-socket` — Landlock scope only, *not* general policy inspection44 - `--live` — actually execute the command under the profile45 - `--deny-command "<cmd>"` — confirm something that should be blocked really is4647 Done when the intended command runs clean and every expected denial reports **PASS**.48 `INCONCLUSIVE` (exit 2) means the command failed without proving a denial — treat it as untested49 and fix the cause, never as a pass.5051## Output format (Step 4)5253```54## Profile: <name> (<path>)5556Extends: <base profile chain, or "none">57Groups: <included groups> | excludes: <excluded groups, if any>5859Filesystem60- Read+write: <paths>61- Read-only: <paths>62- Write-only: <paths>63- Denied: <paths, including anything a group denies that this profile doesn't override>64Workdir: <access level>6566Network67- Blocked entirely: <yes/no>68- Allowed domains: <list, or "none">69- Ports — outbound: <list>, inbound: <list>70- Credentials proxied: <named services, or "none">7172Other73- Security/process isolation: <signal_mode / process_info_mode / anything non-default>74- Anything else non-default worth flagging75```7677Base every line on the actual resolved JSON (`nono profile show`), not just the file you wrote —78inheritance can add or override things the file itself doesn't show.7980## Gotchas8182Verified against the shipped guide; everything else, look up in Step 3 rather than assuming.8384- **Linux rejects `deny` inside a grant.** Landlock is strictly allow-list and has no kernel deny85 primitive, so a `deny` path overlapping an `allow`/`read`/`write` path makes nono refuse to86 start — a hard error, not an override. macOS Seatbelt enforces the same profile fine. Write the87 grants narrowly enough that they never match what you want denied, instead of granting broadly88 and subtracting.89- `bypass_protection` does **not** grant access. It only lifts a group's deny rule; the path still90 needs its own entry under `allow`/`read`/`write` (or a `*_file` variant) to be reachable.91- `groups.include: []` does not mean "no deny rules" — groups marked `required` (`deny_credentials`,92 `deny_shell_history`, …) resolve in regardless. `groups.exclude` is what removes them, and it93 weakens the sandbox.94- `workdir.access` defaults to `none`, so a profile grants no working-directory access unless you95 set it — and a child setting `"none"` *inherits the base* instead of overriding it.96- `open_urls` replaces the parent's value on inheritance instead of merging, unlike every other97 array/map field. To clear it, pass an explicit empty object, not `null` (`null` inherits).98- There is no mechanism to remove an inherited filesystem path. Only `network_profile` supports99 null-clearing.100- `network.block: true` cannot be combined with proxy settings.101- The pre-#594 `policy.*` keys still load but are deprecated and removed in v1.0.0. Write canonical102 keys (`filesystem.*`, `groups.include`, `commands.allow`); `nono profile validate --strict` fails103 on the legacy ones.104- `nono profile show` and `nono run --profile` both accept a name or a file path, but a name only105 resolves once the file is discoverable (typically `~/.config/nono/profiles/<name>.json`). Pass the106 path while drafting.107108## Reference109110`scripts/profile-docs.sh` — all of nono's own profile documentation: `--sections`, `--section111<query>`, `--search <regex>`, `--guide`, `--schema [name]`, `--groups [name]`.
Run npx skillmds@latest add robin0xfull/nono-profile in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Create, edit, validate, and test nono.sh sandbox profiles (JSON/JSONC) that scope a command's filesystem, network, and credential access. Use when the user wants to sandbox a tool/command with nono, write or update a nono profile, check a profile is valid, or test what a profile actually allows/denies. It is listed under Coding & Dev Tools on SkillMD.
This skill has not completed SkillMD's automated safety review yet. Independent scanners report: SkillSpector: PASS, Skill Scanner: PASS. Capability flags: executes scripts. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
robin0xfull (@robin0xfull) published this skill. Their other Agent Skills are listed on their SkillMD profile.