This document describes the security architecture and safety measures in Nebo.
Nebo runs an AI agent with access to your computer's file system, shell, and network. Security is enforced at multiple independent layers so that no single bypass can compromise your system. The design principle is defense in depth — every layer assumes the one above it might fail.
+------------------------------------------------------+
| Hard Safeguard (unconditional, no bypass possible) | <- System paths, sudo, disk formatting
+------------------------------------------------------+
| Origin-Based Restrictions (per request source) | <- Infrastructure ready, deny list pending
+------------------------------------------------------+
| Tool Policy & Approval (user-configurable) | <- Allowlist, approval prompts
+------------------------------------------------------+
| Capability Permissions (per-category toggles) | <- File, shell, web, etc.
+------------------------------------------------------+
| App Sandbox (for third-party apps) | <- Env isolation, signing, permissions
+------------------------------------------------------+
| Compiled-Only Binary Policy (anti-self-modification) | <- No interpreted languages, opaque binaries
+------------------------------------------------------+
| Network Security (auth, CSRF, headers, rate limits) | <- JWT, CORS, CSP, rate limiting
+------------------------------------------------------+
File: crates/tools/src/safeguard.rs
The safeguard is an unconditional safety rail that runs inside Registry::execute() before every tool call. It cannot be bypassed by autonomous mode, policy level, user approval, or any setting.
- sudo — Blocked in all forms: direct, piped, chained, subshell
- su — Blocked entirely
- mkfs (all variants), fdisk / gdisk / cfdisk / sfdisk / sgdisk, parted, wipefs
- diskutil eraseDisk / eraseVolume / partitionDisk (macOS)
- dd to block devices (
of=/dev/...)
- rm -rf / (all variants including
--no-preserve-root) - Writing to /dev/ — Blocked except
/dev/null,/dev/stdout,/dev/stderr - Fork bombs — Detected and blocked
All read operations are allowed. Only write/edit operations are blocked.
| macOS | Linux | Windows |
|---|---|---|
/System/ |
/bin/, /sbin/ |
C:\Windows\ |
/bin/, /sbin/ |
/usr/bin/, /usr/sbin/, /usr/lib/ |
C:\Program Files\ |
/usr/bin/, /usr/sbin/, /usr/lib/ |
/boot/ |
C:\Program Files (x86)\ |
/etc/ |
/etc/ |
C:\ProgramData\ |
/Library/LaunchDaemons/ |
/proc/, /sys/, /dev/ |
C:\Recovery\ |
/Library/LaunchAgents/ |
/root/ |
|
/private/var/db/ |
/var/lib/dpkg/, /var/lib/rpm/ |
Nebo cannot modify or delete its own database, WAL, or data files.
| Platform | Protected Path |
|---|---|
| macOS | ~/Library/Application Support/Nebo/data/ |
| Linux | ~/.config/nebo/data/ |
| Windows | %APPDATA%\Nebo\data\ |
| Override | $NEBO_DATA_DIR/data/ |
~/.ssh/— SSH keys and configuration~/.gnupg/— GPG keys and configuration~/.aws/credentials— AWS credentials~/.kube/config— Kubernetes credentials~/.docker/config.json— Docker registry credentials
The safeguard error messages tell the user to perform the operation manually in a terminal. Nebo is not designed for system administration — it helps with development and productivity tasks within user-space directories.
File: crates/tools/src/policy.rs
Every request is tagged with an origin that tracks where it came from.
| Origin | Source | Intended Restrictions |
|---|---|---|
user |
Direct interaction (web UI, CLI) | None (governed by policy) |
system |
Internal tasks (heartbeat, cron, recovery) | None |
comm |
Inter-agent communication | Shell denied |
app |
External app binary | Shell denied |
skill |
Matched skill template | Shell denied |
File: crates/tools/src/policy.rs
- Allowlist (default) — Only whitelisted commands auto-approve; everything else prompts
- Deny — All operations require approval
- Full — All operations auto-approve (dangerous, opt-in only)
Read-only and inspection commands: ls, pwd, cat, head, tail, grep, find, which, jq, cut, sort, uniq, wc, echo, date, env, git status, git log, git diff, git branch, git show, go version, node --version, python --version
The is_dangerous() function flags: rm -rf, rm -r, sudo, su, chmod 777, chown, dd, mkfs, curl | sh, wget | sh, eval, exec, fork bombs.
When enabled, bypasses all approval prompts. When disabled, all permissions reset to defaults (only Chat & Memory enabled).
Users can enable/disable entire capability categories:
| Category | What It Controls |
|---|---|
| Chat & Memory | Conversations, memory, scheduled tasks (always on) |
| File System | Read, write, edit, search files |
| Shell & Terminal | Execute commands, manage processes |
| Web Browsing | Fetch pages, search, browser automation |
| Contacts & Calendar | Contacts, calendar, reminders, mail |
| Desktop Control | Window management, accessibility, clipboard |
| Media & Capture | Screenshots, image analysis, music, TTS |
| System | Spotlight, keychain, Siri shortcuts, notifications |
Disabled categories prevent the tool from being registered with the agent entirely — the LLM never sees it as an available tool. Enforced in Registry::register_all_with_permissions() which filters tool registration by category map.
Third-party apps (.napp packages) run in a sandboxed environment.
- Allowlist-only environment: Only
PATH,HOME,TMPDIR,LANG,LC_ALL,TZ, andNEBO_APP_*variables are passed to apps - All parent environment variables (API keys, secrets, tokens) are stripped
- Rejects symlinks (path traversal prevention)
- Rejects non-regular files (devices, pipes)
- Checks executable bit
- Enforces size limit (500MB default)
- ED25519 signature verification over raw bytes
- Manifest signature: verifies manifest.json integrity
- Binary signature: SHA256 integrity check + ED25519 verification
- Key ID rotation detection (
SHA256(publicKey)[:8])
Apps must declare every permission they need. Unknown permissions are rejected. Categories include:
network:*(DNS, HTTP, WebSocket)filesystem:read,filesystem:writeshell:execute,shell:backgroundmemory:read,memory:writesession:*,tool:*,model:*,channel:*,mcp:*
Nebo's app platform exclusively runs compiled native binaries. Apps written in interpreted or scripted languages are rejected.
Nebo is an AI agent platform. The agent has file system access. If an app's source code is accessible at runtime, the agent can modify it. An interpreted app exposes its entire logic as readable, modifiable plaintext.
| Language | Status |
|---|---|
| Go | Recommended |
| Rust | Supported |
| C / C++ | Supported |
| Zig | Supported |
| Language | Reason |
|---|---|
| Node.js / JavaScript | Source code is plaintext .js files |
| Python | Source code is plaintext .py files |
| Ruby, PHP, Perl | Source code is plaintext |
| Shell scripts | Direct command injection vector |
| Java / Kotlin (JVM) | Decompilable bytecode, requires runtime |
| .NET / C# (Mono) | Decompilable IL, requires runtime |
NeboAI (distribution-time): Magic byte verification, hidden interpreter detection, dynamic link analysis, Go build info verification, dropper pattern detection, ED25519 signing.
Nebo (install/launch-time): Magic byte check, shebang rejection, ED25519 signature verify, file permission check.
- JWT (HMAC-SHA256) for all API requests (
crates/auth/src/jwt.rs) - WebSocket authentication required — unauthenticated connections rejected
- Password hashing: bcrypt with cost factor 12
All routes:
- Permissions-Policy:
accelerometer=(), camera=(self), geolocation=(), gyroscope=(), magnetometer=(), microphone=(self), payment=(), usb=() - Strict-Transport-Security:
max-age=31536000; includeSubDomains; preload - X-Frame-Options:
DENY - X-Content-Type-Options:
nosniff - X-XSS-Protection:
1; mode=block - Referrer-Policy:
strict-origin-when-cross-origin
API routes (/api/v1/*) only:
- Content-Security-Policy:
default-src 'none'; frame-ancestors 'none'— blocks all content loading in API responses - Cache-Control:
no-store, no-cache, must-revalidate, private - Pragma:
no-cache
SPA routes intentionally omit CSP to allow embedded content (YouTube, Vimeo, Twitter, external fonts). The SPA is static files served from localhost — the attack surface is the API, not the frontend shell.
- Localhost-only whitelist (ports 27895, 5173, 4173)
- Not
*— explicit origin validation
- Token bucket algorithm, per-client IP
- Auth endpoints: 10 requests/minute
- Uses
RemoteAddronly — does not trustX-Forwarded-For
- All credentials encrypted with AES-256-GCM (
crates/auth/src/credential.rs) - MCP tokens: AES-256-GCM with random nonces (
crates/mcp/src/crypto.rs) - Master encryption key stored in OS keychain via
keyringcrate (crates/auth/src/keyring.rs) - Fallback: key file with
0600permissions if keychain unavailable
Only one Nebo instance per computer. Filesystem locks with PID tracking.
- Maximum message size: 32KB
- Ping/pong keepalive
- Buffered broadcast channel (256 messages)
- Graceful close on connection drop
Agent runs use tokio_util::sync::CancellationToken for graceful cancellation — never JoinHandle::abort().
If you discover a security vulnerability, please report it responsibly:
- Do not open a public GitHub issue
- Email security concerns to the maintainers directly
- Include steps to reproduce the vulnerability
- Allow reasonable time for a fix before disclosure
- Defense in depth — Multiple independent layers, each assuming others might fail
- Fail closed — When in doubt, block the operation
- Least privilege — Deny by default, require explicit opt-in
- No silent failures — Blocked operations explain why and what the user can do instead
- Unconditional safety — Critical protections cannot be overridden by any setting, mode, or configuration