paste

module
v1.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 12, 2026 License: MIT

README ΒΆ

Paste πŸ“

A lightweight, self-hosted pastebin and diff viewer built with Go.

Go Version Docker License

Overview

Paste is a self-hosted web application for saving, sharing, and comparing text. It uses file-based storage β€” every paste is a plain file on disk, and every diff is a JSON document. No database required.

Key Features

Core

  • Dual workspaces β€” Paste mode and Diff mode, switchable from the sidebar or the Cmd+K command palette
  • Syntax highlighting for 12 languages via Prism.js, with line numbers
  • Markdown rendering via Marked.js with highlighted fenced code blocks and per-block copy buttons
  • Side-by-side diff viewer with structured, line-level comparison powered by go-difflib
  • Full-text search across all pastes and diffs, served from an in-memory cache

UX

  • Command palette (Cmd/Ctrl + K) for fast navigation and actions
  • Auto-detected language from content heuristics (shebangs, keywords)
  • Smart sidebar grouping β€” Today, Yesterday, Past Week, Past Month, Beyond
  • Editable pastes, one-click share/download/duplicate, raw text endpoint for CLI usage
  • Dark mode, responsive layout, glassmorphism UI

Ops

  • Single binary, multi-stage Alpine Docker image
  • OpenGraph preview images generated server-side via Chroma and gg
  • Cryptographically random 6-character IDs, 2 MB upload limit, path traversal protection

Quick Start

git clone https://github.com/arvarik/paste.git && cd paste
docker compose up -d

Pulls from ghcr.io/arvarik/paste:latest. The app is available at http://localhost:8083. To build locally, swap the image: line for build: . in compose.yaml.

From Source

Requires Go 1.25+:

git clone https://github.com/arvarik/paste.git && cd paste
go run ./cmd/server/

Server starts on :8083 with data in ./data/.


Configuration

Variable Default Description
PORT 8083 HTTP listen port
DATA_DIR /app/data Filesystem path for stored pastes/diffs
PUID 3000 Container process user ID (Docker)
PGID 3000 Container process group ID (Docker)
PORT=3000 DATA_DIR=/var/pastes go run ./cmd/server/

For Docker, PUID/PGID are set in the .env file.


Usage

Keyboard Shortcuts
Shortcut Action
Ctrl/Cmd + S Save current paste or diff
Ctrl/Cmd + K Open command palette
Ctrl/Cmd + N New paste or diff
Ctrl/Cmd + Shift + F Format code (paste editor)
Escape Close palette / sidebar
Diffs

Switch to the Diff workspace, enter text in the Base and Compare panels, and click Compare. Save the diff to get a shareable URL at /diff/<id>.


API

All endpoints accept and return application/json unless noted.

Pastes
Method Path Description
GET /api/pastes List all pastes (time-bucketed groups)
POST /api/pastes Create a paste
GET /api/pastes/{id} Get paste content and metadata
PUT /api/pastes/{id} Update a paste
DELETE /api/pastes/{id} Delete a paste
GET /api/search?q={query} Full-text search across pastes
GET /raw/{id} Raw content as text/plain
GET /api/pastes/{id}/preview.png Syntax-highlighted OG preview image
Diffs
Method Path Description
POST /api/diff Compute a diff (returns opcodes + lines)
POST /api/saved_diffs Save a diff
GET /api/saved_diffs List saved diffs (time-bucketed groups)
GET /api/saved_diffs/{id} Get a saved diff
DELETE /api/saved_diffs/{id} Delete a saved diff
GET /api/search_diffs?q={query} Full-text search across diffs

Request bodies are limited to 2 MB. All IDs are 6-character alphanumeric strings.

Example: Create and retrieve a paste
# Create
curl -s -X POST http://localhost:8083/api/pastes \
  -H 'Content-Type: application/json' \
  -d '{"title":"Hello","content":"print(\"world\")","language":"python"}'
# β†’ {"id":"xK9mPq","title":"Hello"}

# Retrieve
curl -s http://localhost:8083/api/pastes/xK9mPq
# β†’ {"id":"xK9mPq","title":"Hello","language":"python","content":"print(\"world\")"}

# Raw
curl -s http://localhost:8083/raw/xK9mPq
# β†’ print("world")

Project Structure

paste/
β”œβ”€β”€ cmd/server/             # Entrypoint β€” routing, template rendering, OG tag injection
β”œβ”€β”€ internal/
β”‚   β”œβ”€β”€ api/                # HTTP handlers, middleware, diff computation, preview image gen
β”‚   β”œβ”€β”€ models/             # Shared data types (PasteMeta, CachedPaste, DiffMeta, DiffData)
β”‚   β”œβ”€β”€ storage/            # File I/O, in-memory cache, CRUD for pastes and diffs
β”‚   └── util/               # ID generation, language mapping, sanitization, helpers
β”œβ”€β”€ templates/
β”‚   β”œβ”€β”€ layout/             # head.html, tail.html (shared <head> and script tags)
β”‚   └── components/         # sidebar, paste_app, diff_app, cmdk (command palette)
β”œβ”€β”€ static/js/              # ES module frontend (main, state, dom, ui, paste, diff, utils)
β”œβ”€β”€ Dockerfile              # Multi-stage Alpine build
β”œβ”€β”€ compose.yaml            # Production Docker Compose config
└── dev.sh                  # Local dev script (build + run + open browser)

Pastes are stored as {id}_{title}.{ext} (e.g., aB3xYz_My-Script.py). Diffs are stored as {id}_{title}.json in a diffs/ subdirectory.


Deployment

Volume Mapping

Edit the left side of the volume mount in compose.yaml:

volumes:
  - /your/host/path:/app/data
CI/CD

Push to main β†’ GitHub Actions builds and pushes to ghcr.io/arvarik/paste:latest. If running Watchtower, the container updates automatically.

Manual update:

docker compose pull && docker compose up -d
Backup

All data is plain files. Back up the data directory to preserve everything. To migrate, copy files into the new container's data volume.


Development

./dev.sh          # Build, start on :8083, open browser β€” Ctrl+C to stop
./dev.sh 9090     # Custom port

Run tests:

go test ./...
go vet ./...

Contributing

  1. Fork the repo and branch from main.
  2. Ensure go build ./..., go vet ./..., and go test ./... pass.
  3. Open a PR with a clear description.

License

MIT β€” see LICENSE.

Directories ΒΆ

Path Synopsis
cmd
server command
internal
api

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL