ollie/toolsrv/server9p.go

125 lines
2.9 KiB
Go

// server9p.go - State management for 9P toolsrv
package toolsrv
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"fmt"
"runtime"
"sync"
"ollie/fsedsl"
)
// Server9P holds the state for the 9P tool server.
// The actual 9P protocol handling is done by the generic 9P server code;
// this struct just manages agents, tools, and execution.
type Server9P struct {
mu sync.RWMutex
secret []byte // shared secret for auth
registry *Registry // tool registry
sessID string // session ID for registry scoping
yolo bool // skip sandbox
// Agent registration: token → agent info
agents map[string]*AgentInfo
// Process management
fs *FS9P
}
// AgentInfo holds registered agent data.
type AgentInfo struct {
ID string // immutable agent ID (UUID)
CWD string // working directory
Token string // auth token
}
// NewServer9P creates a new 9P tool server.
func NewServer9P(secret []byte) *Server9P {
return &Server9P{
secret: secret,
agents: make(map[string]*AgentInfo),
fs: NewFS9P(""), // cwd set per-agent
}
}
// SetRegistry configures the tool registry and session ID.
func (s *Server9P) SetRegistry(r *Registry, sessID string) {
s.mu.Lock()
s.registry = r
s.sessID = sessID
s.fs.SetRegistry(r, sessID)
s.mu.Unlock()
}
// SetYolo enables/disables sandbox bypass.
func (s *Server9P) SetYolo(yolo bool) {
s.mu.Lock()
s.yolo = yolo
s.fs.SetYolo(yolo)
s.mu.Unlock()
}
// RegisterAgent registers an agent and returns a token.
// Verifies the signature using the shared secret.
func (s *Server9P) RegisterAgent(id, cwd, sig string) (string, error) {
// Verify signature
expected := s.computeSig(id, cwd)
if !hmac.Equal([]byte(sig), []byte(expected)) {
return "", fmt.Errorf("invalid signature")
}
// Generate token
tokenBytes := make([]byte, 16)
if _, err := rand.Read(tokenBytes); err != nil {
return "", err
}
token := hex.EncodeToString(tokenBytes)
// Store agent info
s.mu.Lock()
s.agents[token] = &AgentInfo{
ID: id,
CWD: cwd,
Token: token,
}
s.mu.Unlock()
return token, nil
}
// GetAgent returns agent info for a token.
func (s *Server9P) GetAgent(token string) (*AgentInfo, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
agent, ok := s.agents[token]
return agent, ok
}
// computeSig computes HMAC signature for agent registration.
func (s *Server9P) computeSig(id, cwd string) string {
h := hmac.New(sha256.New, s.secret)
h.Write([]byte(id + "|" + cwd))
return hex.EncodeToString(h.Sum(nil))
}
// HostInfo returns platform info as key=value lines.
func (s *Server9P) HostInfo() string {
return fmt.Sprintf("platform=%s\narch=%s\n", runtime.GOOS, runtime.GOARCH)
}
// FS returns the underlying filesystem state.
func (s *Server9P) FS() *FS9P {
return s.fs
}
// BuildTree creates the fsedsl tree for this server.
func (s *Server9P) BuildTree() *fsedsl.Tree {
ctx := ToolsrvCtx{Server: s}
return fsedsl.BuildTree(ToolsrvSpec(), ctx)
}