Interactive terminal pastebin & log analysis canvas
Pipe any terminal output to a secure share link — ANSI colors, filters, JSON trees, and line deep-links.
yarn build 2>&1 | papercut
# → https://your-host/paste/V1StGXR8_ZMost pastebins dump plain text. PaperCut turns build logs and terminal streams into a developer workspace: virtualized scrolling for huge files, log-level filters, fuzzy/regex search, expandable JSON, and GitHub-style #L12 links — self-hosted on SQLite with zero analytics.
| CLI | Zero npm deps · stdin upload · clipboard share card |
| Canvas | ANSI · filters · grep · JSON tree · line links |
| Self-host | Docker + SQLite · password pastes · expiry |
| Privacy | No analytics · no IP/body logging in app code |
- Fast CLI — zero npm dependencies; read stdin, upload, print a share card, copy the URL
- ANSI-aware viewer — terminal colors and styles in the browser
- Virtualized log list — comfortable browsing of very large logs
- Log level filters — DEBUG / INFO / WARN / ERROR / FATAL toggles
- JSON inspector — expandable trees for structured log lines
- Search & grep — local keyword or
/regex/flags - Line linking —
#L120/#L120-L125deep links - Private pastes — password-protected links (
--private) - Expiry — auto-delete after
1h,1d,7d, … - Privacy-minded — no analytics; you control the data plane
- SQLite — zero-config persistence for simple deploys
stdin ──► papercut CLI ──POST /api/pastes──► Next.js server
│
SQLite (pastes)
│
browser ◄── /paste/[id] (log canvas)
| Path | Role |
|---|---|
cli/ |
Lightweight Node CLI — npm package papercut-cli (npx papercut-cli) |
server/ |
Next.js (App Router) API + log canvas UI |
Requirements: Node.js ≥ 20 · pnpm 9.x
git clone https://github.com/ArianAr/PaperCut.git
cd PaperCut
pnpm install
cp .env.example server/.env.local
# Set PASTE_AUTH_SECRET to a long random value
pnpm dev# another terminal
echo -e '\033[31merror\033[0m\n[INFO] all good' | pnpm cli --url http://localhost:3000Open the printed URL in your browser.
some-command 2>&1 | papercut
some-command 2>&1 | papercut --private
some-command 2>&1 | papercut --expire 1d
some-command 2>&1 | papercut --url https://papercut.example.com| Flag / env | Description |
|---|---|
-p, --private |
Password-protect (prompt or PAPERCUT_PASSWORD) |
--expire <time> |
30m, 1h, 1d, 7d, … |
--url <base> |
Server origin (or PAPERCUT_URL) |
-V, --version |
Print CLI version |
-h, --help |
Show help |
export PASTE_AUTH_SECRET="$(openssl rand -hex 32)"
export PAPERCUT_PUBLIC_URL="http://localhost:3000"
docker compose up --build -d| App | http://localhost:3000 |
| SQLite | volume papercut-data → /data/papercut.db |
| Health | GET /api/health |
docker build -t papercut .
docker run --rm -p 3000:3000 \
-e PASTE_AUTH_SECRET="$PASTE_AUTH_SECRET" \
-e PAPERCUT_PUBLIC_URL=http://localhost:3000 \
-v papercut-data:/data \
papercutFor custom domain + HTTPS, put nginx/Caddy/Traefik in front — see docs/deploy.
See .env.example.
| Variable | Description | Default |
|---|---|---|
PAPERCUT_PUBLIC_URL |
Base URL for share links | request origin |
DATABASE_PATH |
SQLite file path | ./data/papercut.db |
MAX_PASTE_SIZE |
Max body size (bytes) | 10485760 (10 MiB) |
PASTE_AUTH_SECRET |
HMAC secret for unlock cookies | required in production |
UNLOCK_RATE_LIMIT |
Unlock attempts / window | 10 |
UNLOCK_RATE_WINDOW_MS |
Unlock window (ms) | 600000 |
CREATE_RATE_LIMIT |
Creates / window | 60 |
CREATE_RATE_WINDOW_MS |
Create window (ms) | 600000 |
PAPERCUT_METRICS |
Enable GET /api/metrics (1/true/yes/on) |
off |
TRUSTED_PROXY_HOPS |
X-Forwarded-For hops to trust (0 = ignore XFF; use 1 behind a proxy) |
0 |
COOKIE_SECURE |
Force Secure cookies (1/0); else from public URL |
auto |
REDIS_URL |
Shared rate limits across instances (optional) | off (in-memory) |
| Method | Path | Description |
|---|---|---|
POST |
/api/pastes |
JSON { content, expire?, password? } or text/plain body + X-PaperCut-Expire / X-PaperCut-Password headers (streaming CLI) |
GET |
/api/pastes/:id |
Paste body or 401 + { locked: true } |
POST |
/api/pastes/:id/unlock |
{ password } → httpOnly unlock cookie |
GET |
/api/health |
Liveness/readiness (+ expired purge) |
GET |
/api/metrics |
Opt-in counters (PAPERCUT_METRICS); 404 when disabled |
UI: /paste/:id · deep links #L12, #L12-L20.
- No analytics SDKs in application code
- Paste bodies and client IPs are not intentionally logged
- Rate limiting may use
X-Forwarded-Forin memory only - Optional metrics (
PAPERCUT_METRICS) expose counters only (creates, successful unlocks, 429s) — never content or IPs - Public pastes are capability URLs — use
--privatefor sensitive data
| Doc | |
|---|---|
| Changelog | Release history |
| Roadmap | Planned features after 1.0 |
| Deploy guide | Reverse proxy, HTTPS, SQLite backup |
| Contributing | Dev setup, CI, labels, release checklist |
| Security | Supported versions & vulnerability reporting |
| Releases | Git tags (vX.Y.Z) |
Current version: 1.2.0 · CLI on npm · 1.x API compatibility · roadmap: ROADMAP.md
Most of this project was developed with AI assistance. Changes are peer reviewed, checked, and tested by humans before they land on main (CI, code review, and maintainer oversight). AI does not replace human accountability for what ships.