Arrow is absolutely positioned at left:8px in the th, label padding-left 24px; translateY(-50%) keeps it centered regardless of row height, and being out of flow the label never moves between sorted/unsorted states.
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, with password/expiry/custom-slug parity and a
canbadge in listings - 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. 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
Docker Compose
See docker-compose.yml for a ready-to-use example with every environment variable documented, including which are required (only the /data volume) and which are optional.
License
Palette is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0-only). See LICENSE.
The software is provided AS IS, without warranty of any kind, express or implied, including merchantability and fitness for a particular purpose. In no event shall the authors be liable for any claim, damages, or other liability, including without limitation any security vulnerabilities, data loss, or legal issues arising from use of the software. You use it at your own risk.
If you run a modified version of Palette as a network service, the AGPL requires you to offer your modified source code to its users.


