A high-performance pastebin service for xi.pe, built with Go, AWS DynamoDB, and S3. Creates short, memorable codes using 4-5 character alphanumeric identifiers with 7-day automatic expiration.
- Pastebin Service: Store and share text/code snippets with configurable expiration (default: 7 days)
- Hybrid Storage: Small files (≤10KB) in DynamoDB, large files (>10KB, ≤2MB) in S3 with zstd compression (thresholds configurable)
- Syntax Highlighting: Automatic code syntax highlighting with highlight.js
- High Performance: In-memory LRU cache with TTL support
- REST API: JSON API with optional form-encoded input support
- Static Pages: Built-in support for static content pages
- Owner Authentication: Delete functionality with secure 128-bit tokens
- Automatic Cleanup: All pastes expire after configurable TTL (default: 7 days)
# Clone the repository
git clone https://github.com/drewstreib/xipe-go.git
cd xipe-go
# Set up AWS credentials (needs DynamoDB and S3 access)
export AWS_ACCESS_KEY_ID=your_access_key
export AWS_SECRET_ACCESS_KEY=your_secret_key
export AWS_REGION=us-east-1
# Optional: Configure application settings
export PASTE_TTL=604800 # 7 days (default)
export PASTE_MAX_SIZE=2097152 # 2MB (default)
# Run the service
docker-compose up -d
# Service will be available at http://localhost:8080# Requirements: Go 1.24.3 or later
go mod download
go build -o xipe .
./xipe# Create paste with plain text (default, 7-day expiration, 4-5 character code)
curl -X POST "http://localhost:8080/" \
-d "Hello, world!"
# Response: http://localhost:8080/Ab3d
# Form-encoded alternative (for HTML forms only):
curl -X POST "http://localhost:8080/?input=form" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "data=Hello%20world%21"
# Response: http://localhost:8080/XyZ9xipe uses a hybrid storage approach for optimal performance:
- Small Files (≤configurable size, default: 10KB): Stored directly in DynamoDB for fast access
- Large Files (>cutoff size, ≤configurable max, default: 2MB): Content stored in S3 with zstd compression, metadata in DynamoDB
- All files: Configurable expiration (default: 7 days)
- Code length: 4-5 characters (randomly generated with multiple allocation attempts before failing)
Navigate to http://localhost:8080/[code] to see the paste with:
- Syntax highlighting (toggleable)
- Line numbers (toggleable)
- Copy URL and Copy Text buttons
- Delete button (if you're the owner)
- Creation timestamp and expiration countdown
- Raw text access via
?rawparameter
AWS Configuration:
AWS_ACCESS_KEY_ID- AWS access key (needs DynamoDB and S3 permissions)AWS_SECRET_ACCESS_KEY- AWS secret keyAWS_REGION- AWS region (default: us-east-1)
Application Configuration:
PASTE_TTL- Paste expiration time in seconds (default: 604800 = 7 days)PASTE_DYNAMODB_CUTOFF_SIZE- Size threshold for DynamoDB vs S3 storage in bytes (default: 10240 = 10KB)PASTE_MAX_SIZE- Maximum paste size in bytes (default: 2097152 = 2MB)CACHE_MAX_ITEMS- LRU cache maximum number of items (default: 10000)
Example Configuration:
# Override defaults
export PASTE_TTL=86400 # 1 day instead of 7 days
export PASTE_MAX_SIZE=1048576 # 1MB instead of 2MB
export PASTE_DYNAMODB_CUTOFF_SIZE=5120 # 5KB instead of 10KB
export CACHE_MAX_ITEMS=5000 # 5K items instead of 10KDynamoDB Table: Create xipe_redirects with:
- Primary key:
code(String) - Enable TTL on
ettlattribute - Recommended: Use on-demand billing
S3 Bucket: Create xipe-data with:
- Private access (public access blocked)
- 30-day lifecycle policy for automatic cleanup
- Same region as DynamoDB for optimal performance
- Go 1.24.3 or later
- AWS credentials with DynamoDB and S3 access
- Make (for build commands)
make build # Build binary
make test # Run tests
make run # Run locally
make fmt # Format code
make lint # Run linting
make pre-commit # Run all checks before committingmake install-hooksThis ensures code formatting and tests pass before commits.
# Install ko
go install github.com/google/ko@latest
# Build and push container
export KO_DOCKER_REPO=docker.io/yourusername
ko build .- Owner-based Deletion: Only creators can delete their pastes (128-bit secure tokens)
- Size Limits: 2MB maximum paste size
- IP Tracking: Creator IP stored for abuse prevention
- Input Sanitization: Protection against XSS and injection attacks
- Secure Code Generation: Cryptographically random codes with collision handling
- Same-error Responses: Prevents enumeration attacks on deletion endpoints
┌─────────────┐ ┌─────────────┐ ┌──────────────┐
│ Client │────▶│ Gin HTTP │────▶│ DynamoDB │
└─────────────┘ │ Server │ │ (metadata) │
└─────────────┘ └──────────────┘
│ ▲
▼ │
┌─────────────┐ │
│ LRU Cache │──────────────┘
│ (1hr TTL) │
└─────────────┘
│
▼
┌─────────────┐
│ S3 │
│ (large files│
│ + compress) │
└─────────────┘
- Web Framework: Gin (high-performance HTTP)
- Database: AWS DynamoDB (metadata and small files)
- Object Storage: AWS S3 (large files with zstd compression)
- Cache: HashiCorp golang-lru/v2 (1-hour TTL, respects DynamoDB expiration)
- Compression: klauspost/compress (zstd level 3)
- Syntax Highlighting: highlight.js with line numbers plugin
Create a new paste.
Default: Plain Text Body:
POST /
Content-Type: text/plain
Your text or code hereAlternative (Form-encoded for HTML forms):
POST /?input=form
Content-Type: application/x-www-form-urlencoded
data=Your%20text%20hereResponse (plain text):
http://localhost:8080/Ab3d
Retrieve a paste.
Request:
GET /Ab3dResponse (plain text):
Your stored text or code content
Browser Response: HTML page with syntax highlighting
Delete a paste (requires owner cookie).
Request:
DELETE /Ab3d
Cookie: id=<owner-token>Response (plain text):
Deleted successfully
All errors return plain text for non-browser clients:
Error 404: Short URL not found or has expired
Error 401: unauthorized
Error 403: Data too long
Error 500: Internal server error
Browser clients receive styled HTML error pages.
- Plain text is the default: All API responses use plain text (URLs for success, "Error {code}: {message}" for errors)
- HTML for browsers: Browser clients (detected by User-Agent) receive HTML pages
- No JSON responses: The API uses plain text exclusively for programmatic access
- Form support: The
?input=formparameter exists solely for HTML form compatibility
Response:
200- Successfully deleted401- Unauthorized (no cookie or wrong owner)404- Paste not found
Get service statistics (cache size, etc).
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Run pre-commit checks (
make pre-commit) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Issues: GitHub Issues
- Abuse Contact: abuse@xi.pe
- Built with Gin Web Framework
- Container images built with ko
- Hosted at alt.org