kedge/internal/shellstream/capability.go
Tyler King 6058e62348 Initial commit: Kedge network automation platform
Go-based network automation with YANG models, gRPC, Ansible,
Terraform, and Kubernetes integration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:09:30 -05:00

94 lines
2.6 KiB
Go

package shellstream
import (
"encoding/json"
"fmt"
"os"
)
// AccordPolicy defines the local node's policy for granting capabilities
// to incoming Shellstream sessions, derived from Guildhouse accords.
type AccordPolicy struct {
TrustTiers map[string]TierPolicy `json:"trust_tiers"`
}
// TierPolicy maps a Guildhouse trust tier to allowed Kedge modes and operations.
type TierPolicy struct {
Mode []string `json:"mode"` // ["overlay"], ["overlay", "underlay"]
Operations []string `json:"operations"` // ["read"], ["read", "mutate", "admin"]
Subnets []string `json:"subnets"` // ["172.16.0.0/24", "10.0.1.0/24"]
}
// EvaluateCapability evaluates a capability request against the local accord policy.
// Returns the attenuated grant (requested capabilities intersected with policy).
func EvaluateCapability(req CapabilityRequest, policyPath string) (*CapabilityGrant, error) {
policy, err := loadAccordPolicy(policyPath)
if err != nil {
return nil, fmt.Errorf("failed to load accord policy: %w", err)
}
// TODO: Determine the remote peer's trust tier from its SAT token.
// For Phase 1, assume "journeyman" tier.
tier := "journeyman"
tierPolicy, ok := policy.TrustTiers[tier]
if !ok {
return nil, fmt.Errorf("unknown trust tier: %s", tier)
}
// Attenuate: grant the intersection of requested and allowed.
grantedMode := attenuateMode(req.Mode, tierPolicy.Mode)
if grantedMode == "" {
return nil, fmt.Errorf("requested mode %s not allowed for tier %s", req.Mode, tier)
}
grantedOps := attenuateOperations(req.Operations, tierPolicy.Operations)
if len(grantedOps) == 0 {
return nil, fmt.Errorf("no operations allowed for tier %s", tier)
}
return &CapabilityGrant{
Mode: grantedMode,
Operations: grantedOps,
}, nil
}
func loadAccordPolicy(path string) (*AccordPolicy, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var policy AccordPolicy
if err := json.Unmarshal(data, &policy); err != nil {
return nil, err
}
return &policy, nil
}
func attenuateMode(requested string, allowed []string) string {
for _, m := range allowed {
if m == requested || m == "both" {
return requested
}
}
// If "both" was requested but only one mode allowed, grant the allowed one.
if requested == "both" && len(allowed) > 0 {
return allowed[0]
}
return ""
}
func attenuateOperations(requested, allowed []string) []string {
allowedSet := make(map[string]bool)
for _, op := range allowed {
allowedSet[op] = true
}
var granted []string
for _, op := range requested {
if allowedSet[op] {
granted = append(granted, op)
}
}
return granted
}