# 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. For multi-file sharing, bundle files into an archive (for example `.zip` or `.tar.gz`) before upload. Scratch content is gzip-compressed and AES-encrypted on disk transparently; API/UI reads return original uncompressed content. ## Quick Start 1. Generate defaults directly: - `go run ./cmd/scratchbox generate-config > config.yaml` 2. Generate an encryption key file next to the config: - `go run ./cmd/scratchbox generate-key > config.key` - or: `openssl rand -base64 32 > config.key` 3. Adjust settings for your environment. 4. Start the server: - `go run ./cmd/scratchbox server --config config.yaml` 5. 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` ## Docker Build the container image: - `make docker` - Default image tag: `scratchbox:dev` Optionally set a custom image name/tag: - `make docker DOCKER_IMAGE=scratchbox:latest` Run with persistent data and config directories: - `mkdir -p ./docker-data ./docker-config` - `docker run --rm -p 8080:8080 -v "$(pwd)/docker-data:/data" -v "$(pwd)/docker-config:/config" scratchbox:dev` Inside the container, the default command is: - `scratchbox server --config /config/config.yaml` Create config files with the image (writes to mounted `./docker-config`): - `docker run --rm -v "$(pwd)/docker-config:/config" scratchbox:dev generate-config > ./docker-config/config.yaml` - `docker run --rm -v "$(pwd)/docker-config:/config" scratchbox:dev generate-key > ./docker-config/config.key` ## Release Artifacts Tagged releases provide `.tar.gz` archives with prebuilt Linux binaries. Variant guide: | Variant | Recommended for | Notes | | --- | --- | --- | | `default` | Most Linux distributions (Ubuntu, Debian, Fedora, Arch, etc.) | Smaller, dynamically linked Linux build | | `alpine-static` | Alpine Linux / musl-based environments | Statically linked for better compatibility | To unpack an artifact: - `tar -xzf scratchbox--linux-amd64-default.tar.gz` To unpack into a specific directory: - `mkdir -p ./scratchbox-release` - `tar -xzf scratchbox--linux-amd64-default.tar.gz -C ./scratchbox-release` To run from an unpacked artifact: - `./scratchbox--- server --config config.yaml` If you do not have a config file yet, generate one first: - `go run ./cmd/scratchbox generate-config > config.yaml` - `go run ./cmd/scratchbox generate-key > config.key` ## Configuration All configuration is YAML. ### `server` - `listen_addr`: address for the HTTP server (required). - Default: `:8080` - `read_header_timeout`: maximum time for reading request headers. - Must be `> 0` - Default: `5s` - `read_timeout`: maximum time for reading the full request. - Must be `> 0` - Default: `30s` - `write_timeout`: maximum time allowed for writing the response. - Must be `> 0` - Default: `30s` - `idle_timeout`: maximum keep-alive idle time between requests. - Must be `> 0` - Default: `120s` - `max_header_bytes`: max HTTP header size in bytes. - Must be `> 0` - Default: `1048576` (1 MiB) - `access_log`: structured request logs. - `enabled`: enable HTTP access logs - Default: `true` - `file_path`: optional log file path - When set, logs are tee'd to both stdout and the file - `max_size`: rotation threshold for `file_path` - Supported units: `B`, `KB`, `MB`, `GB`, `KiB`, `MiB`, `GiB` - Default: `100MiB` - `max_backups`: number of rotated files to retain - Default: `5` ### `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` - `default_ttl`: expiration applied to new scratches. - Must be `> 0` and `<= 24h` - Default: `15m` - `raw_cache_max_size`: max total decompressed bytes cached in memory for `/api/raw` range requests. - Supported units: `B`, `KB`, `MB`, `GB`, `KiB`, `MiB`, `GiB` (case-insensitive) - Must be `> 0` - Default: `200MiB` - `raw_cache_max_entries`: max number of decompressed `/api/raw` entries kept in memory. - Must be `> 0` - Used together with `raw_cache_max_size` as a secondary cap - Default: `32` ### `storage` - `data_dir`: directory for scratch files and metadata index. - Default: `./data` - `cleanup_interval`: background interval for deleting expired content. - Must be at least `10s` - Default: `1m` - `encryption_key_file`: path to a file containing a base64-encoded 32-byte key used for at-rest encryption. - Relative paths are resolved from the config file directory - Default: `config.key` (expected alongside `config.yaml`) - Keep this key stable across restarts - Changing the key makes existing scratch files unreadable - `generate-key` emits a fresh random key each run ### `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 - `trust_proxy_headers`: if `true`, client IP extraction honors `X-Forwarded-For` then `X-Real-IP`. - Only used when source IP matches `trusted_proxy_ips`. - Keep `false` unless behind a trusted reverse proxy that sets these headers. - `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` - `rate_limit_ui`: per-IP token-bucket for UI routes (`GET /`, `GET /u`, `GET /s/{id}`). - `enabled` default: `false` - `requests_per_minute` must be `> 0` (default `360`) - `burst` must be `> 0` (default `120`) - `rate_limit_api_read`: per-IP token-bucket for API read routes (`GET /api/config`, `GET /api/scratch/{id}`, `GET /api/raw/{id}`). - `enabled` default: `false` - `requests_per_minute` must be `> 0` (default `300`) - `burst` must be `> 0` (default `100`) - `rate_limit_api_write`: per-IP token-bucket for API write route (`POST /api/scratch`). - `enabled` default: `true` - `requests_per_minute` must be `> 0` (default `30`) - `burst` must be `> 0` (default `10`) ## 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) or `192.168.x.x:8080`. - Use `allowed_ips` to restrict write access to trusted subnets. - Keep `max_upload_size` and `default_ttl` small. - Keep `trust_proxy_headers` disabled 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. - Scratch payload files and metadata index are encrypted at rest. - Scratch IDs are derived from the SHA-256 of the stored blob (encrypted+gzip payload on disk).