--- title: "CLI Architecture" description: "Command-line interface for Spacedrive v2" --- ## Overview The Spacedrive CLI (`sd`) is a command-line interface for interacting with the Spacedrive daemon. It provides a comprehensive set of commands for managing libraries, files, devices, and daemon lifecycle. ## Architecture The CLI follows a client-daemon architecture: - **CLI Binary** (`sd-cli` / `sd`) - The client that sends commands - **Daemon Binary** (`sd-daemon`) - The background service that does the actual work - **Unix Socket** - Communication channel between CLI and daemon ``` ┌─────────┐ Unix Socket ┌──────────┐ │ sd │ ◄──────────────────► │ sd-daemon│ │ (client)│ │ (server) │ └─────────┘ └──────────┘ │ ▼ ┌──────────┐ │ Libraries│ └──────────┘ ``` ## Daemon Management ### Starting the Daemon ```bash # Start in background sd start # Start in foreground (see logs) sd start --foreground ``` The daemon: - Runs as a background process - Listens on a Unix socket at `~/Library/Application Support/spacedrive/daemon/daemon.sock` - Manages all libraries and background tasks - Can run multiple instances with `--instance` flag ### Auto-Start on Login Install the daemon to start automatically on system boot: ```bash # Install LaunchAgent (macOS) sd daemon install # Check status sd daemon status # Uninstall sd daemon uninstall ``` This creates a LaunchAgent at `~/Library/LaunchAgents/com.spacedrive.daemon.plist` that: - Starts daemon on user login - Restarts if it crashes - Logs to `~/Library/Application Support/spacedrive/logs/` ### Multi-Instance Support Run multiple daemon instances for different data directories: ```bash # Start with custom instance name sd --instance work start sd --instance personal start # Each instance gets its own socket # daemon-work.sock # daemon-personal.sock ``` ## Configuration The CLI stores configuration in `~/Library/Application Support/spacedrive/cli.json`: ```json { "current_library_id": "uuid-here", "update": { "repo": "spacedriveapp/spacedrive-cli-releases", "channel": "stable" } } ``` ### Config Commands ```bash # View all configuration sd config show # Get specific value sd config get update.repo # Set value sd config set update.repo "your-org/releases-repo" ``` ## Auto-Update System The CLI includes a built-in update mechanism that fetches releases from a public GitHub repository. ### Update Architecture ``` ┌─────────┐ ┌────────────┐ │sd update│ ──── GitHub API ────────►│ Releases │ └─────────┘ │ Repository │ │ └────────────┘ │ Download binaries │ ▼ │ ┌─────────────────┐ │ │ sd-macos-arm64 │◄──────────────────────┘ │ sd-daemon-... │ └─────────────────┘ │ │ Atomic replacement ▼ ┌─────────────────┐ │ Installed bins │ └─────────────────┘ ``` ### Update Process 1. **Check for updates**: Queries GitHub API for latest release 2. **Download binaries**: Fetches platform-specific binaries 3. **Verify integrity**: Checks file sizes match expected values 4. **Atomic replacement**: Replaces binaries with rollback on failure 5. **Restart daemon**: If daemon was running, restarts it ```bash # Check and install updates sd update # Force reinstall current version sd update --force ``` ### Release Repository Setup The update system uses a public GitHub repository containing only releases (no source code): 1. Create public repo: `your-org/spacedrive-cli-releases` 2. For each release: - Create empty commit: `git commit --allow-empty -m "Release v0.1.0"` - Tag it: `git tag v0.1.0` - Create GitHub release with binaries as assets 3. Configure CLI: `sd config set update.repo "your-org/spacedrive-cli-releases"` The CI workflow automatically builds binaries for: - macOS (arm64, x86_64) - Linux (x86_64) - coming soon - Windows (x86_64) - coming soon ## Command Structure All CLI commands follow this pattern: ```bash sd [--data-dir PATH] [--instance NAME] [--format FORMAT] [args] ``` ### Global Flags - `--data-dir` - Override default data directory - `--instance` - Connect to specific daemon instance - `--format` - Output format: `human` (default) or `json` ### Core Commands | Command | Description | |---------|-------------| | `sd start` | Start the daemon | | `sd stop` | Stop the daemon | | `sd restart` | Restart the daemon | | `sd status` | Show system status | | `sd update` | Update CLI and daemon | ### Domain Commands | Command | Description | |---------|-------------| | `sd library` | Manage libraries | | `sd location` | Manage indexed locations | | `sd file` | File operations | | `sd index` | Indexing operations | | `sd search` | Search files | | `sd tag` | Tag management | | `sd volume` | Volume operations | | `sd device` | Device management | | `sd job` | Job control | | `sd network` | Networking and pairing | | `sd logs` | View daemon logs | | `sd config` | Configuration management | | `sd daemon` | Daemon lifecycle | ## Binary Distribution For development and testing, binaries can be distributed without building from source: 1. **Build release binaries**: ```bash cargo build --release --bin sd-cli --bin sd-daemon ``` 2. **Copy both binaries** to target machine: - `sd-cli` → rename to `sd` - `sd-daemon` → keep as `sd-daemon` 3. **Place in PATH**: ```bash mv sd ~/.local/bin/ mv sd-daemon ~/.local/bin/ chmod +x ~/.local/bin/sd ~/.local/bin/sd-daemon ``` 4. **Remove quarantine** (macOS): ```bash xattr -d com.apple.quarantine ~/.local/bin/sd xattr -d com.apple.quarantine ~/.local/bin/sd-daemon ``` Both binaries must be in the same directory as the CLI expects to find the daemon binary in its parent directory. ## Development ### Building ```bash # Build CLI only cargo build --package sd-cli # Build both CLI and daemon cargo build --bin sd-cli --bin sd-daemon # Release build cargo build --release --bin sd-cli --bin sd-daemon ``` ### Code Structure ``` apps/cli/ ├── src/ │ ├── main.rs # Entry point, daemon lifecycle │ ├── config.rs # CLI configuration │ ├── context.rs # Execution context │ ├── domains/ # Command domains │ │ ├── config/ # Config commands │ │ ├── daemon/ # Daemon management │ │ ├── update/ # Auto-update system │ │ ├── library/ # Library operations │ │ ├── location/ # Location operations │ │ └── ... │ └── util/ # Shared utilities └── Cargo.toml core/ └── src/ └── bin/ └── daemon.rs # Daemon binary ``` ### Adding New Commands 1. Create a new module in `domains/` 2. Define command enum with clap derives 3. Implement `run()` function 4. Register in `domains/mod.rs` and `main.rs` Example: ```rust // domains/myfeature/mod.rs use anyhow::Result; use clap::Subcommand; #[derive(Subcommand, Debug)] pub enum MyFeatureCmd { /// Do something Action { arg: String }, } pub async fn run(ctx: &Context, cmd: MyFeatureCmd) -> Result<()> { match cmd { MyFeatureCmd::Action { arg } => { // Implementation } } Ok(()) } ``` ## Security Considerations ### Instance Name Validation Instance names are validated to prevent path traversal: - Only alphanumeric, dash, and underscore allowed - Max 64 characters - Cannot be empty ### Binary Verification The update system verifies downloaded binaries: - File size must match release asset size - Future: SHA256 checksum verification ### Daemon Socket Permissions The Unix socket is created with restrictive permissions (user-only access). ## Troubleshooting ### Daemon Won't Start ```bash # Check if already running sd status # View logs sd logs follow # Reset data and restart sd restart --reset ``` ### Update Fails ```bash # Check configuration sd config show # Verify repo is accessible curl https://api.github.com/repos/YOUR_ORG/YOUR_REPO/releases/latest # Force clean reinstall sd update --force ``` ### Socket Connection Errors The daemon socket is located at: ``` ~/Library/Application Support/spacedrive/daemon/daemon.sock ``` If connection fails: 1. Verify daemon is running: `ps aux | grep sd-daemon` 2. Check socket exists: `ls -la ` 3. Restart daemon: `sd restart`