Documentation Index
Fetch the complete documentation index at: https://noorle.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
Noorle plugins run in a secure sandbox where everything is denied by default. This guide explains the security model and how to configure permissions.
Security Sandbox Model
Your plugin runs in an isolated execution environment with strict boundaries:
Default Deny
Everything is denied unless explicitly allowed:
- ❌ Cannot access any external networks
- ❌ Cannot read or write any files
- ❌ Cannot access environment variables
- ❌ Cannot execute system commands
- ❌ Cannot access other plugins’ data
Explicit Allow
You grant permissions in noorle.yaml:
permissions:
network:
allow:
- host: "api.example.com"
filesystem:
allow:
- uri: "fs://work/agent/**"
access: [read, write]
environment:
allow:
- key: "API_KEY"
Network Permissions
Control which external hosts your plugin can connect to.
Host Allowlist
permissions:
network:
allow:
- host: "api.example.com"
- host: "*.github.com" # Wildcard support
- host: "data.company.internal"
Supported patterns:
example.com - Exact domain
*.example.com - All subdomains
*.*.example.com - Multiple level wildcards
CIDR Ranges
permissions:
network:
allow:
- cidr: "10.0.0.0/8" # Private network
- cidr: "192.168.0.0/16" # Local network
Deny List
permissions:
network:
deny:
- host: "blocked.example.com"
- host: "*.internal"
Note: Deny rules only apply if allow lists are not specified (allow-first model).
Use Cases
# Integrate with external SaaS
permissions:
network:
allow:
- host: "api.openai.com"
- host: "api.anthropic.com"
- host: "api.stripe.com"
# Connect to internal APIs
permissions:
network:
allow:
- cidr: "10.0.0.0/8"
# Allow multiple services
permissions:
network:
allow:
- host: "*.github.com"
- host: "*.amazonaws.com"
- cidr: "203.0.113.0/24"
Filesystem Permissions
Control which files and directories your plugin can access.
URI Patterns
permissions:
filesystem:
allow:
# Recursive: all nested paths
- uri: "fs://work/agent/**"
access: [read, write]
# Single-level wildcard
- uri: "fs://cache/data/*"
access: [read]
# Specific file
- uri: "fs://config/settings.json"
access: [read]
Access Types
read - Read-only access to file content
write - Write access (implies read)
permissions:
filesystem:
allow:
# Read-only data files
- uri: "fs://data/input/**"
access: [read]
# Read-write cache
- uri: "fs://work/cache/**"
access: [read, write]
# Write-only logs (unusual but valid)
- uri: "fs://logs/output.txt"
access: [write]
Available Directories
Standard filesystem paths:
| Path | Purpose | Default Access |
|---|
fs://work/agent/** | Agent workspace (scratch files) | read, write |
fs://cache/** | Plugin cache directory | read, write |
fs://config/** | Configuration files | read |
fs://input/** | Input data files | read |
fs://output/** | Output files | write |
Use Cases
# Cache processing results
permissions:
filesystem:
allow:
- uri: "fs://cache/models/**"
access: [read, write]
# Read configuration
permissions:
filesystem:
allow:
- uri: "fs://config/app.yaml"
access: [read]
# Process uploaded files
permissions:
filesystem:
allow:
- uri: "fs://input/uploads/**"
access: [read]
- uri: "fs://output/results/**"
access: [write]
Environment Variable Permissions
Control which environment variables your plugin can access.
permissions:
environment:
allow:
- key: "API_KEY"
- key: "DATABASE_URL"
- key: "LOG_LEVEL"
- key: "DEBUG"
Setting Environment Variables
Via .env file in your plugin:
API_KEY=sk-1234567890
DATABASE_URL=postgres://localhost/mydb
DEBUG=false
Via CLI:
noorle env set API_KEY=sk-1234567890
noorle env set DATABASE_URL=postgres://localhost/mydb
Important Notes
- Only explicitly allowed variables are accessible
- Values are encrypted at rest in the database
- Each plugin has isolated variables (plugins can’t access each other’s env)
- System environment variables are not accessible to plugins
Use Cases
# Third-party API keys
permissions:
environment:
allow:
- key: "OPENAI_API_KEY"
- key: "STRIPE_API_KEY"
# Database connections
permissions:
environment:
allow:
- key: "DATABASE_URL"
- key: "DATABASE_USER"
- key: "DATABASE_PASSWORD"
# Configuration flags
permissions:
environment:
allow:
- key: "FEATURE_FLAG_BETA"
- key: "LOG_LEVEL"
- key: "CACHE_TTL"
Resource Limits
Protect platform resources from plugin runaway behavior.
Memory Limits
resources:
limits:
memory: "512Mi" # Maximum memory allocation
Kubernetes-style format:
256Ki = 256 Kibibytes
512Mi = 512 Mebibytes
1Gi = 1 Gibibyte
Memory exceeded → Plugin execution terminates with error.
Execution Timeouts
resources:
limits:
timeout: "30s" # Maximum execution time
Supported formats:
10s = 10 seconds
5m = 5 minutes
1h = 1 hour
Timeout exceeded → Plugin execution is killed, request returns timeout error.
CPU Limits
Currently managed via fuel system (internal). Platform provides sensible defaults.
Use Cases
# Long-running data processing
resources:
limits:
memory: "1Gi"
timeout: "5m"
# Real-time API responses
resources:
limits:
memory: "256Mi"
timeout: "10s"
# ML model inference
resources:
limits:
memory: "2Gi"
timeout: "60s"
Security Best Practices
Principle of Least Privilege
Grant only permissions your plugin actually needs:
# ❌ Too permissive
permissions:
network:
allow:
- host: "*" # All hosts!
filesystem:
allow:
- uri: "fs://**" # All files!
# ✅ Specific and minimal
permissions:
network:
allow:
- host: "api.openai.com" # Only what we need
filesystem:
allow:
- uri: "fs://cache/models/**" # Only cache
Secrets Management
Never hardcode secrets:
// ❌ Don't do this
const API_KEY: &str = "sk-1234567890";
// ✅ Use environment variables
let api_key = std::env::var("API_KEY")
.expect("API_KEY not set");
Declare environment variable access in noorle.yaml:
permissions:
environment:
allow:
- key: "API_KEY"
Network Safety
Use specific hostnames and validate TLS:
# ✅ Specific and validated
permissions:
network:
allow:
- host: "api.openai.com" # Exact domain
- host: "api.anthropic.com" # Another service
# ❌ Too broad
permissions:
network:
allow:
- host: "*.com" # All .com domains?!
File Validation
Validate file paths before accessing:
use std::path::Path;
fn safe_read_file(path: &str) -> Result<String> {
// Validate path is within allowed directory
let full_path = Path::new("cache").join(path);
let canonical = full_path.canonicalize()?;
// Ensure it's still in cache directory
if !canonical.starts_with("/allowed/path/cache") {
return Err("Path traversal attempt".into());
}
std::fs::read_to_string(canonical)
}
Viewing Granted Permissions
See what your plugin can access:
# Show plugin permissions
noorle plugin info my-plugin
# Output includes:
# Network Access:
# - api.example.com
# - *.github.com
#
# Filesystem Access:
# - fs://work/agent/** (read, write)
# - fs://cache/** (read)
#
# Environment Variables:
# - API_KEY
# - DATABASE_URL
#
# Resource Limits:
# - Memory: 512Mi
# - Timeout: 30s
Permission Violations
If your plugin tries to access unpermitted resources:
❌ Network access denied: host "blocked.com" not in allowlist
❌ File access denied: path "/forbidden/file.txt" not permitted
❌ Environment variable access denied: "SECRET_KEY" not allowed
❌ Memory limit exceeded: 512Mi limit reached
❌ Timeout exceeded: 30s execution time limit exceeded
All violations are logged and return an error to the caller.
Next Steps