A Crystal-based pastebin application with live syntax highlighting, SSH access, user accounts, and extensive theme support.
- π Fast & Lightweight: Built with Crystal for excellent performance
- π¨ Live Preview: Real-time syntax highlighting as you type
- π Extensive Theming: 321+ syntax highlighting themes from Tartrazine
- π Smart Language Detection: Auto-detects language with Hansa classification
- π± Responsive Design: Works beautifully on desktop and mobile
- π Built-in Security: Rate limiting and size validation
- π SSH Paste Creation: Create pastes directly via SSH (
cat file | ssh pasto.example.com) - π€ SSH Key Authentication: Your SSH key becomes your identity
- π Automatic Account Linking: SSH keys automatically linked to user accounts
- π User Profiles: View and manage all your pastes in one place
- βοΈ Editable Display Names: Personalize your profile
- π¨ Theme Preferences: UI and syntax theme preferences saved per user
- π Session Management: Secure web sessions with cookie-based auth
- π Paste Titles: Add optional titles or auto-generate from content
- π Version History: Full edit history with Sepia versioning
- π View Past Versions: Browse and compare previous versions
- βοΈ Edit Your Pastes: Modify pastes you own
- ποΈ Delete Pastes: Remove pastes from your profile
- π― Clean Interface: Modern design with Pico CSS and Lucide icons
- π Collapsible Sidebar: Theme controls in a space-saving sidebar
- β¨οΈ CodeJar Editor: Lightweight code editor with syntax highlighting
- π Live Preview Pane: See rendered output as you type
- π€ Model Context Protocol: Native MCP support for AI assistant integration
- π§ Zero Installation: AI assistants can access pastes without local setup
- π‘οΈ Secure Access: Uses existing API keys and authentication
- π Full CRUD: Create, retrieve, list, update, and delete pastes via MCP
- π― Tool Discovery: AI assistants automatically discover available paste operations
The easiest way to run Pasto is with Docker Compose:
# Clone the repository
git clone https://github.com/ralsina/pasto.git
cd pasto
# Start Pasto (web + SSH server)
docker compose up -d
# View logs
docker compose logs -f
# Stop Pasto
docker compose downThe services will be available at:
- Web interface: http://localhost:3000
- SSH access:
ssh -p 2222 localhost - CLI tool:
pasto-cli --server=localhost login(see CLI.md)
The docker-compose.yml file includes:
- pasto service: Web interface on port 3000
- pasto-ssh service: SSH server on port 2222
- Persistent volumes for data, sessions, cache, and SSH keys
To customize, edit the environment variables in docker-compose.yml:
environment:
PASTO_PORT: 3000
PASTO_MAX_PASTE_SIZE: 1048576 # 1MB
PASTO_THEME: monokai
PASTO_RATE_LIMIT: 10
PASTO_SESSION_SECRET: "your-secret-here" # Change in production!| Volume | Path | Description |
|---|---|---|
pasto-data |
/app/data |
Pastes, users, SSH keys (back this up!) |
pasto-sessions |
/app/sessions |
Web sessions |
pasto-cache |
/app/public/cache |
Rendered HTML cache |
pasto-ssh-keys |
/app/ssh-keys |
SSH host keys |
# Backup all data
docker compose exec pasto tar -czf - /app/data > pasto-backup.tar.gz
# Or copy volumes
docker run --rm -v pasto-data:/data -v $(pwd):/backup alpine \
tar -czf /backup/pasto-data-backup.tar.gz /data# Clone the repository
git clone https://github.com/ralsina/pasto.git
cd pasto
# Install dependencies
shards install
# Build the application
shards build --release
# Run the server
./bin/pastoCreate a paste via SSH:
# Simplest form - just pipe content
cat myfile.py | ssh -p 2222 pasto.example.com
# Or use echo
echo "Hello, World!" | ssh -p 2222 pasto.example.com
# Redirect from file
ssh -p 2222 pasto.example.com < myfile.py
# Explicit paste command (also works)
cat myfile.py | ssh -p 2222 pasto.example.com pasteThe server returns the URL of your new paste.
Other SSH commands:
# Login to link SSH key with web account
ssh -p 2222 pasto.example.com login
# Show help
ssh -p 2222 pasto.example.com helpThe pasto-cli tool provides a convenient command-line interface for Pasto:
# Login to a Pasto server (SSH authentication)
pasto-cli --server=pasto.example.com --ssh-port=2222 login
# Create a paste
echo 'print("Hello, World!")' | pasto-cli paste --language=python
# List your pastes
pasto-cli list
# Get a paste
pasto-cli get <paste-id>
# Delete a paste
pasto-cli delete <paste-id>The CLI supports:
- SSH-based authentication using your SSH keys
- Automatic host derivation - specify
--serveror--ssh-host, the other is auto-detected - Terminal hyperlinks - clickable paste IDs in supported terminals
- Human-readable timestamps - relative time display
- Full CRUD operations - create, read, update, delete pastes
- Credential storage - saves API keys securely in
~/.config/pasto/credentials.yml
For detailed CLI documentation, see CLI.md.
Pasto supports zero-knowledge encryption where you encrypt content locally before sending it to the server. The server never sees your password or can decrypt your content.
The pasto-crypto tool enables you to encrypt content locally using AES-256-GCM encryption with PBKDF2 key derivation.
Encrypt content locally:
# Encrypt with a random password (recommended for maximum security)
./bin/pasto-crypto encrypt --random-pass --output encrypted.txt input.txt
# Output: Environment variables needed for decryption
# PASTO_PASSWORD=wxyeniX58PDup7O!VR2UGyQejOib%pk!
# PASTO_SALT=SEp4IYOd36ICRA4szDdy2w==
# PASTO_IV=wCKCNS9IW3r/48Md
# PASTO_ITERATIONS=100000Create encrypted paste via SSH:
# Save the environment variables from encryption
export PASTO_PASSWORD="wxyeniX58PDup7O!VR2UGyQejOib%pk!"
export PASTO_SALT="SEp4IYOd36ICRA4szDdy2w=="
export PASTO_IV="wCKCNS9IW3r/48Md"
# Create paste with zero-knowledge encryption
cat encrypted.txt | ssh -p 2222 pasto.example.com paste \
--iv "$PASTO_IV" \
--salt "$PASTO_SALT" \
--iterations 100000Decrypt in the browser:
- Open the paste URL in your browser
- Enter the password (not the key!) when prompted
- The content is decrypted client-side using Web Crypto API
What makes it zero-knowledge?
- Password stays with you: The server only stores the salt and IV, never the password
- PBKDF2 key derivation: Your password is securely stretched with 100,000 iterations
- AES-256-GCM encryption: Military-grade encryption
- Client-side decryption: Content is decrypted in your browser, not on the server
- Server can't read: Even if the server is compromised, your encrypted pastes remain secure
Full example workflow:
# 1. Create a file with sensitive content
cat > secret.txt <<EOF
API_KEY=sk_live_1234567890abcdef
DATABASE_URL=postgres://user:pass@localhost/db
EOF
# 2. Encrypt it locally with a random password
./bin/pasto-crypto encrypt --random-pass --output secret.enc secret.txt
# 3. Save the credentials (keep these safe!)
export PASTO_PASSWORD="iJOK#rPpD!GfRwBA5kQZT@0RA5YV7GCG"
export PASTO_SALT="utJ7JzIALDE9Ak1WzCGOnQ=="
export PASTO_IV="AwnXIR7s2313cdBr"
# 4. Create the paste via SSH
cat secret.enc | ssh -p 2222 pasto.example.com paste \
--iv "$PASTO_IV" \
--salt "$PASTO_SALT" \
--title "Production Credentials"
# Output: http://pasto.example.com/abc123-def456
# π Zero-trust encrypted paste created
# 5. Share the URL + PASSWORD with your teammate
# Teammate opens URL, enters password, and sees decrypted content
# The server administrators cannot see the content even if they wanted to!Decrypting locally (optional):
# Decrypt the encrypted file back to plaintext
./bin/pasto-crypto decrypt \
--password "$PASTO_PASSWORD" \
--salt "$PASTO_SALT" \
--iv "$PASTO_IV" \
--output decrypted.txt \
encrypted.txtSecurity best practices:
- Use
--random-passfor generating strong passwords - Save the password securely (password manager, not in plain text files)
- Share the password through a different channel than the paste URL
- The server stores
encryption_ivandencryption_saltbut cannot decrypt without your password
- Navigate to
http://localhost:3000 - Type or paste your code in the editor
- Watch the live preview update
- Click + to create the paste
Pasto supports three configuration methods (in order of priority):
- Command line arguments
- Environment variables (prefixed with
PASTO_) - Configuration file (
pasto.yml)
./bin/pasto \
--port 3000 \
--bind 0.0.0.0 \
--max-paste-size 102400 \
--theme default-dark \
--ssh-enabled true \
--ssh-port 2222 \
--storage-dir ./data \
--cache-dir ./public/cacheexport PASTO_PORT=3000
export PASTO_MAX_PASTE_SIZE=102400
export PASTO_THEME=monokai
export PASTO_SSH_ENABLED=true
export PASTO_SSH_PORT=2222
export PASTO_RATE_LIMIT=10port: 3000
bind: "0.0.0.0"
max_paste_size: 102400 # 100KB
theme: default-dark
ssh_enabled: true
ssh_port: 2222
storage_dir: ./data
cache_dir: ./public/cache
rate_limit: 10
rate_window: 60curl -X POST http://localhost:3000 \
-d "content=print('Hello!')&language=python&title=My%20Paste"# View paste
curl http://localhost:3000/{paste-id}
# View with language override
curl http://localhost:3000/{paste-id}?lang=python
# View with file extension
curl http://localhost:3000/{paste-id}.py# View all versions (owner only)
curl http://localhost:3000/{paste-id}/history
# View specific version
curl http://localhost:3000/{paste-id}/version/1curl -X POST http://localhost:3000/highlight \
-d "content=def hello(): pass&language=python&theme=monokai"Pasto supports the Model Context Protocol (MCP), enabling AI assistants like Claude to directly interact with your pastes. This allows AI assistants to create, retrieve, and manage pastes without any local setup.
- Generate an API Key: Visit
/profileon your Pasto instance and click "Generate API Key" - Copy the API Key: It will start with
pasto_ak_ - Configure Your AI Assistant: Use the MCP client configuration below
Pasto provides these MCP tools for AI assistants:
Create a new paste with content and optional metadata.
Parameters:
content(required): The paste contenttitle(optional): Paste titlelanguage(optional): Programming language for syntax highlightingfilename(optional): Filename for language detectionprivate(optional): Make paste private (default: false)encrypted(optional): Encrypt paste content (default: false)burn_after_reading(optional): Delete after first view (default: false)expires_in(optional): Expiration time - "1h", "1d", "1w", "1m", "never"
Example:
Please create a paste with this Python code: print("Hello, World!")
Make it private and set it to expire in 1 week.
Retrieve paste content and metadata by ID.
Parameters:
id(required): The paste ID to retrieve
Example:
Please retrieve the paste with ID abc123 and show me its details.
List user's pastes with pagination and filtering.
Parameters:
page(optional): Page number (default: 1)limit(optional): Items per page, max 100 (default: 20)private_only(optional): Filter to private pastes onlypublic_only(optional): Filter to public pastes onlyencrypted_only(optional): Filter to encrypted pastes onlylanguage(optional): Filter by programming language
Example:
Show me my private Python pastes, 10 at a time.
Update an existing paste's content or metadata (creates new version).
Parameters:
id(required): The paste ID to updatecontent(required): New content for the pastetitle(optional): New titlelanguage(optional): New programming languagefilename(optional): New filenameprivate(optional): Make paste privateburn_after_reading(optional): Set burn-after-reading flagexpires_in(optional): Update expiration time
Example:
Update paste xyz789 with this new JavaScript code and make it public.
Permanently delete a paste.
Parameters:
id(required): The paste ID to deleteconfirm(required): Set to true to confirm deletion
Example:
Please permanently delete paste xyz789 and confirm you want to do this.
Add this to your Claude Desktop configuration:
{
"mcpServers": {
"pasto": {
"transport": "http",
"url": "https://your-pasto-instance.com/mcp",
"headers": {
"Authorization": "Bearer pasto_ak_xxxxxxxxxxxx"
}
}
}
}User: Create a paste with this Rust code:
fn main() {
println!("Hello, Pasto!");
}
Claude: I'll create a paste with that Rust code for you.
β
Paste created successfully!
π URL: https://your-pasto-instance.com/abc123
π ID: abc123
π·οΈ Title: Untitled
π Private: false
π Encrypted: false
User: Show me my recent pastes
Claude: π **Your Pastes (Page 1 of 1)**
π Total: 5 pastes | Showing: 20 per page
π **My Rust Script** (abc123)
π€ rust
π
2024-12-22T10:30:00Z
β° Never expires
π**Secret Algorithm** (def456)
π python
π
2024-12-21T15:45:00Z
β° Expires: 2024-12-28T15:45:00Z
- Authentication Required: All MCP requests require valid API keys
- Permission Control: Users can only access their own pastes
- Rate Limiting: MCP endpoints respect existing rate limits
- HTTPS Recommended: Use HTTPS in production environments
Authentication Errors: Ensure your API key starts with pasto_ak_ and is valid.
Permission Denied: Make sure you're trying to access your own pastes.
Not Found: Verify the paste ID exists and hasn't expired.
Pasto supports 35+ programming languages including:
- Popular: Python, JavaScript, TypeScript, Java, C, C++, C#, Go, Rust
- Web: HTML, CSS, SCSS, Sass, JSON, XML, YAML, Markdown
- Systems: Bash, Shell, PowerShell, Dockerfile
- Data: SQL, MySQL, PostgreSQL, CSV
- And more: PHP, Ruby, Perl, Kotlin, Scala, ActionScript...
Only languages with working Tartrazine lexers are available.
- 15 color schemes: Slate, Zinc, Gray, Neutral, Stone, Red, Orange, Amber, Yellow, Lime, Green, Emerald, Cyan, Sky, Indigo, Violet, Fuchsia, Pink
- Light/Dark/Auto modes with system preference detection
- 321+ themes including popular ones like Dracula, Monokai, Nord, Solarized, One Dark, GitHub, VS Code themes, and many more
pasto/
βββ src/
β βββ pasto.cr # Entry point, configuration
β βββ server.cr # Kemal routes and middleware
β βββ paste.cr # Paste model, highlighting, themes
β βββ pasto_ssh.cr # SSH server entry point
β βββ views/
β βββ layout.ecr # Main layout with sidebar
β βββ index.ecr # Create paste page
β βββ show.ecr # View paste page
β βββ edit.ecr # Edit paste page
β βββ history.ecr # Version history page
β βββ profile_content.ecr # User profile
βββ data/ # Sepia storage directory
βββ public/cache/ # Cached rendered pastes
βββ sessions/ # Session storage
βββ pasto.yml # Configuration file
# Install dependencies
shards install
# Build
shards build
# Build release
shards build --release
# Run tests
crystal spec
# Format code
crystal tool format src/
# Lint
./bin/ameba src/- Rate Limiting: Configurable per-IP rate limits
- Size Validation: Maximum paste size enforcement
- HTML Escaping: All content properly sanitized
- Session Security: Secure cookie-based sessions
- SSH Key Auth: Public key authentication for SSH access
- Kemal: Web framework
- Tartrazine: Syntax highlighting (321+ themes)
- Hansa: Language classification
- Sepia: Object persistence with versioning
- Shirk: SSH server
- Pico CSS: Minimal CSS framework
- Lucide: Icon library
- CodeJar: Code editor
MIT License - see LICENSE for details.
Created by Roberto Alsina
Pasto - Modern pastebin with SSH access and live preview.
A public Pasto instance is available at https://pasto1.ralsina.me.