REST API server with WebSocket support for real-time clip updates, includes built-in web UI.
# Build
cargo build -p clipper-server
# Build with embedded web UI (for Docker/deployment)
cargo build -p clipper-server --release --features embed-web
# Build with TLS support (manual certificates)
cargo build -p clipper-server --features tls
# Build with ACME (Let's Encrypt automatic certificates)
cargo build -p clipper-server --features acme
# Build with full TLS + ACME + secure storage (OS keychain)
cargo build -p clipper-server --features full-tls
# Test (must run sequentially to avoid database conflicts)
cargo test -p clipper-server -- --test-threads=1
cargo test --test api_tests -p clipper-server -- --test-threads=1
# Run single test
cargo test test_create_clip -p clipper-server# Run (requires CLIPPER_DB_PATH and CLIPPER_STORAGE_PATH env vars or uses defaults)
cargo run --bin clipper-server
# With custom paths
CLIPPER_DB_PATH=./data/db CLIPPER_STORAGE_PATH=./data/storage cargo run --bin clipper-server
# With configuration file
cargo run --bin clipper-server -- --config config.toml# Build Docker image (includes embedded web UI)
docker build -t clipper-server .
# Run container
docker run -d -p 3000:3000 -v clipper-data:/data clipper-server
# Access at http://localhost:3000- Built with Axum framework
AppStatewrapsArc<ClipperIndexer>and broadcast channel for WebSocket updates- REST endpoints in
api.rs: CRUD operations, search with pagination, file upload - WebSocket in
websocket.rs: real-time clip updates - All state mutations trigger WebSocket notifications
- Configuration: Multi-source configuration (CLI args, env vars, TOML files)
- Built-in Web UI: Serves static files from
web/dist/directory - Web UI features: View, search, edit, delete clips with i18n support (English/Chinese)
Multiple configuration sources (in priority order):
- Command line arguments
- Environment variables
- Configuration file (TOML)
- Default values
CLIPPER_CONFIG- Path to configuration fileCLIPPER_DB_PATH(default:./data/db)CLIPPER_STORAGE_PATH(default:./data/storage)CLIPPER_LISTEN_ADDR(default:0.0.0.0)CLIPPER_WEB_DIR- Path to web UI dist directory (default: auto-detected./web/dist)PORT(default:3000)RUST_LOGfor tracing (default:clipper_server=debug,tower_http=debug)
CLIPPER_TLS_ENABLED- Enable HTTPS (default:false)CLIPPER_TLS_PORT- HTTPS port (default:443)CLIPPER_TLS_CERT- Path to TLS certificate file (PEM format)CLIPPER_TLS_KEY- Path to TLS private key file (PEM format)CLIPPER_TLS_REDIRECT- Redirect HTTP to HTTPS (default:true)CLIPPER_TLS_RELOAD_INTERVAL- Seconds between certificate reload checks (default:0= disabled)
For development or private networks where you don't have a domain name, you can use self-signed certificates:
- Generate a self-signed certificate using OpenSSL:
# Create a directory for certificates
mkdir -p certs
# Generate a private key and self-signed certificate (valid for 365 days)
openssl req -x509 -newkey rsa:4096 -keyout certs/key.pem -out certs/cert.pem -days 365 -nodes \
-subj "/CN=clipper-server" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1,IP:192.168.1.100"Replace 192.168.1.100 with your server's actual IP address. You can add multiple IPs or DNS names.
- Run the server with TLS:
CLIPPER_TLS_ENABLED=true \
CLIPPER_TLS_PORT=3443 \
CLIPPER_TLS_CERT=./certs/cert.pem \
CLIPPER_TLS_KEY=./certs/key.pem \
cargo run --bin clipper-server --features tls- Connect from the Clipper desktop app:
When connecting to a server with a self-signed certificate, the Clipper app will show a confirmation dialog displaying the certificate's SHA-256 fingerprint. If you trust the server:
- Verify the fingerprint matches (you can check it with:
openssl x509 -in certs/cert.pem -noout -fingerprint -sha256) - Click "Trust Certificate" to save the fingerprint
- Future connections to this server will automatically trust this certificate
Note: The certificate fingerprint is stored per-host in the app's settings. If you regenerate the certificate, you'll need to trust the new fingerprint.
CLIPPER_ACME_ENABLED- Enable automatic certificate management (default:false)CLIPPER_ACME_DOMAIN- Domain name for the certificateCLIPPER_ACME_EMAIL- Contact email for Let's Encrypt notificationsCLIPPER_ACME_STAGING- Use staging environment for testing (default:false)CLIPPER_CERTS_DIR- Directory for certificate cache (default:~/.config/com.0d0a.clipper/certs/)
Note: When ACME is enabled, CLIPPER_BEARER_TOKEN is required for security. Since ACME exposes the server to the internet with a public domain, authentication is mandatory to prevent unauthorized access.
CLIPPER_CLEANUP_ENABLED- Enable automatic cleanup of old clips (default:false)CLIPPER_CLEANUP_RETENTION_DAYS- Delete clips older than this many days (default:30)CLIPPER_CLEANUP_INTERVAL_HOURS- Interval in hours between cleanup runs (default:24)
CLIPPER_BEARER_TOKEN- Bearer token for authentication (if set, all requests requireAuthorization: Bearer <token>header)
CLIPPER_SHORT_URL_BASE- Base URL for short URLs (e.g.,https://clip.example.com). If not set, sharing is disabled.CLIPPER_SHORT_URL_EXPIRATION_HOURS- Default expiration time for short URLs in hours (default:24,0= no expiration)
GET /health- Health checkGET /auth/check- Check authentication statusGET /version- Server version and status (version, uptime, active connections, config)POST /clips- Create clip from textPOST /clips/upload- Upload file as clip (streaming, multipart)GET /clips- List clips with pagination (query params: start_date, end_date, tags, page, page_size)GET /clips/search- Search clips with pagination (query params: q, start_date, end_date, tags, page, page_size, highlight_begin, highlight_end)GET /clips/:id- Get clip by IDPUT /clips/:id- Update clip metadataDELETE /clips/:id- Delete clipGET /clips/:id/file- Download file attachment (streaming)GET /tags- List all tagsGET /tags/search- Search tags with autocompletePOST /clips/:id/short-url- Create a short URL for sharing a clip (requiresCLIPPER_SHORT_URL_BASE)GET /short/:code- Redirect short URL to full clip URLGET /s/:code- Public endpoint to resolve short URL (returns HTML page, JSON, or file based on Accept header)GET /shared-assets/:filename- Static assets for shared clip page (CSS/JS)GET /export- Export all clips as tar.gz archive (streaming)POST /import- Import clips from tar.gz archive (streaming, multipart)
WS /ws- Real-time clip notifications
Server broadcasts four types of notifications:
// NewClip: { type: "new_clip", id, content, tags }
// UpdatedClip: { type: "updated_clip", id }
// DeletedClip: { type: "deleted_clip", id }
// ClipsCleanedUp: { type: "clips_cleaned_up", ids, count }- Add handler function in
src/api.rs - Register route in
pub fn routes() - If it modifies clips, call
state.notify_*()for WebSocket updates - Add test in
tests/api_tests.rs - Add client method in
clipper-client/src/lib.rs - Add test in
clipper-client/tests/integration_tests.rs - Add CLI command in
clipper-cli/src/main.rsif user-facing
clipper_server::ServerError- server-specific errors (implements IntoResponse)- Server errors automatically converted to JSON responses with appropriate HTTP status codes
- Server tests must run sequentially:
-- --test-threads=1 - Each test creates isolated temporary database
- Server tests use raw HTTP requests via tower::ServiceExt
- Multipart file upload tests construct raw HTTP multipart bodies
- Test coverage: 81 tests (23 unit tests + 58 API tests)