Watch
1
0
Fork
You've already forked souveraine
0
souveraine/REMOTE_CONNECTION_SYSTEM_SPEC.md
Fimeg d64a6e9794 feat: WIP - Implement companion buddy system for Souveraine TUI
- Added src/ui/buddy.rs with CompanionSprite, BuddyState, and draw functions
- Integrated buddy into app.rs for welcome screen and dashboard
- Added agent selection via 'a' key on welcome screen
- Buddy shows agent name, mood, energy, health, and subconscious status
- WIP: Needs full agent alias creation/removal flow
2026-05-07 02:31:44 -04:00

11 KiB

Souveraine Remote Connection System

Multi-Server Configuration & Nicknames

Vision: souveraine tui --server work connects to your "work" server
Multiple consciousnesses, one CLI.
Date: 2026-05-06


Core Concept

Users have multiple Souveraine servers they connect to:

  • home - Home server (always on)
  • work - Work laptop server
  • lab - Lab workstation
  • cloud - VPS somewhere

CLI manages these as named connections with full configuration.


User Experience

Configuration File

# ~/.config/souveraine/remotes.toml

[remote.home]
name = "Home Server"
url = "https://home.example.com:8283"
api_key = "souv_sk_xxx"  # Or token-based auth
nickname = "home"
default_agent = "agent-ani-xxx"

[remote.work]
name = "Work Laptop"
url = "http://192.168.1.100:8283"
# No api_key - local network trust
nickname = "work"
default_agent = "agent-work-xxx"

[remote.lab]
name = "Lab Workstation"
url = "http://10.10.20.50:8283"
nickname = "lab"
# Discover agents on connect

[remote.cloud]
name = "Cloud VPS"
url = "https://souv.example.com:443"
api_key = "souv_sk_yyy"
nickname = "cloud"
tls_verify = true

CLI Commands

# List configured remotes
$ souveraine remotes
NAME    URL                              STATUS    DEFAULT_AGENT
home    https://home.example.com:8283    online    ani
work    http://192.168.1.100:8283        offline   -
lab     http://10.10.20.50:8283          online    devops
default localhost:8283                   online    ani

# Add new remote
$ souveraine remotes add
Name: staging
URL: https://staging.internal:8283
API Key: souv_sk_abc123
Default agent (leave blank to discover): 
Added "staging" remote

# Quick connect via nickname
$ souveraine tui --server home
# or
$ souveraine chat --server work "Deploy the new config"

# Switch default remote
$ souveraine remotes default work
Default remote set to "work"

