Files
palette/README.md
T

3.3 KiB

Palette

Palette is a fast, self-hosted pastebin. One Go binary, a SQLite database, and a web UI for sharing code and text with links that expire on your terms.

Features

  • Paste cans — bundle notes, text, and files into one shareable page
  • Password lock — protect individual pastes with a password
  • Custom expiry — from 1 minute up to 1 year, or never
  • Burn after N reads — a paste that vanishes after a chosen number of reads
  • Custom URLs — reserve /my-snippet instead of a random slug
  • Syntax highlighting with language auto-detection (go-enry)
  • Rate limiting on create and unlock
  • Saved page — see and manage everything created from your browser
  • API-first — every UI action is also a plain HTTP call
  • Single binary — templates and assets are embedded; no external deps

Get Started

Build from source

Requires Go 1.21+.

go build -o palette .
./palette
# open http://localhost:8080

Docker

docker run -p 8080:8080 -v palette-data:/data git.archfox.org/poslop/palette

The SQLite database lives in the /data volume inside the container.

Screenshots

Editor Paste view
History

Configuration

Setting Default Description
PALETTE_ADDR :8080 Listen address
PALETTE_DB palette.db SQLite database path
PALETTE_MAX_TEXT 5242880 Max paste size in bytes (5 MB)
PALETTE_MAX_ITEM 26214400 Max can item size in bytes (25 MB)
PALETTE_ADMIN_KEY generated Admin key; if unset a 32-char hex key is generated and persisted to <db-dir>/admin-key (0600)

Admin

GET /admin serves the admin page. Enter the admin key there — it is stored in sessionStorage (never a cookie) and sent as the X-Admin-Key header on GET/POST /admin/api/settings.

The admin API reads/sets: rate-limit burst, rate-limit refill per minute, max content bytes, default expiry, custom URL reservation days, and the burn viewer window (minutes). All admin access attempts are logged.

./palette --reset-admin-key   # regenerate the admin key and print it

API

Create a paste with one call:

curl -X POST http://localhost:8080/api/pastes \
  -H "Content-Type: application/json" \
  -d '{"content": "print(hello)", "language": "python", "expires_in": "168h"}'

Full API docs: docs/API.md. Design docs: docs/design/ (currently: client-side E2E encryption, issue #39).

Performance Notes

The history and Saved pages use client-side filtering: when you type in the search box, the UI fetches the most recent 100 pastes (limit=100, the API maximum) once per query and filters/sorts them in the browser. Pastes beyond the newest 100 are not searched; a match count against the full total is still shown. This keeps search instant without a server-side query. If large instances need full search later, it will be a server-side endpoint (see issue #32).

CI

Gitea Actions workflow at .gitea/workflows/ci.yml:

  • On push to main: go vet + go test
  • On tags: build and push Docker image to git.archfox.org/poslop/palette