docs: add pow, cli client, and head method documentation

This commit is contained in:
Username
2025-12-20 04:09:08 +01:00
parent efd48c5563
commit ccfd8509cc
2 changed files with 155 additions and 2 deletions

View File

@@ -11,7 +11,9 @@ A lightweight, secure pastebin REST API built with Flask.
- **Automatic expiry** - Pastes expire after configurable period of inactivity - **Automatic expiry** - Pastes expire after configurable period of inactivity
- **Size limits** - Configurable limits for anonymous and authenticated users - **Size limits** - Configurable limits for anonymous and authenticated users
- **Abuse prevention** - Content-hash deduplication throttles repeated identical submissions - **Abuse prevention** - Content-hash deduplication throttles repeated identical submissions
- **Proof-of-work** - Configurable computational puzzle prevents automated spam
- **Security headers** - HSTS, CSP, X-Frame-Options, Cache-Control, and more - **Security headers** - HSTS, CSP, X-Frame-Options, Cache-Control, and more
- **CLI client** - Standalone `fpaste` command-line tool included
- **Request tracing** - X-Request-ID support for log correlation - **Request tracing** - X-Request-ID support for log correlation
- **Proxy trust validation** - Optional shared secret for defense-in-depth - **Proxy trust validation** - Optional shared secret for defense-in-depth
- **Minimal dependencies** - Flask only, SQLite built-in - **Minimal dependencies** - Flask only, SQLite built-in
@@ -36,9 +38,12 @@ python run.py
|--------|----------|-------------| |--------|----------|-------------|
| `GET /` | API information and usage | | `GET /` | API information and usage |
| `GET /health` | Health check (returns DB status) | | `GET /health` | Health check (returns DB status) |
| `GET /challenge` | Get proof-of-work challenge |
| `POST /` | Create a new paste | | `POST /` | Create a new paste |
| `GET /<id>` | Retrieve paste metadata | | `GET /<id>` | Retrieve paste metadata |
| `HEAD /<id>` | Retrieve paste metadata (headers only) |
| `GET /<id>/raw` | Retrieve raw paste content | | `GET /<id>/raw` | Retrieve raw paste content |
| `HEAD /<id>/raw` | Retrieve paste headers (no body) |
| `DELETE /<id>` | Delete paste (requires auth) | | `DELETE /<id>` | Delete paste (requires auth) |
## Usage Examples ## Usage Examples
@@ -77,6 +82,46 @@ curl -X DELETE \
http://localhost:5000/abc12345 http://localhost:5000/abc12345
``` ```
## CLI Client
A standalone command-line client `fpaste` is included (no external dependencies).
### Basic Usage
```bash
# Create paste from file
./fpaste create file.txt
# Create paste from stdin
echo "Hello" | ./fpaste
# Get paste content
./fpaste get abc12345
# Get paste metadata
./fpaste get -m abc12345
# Delete paste (requires auth)
./fpaste delete abc12345
# Show server info
./fpaste info
```
### Configuration
Set server URL and authentication via environment or config file:
```bash
# Environment variables
export FLASKPASTE_SERVER="https://paste.example.com"
export FLASKPASTE_CERT_SHA1="your-cert-fingerprint"
# Or config file (~/.config/fpaste/config)
server = https://paste.example.com
cert_sha1 = your-cert-fingerprint
```
## Configuration ## Configuration
Configuration via environment variables: Configuration via environment variables:
@@ -92,6 +137,9 @@ Configuration via environment variables:
| `FLASKPASTE_DEDUP_WINDOW` | `3600` (1 hour) | Dedup throttle window in seconds | | `FLASKPASTE_DEDUP_WINDOW` | `3600` (1 hour) | Dedup throttle window in seconds |
| `FLASKPASTE_DEDUP_MAX` | `3` | Max identical submissions per window | | `FLASKPASTE_DEDUP_MAX` | `3` | Max identical submissions per window |
| `FLASKPASTE_PROXY_SECRET` | (empty) | Shared secret for proxy trust validation | | `FLASKPASTE_PROXY_SECRET` | (empty) | Shared secret for proxy trust validation |
| `FLASKPASTE_POW_DIFFICULTY` | `20` | PoW difficulty (leading zero bits, 0=disabled) |
| `FLASKPASTE_POW_TTL` | `300` (5 min) | PoW challenge validity period |
| `FLASKPASTE_POW_SECRET` | (auto) | Secret for signing PoW challenges |
## Authentication ## Authentication

View File

