scuttled.net/paste

v1.1.1  ·  paste and file host for the 2600net IRC network

what is this

A self-hosted paste and file sharing service. Upload text, images, and video — no account required. Files expire automatically. Part of the scuttled.net network.

curl usage

# paste from stdin
echo "hello world" | curl -F "content=<-" https://scuttled.net/paste/

# paste a file with syntax hint
curl -F "content=@script.py" -F "lang=python" https://scuttled.net/paste/

# upload an image with description
curl -F "file=@screenshot.png" -F "description=my screenshot" https://scuttled.net/paste/

# upload video
curl -F "file=@demo.mp4" https://scuttled.net/paste/

# request a deletion token
curl -F "content=<-" -F "token=1" https://scuttled.net/paste/

# delete with token
curl -X DELETE "https://scuttled.net/paste/p-slug-here?token=YOUR_TOKEN"

# json response (for bots/IRC)
curl -H "Accept: application/json" -F "content=<-" https://scuttled.net/paste/

file types & limits

type formats max size ttl
pasteany text500 KB90–365 days
imagepng jpg webp10 MB180–365 days
videomp4 mpg gif50 MB30–180 days

Daily quota: 200 MB per IP. Smaller files get longer TTLs.

contact

Find us on 2600net IRCirc.2600.net port 6697 (SSL) · #2600

changelog

# Changelog

