Permission System¶
Axium provides a declarative permission system for Spokes with daemon-enforced security and user-configurable overrides.
Overview¶
Spokes declare required permissions in their spoke.yaml manifest. All privileged operations (exec, notify, filesystem access) are enforced by the daemon via IPC. Users can extend or restrict permissions without modifying spoke code.
Key Features¶
- Declarative Permissions - Spokes declare needs in
spoke.yaml - Daemon Enforcement - All privileged actions go through daemon IPC
- User Overrides - Users can extend/restrict via
~/.config/axium/permissions.yaml - Glob Matching - Filesystem permissions support wildcards
- Security Logging - All permission checks logged with
[SECURITY]prefix - Strict Mode -
AXIUM_PERMS_STRICT=1disables all overrides
Permission Types¶
Boolean Permissions¶
exec- Allow background command execution viadaemon_execnotify- Allow user notificationsnet- (Reserved) Future HTTP request capability
List Permissions¶
fs_read- List of allowed readable paths (glob patterns)fs_write- List of allowed writable paths (glob patterns)
Declaring Permissions¶
In spoke.yaml¶
name: creds
version: 0.1.0
description: Credential validity checker
entrypoint: main:register
permissions:
exec: true # Allow background commands
notify: true # Allow notifications
net: false # No network access
fs_read:
- ~/.aws/credentials # Can read AWS creds
- ~/.aws/config
- /opt/company/creds/* # Wildcard pattern
fs_write:
- /tmp/creds-cache/* # Can write to cache
Default Permissions¶
If no permissions section exists, all permissions default to false and empty lists (most restrictive).
User Overrides¶
Users can customize spoke permissions via ~/.config/axium/permissions.yaml:
# User permission overrides
creds:
exec: false # Disable exec even if spoke requests it
fs_read:
- ~/.aws/credentials
- /extra/path/* # Add additional path
aws-spoke:
notify: true # Enable notifications
fs_write:
- ~/aws-logs/* # Add write permission
Merge Rules¶
- Booleans (exec, notify, net): Override replaces base value
- Lists (fs_read, fs_write): Union of base + override (deduplicated)
Example:
Base (spoke.yaml):
Override (permissions.yaml):
Result:
exec: false # Overridden
fs_read:
- ~/.aws/credentials # From base
- /opt/creds.json # From override
Using Permissions in Spokes¶
Sending Notifications¶
from axium.core.ipc import send_request_sync
def send_notification(title: str, body: str):
"""Send notification via daemon (requires notify permission)."""
try:
resp = send_request_sync({
"cmd": "notify",
"spoke": "creds",
"title": title,
"body": body,
"level": "info"
})
return resp.get("ok", False)
except Exception:
return False
Requesting Background Execution¶
def request_daemon_exec(command: str) -> bool:
"""Request daemon to run command in background (requires exec permission)."""
try:
resp = send_request_sync({
"cmd": "daemon_exec",
"spoke": "creds",
"command": command,
"mode": "background"
})
return resp.get("ok", False)
except Exception:
return False
# Usage
if request_daemon_exec("aws sso login --profile prod"):
print("✓ Refresh started in background")
Updating HUD Segments¶
def update_hud_segment(value: str):
"""Update spoke's HUD segment (no special permission needed)."""
try:
send_request_sync({
"cmd": "hud_segment_value",
"spoke": "creds",
"value": value # e.g., "[creds:Y]" or ""
})
except Exception:
pass # Non-fatal
Filesystem Permissions¶
Filesystem permissions use glob pattern matching with the fnmatch module (shell-style wildcards).
Pattern Examples¶
fs_read:
- ~/.aws/credentials # Exact file
- ~/.aws/* # All files in dir (not recursive)
- ~/.config/axium/** # Recursive (all files in tree)
- /tmp/spoke-*.log # Pattern matching
- ~/data/*.{json,yaml} # Multiple extensions (if shell expanded)
Path Expansion¶
Paths are automatically expanded:
- Tilde (~) → User's home directory
- Environment variables work in override files
Checking Filesystem Permissions¶
Daemon automatically checks filesystem permissions when spokes use config loading:
# Config system automatically checks fs_read permissions
config = load_spoke_config("creds", "creds.yaml")
# If creds spoke doesn't have fs_read permission for config file, loading fails
CLI Commands¶
View Permissions¶
# List all spoke permissions
$ axium perms list
Spoke Permissions:
creds (v0.1.0)
exec: true (base)
notify: true (base)
net: false (base)
fs_read: 2 paths
fs_write: 1 path
aws (v1.0.0)
exec: false (base)
notify: true (override)
...
Show Detailed Permissions¶
$ axium perms show creds
Permissions for 'creds' (v0.1.0):
Boolean Permissions:
exec: true (base)
notify: true (base)
net: false (base)
Filesystem Permissions:
fs_read:
• ~/.aws/credentials (base)
• ~/.aws/config (base)
• /opt/company/creds/* (override)
fs_write:
• /tmp/creds-cache/* (base)
Override source: ~/.config/axium/permissions.yaml
Edit Overrides¶
$ axium perms edit creds
# Opens ~/.config/axium/permissions.yaml in $EDITOR
# Creates file if it doesn't exist
Notification Management¶
Drain Notifications¶
$ axium notify drain
Queued Notifications (2):
[2025-10-07 16:30:00] creds: Credentials expired
Run: aws sso login or 'axium creds refresh'
[2025-10-07 16:31:15] system: Update available
Run: axium update
Test Notification¶
$ axium notify send --title "Test" --body "Hello world"
✓ Notification sent
View with: axium notify drain
Security Logging¶
All permission checks are logged with [SECURITY] prefix for audit trails:
[SECURITY] spoke=creds action=exec allowed=true detail=None via_override=false
[SECURITY] spoke=creds action=notify allowed=true detail=None via_override=false
[SECURITY] spoke=aws action=fs_read:/etc/passwd allowed=false detail=/etc/passwd via_override=false
Log Format¶
- spoke: Spoke name
- action: Permission type (exec, notify, fs_read:path, etc.)
- allowed: true if permitted, false if denied
- detail: Additional context (command, path, etc.)
- via_override: true if permission came from user override
Strict Mode¶
Strict mode disables all user overrides, using only spoke-declared permissions. Useful for CI/CD, locked-down hosts, or security compliance.
Enable Strict Mode¶
In strict mode:
- All ~/.config/axium/permissions.yaml overrides ignored
- Only permissions in spoke.yaml are active
- Logged: "Strict mode enabled, ignoring overrides for spoke: <name>"
Best Practices¶
For Spoke Developers¶
✅ Request minimal permissions
✅ Document why you need permissions
# Good - explain in spoke README
permissions:
exec: true # Required to refresh credentials via 'aws sso login'
✅ Fail gracefully when denied
# Good - handle permission denial
if not request_daemon_exec(command):
logger.warning("Could not refresh credentials (exec permission denied)")
# Continue with cached data or manual prompt
❌ Don't request blanket permissions
# Bad - too broad
permissions:
fs_read:
- ~/** # Everything in home dir!
- /etc/** # All system configs!
❌ Don't hardcode actions without permission checks
# Bad - bypasses permission system
import subprocess
subprocess.run(["aws", "sso", "login"]) # Should use daemon_exec
For Users¶
✅ Use overrides to grant additional access
# Good - allow spoke to access extra credential files
creds:
fs_read:
- /opt/company/special-creds.json
✅ Use strict mode in production
❌ Don't grant excessive permissions
Architecture¶
Permission Flow¶
- Spoke declares permissions in
spoke.yaml - Daemon loads permissions on spoke load
- User overrides merged (unless strict mode)
- Spoke requests action via IPC
- Daemon checks permission before executing
- Action allowed/denied with security log
- Response sent to spoke
IPC Handlers¶
load_spoke_permissions- Load/merge permissions on spoke loadnotify- Check notify permission, queue notificationdaemon_exec- Check exec permission, run background commandget_permissions- Return effective permissions for spokenotify_drain- Return and clear notification queue
Example: Complete Workflow¶
1. Spoke Declares Permissions¶
spokes/creds/spoke.yaml:
2. User Adds Override¶
~/.config/axium/permissions.yaml:
3. Daemon Loads Merged Permissions¶
# Result:
{
"exec": false, # From override
"notify": true, # From base
"fs_read": [
"~/.aws/credentials", # From base
"~/extra-creds.json" # From override
]
}
4. Spoke Requests Action¶
# Spoke tries to refresh
result = request_daemon_exec("aws sso login")
# Returns: False (exec denied by override)
5. Security Log¶
See Also¶
- Configuration - Config system documentation
- Spokes - Spoke development guide
- Security - Security considerations (if exists)