125 lines
2.9 KiB
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)
|
|
}
|