A modern, high-performance HTTP clipboard app written in Go with Gin framework.
nclip is a versatile clipboard app that accepts content via:
- Web UI - Browser interface at
http://localhost:8080 - Curl - Modern web API:
echo "text" | curl -sL --data-binary @- http://localhost:8080 - File upload - Upload (small) files via web UI or curl:
curl -sL --data-binary @/path/file http://localhost:8080 - Raw access - Access raw content via
http://localhost:8080/raw/SLUG - Burn after reading - Content that self-destructs after being accessed once
- π Dual Deployment: Server mode (local or container) + AWS Lambda
- π― Unified Codebase: Same code, logic, and UI for both environments
- ποΈ Multi-Storage Backend: Filesystem for server mode, S3 for Lambda
- π³ Container Ready: Docker & Kubernetes deployment
- β° Auto-Expiration: TTL support with configurable defaults
- π‘οΈ Production Ready: Health checks, structured logging
- π§ Configurable: Environment variables & CLI flags
# Install with go install (requires Go 1.23+)
go install github.com/johnwmail/nclip@latest
# Download pre-built binary
wget https://github.com/johnwmail/nclip/releases/latest/download/nclip_linux_amd64.tar.gz
tar -xzf nclip_linux_amd64.tar.gz
cd nclip_linux_amd64
# Run nclip from this directory to ensure static/ assets are found
./nclip
# Build from source
git clone https://github.com/johnwmail/nclip.git
cd nclip
go build -o nclip .
# Custom URL and TTL
export NCLIP_URL=https://demo.nclip.app
export NCLIP_TTL=24h
./nclip# Start the service (automatically uses local filesystem in server mode)
./nclip
# Upload content via curl
echo "Hello World!" | curl -sL --data-binary @- http://localhost:8080
# Returns: http://localhost:8080/2F4D6
# Access content
curl -sL http://localhost:8080/2F4D6 # HTML view
curl -sL http://localhost:8080/raw/2F4D6 # Raw content
# Upload with base64 encoding (bypass WAF/security filters)
cat script.sh | base64 | curl -sL --data-binary @- http://localhost:8080/base64
# Server automatically decodes and stores original content
# Slug length: Slugs must be 3β32 characters. If out of range, default is 5.
# Web interface
open http://localhost:8080For comprehensive client usage examples with curl, wget, PowerShell, HTTPie, and advanced features (custom TTL, slugs, base64, burn, etc.), see:
π Documents/CLIENTS.md - Complete client usage guide
For detailed reference on custom HTTP headers (X-Base64, X-Burn, X-TTL, X-Slug, etc.), see:
π Documents/X-HEADERS.md - Header behavior, examples, and middleware/route precedence
| Method | Use Case | Setup Time | Scaling |
|---|---|---|---|
| Docker | Local development, small deployments | 2 minutes | Single instance |
| Kubernetes | Production, high availability | 10 minutes | Auto-scaling |
| AWS Lambda | Serverless, pay-per-use | 15 minutes | Automatic |
# Clone and run with Docker Compose
git clone https://github.com/johnwmail/nclip.git
cd nclip
docker-compose up -dAccess: http://localhost:8080
# Pull and run the official image
docker run -d -p 8080:8080 --name nclip ghcr.io/johnwmail/nclip:latest# Use the provided Kubernetes manifests
kubectl apply -f k8s/π Kubernetes Guide - Complete deployment, scaling, and monitoring instructions
nclip automatically detects AWS Lambda environment and switches to S3 storage for serverless deployment.
- AWS Account with appropriate permissions
- S3 Bucket for paste storage
- IAM Role with S3 permissions
# 1. Create S3 bucket
aws s3api create-bucket --bucket your-nclip-bucket --region us-east-1
# Build for Lambda
GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o bootstrap .
# Create deployment package
zip lambda-function.zip bootstrap
# Create/update Lambda function
aws lambda create-function \
--function-name your-nclip-function \
--runtime provided.al2023 \
--role arn:aws:iam::ACCOUNT:role/nclip-lambda-role \
--handler bootstrap \
--timeout 10 \
--zip-file fileb://lambda-function.zip \
--environment "Variables={NCLIP_S3_BUCKET=your-bucket,GIN_MODE=release}"{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject",
"s3:ListBucket"
],
"Resource": "*"
}
]
}π Lambda Guide - Complete AWS Lambda deployment, monitoring, and troubleshooting
nclip supports configuration via environment variables and CLI flags. Environment variables take precedence over CLI flags.
| Variable | CLI Flag | Default | Description |
|---|---|---|---|
NCLIP_PORT |
--port |
8080 |
HTTP port to listen on |
NCLIP_URL |
--url |
"" |
Base URL for paste links (auto-detected if empty) |
NCLIP_SLUG_LENGTH |
--slug-length |
5 |
Length of generated slugs (3-32 characters) |
NCLIP_BUFFER_SIZE |
--buffer-size |
5242880 |
Maximum upload size in bytes (5MB) |
NCLIP_TTL |
--ttl |
24h |
Default paste expiration time |
NCLIP_S3_BUCKET |
--s3-bucket |
"" |
S3 bucket name for Lambda mode |
NCLIP_S3_PREFIX |
--s3-prefix |
"" |
S3 key prefix for Lambda mode |
NCLIP_UPLOAD_AUTH |
--upload-auth |
false |
Require API key for upload endpoints |
NCLIP_API_KEYS |
--api-keys |
"" |
Comma-separated API keys for upload authentication |
NCLIP_MAX_RENDER_SIZE |
--max-render-size |
262144 |
Maximum size (bytes) to render inline in the HTML view; also used as preview length when content exceeds this size |
Optionally require API keys for upload endpoints to prevent unauthorized usage. This is disabled by default.
Enable Authentication:
export NCLIP_UPLOAD_AUTH=true
export NCLIP_API_KEYS="secret-key-1,secret-key-2,secret-key-3"
./nclipUpload with API Key (curl):
# Using Authorization: Bearer header
echo "protected content" | curl -sL --data-binary @- \
-H "Authorization: Bearer secret-key-1" \
http://localhost:8080
# Using X-Api-Key header
echo "protected content" | curl -sL --data-binary @- \
-H "X-Api-Key: secret-key-1" \
http://localhost:8080Upload with API Key (PowerShell):
# Using Authorization: Bearer header
$headers = @{ "Authorization" = "Bearer secret-key-1" }
"protected content" | Invoke-RestMethod -Uri http://localhost:8080 -Method Post -Headers $headers
# Using X-Api-Key header
$headers = @{ "X-Api-Key" = "secret-key-1" }
"protected content" | Invoke-RestMethod -Uri http://localhost:8080 -Method Post -Headers $headersWeb UI: When API key authentication is enabled, the web UI includes an optional "API Key" input field. Simply paste your API key into this field before uploading content.
Important Notes:
- When
NCLIP_UPLOAD_AUTH=true, all POST requests to/and/burn/require authentication - GET requests (viewing/downloading pastes) do not require authentication
- When deployed behind CDNs (CloudFront/Cloudflare), ensure the distribution forwards
AuthorizationorX-Api-Keyheaders to the origin - Multiple API keys can be configured (comma-separated) for different users or applications
When NCLIP_UPLOAD_AUTH is enabled, nclip enforces API key authentication for all upload endpoints (POST / and POST /burn/). This is intended to protect public-facing instances from abuse.
- Env var:
NCLIP_UPLOAD_AUTH=trueto enable - Keys:
NCLIP_API_KEYSshould contain one or more comma-separated keys (for example:key1,key2).
Best practices and notes:
- Use secrets/parameters manager (Docker secrets, Kubernetes Secrets, or environment management) to avoid committing API keys into repo or images.
- For containers behind CDNs or proxies, make sure the proxy forwards either the
Authorization: Bearer <key>header or theX-Api-Key: <key>header. - Web UI: the UI will include an "API Key" input. Browsers will not send this automatically β paste or inject via browser extension if needed.
- Testing: for CI/integration tests you can set a single test key and pass it as
NCLIP_TEST_API_KEY(used by integration scripts). This variable is optional and only used by the test harness.
Example (bash):
export NCLIP_UPLOAD_AUTH=true
export NCLIP_API_KEYS="secret-key-1,secret-key-2"
./nclip
Using Environment Variables:
export NCLIP_PORT=3000
export NCLIP_URL=https://demo.nclip.app
export NCLIP_TTL=48h
./nclipUsing CLI Flags:
./nclip --port 3000 --url https://demo.nclip.app --ttl 48hCombined (Environment takes precedence):
export NCLIP_PORT=3000
./nclip --url https://demo.nclip.app --ttl 48hGET /β Web UI (upload form, stats)POST /β Upload paste (returns URL, supports all headers)POST /burn/β Create burn-after-read paste (useX-Burnheader)POST /base64β Upload base64-encoded content (useX-Base64header)GET /{slug}β HTML view of pasteGET /raw/{slug}β Raw content download
Supported Headers: X-TTL, X-Slug, X-Base64, X-Burn, X-Api-Key / Authorization
GET /api/v1/meta/{slug}β JSON metadata (no content)GET /json/{slug}β Alias for/api/v1/meta/{slug}(shortcut)
GET /healthβ Health check (200 OK)
Returned by GET /api/v1/meta/{slug} or GET /json/{slug}. Does not include the actual content.
{
"id": "string", // Unique paste ID
"created_at": "2025-09-17T12:34:56Z", // ISO8601 timestamp
"expires_at": "2025-09-18T12:34:56Z", // ISO8601 (null if no expiry)
"size": 12345, // Size in bytes
"content_type": "text/plain", // MIME type
"burn_after_read": true, // true if burn-after-read
"read_count": 0 // Number of times read
}- Go: 1.23 or higher (minimum supported version)
- Docker: For container builds and testing
nclip follows a compatibility-first approach:
- Minimum Go Version: 1.23 (in
go.mod) - Required by AWS SDK v2 - Build/Release Go Version: 1.25 (latest) - Uses newest optimizations and security features
- CI Testing: Tests against Go 1.23, 1.24, and 1.25
This means your code runs on Go 1.23+ systems while benefiting from the latest compiler optimizations in production builds.
# Clone and build
git clone https://github.com/johnwmail/nclip.git
cd nclip
go mod download
go build -o nclip .
# Run with local filesystem
./nclip# Format, vet, and test
go fmt ./... && go vet ./... && go test -v ./...
# Linting
golangci-lint run
# Run integration tests
go run main.go
bash scripts/integration-tests.sh- Health Check:
GET /health- Returns 200 OK with system status - Structured Logging: JSON format with request tracing
- Documentation: Documents/
- GitHub Registry:
docker pull ghcr.io/johnwmail/nclip - GitHub: https://github.com/johnwmail/nclip
- Issues: https://github.com/johnwmail/nclip/issues
β Star this repository if you find it useful!