a3349b4a98edf0aeb0da9148c75374bb9acace4c
README rework: user-first structure, features, screenshots, config table; API detail moved to docs/API.md (#47)
Admin endpoint: ENV/file admin key, --reset-admin-key, settings API wired into ratelimit/max-bytes/default-expiry/burn-window (#40)
Admin endpoint: ENV/file admin key, --reset-admin-key, settings API wired into ratelimit/max-bytes/default-expiry/burn-window (#40)
Admin endpoint: ENV/file admin key, --reset-admin-key, settings API wired into ratelimit/max-bytes/default-expiry/burn-window (#40)
Admin endpoint: ENV/file admin key, --reset-admin-key, settings API wired into ratelimit/max-bytes/default-expiry/burn-window (#40)
Burn after N reads: reads_limit/reads_used, per-viewer 15min dedupe via paste_views, reads_remaining in API+stats pill, raw counts as read (#49)
Cans API: multipart create with mixed items, password-protected cans, item fetch inheriting can auth
pentest: bind unlock cookie to HMAC per-paste token; serve only safe content types on /raw and can items with nosniff (#34)
Admin endpoint: ENV/file admin key, --reset-admin-key, settings API wired into ratelimit/max-bytes/default-expiry/burn-window (#40)
Admin endpoint: ENV/file admin key, --reset-admin-key, settings API wired into ratelimit/max-bytes/default-expiry/burn-window (#40)
Admin endpoint: ENV/file admin key, --reset-admin-key, settings API wired into ratelimit/max-bytes/default-expiry/burn-window (#40)
Mobile responsiveness: media query for 375-640px across topbar, deck, history, paste view, unlock (#45)
pentest: bind unlock cookie to HMAC per-paste token; serve only safe content types on /raw and can items with nosniff (#34)
pentest: bind unlock cookie to HMAC per-paste token; serve only safe content types on /raw and can items with nosniff (#34)
pentest: bind unlock cookie to HMAC per-paste token; serve only safe content types on /raw and can items with nosniff (#34)
Admin endpoint: ENV/file admin key, --reset-admin-key, settings API wired into ratelimit/max-bytes/default-expiry/burn-window (#40)
Admin endpoint: ENV/file admin key, --reset-admin-key, settings API wired into ratelimit/max-bytes/default-expiry/burn-window (#40)
Admin endpoint: ENV/file admin key, --reset-admin-key, settings API wired into ratelimit/max-bytes/default-expiry/burn-window (#40)
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-snippetinstead 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
![]() |
![]() |
![]() |
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.
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


