Skip to content

Lightweight, self-hosted clipboard sharing for local networks. Share text & images instantly between devices with web UI, CLI API, and real-time sync.

Notifications You must be signed in to change notification settings

maddefientist/crosssite

Repository files navigation

CrossSite - LAN Clipboard

A lightweight, self-hosted clipboard sharing tool for your local network. Share text snippets instantly between devices on your LAN.

Features

  • Web UI: Simple, responsive interface for pasting and viewing snippets
  • Image support: Paste images directly (Ctrl+V), upload files, or send via API
  • Real-time sync: SSE (Server-Sent Events) for instant updates across devices
  • CLI-friendly: Full REST API for curl and scripts
  • Local-only: Restricted to private network IPs by default (RFC1918)
  • Secure: XSS protection, security headers, rate limiting
  • Minimal: Single binary, no external dependencies
  • Optional persistence: In-memory by default, SQLite for persistence
  • Large files: Supports up to 10MB per snippet (configurable)

Quick Start

Binary

# Build
go build -o crosssite .

# Run (default: http://0.0.0.0:8787)
./crosssite

# With persistence
./crosssite --persist --db-path ./clips.db

# Custom port and admin token
./crosssite --port 9000 --admin-token mysecret

Docker

# Build and run
docker compose up -d

# Or manually
docker build -t crosssite .
docker run -d -p 8787:8787 --name crosssite crosssite

Unraid

  1. Build the image on Unraid:

    cd /mnt/user/appdata
    git clone <repo-url> crosssite-src
    cd crosssite-src
    docker build -t crosssite:latest .
  2. Add container in Unraid:

    • Go to Docker tab → Add Container
    • Use the unraid-template.xml as reference, or manually configure:
    • Repository: crosssite:latest
    • Port: 87878787
    • Path: /mnt/user/appdata/crosssite/data
    • Variables:
      • PERSIST=true
      • DB_PATH=/data/crosssite.db
      • TTL_HOURS=168 (7 days)
      • ADMIN_TOKEN=your-secret (optional)
  3. Or use docker-compose:

    cd /mnt/user/appdata/crosssite-src
    docker compose up -d

Access at http://<unraid-ip>:8787

Environment Variables / Flags

Env Variable Flag Default Description
PORT --port 8787 Server port
BIND --bind 0.0.0.0 Bind address
MAX_SNIPPET_BYTES --max-snippet-bytes 65536 Max snippet size (64KB)
MAX_SNIPPETS --max-snippets 200 Max stored snippets
PERSIST --persist false Enable SQLite persistence
DB_PATH --db-path crosssite.db SQLite database path
TTL_HOURS --ttl-hours 0 Auto-delete after N hours (0=disabled)
ADMIN_TOKEN --admin-token `` Token for protected operations
ALLOW_CIDRS --allow-cidrs `` Additional allowed CIDRs
TRUST_PROXY --trust-proxy false Trust X-Forwarded-For header

API Reference

Create Text Snippet

# JSON
curl -X POST http://192.168.1.100:8787/api/snippets \
  -H "Content-Type: application/json" \
  -d '{"text":"Hello from curl!"}'

# Plain text
curl -X POST http://192.168.1.100:8787/api/snippets \
  -d 'Hello from curl!'

# Pipe content
echo "clipboard content" | curl -X POST http://192.168.1.100:8787/api/snippets -d @-

# From file
curl -X POST http://192.168.1.100:8787/api/snippets -d @myfile.txt

Share Images

# Upload image file (multipart)
curl -X POST http://192.168.1.100:8787/api/snippets \
  -F "file=@screenshot.png"

# JSON with base64 data
curl -X POST http://192.168.1.100:8787/api/snippets \
  -H "Content-Type: application/json" \
  -d "{\"type\":\"image\",\"mime_type\":\"image/png\",\"data\":\"$(base64 -i screenshot.png)\"}"

# Pipe from clipboard (macOS)
osascript -e 'get the clipboard as «class PNGf»' | xxd -r -p | base64 | \
  xargs -I {} curl -X POST http://192.168.1.100:8787/api/snippets \
    -H "Content-Type: application/json" \
    -d '{"type":"image","mime_type":"image/png","data":"{}"}'

Response:

{
  "id": 1,
  "text": "Hello from curl!",
  "created_at": "2024-01-15T10:30:00Z"
}

