forked from username/flaskpaste
docs: add pow, cli client, and head method documentation
This commit is contained in:
@@ -28,6 +28,39 @@ The fingerprint must be exactly 40 lowercase hexadecimal characters (SHA1).
|
||||
|
||||
## 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
|
||||
|
||||
Health check endpoint for load balancers and monitoring.
|
||||
@@ -112,6 +145,20 @@ Content-Type: application/json
|
||||
|
||||
**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",
|
||||
"url": "/abc12345",
|
||||
"raw": "/abc12345/raw",
|
||||
@@ -128,6 +175,8 @@ Content-Type: application/json
|
||||
| 400 | Proof-of-work required (when PoW enabled) |
|
||||
| 400 | Proof-of-work failed (invalid/expired challenge) |
|
||||
| 413 | Paste too large |
|
||||
| 429 | Duplicate content rate limit exceeded |
|
||||
|
||||
**Size Limits:**
|
||||
- Anonymous: 3 MiB (configurable via `FLASKPASTE_MAX_ANON`)
|
||||
- Authenticated: 50 MiB (configurable via `FLASKPASTE_MAX_AUTH`)
|
||||
@@ -139,7 +188,9 @@ Content-Type: application/json
|
||||
### HEAD /{id}
|
||||
|
||||
Retrieve paste metadata. HEAD returns headers only (no body).
|
||||
**Request:**
|
||||
|
||||
**Request:**
|
||||
```http
|
||||
GET /abc12345 HTTP/1.1
|
||||
Host: localhost:5000
|
||||
```
|
||||
@@ -168,7 +219,9 @@ Host: localhost:5000
|
||||
### 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).
|
||||
**Request:**
|
||||
|
||||
**Request:**
|
||||
```http
|
||||
GET /abc12345/raw HTTP/1.1
|
||||
Host: localhost:5000
|
||||
```
|
||||
@@ -290,6 +343,58 @@ export FLASKPASTE_DEDUP_MAX=3 # Max duplicates per window (default: 3)
|
||||
## Proof-of-Work
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user