# FlaskPaste API Reference ## Overview FlaskPaste provides a RESTful API for creating, retrieving, and deleting text and binary pastes. **Base URL:** `http://your-server:5000/` **Content Types:** - Requests: `application/json`, `text/plain`, `application/octet-stream`, or any binary type - Responses: `application/json` for metadata, original MIME type for raw content ## Authentication Authentication is optional and uses client certificate fingerprints passed via the `X-SSL-Client-SHA1` header. ```http X-SSL-Client-SHA1: a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2 ``` The fingerprint must be exactly 40 lowercase hexadecimal characters (SHA1). **Benefits of authentication:** - Larger upload limit (50 MiB vs 3 MiB) - Ability to delete owned pastes --- ## Endpoints ### GET /health Health check endpoint for load balancers and monitoring. **Request:** ```http GET /health HTTP/1.1 Host: localhost:5000 ``` **Response (200 OK):** ```json { "status": "healthy", "database": "ok" } ``` **Response (503 Service Unavailable):** ```json { "status": "unhealthy", "database": "error" } ``` --- ### GET / Returns API information and usage instructions. **Request:** ```http GET / HTTP/1.1 Host: localhost:5000 ``` **Response:** ```json { "name": "FlaskPaste", "version": "1.0.0", "endpoints": { "GET /": "API information", "GET /health": "Health check", "POST /": "Create paste", "GET /": "Retrieve paste metadata", "GET //raw": "Retrieve raw paste content", "DELETE /": "Delete paste" }, "usage": { "raw": "curl --data-binary @file.txt http://host/", "pipe": "cat file.txt | curl --data-binary @- http://host/", "json": "curl -H 'Content-Type: application/json' -d '{\"content\":\"...\"}' http://host/" }, "note": "Use --data-binary (not -d) to preserve newlines" } ``` --- ### POST / Create a new paste. **Request (Raw Binary):** ```http POST / HTTP/1.1 Host: localhost:5000 Content-Type: application/octet-stream ``` **Request (JSON):** ```http POST / HTTP/1.1 Host: localhost:5000 Content-Type: application/json {"content": "Hello, World!"} ``` **Response (201 Created):** ```json { "id": "abc12345", "url": "/abc12345", "raw": "/abc12345/raw", "mime_type": "text/plain", "created_at": 1700000000, "owner": "a1b2c3..." // Only present if authenticated } ``` **Errors:** | Code | Description | |------|-------------| | 400 | No content provided | | 413 | Paste too large | **Size Limits:** - Anonymous: 3 MiB (configurable via `FLASKPASTE_MAX_ANON`) - Authenticated: 50 MiB (configurable via `FLASKPASTE_MAX_AUTH`) --- ### GET /{id} Retrieve paste metadata. **Request:** ```http GET /abc12345 HTTP/1.1 Host: localhost:5000 ``` **Response (200 OK):** ```json { "id": "abc12345", "mime_type": "text/plain", "size": 1234, "created_at": 1700000000, "raw": "/abc12345/raw" } ``` **Errors:** | Code | Description | |------|-------------| | 400 | Invalid paste ID format | | 404 | Paste not found | --- ### GET /{id}/raw Retrieve raw paste content with correct MIME type. **Request:** ```http GET /abc12345/raw HTTP/1.1 Host: localhost:5000 ``` **Response (200 OK):** ```http HTTP/1.1 200 OK Content-Type: image/png Content-Disposition: inline ``` - `Content-Disposition: inline` is set for `image/*` and `text/*` types - Content-Type matches the detected/stored MIME type **Errors:** | Code | Description | |------|-------------| | 400 | Invalid paste ID format | | 404 | Paste not found | --- ### DELETE /{id} Delete a paste. Requires authentication and ownership. **Request:** ```http DELETE /abc12345 HTTP/1.1 Host: localhost:5000 X-SSL-Client-SHA1: a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2 ``` **Response (200 OK):** ```json { "message": "Paste deleted" } ``` **Errors:** | Code | Description | |------|-------------| | 400 | Invalid paste ID format | | 401 | Authentication required | | 403 | Permission denied (not owner) | | 404 | Paste not found | --- ## MIME Type Detection FlaskPaste automatically detects MIME types using: 1. **Magic byte signatures** (highest priority) - PNG: `\x89PNG\r\n\x1a\n` - JPEG: `\xff\xd8\xff` - GIF: `GIF87a` or `GIF89a` - WebP: `RIFF....WEBP` - ZIP: `PK\x03\x04` - PDF: `%PDF` - GZIP: `\x1f\x8b` 2. **Explicit Content-Type header** (if not generic) 3. **UTF-8 detection** (falls back to `text/plain`) 4. **Binary fallback** (`application/octet-stream`) --- ## Paste Expiry Pastes expire based on last access time (default: 5 days). - Every `GET /{id}` or `GET /{id}/raw` updates the last access timestamp - Cleanup runs automatically (hourly, throttled) - Configurable via `FLASKPASTE_EXPIRY` environment variable --- ## Error Response Format All errors return JSON: ```json { "error": "Description of the error" } ``` For size limit errors (413): ```json { "error": "Paste too large", "size": 5000000, "max_size": 3145728, "authenticated": false } ``` --- ## Security Headers All responses include the following security headers: | Header | Value | |--------|-------| | `X-Content-Type-Options` | `nosniff` | | `X-Frame-Options` | `DENY` | | `X-XSS-Protection` | `1; mode=block` | | `Referrer-Policy` | `strict-origin-when-cross-origin` | | `Content-Security-Policy` | `default-src 'none'; frame-ancestors 'none'` | | `Permissions-Policy` | `geolocation=(), microphone=(), camera=()` | | `Strict-Transport-Security` | `max-age=31536000; includeSubDomains` | | `Cache-Control` | `no-store, no-cache, must-revalidate, private` | | `Pragma` | `no-cache` | --- ## Request Tracing All requests include an `X-Request-ID` header for log correlation: - If the client provides `X-Request-ID`, it is passed through - If not provided, a UUID is generated - The ID is echoed back in the response - All log entries include `[rid=]` **Example:** ```bash # Client-provided ID curl -H "X-Request-ID: my-trace-123" https://paste.example.com/health # Response includes: # X-Request-ID: my-trace-123 ``` --- ## Proxy Trust Validation When `FLASKPASTE_PROXY_SECRET` is configured, the application validates that requests come from a trusted reverse proxy by checking the `X-Proxy-Secret` header. This provides defense-in-depth against header spoofing if an attacker bypasses the reverse proxy. **Configuration:** ```bash export FLASKPASTE_PROXY_SECRET="your-secret-value" ``` **Proxy Configuration (HAProxy):** ``` http-request set-header X-Proxy-Secret your-secret-value ``` If the secret doesn't match, authentication headers (`X-SSL-Client-SHA1`) are ignored and the request is treated as anonymous.