OPAL Permissions
Security and access control for Plans
OPAL Permissions
Reference for the OPAL permission system that controls what Plans can access and modify.
Security Note
OPAL's capability-based permission model means Plans must explicitly request the resources they need, and users review these requests before approval. This "principle of least privilege" protects your robot from malicious or buggy Plans - a Plan that only requests
sensors.distancecan never accidentally (or intentionally) spin your motors.
Permission model
OPAL uses a capability-based security model:
- Plans declare required permissions
- User reviews and approves permissions
- Runtime enforces permission checks
┌─────────────────┐
│ Plan │
│ (requests │
│ permissions) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Permission │
│ Checker │
│ (grants/denies) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Hardware/ │
│ Resources │
└─────────────────┘
Permission categories
| Category | Prefix | Description |
|---|---|---|
| Sensors | sensors.* |
Read sensor data |
| Actuators | actuators.* |
Control motors, servos |
| Communication | comms.* |
Send/receive messages |
| Storage | storage.* |
Persist data |
| System | system.* |
System operations |
| Network | network.* |
Network access |
Sensor permissions
sensors.read
Read any sensor value.
// Requires: sensors.read
var dist = sensors.distance.front
var heading = sensors.imu.heading
sensors.distance
Read distance sensors only.
// Requires: sensors.distance
var dist = sensors.distance.front
var left = sensors.distance.left
sensors.imu
Read IMU (orientation) sensors.
// Requires: sensors.imu
var heading = sensors.imu.heading
var pitch = sensors.imu.pitch
sensors.line
Read line following sensors.
// Requires: sensors.line
var on_line = sensors.line.center
sensors.camera
Access camera feed.
// Requires: sensors.camera
var frame = camera.capture()
sensors.gps
Access GPS location.
// Requires: sensors.gps
var lat = gps.latitude
var lon = gps.longitude
Actuator permissions
actuators.control
Control any actuator.
// Requires: actuators.control
drive(50, 50)
set_servo("arm", 90)
actuators.motors
Control DC motors only.
// Requires: actuators.motors
drive(50, 50)
set_motor("left", 60)
actuators.servos
Control servos only.
// Requires: actuators.servos
set_servo("head.pan", 90)
set_servo("head.tilt", 45)
actuators.relays
Control relay outputs.
// Requires: actuators.relays
set_relay("lights", true)
actuators.emergency_stop
Access emergency stop function.
// Requires: actuators.emergency_stop
emergency_stop()
Note: Emergency stop is always allowed regardless of other permissions for safety.
Communication permissions
comms.broadcast
Send broadcast messages.
// Requires: comms.broadcast
broadcast("status", {state: "idle"})
comms.send
Send direct messages to peers.
// Requires: comms.send
send_to("Worker A", "command", {action: "start"})
comms.receive
Receive messages from peers.
// Requires: comms.receive
on_message("task") { data ->
execute(data)
}
comms.peers
Access peer information.
// Requires: comms.peers
var peer_list = peers.list()
var status = peers["Worker A"].status
Storage permissions
storage.read
Read stored data.
// Requires: storage.read
var config = storage.get("config")
var history = storage.get("patrol_history")
storage.write
Write data to storage.
// Requires: storage.write
storage.set("last_patrol", now())
storage.set("config", {speed: 50})
storage.delete
Delete stored data.
// Requires: storage.delete
storage.delete("old_data")
storage.clear()
System permissions
system.info
Access system information.
// Requires: system.info
var version = system.version
var uptime = system.uptime
var name = system.personality_name
system.battery
Access battery status.
// Requires: system.battery
var level = battery.level
var voltage = battery.voltage
system.time
Access time functions.
// Requires: system.time
var now = now()
var hour = time.hour
system.plans
Control other plans.
// Requires: system.plans
activate("PatrolPlan")
deactivate("OldPlan")
pause("BackgroundTask")
system.config
Modify system configuration.
// Requires: system.config
// Very restricted - rarely granted
config.set("network.port", 8125)
Network permissions
network.local
Access local network.
// Requires: network.local
// Used for LAN discovery and communication
network.internet
Access external internet.
// Requires: network.internet
// Rarely granted - for cloud services
http.get("https://api.example.com/data")
Declaring permissions
In plan header
#!octomy-plan v1.0
# @permissions sensors.distance, actuators.motors
# @permissions comms.broadcast
plan MyPlan {
// Plan code
}
In metadata.json
{
"name": "My Plan",
"permissions": [
"sensors.distance",
"actuators.motors",
"comms.broadcast"
]
}
Permission groups
Common permission bundles:
basic
Minimal permissions for simple plans.
sensors.read
system.time
mobile
Permissions for mobile robots.
sensors.read
actuators.motors
system.battery
autonomous
Full autonomous operation.
sensors.read
actuators.control
comms.broadcast
storage.read
storage.write
system.info
admin
Administrative access (rarely used).
sensors.read
actuators.control
comms.*
storage.*
system.*
Permission UI
When importing a plan:
┌─────────────────────────────────────────────┐
│ Permission Review │
├─────────────────────────────────────────────┤
│ │
│ Plan: Obstacle Avoider │
│ Author: RobotBuilder42 │
│ │
│ This plan requests: │
│ │
│ ✓ sensors.distance │
│ Read distance sensor values │
│ │
│ ✓ actuators.motors │
│ Control drive motors │
│ │
│ ✓ system.battery │
│ Check battery level │
│ │
│ ⚠ Risk Level: LOW │
│ Only accesses sensors and motors │
│ │
│ [Approve] [Deny] [Review Code] │
│ │
└─────────────────────────────────────────────┘
Risk levels
| Level | Description |
|---|---|
| LOW | Read-only sensors, no network |
| MEDIUM | Motor control, local storage |
| HIGH | Network access, all actuators |
| CRITICAL | System config, internet access |
Runtime enforcement
Permission check
At runtime, each operation checks permissions:
Plan calls: drive(50, 50)
↓
Permission check: actuators.motors
↓
Granted? → Execute
↓
Denied? → Throw E501 Permission Denied
Error handling
try {
drive(50, 50)
} catch error {
if error.code == "E501" {
log_error("No motor permission!")
// Fallback behavior
}
}
Trust levels and permissions
Permissions interact with trust levels:
| Trust Level | Max Permissions |
|---|---|
| Block | None |
| Ignore | None |
| Meet | sensors.read |
| Handshake | sensors.read, comms.receive |
| Trust | All except system.config |
| Depend | All |
Remote control from a peer is limited by:
- Plan's declared permissions
- Peer's trust level
- User's approval
Best practices
Principle of least privilege
Request only what you need:
// Bad - requests everything
# @permissions actuators.control
// Good - requests only motors
# @permissions actuators.motors
Explain why
Document permission use:
# @permissions sensors.distance
# Used for obstacle detection
# @permissions actuators.motors
# Used for driving and avoidance
Handle denials gracefully
try {
var dist = sensors.distance.front
} catch error {
if error.code == "E501" {
// Fall back to simpler behavior
use_bump_sensors_only()
}
}
Avoid permission: network.internet
Internet access should rarely be needed. Consider:
- Local processing instead of cloud
- Hub-mediated cloud access
- Offline-first design
Security considerations
Plan signing
Plans from trusted sources can be signed:
# @signature sha256:abc123...
# @author verified:RobotBuilder42
Signed plans show verified author in permission dialog.
Sandboxing
Plans run in a sandbox:
- Cannot access filesystem directly
- Cannot execute system commands
- Limited memory and CPU
- Network access controlled
Audit logging
All permission-controlled actions are logged:
[12:34:56] Plan "Patrol" used sensors.distance
[12:34:56] Plan "Patrol" used actuators.motors