7968d47030
- Added validation for accepted release tag formats in the release workflow. - Implemented a step to generate release notes based on changes since the last tag. - Updated README.md to include accepted tag formats for stable and prerelease versions. - Adjusted archive naming convention for consistency in release artifacts.
216 lines
8.3 KiB
Markdown
216 lines
8.3 KiB
Markdown
# 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.
|
|
|
|
Accepted release tag formats:
|
|
|
|
- `1.2.3` (stable release)
|
|
- `1.2.3-alpha1`, `1.2.3-beta1`, `1.2.3-rc1` (prerelease)
|
|
|
|
Variant guide:
|
|
|
|
| Variant | Recommended for | Notes |
|
|
| --- | --- | --- |
|
|
| `default` | Most Linux distributions (Ubuntu, Debian, Fedora, Arch, etc.) | Smaller, dynamically linked Linux build |
|
|
| `static` | Any Linux distro (including Alpine/musl) | Statically linked for maximum Linux compatibility |
|
|
|
|
To unpack an artifact:
|
|
|
|
- `tar -xzf scratchbox-<tag>-linux-amd64-default.tar.gz`
|
|
|
|
To unpack into a specific directory:
|
|
|
|
- `mkdir -p ./scratchbox-release`
|
|
- `tar -xzf scratchbox-<tag>-linux-amd64-default.tar.gz -C ./scratchbox-release`
|
|
|
|
To run from an unpacked artifact:
|
|
|
|
- `./scratchbox-<os>-<arch>-<variant> 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).
|