Add explicit server timeout/header limits, require trusted proxy CIDRs when honoring forwarded IP headers, and document single-instance storage expectations. Co-authored-by: Cursor <cursoragent@cursor.com>
Scratchbox
Scratchbox is a minimal self-hosted scratch service written in Go. It is intentionally unauthenticated and optimized for temporary sharing of text or small files with an expiration time.
Each upload creates exactly one scratch. For multi-file sharing, bundle files into an archive (for example .zip or .tar.gz) and upload that single archive file.
Scratch content is stored on disk gzip-compressed transparently to reduce filesystem usage; API/UI reads return original uncompressed content.
Quick Start
- Generate defaults directly:
go run ./cmd/scratchbox generate-config > config.yaml
- Adjust settings for your environment.
- Start the server:
go run ./cmd/scratchbox server --config config.yaml
- Open:
http://127.0.0.1:8080/
Without --config, built-in defaults are used.
If you modify the web UI source (web/ui), rebuild frontend assets with:
make build
Configuration
All configuration is YAML.
server
listen_addr: address for the HTTP server (required).- Default:
:8080
- Default:
read_header_timeout: maximum time for reading request headers.- Must be
> 0 - Default:
5s
- Must be
read_timeout: maximum time for reading the full request.- Must be
> 0 - Default:
30s
- Must be
write_timeout: maximum time allowed for writing the response.- Must be
> 0 - Default:
30s
- Must be
idle_timeout: maximum keep-alive idle time between requests.- Must be
> 0 - Default:
120s
- Must be
max_header_bytes: max HTTP header size in bytes.- Must be
> 0 - Default:
1048576(1 MiB)
- Must be
limits
max_upload_size: max request body size for uploads.- Supported units:
B,KB,MB,GB,KiB,MiB,GiB(case-insensitive) - If no unit is provided, value is interpreted as bytes
- Examples:
5MB,100MiB,1GiB,1048576 - Default:
100MiB
- Supported units:
default_ttl: expiration applied to new scratches.- Must be
> 0and<= 24h - Default:
1h
- Must be
raw_cache_max_size: max total decompressed bytes cached in memory for/api/rawrange requests.- Supported units:
B,KB,MB,GB,KiB,MiB,GiB(case-insensitive) - Must be
> 0 - Default:
200MiB
- Supported units:
raw_cache_max_entries: max number of decompressed/api/rawentries kept in memory.- Must be
> 0 - Used together with
raw_cache_max_sizeas a secondary cap - Default:
32
- Must be
storage
data_dir: directory for scratch files and metadata index.- Default:
./data
- Default:
cleanup_interval: background interval for deleting expired content.- Must be at least
10s - Default:
1m
- Must be at least
security
allowed_ips: optional list of IPs and CIDRs allowed to create scratches.- Applies to write route only:
POST /api/scratch - Default includes
127.0.0.1(localhost-only write access) - Add trusted IPs/CIDRs for LAN/proxy clients (for example
192.168.1.0/24) - Empty list means no allowlist restriction
- Applies to write route only:
trust_proxy_headers: iftrue, client IP extraction honorsX-Forwarded-ForthenX-Real-IP.- Only used when source IP matches
trusted_proxy_ips. - Keep
falseunless behind a trusted reverse proxy that sets these headers.
- Only used when source IP matches
trusted_proxy_ips: list of reverse-proxy IPs/CIDRs permitted to supply forwarded headers.- Required when
trust_proxy_headers: true - Examples:
127.0.0.1,10.0.0.0/8
- Required when
rate_limit.enabled: enable per-IP token-bucket rate limiting on write route.- Default:
true
- Default:
rate_limit.requests_per_minute: steady refill rate per client IP.- Must be
> 0 - Default:
30
- Must be
rate_limit.burst: immediate burst capacity per client IP.- Must be
> 0 - Default:
10
- Must be
Security Posture
This service is intentionally unauthenticated. Anyone with network access can read scratches, and (unless restricted) create scratches.
- No authentication, user accounts, or moderation controls.
- No malware scanning.
- Abuse controls are limited to upload size limits, TTL expiration, optional IP allowlisting, and write-route rate limiting.
- Treat this as a convenience utility, not a hardened internet-facing platform.
LAN Deployment Notes
For home/lab/LAN-only use:
- Bind to a private address, for example
127.0.0.1:8080(single host) or192.168.x.x:8080. - Use
allowed_ipsto restrict write access to trusted subnets. - Keep
max_upload_sizeanddefault_ttlsmall. - Keep
trust_proxy_headersdisabled unless using a trusted reverse proxy.
Public Deployment Notes
If exposed beyond a trusted LAN, place a reverse proxy in front (Nginx, Caddy, Traefik, etc.) and add external controls:
- TLS termination and strict transport settings.
- Additional request filtering/WAF controls.
- Stronger rate limits at the edge.
- Optional network-level restrictions to write route.
- Monitoring and log retention suitable for abuse triage.
Recommended baseline: run behind a reverse proxy, keep low TTL and size limits, and use allowed_ips for write access whenever possible.
Storage Model
Scratchbox storage is designed for a single process instance per storage.data_dir.
- Do not run multiple scratchbox processes against the same data directory.
- Metadata/index writes are process-local and are not coordinated with cross-process locking.