@@ -28,6 +28,39 @@ The fingerprint must be exactly 40 lowercase hexadecimal characters (SHA1).
## Endpoints ## Endpoints
### GET /challenge
Get a proof-of-work challenge for paste creation. Required when PoW is enabled.
**Request:**
```http
GET /challenge HTTP/1.1
Host: localhost:5000
```
**Response (PoW disabled):**
```json
{
"enabled": false,
"difficulty": 0
}
```
**Response (PoW enabled):**
```json
{
"enabled": true,
"nonce": "a1b2c3d4e5f6a7b8a1b2c3d4e5f6a7b8",
"difficulty": 20,
"expires": 1700000300,
"token": "a1b2c3d4...:1700000300:20:signature"
}
```
The client must find a number `N` such that `SHA256(nonce + ":" + N)` has at least `difficulty` leading zero bits.
---
### GET /health ### GET /health
Health check endpoint for load balancers and monitoring. Health check endpoint for load balancers and monitoring.
@@ -112,6 +145,20 @@ Content-Type: application/json
**Request (with Proof-of-Work):** **Request (with Proof-of-Work):**
When PoW is enabled, include the challenge token and solution:
```http
POST / HTTP/1.1
Host: localhost:5000
Content-Type: text/plain
X-PoW-Token: a1b2c3d4...:1700000300:20:signature
X-PoW-Solution: 12345678
```
**Response (201 Created):**
```json
{
"id": "abc12345", "id": "abc12345",
"url": "/abc12345", "url": "/abc12345",
"raw": "/abc12345/raw", "raw": "/abc12345/raw",
@@ -128,6 +175,8 @@ Content-Type: application/json
| 400 | Proof-of-work required (when PoW enabled) | | 400 | Proof-of-work required (when PoW enabled) |
| 400 | Proof-of-work failed (invalid/expired challenge) | | 400 | Proof-of-work failed (invalid/expired challenge) |
| 413 | Paste too large | | 413 | Paste too large |
| 429 | Duplicate content rate limit exceeded |
**Size Limits:** **Size Limits:**
- Anonymous: 3 MiB (configurable via `FLASKPASTE_MAX_ANON`) - Anonymous: 3 MiB (configurable via `FLASKPASTE_MAX_ANON`)
- Authenticated: 50 MiB (configurable via `FLASKPASTE_MAX_AUTH`) - Authenticated: 50 MiB (configurable via `FLASKPASTE_MAX_AUTH`)
@@ -139,7 +188,9 @@ Content-Type: application/json
### HEAD /{id} ### HEAD /{id}
Retrieve paste metadata. HEAD returns headers only (no body). Retrieve paste metadata. HEAD returns headers only (no body).
**Request:**
**Request:**
```http
GET /abc12345 HTTP/1.1 GET /abc12345 HTTP/1.1
Host: localhost:5000 Host: localhost:5000
``` ```
@@ -168,7 +219,9 @@ Host: localhost:5000
### HEAD /{id}/raw ### HEAD /{id}/raw
Retrieve raw paste content with correct MIME type. HEAD returns headers only (useful for checking MIME type and size without downloading content). Retrieve raw paste content with correct MIME type. HEAD returns headers only (useful for checking MIME type and size without downloading content).
**Request:**
**Request:**
```http
GET /abc12345/raw HTTP/1.1 GET /abc12345/raw HTTP/1.1
Host: localhost:5000 Host: localhost:5000
``` ```
@@ -290,6 +343,58 @@ export FLASKPASTE_DEDUP_MAX=3 # Max duplicates per window (default: 3)
## Proof-of-Work ## Proof-of-Work
FlaskPaste includes an optional proof-of-work system to prevent automated spam. FlaskPaste includes an optional proof-of-work system to prevent automated spam.
**How it works:**
1. Client requests a challenge via `GET /challenge`
2. Server returns a nonce, difficulty, expiry time, and signed token
3. Client computes SHA256 hashes until finding one with enough leading zero bits
4. Client submits paste with `X-PoW-Token` and `X-PoW-Solution` headers
**Algorithm:**
```
For N = 0, 1, 2, ...:
hash = SHA256(nonce + ":" + N)
if leading_zero_bits(hash) >= difficulty:
return N as solution
```
**Configuration:**
```bash
export FLASKPASTE_POW_DIFFICULTY=20 # Leading zero bits required (0=disabled)
export FLASKPASTE_POW_TTL=300 # Challenge validity in seconds
export FLASKPASTE_POW_SECRET="..." # Optional signing key (auto-generated if empty)
```
**Headers:**
| Header | Description |
|--------|-------------|
| `X-PoW-Token` | The full token from `/challenge` response |
| `X-PoW-Solution` | The computed solution number |
**Error responses:**
```json
{
"error": "Proof-of-work required",
"hint": "GET /challenge for a new challenge"
}
```
```json
{
"error": "Proof-of-work failed: Challenge expired"
}
```
**Notes:**
- Difficulty 20 requires approximately 1 million hash attempts on average
- Challenges are signed to prevent tampering
- Each challenge can only be used once (nonce uniqueness)
---
## Error Response Format
All errors return JSON:
```json ```json
{ {