# Check server health
$ souveraine remotes check home
✓ Home Server (https://home.example.com:8283)
  Status: online
  Agents: 3
  Version: souveraine 0.5.0
  Latency: 12ms

# Remove remote
$ souveraine remotes remove lab
Removed "lab" remote

Interactive TUI Selector

┌─────────────────────────────────────────────┐
│  Souveraine - Select Remote                 │
├─────────────────────────────────────────────┤
│                                             │
│  ★ home     Home Server              [online]  │
│    work     Work Laptop              [offline] │
│    lab      Lab Workstation          [online]  │
│    cloud    Cloud VPS                [online]  │
│                                             │
│  [n] Add new  [d] Set default  [c] Check    │
│  [q] Quit                                   │
└─────────────────────────────────────────────┘

Architecture

Remote Registry

// src/remote/registry.rs

pub struct RemoteRegistry {
    config_path: PathBuf,
    remotes: HashMap<String, RemoteConfig>,
    default: Option<String>,
}

pub struct RemoteConfig {
    pub name: String,           # Display name
    pub nickname: String,       # Short alias (home, work, etc.)
    pub url: String,            # http://host:port
    pub api_key: Option<String>,
    pub default_agent: Option<String>,
    pub tls_verify: bool,
    pub timeout_secs: u64,
}

impl RemoteRegistry {
    /// Load from ~/.config/souveraine/remotes.toml
    pub fn load() -> Result<Self>;
    
    /// Save configuration
    pub fn save(&self) -> Result<()>;
    
    /// Add new remote
    pub fn add(&mut self, config: RemoteConfig) -> Result<()>;
    
    /// Remove remote
    pub fn remove(&mut self, nickname: &str) -> Result<()>;
    
    /// Get remote by nickname
    pub fn get(&self, nickname: &str) -> Option<&RemoteConfig>;
    
    /// Get default remote
    pub fn default(&self) -> Option<&RemoteConfig>;
    
    /// Set default
    pub fn set_default(&mut self, nickname: &str) -> Result<()>;
    
    /// Check all remote statuses
    pub async fn check_all(&self) -> Vec<RemoteStatus>;
    
    /// List with connection status
    pub async fn list_with_status(&self) -> Vec<(RemoteConfig, RemoteStatus)>;
}

Client Connection

// src/remote/client.rs

pub struct RemoteClient {
    config: RemoteConfig,
    http: reqwest::Client,
    current_agent: Option<String>,
}

impl RemoteClient {
    /// Create client for remote
    pub fn new(config: RemoteConfig) -> Self;
    
    /// Check server health
    pub async fn health_check(&self) -> Result<ServerInfo>;
    
    /// List remote agents
    pub async fn list_agents(&self) -> Result<Vec<AgentSummary>>;
    
    /// Get default or discover
    pub async fn default_agent(&self) -> Result<String>;
    
    /// Start streaming session
    pub async fn stream(
        &self,
        agent_id: &str,
        message: &str,
    ) -> Result<SseStream<StreamEvent>>;
    
    /// Execute tool via remote
    pub async fn execute_tool(
        &self,
        tool_name: &str,
        input: Value,
    ) -> Result<Value>;
}

/// Stream events from remote
pub enum StreamEvent {
    AssistantChunk { content: String },
    ToolCall { name: String, input: Value },
    ToolReturn { output: Value },
    Surfacing { source: String, content: String },
    Reflection { content: String },
    Archivist { synthesis: String, pressure: f32 },
    Done,
    Error { message: String },
}

TUI Remote Mode

// src/ui/remote_mode.rs

pub struct RemoteTuiApp {
    client: RemoteClient,
    conversation_id: Option<String>,
    messages: Vec<Message>,
    input: String,
    streaming: bool,
}

impl RemoteTuiApp {
    /// Connect to remote and start TUI
    pub async fn run(client: RemoteClient) -> Result<()> {
        // Same TUI as local, but all operations go to remote
        // - Messages → POST /api/v1/sessions/{id}/messages
        // - Surfacing → SSE events
        // - Tool calls → Remote executes, returns result
    }
}

Connection Discovery

Auto-Discover Local Servers

// src/remote/discovery.rs

pub struct LocalDiscovery;

impl LocalDiscovery {
    /// Scan network for Souveraine servers
    pub async fn scan_network() -> Vec<DiscoveredServer> {
        // mDNS/Bonjour discovery
        // Or scan common ports on local subnet
    }
    
    /// Check if localhost:8283 has server
    pub async fn check_local() -> Option<ServerInfo>;
}

// On first run, if no remotes configured:
// 1. Check localhost:8283
// 2. If found, add as "default"
// 3. Prompt user to confirm

Server Advertisement

// Server can advertise itself via mDNS

pub struct ServerAdvertisement {
    name: String,
    version: String,
    port: u16,
    agents: Vec<String>,
}

// Clients discover: "Souveraine home-server on 192.168.1.100:8283"

Security

Authentication Options

pub enum AuthMethod {
    /// No auth (local network)
    None,
    
    /// API key in header: X-API-Key: souv_sk_xxx
    ApiKey { key: String },
    
    /// Bearer token: Authorization: Bearer eyJ...
    Bearer { token: String },
    
    /// Client certificates (mTLS)
    MutualTLS {
        cert_path: PathBuf,
        key_path: PathBuf,
    },
}

Key Storage

// API keys stored in system keyring
use keyring::Entry;

pub fn store_api_key(remote: &str, key: &str) -> Result<()> {
    let entry = Entry::new("souveraine", remote)?;
    entry.set_password(key)?;
    Ok(())
}

pub fn get_api_key(remote: &str) -> Result<String> {
    let entry = Entry::new("souveraine", remote)?;
    entry.get_password()
}

Workflows

Workflow 1: Setup New Remote

# User adds work laptop
$ souveraine remotes add
Name: work-laptop
URL: http://192.168.1.50:8283
Save API key? (y/n): n
Discover agents? (y/n): y

Discovered agents:
  1. ani (primary)
  2. dev-helper
Set default: 1

Added "work-laptop" with default agent "ani"

# Use it
$ souveraine tui --server work-laptop

Workflow 2: Switch Context

# At home, use home server
$ souveraine chat "What's the weather?"
# → Uses default (home)

# At coffee shop, connect to work
$ souveraine remotes default work
Default remote set to "work"

$ souveraine tui
# → Connects to work server

Workflow 3: Multi-Server Awareness

# Check all your servers
$ souveraine remotes status
home           ● online  3 agents  12ms
work           ○ offline 0 agents  -
lab            ● online  1 agent   45ms
cloud          ● online  2 agents  120ms  ← slow

# Work laptop is offline (maybe suspended)
# Auto-fallback? Or prompt?

Implementation Phases

Phase 1: Basic Remote Support

// src/main.rs additions
#[derive(Args)]
struct Cli {
    #[arg(long, short)]
    server: Option<String>,  // URL or nickname
}

// If --server provided:
// - Parse as URL or look up in registry
// - Create RemoteClient
// - Run in remote mode

Phase 2: Registry & Management

// Add subcommands:
// souveraine remotes list
// souveraine remotes add
// souveraine remotes remove
// souveraine remotes default

Phase 3: TUI Remote Selector

// Interactive remote picker
// Shows status, latency, agent count
// Visual connection manager

Phase 4: Advanced Features

// - Auto-discovery
// - Connection pooling
// - Offline queue (queue messages when offline)
// - Sync between servers (agent migration)

Summary

The Vision:

┌──────────────────────────────────────────────┐
│  souveraine CLI                              │
│                                              │
│  Multiple remote consciousness servers         │
│  Managed by nickname, easy switching         │
│                                              │
│  $ souveraine tui --server home              │
│  $ souveraine chat --server work "deploy"    │
│  $ souveraine remotes status                 │
└──────────────────────────────────────────────┘

Key Features:

  1. Named remotes (home, work, lab, cloud)
  2. URL or nickname resolution
  3. Per-remote configuration (default agent, auth)
  4. Health checking & status
  5. Secure credential storage
  6. Auto-discovery of local servers
  7. Interactive TUI selector

One CLI, Multiple Consciousnesses.