Get All Snippets

# Get last 50 (default)
curl http://192.168.1.100:8787/api/snippets

# Get last 10
curl http://192.168.1.100:8787/api/snippets?limit=10

Response:

[
  {"id": 2, "text": "newest", "created_at": "2024-01-15T10:31:00Z"},
  {"id": 1, "text": "older", "created_at": "2024-01-15T10:30:00Z"}
]

Get Latest Snippet

curl http://192.168.1.100:8787/api/snippets/latest

Clear All Snippets

# If ADMIN_TOKEN is set
curl -X DELETE http://192.168.1.100:8787/api/snippets \
  -H "Authorization: Bearer your-token"

# Alternative header
curl -X DELETE http://192.168.1.100:8787/api/snippets \
  -H "X-Admin-Token: your-token"

# Query parameter
curl -X DELETE "http://192.168.1.100:8787/api/snippets?token=your-token"

Real-time Stream (SSE)

# Stream new snippets as they arrive
curl -N http://192.168.1.100:8787/api/stream

Events:

event: connected
data: {"clients": 3}

event: snippet
data: {"id": 1, "text": "new snippet", "created_at": "..."}

Health Check

curl http://192.168.1.100:8787/health

Security

Local Network Only

By default, CrossSite only accepts connections from:

  • 127.0.0.0/8 - Localhost
  • 10.0.0.0/8 - RFC1918 private
  • 172.16.0.0/12 - RFC1918 private
  • 192.168.0.0/16 - RFC1918 private
  • ::1/128 - IPv6 localhost
  • fc00::/7 - IPv6 private
  • fe80::/10 - IPv6 link-local

Public IPs will receive a 403 Forbidden response.

Add custom CIDRs with --allow-cidrs:

./crosssite --allow-cidrs "203.0.113.0/24,198.51.100.0/24"

Behind a Reverse Proxy

If running behind nginx/Caddy/Traefik, enable proxy trust:

./crosssite --trust-proxy

This will respect X-Forwarded-For and X-Real-IP headers.

Admin Token

Protect the clear endpoint with a token:

./crosssite --admin-token "your-secret-token"

Rate Limiting

Built-in rate limiting: 10 requests/second with burst of 20 per IP.

Security Headers

All responses include:

  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • X-XSS-Protection: 1; mode=block
  • Referrer-Policy: strict-origin-when-cross-origin
  • Content-Security-Policy: default-src 'self'; ...

Building

Requirements

  • Go 1.21+
  • GCC (for SQLite CGO)

Build Commands

# Standard build
go build -o crosssite .

# With optimizations
go build -ldflags="-w -s" -o crosssite .

# Cross-compile (Linux)
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o crosssite-linux .

# Without SQLite (in-memory only)
go build -tags nosqlite -o crosssite .

Running Tests

go test -v ./...

Example Usage

Quick Copy-Paste Workflow

  1. Open http://192.168.1.100:8787 on your phone
  2. Paste a link or text
  3. Click "Share"
  4. On your laptop, the snippet appears instantly
  5. Click "Copy" to grab it

CLI Script Integration

# .bashrc alias to share clipboard
alias share='pbpaste | curl -sX POST http://192.168.1.100:8787/api/snippets -d @- && echo "Shared!"'

# Get latest snippet
alias grab='curl -s http://192.168.1.100:8787/api/snippets/latest | jq -r .text'

# Watch for new snippets
alias watch-clips='curl -sN http://192.168.1.100:8787/api/stream'

Windows PowerShell

# Share clipboard
Get-Clipboard | Invoke-RestMethod -Method Post -Uri "http://192.168.1.100:8787/api/snippets" -Body $_

# Get latest
(Invoke-RestMethod -Uri "http://192.168.1.100:8787/api/snippets/latest").text | Set-Clipboard

Architecture

crosssite/
├── main.go           # Entry point, config
├── server.go         # HTTP server, routing
├── handlers.go       # API handlers
├── middleware.go     # Access control, security, rate limiting
├── storage.go        # In-memory ring buffer
├── sqlite.go         # SQLite persistence
├── sse.go            # Server-Sent Events
└── static/           # Embedded frontend
    ├── index.html
    ├── style.css
    └── app.js

License

MIT

About

Lightweight, self-hosted clipboard sharing for local networks. Share text & images instantly between devices with web UI, CLI API, and real-time sync.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •