Skip to main content
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:
PathPurposeDefault Access
fs://work/agent/**Agent workspace (scratch files)read, write
fs://cache/**Plugin cache directoryread, write
fs://config/**Configuration filesread
fs://input/**Input data filesread
fs://output/**Output fileswrite

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