13 KiB
Nono profiles
This repository stores local nono profiles under profiles/nono/. The active nono directory contains one symlink per profile:
~/.config/nono/profiles/pi-local.json
-> /srv/data/code/harness-tools/profiles/nono/pi-local.json
Keeping the JSON in Git makes profile changes reviewable. The symlink lets nono load the repository copy by profile name.
Run Pi with nono
Launch Pi from the repository you want to edit:
cd /path/to/project
nono run --profile pi-local -- pi
pi-local gives the launch working directory read/write access. A session that needs several repositories also needs explicit paths in the profile.
Check which profiles nono can load:
nono profile list
nono profile validate pi-local
Inspect a denied operation from inside a sandbox:
nono why --self --path /resolved/path --op read
nono why --self --path /resolved/path --op write
nono why --self --path /resolved/path --op readwrite
A profile change affects the next sandbox launch. It cannot expand the permissions of the current process.
Directory-based Pi sandbox routing
pi-sbx launches Pi with a nono profile and runtime selected from the current directory. It works from Orca or any terminal. pi-sbx-profile manages the trusted user configuration at ~/.config/pi-sbx/config.json. Set PI_SBX_CONFIG to an absolute path to use a different configuration file. Both commands resolve nono from PATH.
One-time setup links the commands and systemd slice, then copies the example configuration:
mkdir -p ~/.local/bin ~/.config/pi-sbx ~/.config/systemd/user
ln -s /absolute/path/to/harness-tools/scripts/pi-sbx.fish ~/.local/bin/pi-sbx
ln -s /absolute/path/to/harness-tools/scripts/pi-sbx-profile.py ~/.local/bin/pi-sbx-profile
ln -s /absolute/path/to/harness-tools/systemd/user/pi-agents.slice ~/.config/systemd/user/pi-agents.slice
cp /absolute/path/to/harness-tools/config/pi-sbx.example.json ~/.config/pi-sbx/config.json
chmod 600 ~/.config/pi-sbx/config.json
systemctl --user daemon-reload
Directory keys must be canonical absolute physical paths. Directory mappings apply to the configured directory and all descendants. The longest matching directory wins; otherwise defaultProfile is used. Resolution uses the physical launch path and does not inspect Git metadata. A linked worktree therefore inherits the route for the directory where the worktree is located.
pi-sbx-profile show # explain the route for CWD
pi-sbx-profile set pi-work # assign CWD and descendants
pi-sbx-profile set pi-work /srv/work # assign an explicit directory
pi-sbx-profile unset # remove the CWD assignment
pi-sbx-profile resolve /srv/work/example # print the selected profile
pi-sbx-profile list # print the configuration
pi-sbx # launch Pi using the selected route
The configuration keeps the sandbox, Node runtime, Pi executable, and Pi agent directory together. Start from config/pi-sbx.example.json. defaultRuntime applies to every profile; an entry in profileRuntimes overrides only the fields that differ for that profile.
Separate Pi agent directories are required when runtimes use different Node ABIs. Native dependencies such as better-sqlite3 must not be shared between the Node 22 and Node 26 agent directories.
The launcher exports PI_SBX_PROFILE and, for Orca compatibility, ORCA_NONO_PROFILE. Existing pi-orca and orca-nono-profile commands remain compatibility entry points. Restart an existing terminal after changing its route, runtime, or agent directory because a running Pi process cannot switch them.
Edit profiles with the skill
The repository skill lives at skills/tools/nono-profiles/. Pi invokes it when you ask to create or edit a nono profile, change sandbox directories, select an Internet tier, add domains, configure sockets, or troubleshoot profile access.
Example requests:
- “Update my nono profile so this session can edit three client repositories.”
- “Create a project profile with npm, GitHub, and the staging API allowlisted.”
- “Make an offline profile for this repository.”
- “Give this profile read-only access to a shared documentation checkout.”
The skill asks whether to change pi-local or create a derived profile. It then collects exact paths, access levels, an Internet tier, and any required domains. It shows the resolved policy and a diff before writing the repository profile.
The skill stages candidates under ~/.config/nono/profile-drafts/ and validates them in strict mode. It does not edit ~/.config/nono/profiles/ from inside a sandbox. When a new profile needs activation, run the supplied symlink command outside that sandbox.
Current pi-local baseline
profiles/nono/pi-local.json extends the registry-managed nolabs-ai/pi profile. It adds:
- read/write access to the launch working directory and temporary directory
- read-only access to local Pi extension source and
$HOME/.bun; the Orca runner invokes the selected Node executable explicitly rather than relying onPATH - pathname-mediated access to the system resolver Unix socket and the exact
/run/user/1000/pi-dictate.sockmediated microphone socket on Linux - an explicit deny for
~/.local/share/pi-dictate, which overrides$WORKDIRif Pi is launched from the service data directory and protects the model, dictionary, caches, and runtime - an Internet hostname allowlist for model providers, source hosts, package registries, and toolchain downloads
The parent profile contributes more policy, including Pi state access, security groups, credential route definitions, URL-opening origins, localhost URL opening, and service launching. Inspect the resolved profile before treating a child as narrower than its parent:
nono profile show pi-local
Profile settings
extends
extends names one parent profile:
{
"extends": "nolabs-ai/pi"
}
Inheritance is transitive. List-valued capabilities and included groups are generally additive. An empty child list does not reliably remove a parent capability, so choose a narrower parent when the child needs fewer permissions.
Use pi-local as the parent when its existing filesystem and domain permissions are acceptable. Use nolabs-ai/pi when building an offline profile, a smaller domain allowlist, or an open-network profile without inheriting pi-local's hostname list.
meta
Give each profile a stable command-line name and a short description:
{
"meta": {
"name": "pi-client-a",
"description": "Pi access for Client A repositories and development services"
}
}
Keep the filename aligned with the name: profiles/nono/pi-client-a.json.
groups.include
groups.include adds reusable policy groups defined by built-in or package profiles. Group entries may include platform conditions. Treat groups as capabilities inherited from the parent; an empty list does not remove parent groups.
workdir.access
The working directory is the directory where you launch nono, not the directory containing the profile.
{
"workdir": {
"access": "readwrite"
}
}
Launch from the project root when one repository needs write access. Add explicit filesystem entries for additional repositories.
Filesystem directories
Directory fields apply recursively:
| Field | Access |
|---|---|
filesystem.read |
Read-only |
filesystem.write |
Write-only |
filesystem.allow |
Read/write |
A multi-repository profile can enumerate repositories:
{
"filesystem": {
"allow": [
"/srv/data/code/client-a/frontend",
"/srv/data/code/client-a/backend"
],
"read": [
"/srv/data/code/shared-docs"
]
}
}
Enumerating repositories limits the profile's reach. Granting /srv/data/code/client-a gives the selected access to every current and future child directory, which may suit a dedicated client workspace but carries a larger blast radius.
Resolve symlinks before adding paths. nono may enforce access against the canonical target.
Exact files
Use exact-file fields when the session needs one file rather than its directory:
| Field | Access |
|---|---|
filesystem.read_file |
Read-only |
filesystem.write_file |
Write-only |
filesystem.allow_file |
Read/write |
Temporary files
$TMPDIR under filesystem.allow permits temporary-file creation. Keep it when tools need scratch files. Remove it only after checking the tools used by that profile.
Unix sockets
filesystem.unix_socket lists local socket paths. Socket access is separate from Internet access:
{
"filesystem": {
"unix_socket": [
"/run/systemd/resolve/io.systemd.Resolve"
]
}
}
Add exact sockets for required local services. Avoid granting a socket directory.
linux.af_unix_mediation: "pathname" enables pathname-based Unix-socket mediation on Linux.
pi-local also grants /run/user/1000/pi-dictate.sock. This socket is a mediated microphone and transcription capability, not a health-only endpoint. Any code running under pi-local or an inheriting profile can request recording and receive transcripts, while direct PipeWire, audio-device, GPU, model, and dictionary access remains outside Pi.
Network access
network.block and network.allow_domain define three useful tiers.
Offline
Derive from nolabs-ai/pi, block networking, and clear inherited host-opening capabilities:
{
"extends": "nolabs-ai/pi",
"network": {
"block": true
},
"open_urls": {
"allow_origins": [],
"allow_localhost": false
},
"allow_launch_services": false
}
Do not derive this tier from allowlisted pi-local. Its inherited proxy domains conflict with blocked networking.
Allowlisted
Enable networking and list exact hostnames:
{
"network": {
"block": false,
"allow_domain": [
"git.example.com",
"registry.npmjs.org",
"api.staging.example.com"
]
}
}
Entries are hostnames, not full URLs or path patterns. Include artifact hosts and CDNs used by package managers, not only their metadata endpoint. Start with the operations the project performs and add the hosts those operations require.
A child of pi-local inherits its existing domains. Derive from nolabs-ai/pi when the project needs a smaller or different list.
Open
Enable networking without an inherited domain allowlist:
{
"extends": "nolabs-ai/pi",
"network": {
"block": false
}
}
Open mode permits destinations beyond the hosts currently known to the project. Review the parent's URL-opening and credential settings separately.
URL opening
open_urls.allow_origins lists origins that sandboxed tools may ask the host to open. open_urls.allow_localhost controls localhost origins. These settings do not replace network.allow_domain.
The nolabs-ai/pi parent grants selected authentication and source-host origins plus localhost. Keep, narrow, or clear them according to the project.
Service launching
allow_launch_services controls service launching on supported platforms. Set it to false for offline profiles. Review its inherited value for the other network tiers.
Credential routes
Credential routes let nono mediate secrets and restrict upstream endpoints. Network access to a host and access to a credential route are separate permissions. Prefer mediated routes over storing API keys where the sandboxed process can read them.
Check the installed Pi package guide before enabling a route:
~/.config/nono/packages/nolabs-ai/pi/README.md
Safe change workflow
-
Create
~/.config/nono/profile-drafts/<name>.json. -
Validate and inspect the resolved policy:
nono profile validate --draft <name> --strict nono profile show ~/.config/nono/profile-drafts/<name>.json -
Review the parent, filesystem capabilities, Internet tier, domains, URL-opening origins, service launching, sockets, and credential routes.
-
Copy the approved draft to
profiles/nono/<name>.json. -
Validate the repository copy:
nono profile validate --strict profiles/nono/<name>.json -
For a new repository-managed profile, create its active symlink outside the sandbox:
ln -sfn /absolute/repo/profiles/nono/<name>.json ~/.config/nono/profiles/<name>.json nono profile validate <name> -
Start a new nono session with the profile.
Use nono profile promote <name> only when you want a regular active profile instead of a repository symlink. Replacing an existing regular profile requires nono's draft base-hash guard; use nono's edit workflow rather than bypassing that check.