A fast, minimal, and secure pastebin alternative β built with FastAPI.
cText lets you share text snippets privately and securely. Pastes are encrypted at rest, can be password-protected, and automatically expire. No accounts, no tracking, no ads.
- π End-to-end encryption β paste content is encrypted at rest using Fernet (AES-128-CBC)
- π Password protection β optional bcrypt-hashed password per paste
- β³ Auto-expiry β choose expiration from 10 minutes up to 30 days
- π‘οΈ CSRF protection β HMAC-based CSRF tokens on all forms
- π Raw view β direct plaintext access via
/raw/{id} - ποΈ Manual delete β delete your paste at any time
- π§Ή Auto-cleanup β daily cron job removes expired pastes
- π« No registration required β completely anonymous usage
- π HTTPS-first β designed to run behind Nginx with Let's Encrypt SSL
| Layer | Technology |
|---|---|
| Backend | FastAPI + Uvicorn |
| ORM | SQLAlchemy |
| Database | SQLite |
| Encryption | Cryptography (Fernet) |
| Password Hashing | bcrypt |
| Sessions | Starlette SessionMiddleware |
| Templating | Jinja2 |
| Reverse Proxy | Nginx |
| Process Manager | Systemd |
| SSL | Let's Encrypt (Certbot) |
ctext/
βββ app/
β βββ main.py # FastAPI routes and application logic
β βββ models.py # SQLAlchemy models
β βββ db.py # Database session setup
β βββ static/ # CSS, JS, assets
β βββ templates/ # Jinja2 HTML templates
β βββ index.html
β βββ view.html
β βββ created.html
β βββ 404.html
βββ cleanup_expired.py # Standalone cleanup script (used by cron)
βββ requirements.txt
βββ .env # Secret keys (not committed)
βββ pastes.db # SQLite database (auto-created)
- Ubuntu 22.04+ (or similar Debian-based distro)
- Python 3.10+
- Nginx
- Certbot (for SSL)
- A domain pointing to your server
apt update -y && apt upgrade -y && apt autoremove -ysudo apt install -y python3 python3-pip nginxIf you encounter conflicts with typing-extensions:
apt remove --purge python3-typing-extensionsmkdir /var/www/ctext/
cd /var/www/ctext/
git clone https://github.com/DevURANIUM/cText.git .
pip install -r requirements.txt --break-system-packagesCreate the .env file:
nano /var/www/ctext/.envAdd the following:
PASTE_SECRET_KEY=
SESSION_SECRET_KEY=
CSRF_SESSION_KEY=How to generate each key:
# PASTE_SECRET_KEY (Fernet key β must be this exact format)
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
# SESSION_SECRET_KEY (random URL-safe string)
python3 -c "import secrets; print(secrets.token_urlsafe(64))"
# CSRF_SESSION_KEY (just a session key name, e.g. a short string)
# Example value: csrf_tokenExample .env after filling in:
PASTE_SECRET_KEY=Ib7k2YourGeneratedFernetKeyHere=
SESSION_SECRET_KEY=yourLongRandomSessionSecretHere
CSRF_SESSION_KEY=csrf_token
β οΈ Never commit.envto version control. Add it to.gitignore.
Create /etc/systemd/system/ctext.service:
[Unit]
Description=ctext FastAPI application
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/ctext
EnvironmentFile=/var/www/ctext/.env
ExecStart=/usr/local/bin/uvicorn app.main:app \
--host 127.0.0.1 \
--port 8001 \
--proxy-headers
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.targetEnable and start:
sudo systemctl daemon-reload
sudo systemctl start ctext
sudo systemctl enable ctext
sudo systemctl status ctextCreate /etc/nginx/sites-available/ctext.ir:
server {
listen 80;
server_name ctext.ir;
location / {
proxy_pass http://127.0.0.1:8001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Enable the site:
sudo ln -s /etc/nginx/sites-available/ctext.ir /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxsudo certbot --nginx -d ctext.ir
sudo certbot renew --dry-runchown www-data:www-data /var/www/ctext/pastes.db
chmod 664 /var/www/ctext/pastes.db
chown www-data:www-data /var/www/ctext
chmod 775 /var/www/ctextsudo timedatectl set-timezone Asia/TehranRun crontab -e and add:
# Run cleanup every day at 03:00 AM
0 3 * * * cd /var/www/ctext && /usr/bin/python3 cleanup_expired.py >> /var/www/ctext/ctext_cleanup.log 2>&1Verify:
crontab -lClient (Browser)
β
βΌ
Nginx :80/:443 ββββ SSL termination (Let's Encrypt)
β
βΌ
Uvicorn :8001 (127.0.0.1 only)
β
βΌ
FastAPI Application
β
βΌ
SQLite (pastes.db)
- Nginx handles all public traffic and TLS
- Uvicorn binds only to localhost for security
- Systemd ensures the process restarts automatically on failure
- Paste content is encrypted at rest using Fernet symmetric encryption
- Passwords are hashed with bcrypt (12 rounds); passwords longer than 72 bytes are SHA-256-prehashed before bcrypt to prevent truncation
- CSRF tokens are signed using
hmac.compare_digestto prevent timing attacks - Sessions use
SameSite=Laxandhttps_only=True - The application process runs as
www-data(least privilege) - Uvicorn binds only to
127.0.0.1β never exposed directly to the internet
sudo systemctl status ctext
sudo nginx -t
sudo certbot renew --dry-runYour application will be live at:
https://ctext.ir
fastapi
uvicorn
gunicorn
jinja2
sqlalchemy
python-dotenv
python-multipart
bcrypt
cryptography
itsdangerous
Install with:
pip install -r requirements.txt --break-system-packagesIf cText is useful to you, consider supporting its development:
| Network | Address |
|---|---|
| BTC | bc1qcclcp574hnznm0nmdzzf0ta7366svjskttqks3 |
| LTC | ltc1qcrkelw38gjrmg0ptjy2nshqej622kp76het7q0 |
| XRP | rPoK5SBChFPqEiQv1W97LW6FKoJZLipDVQ |
| XLM | GDMUQREEZNBSTQOT5BV7MYEMXJFV3CYRZXUVOYCTIUZTHUWPHLVASFVD |
| TON | UQAJH2N0pqpvC9YN841w5NH1dCN9Lakwkpjvoy7vXf-vfqgv |
| TRON | TXJqhhwvkrTdnf5HReZf55hEzZuxjto3R4 |
| USDT (BEP20) | 0x1591036c4bD05b046532B65Df939fcd7824E18c7 |
Every contribution, no matter how small, helps keep the project running. β€οΈ
This project is open source. See LICENSE for details.