spacedrive/docs/core/cli.mdx
2025-11-14 21:31:21 -08:00

363 lines
9.1 KiB
Plaintext

---
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] <command> [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 <socket-path>`
3. Restart daemon: `sd restart`