All notable changes to wpm-paste will be documented in this file.
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
Versioning follows [Semantic Versioning](https://semver.org/).

## [Unreleased]

## [1.1.1] - 2026-03-26

### Added
- Stats page at /stats — live counts by type (paste/image/video), dead drops,
  storage used, expiry breakdown (7d/30d/beyond), next expiry, last upload,
  top 5 languages
- /stats.json — same data as JSON, for IRC bots or monitoring scripts
- Stats link added to footer alongside about
- Refresh and json links on the stats page itself

### Changed
- Version strings normalised across all files — all now use the same regex-
  replaceable format and are updated atomically on each release


## [1.1.0] - 2026-03-26

### Added
- Dead drop: upload with "dead drop" checked and the content is permanently
  deleted after the first view. Works for pastes, images, and video.
  - Browser: checkbox on upload form next to "deletion token"
  - curl: -F "dead_drop=1"
  - Response includes X-Dead-Drop: 1 header (plain text) or dead_drop: true (JSON)
  - View page shows an orange banner confirming the link has been consumed
  - Delete form is suppressed on dead drop views (already gone)
  - Deletion is scheduled via register_shutdown_function() so the file is
    still readable during the response and wiped cleanly after exit()
- Schema migration 3: burn column (INTEGER DEFAULT 0) on uploads table


## [1.0.9] - 2026-03-26

### Fixed
- "Unexpected end of JSON input" on all pastes: migration 2 (ADD COLUMN description)
  threw "duplicate column name" if the column was added manually as instructed in
  1.0.8 release notes, causing get_db() to throw an uncaught exception and return
  an empty HTTP body. ALTER TABLE is now wrapped in try/catch.
- CHANGELOG.md and README.md added to app_files() so they are copied to the
  webroot on install and update (previously had to be copied manually).


## [1.0.8] - 2026-03-26

### Added
- Header logo now two separate links: site name links to main site (site_root),
  tagline links to the paste app (BASE_URL) — configurable in config.php
- 2600net network name in header is now a link to the web IRC client (irc_web_url)
- Description field for image and video uploads (optional, max 200 chars)
  — shown on view page, sets og:description and page title for URL previews
  — curl: -F "description=my caption"
- OpenGraph meta tags (og:type, og:image, og:url, og:description) on image
  and video view pages for URL preview in IRC clients and social platforms
- About page at /about — curl docs, file type table, limits, contact info,
  live changelog rendered from CHANGELOG.md
- About link in footer
- site_root and irc_web_url added to THEME in config.php and installer

### Fixed
- VERSION file now included in app_files() so it is copied to webroot during
  install and update — version number in footer now works correctly

### Changed
- Footer links are now active: site name links to site_root, network name
  links to irc_web_url


## [1.0.7] - 2026-03-17

### Fixed
- Footer prompt in installer had no effect because config.php is never
  overwritten after first install; footer_html config key removed entirely
- Footer is now built automatically from THEME values (site_name, irc_network)
  so it is always correct and requires no manual configuration

### Changed
- Footer format: "site_name — part of the irc_network IRC network · vVERSION"
- Removed footer_html from THEME in config.php and from installer prompts


## [1.0.7] - 2026-03-17

### Fixed
- Footer default was a bare URL string instead of HTML; now renders as a
  proper anchor link with 2600.net network attribution


## [1.0.6] - 2026-03-17

### Changed
- install.php: updated defaults to scuttled.net/paste, updated tagline,
  site name, base URL, and footer HTML defaults
- Footer now displays the app version number dynamically from VERSION file
  (e.g. "v1.0.6"), updates automatically on future installs/updates


## [1.0.5] - 2026-03-17

### Fixed
- Reverted 1.0.4 entirely (CSP, TRUST_PROXY, hljs relocation caused breakage)
- .htaccess: replaced unreliable negative-lookahead RewriteRule with FilesMatch
  blocks — all .php files denied except index.php, more compatible with Apache
- quota_add() is now an atomic SQLite transaction; closes TOCTOU race where two
  concurrent uploads from the same IP could both pass the daily quota check


## [1.0.4] - 2026-03-17

### Security
- IP quota could be bypassed by spoofing `X-Forwarded-For` header; now only
  `REMOTE_ADDR` is used unless `TRUST_PROXY = true` is set in config.php
- Added `TRUST_PROXY` config option (default false) with clear documentation
- Content-Security-Policy header added: `default-src 'self'`, no `unsafe-inline`
- Moved `hljs.highlightAll()` from inline `<script>` into `app.js`, eliminating
  the need for `unsafe-inline` in the CSP
- `.htaccess` now blocks ALL `.php` files except `index.php` (previously only
  blocked four named files; any new PHP file would have been web-accessible)
- `quota_add()` is now atomic (SQLite transaction); fixes TOCTOU race where two
  concurrent uploads from the same IP could both pass the quota check
- Added runtime `error_log` warning if `IP_SALT` is still the installer default


## [1.0.3] - 2026-03-17

### Fixed
- `http_error()` always returned HTML, causing `Unexpected token '<'` JSON parse
  error in the browser when any PHP error occurred during an upload POST.
  Now returns JSON when request is POST or sends `Accept: application/json`.


## [1.0.2] - 2026-03-17

### Fixed
- Router failed on subdirectory installs (`/paste/`): `REQUEST_URI` was not stripped
  of the base path, causing all slug URLs to 404
- `http_error()` used `never` return type, requiring PHP 8.1+; changed to `void`
  for PHP 8.0 compatibility as specced
- Redundant double-require of `words.php` removed

### Changed
- `install.php` source/destination separated: installer now runs from staging dir
  (e.g. `/root/paste/`) and deploys to a separate webroot
- Default webroot updated to `/var/www/wpm.2600.chat/paste`
- Default `BASE_URL` updated to `https://wpm.2600.chat/paste`
- Added `--checklist` flag to re-display post-install checklist at any time


## [1.0.0] - 2026-03-17

### Added
- Initial release
- Paste hosting with syntax highlighting (highlight.js, client-side)
- Image hosting: PNG, JPEG, WebP
- Video/animation hosting: MP4, MPEG, GIF
- Short human-readable slugs with type prefix (p-, i-, v-)
- Adjective-noun slug scheme with collision fallback
- curl-uploadable API, no account required
- JSON and plain-text response modes
- Optional deletion token returned on upload
- Configurable retention tiers by file size (up to 365 days)
- Daily 200 MB per-IP upload quota (IP stored as salted SHA-256 hash)
- QR code generation on result screen (qrcode.js, client-side)
- Ctrl+V anywhere on landing page captures clipboard (text or image)
- Drag-and-drop file upload on landing page
- Raw view for pastes
- Dark terminal theme, 2600net IRC callout
- CLI installer with interactive prompts (install.php)
- Non-destructive updater: preserves config.php and data dir
- Idempotent SQLite schema migrations
- Cron-driven expiry script (expire.php)
- Per-file .bak rollback on failed update