Share text in seconds. Encrypted, password-protected, auto-expiring.
A minimal, self-hosted pastebin built with FastAPI.
cText is a lightweight pastebin. Paste some text, pick how long it should live, and share a short code or link. Content is encrypted in the database and removed automatically when it expires. No accounts and no tracking. All assets, including fonts, are self-hosted.
- Encrypted at rest: paste content is encrypted with Fernet (AES-128-CBC + HMAC) before it is stored.
- Optional password: each paste can have its own password, hashed with bcrypt (cost 12). Long passwords are SHA-256 pre-hashed, so input beyond bcrypt's 72-byte limit still counts.
- Auto-expiry: choose from 10 minutes to 30 days. Expired pastes are deleted when someone opens them, and a nightly cron job removes the rest.
- Short codes: 6-character IDs, either digits only (
482913), letters only (kqxmpa), or mixed (k3x9q2). - Viewer: line numbers, line-wrap toggle, raw view,
.txtdownload, and one-click copy. - Editor: live line and character counter.
Ctrl/⌘ + Entersubmits andTabinserts spaces. - UI: light and dark themes (follows the system, with a manual toggle), mobile-friendly, and RTL/Persian text support with the bundled Vazir font.
- Security: CSRF protection on every form, signed session cookies, and paste IDs generated with
secrets. - One-command install:
install.shsets up venv, systemd, nginx, SSL and cron on Ubuntu/Debian.
| Layer | Technology |
|---|---|
| Backend | FastAPI, Starlette sessions |
| Templates | Jinja2 (server-rendered, no JS framework) |
| Database | SQLite via SQLAlchemy 2.0 |
| Crypto | cryptography (Fernet), bcrypt |
| Server | Uvicorn behind Nginx, managed by systemd |
| TLS | Let's Encrypt (certbot) |
Client ──► Nginx :80/:443 ──► Uvicorn 127.0.0.1:8001 ──► FastAPI ──► SQLite (pastes.db)
│
└── /static served directly by Nginx
ctext/
├── app/
│ ├── main.py # routes, crypto, CSRF, expiry logic
│ ├── db.py # SQLAlchemy engine/session (pastes.db in project root)
│ ├── models.py # Paste model
│ ├── requirements.txt
│ ├── templates/ # base, index, created, view, 404
│ └── static/ # styles.css, favicon.svg, fonts/ (Vazir)
├── cleanup_expired.py # deletes expired pastes (run by cron)
├── install.sh # server installer / updater / uninstaller
├── .env.example # configuration template
└── README.md
Copy the project to your server (git clone, scp, …), then run this inside the project folder:
sudo bash install.shThe installer asks two questions:
- Domain: for example
ctext.ir. Leave it empty to serve over plain HTTP on the server's IP. - Email: used for the Let's Encrypt certificate. Leave it empty to skip SSL.
To install without prompts:
sudo DOMAIN=ctext.ir EMAIL=you@example.com bash install.sh
sudo DOMAIN= bash install.sh # no domain, HTTP on server IP
sudo APP_PORT=8002 bash install.sh # custom internal portBefore requesting a certificate, point your domain's A record to the server's IP.
| Step | Details |
|---|---|
| Packages | python3, python3-venv, nginx, rsync, curl (+ certbot when SSL is used) |
| Files | Syncs the project to /var/www/ctext. An existing .env and pastes.db are never overwritten |
| Python | Creates a virtualenv at /var/www/ctext/venv. No --break-system-packages needed |
| Secrets | Generates .env with strong random keys on first install only (chmod 600) |
| Service | ctext.service (systemd) on 127.0.0.1:8001, running as www-data, auto-restart |
| Nginx | Reverse proxy, with /static served directly and a 10 MB body limit |
| SSL | Certificate via certbot plus an HTTP → HTTPS redirect. Renewal is handled by certbot's timer |
| Cron | /etc/cron.d/ctext-cleanup removes expired pastes daily at 03:00 |
| Check | Runs a health check and prints the final URL |
Replace the project files with the new version and run the installer again. Data and keys are kept.
sudo bash install.shsudo bash install.sh --uninstall # removes service, nginx site and cron job
sudo rm -rf /var/www/ctext # optional: delete files and databaseIf you prefer not to use the installer:
sudo apt install -y python3 python3-venv nginx
sudo mkdir -p /var/www/ctext && sudo cp -r . /var/www/ctext && cd /var/www/ctext
sudo python3 -m venv venv
sudo venv/bin/pip install -r app/requirements.txt
sudo cp .env.example .env # then fill in the keys (see Configuration)
sudo chown -R www-data:www-data /var/www/ctextAfter that, create a systemd unit that runs:
/var/www/ctext/venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8001 --proxy-headers
Set WorkingDirectory=/var/www/ctext and EnvironmentFile=/var/www/ctext/.env, put Nginx in front as a reverse proxy, and add a cron entry for cleanup_expired.py. install.sh contains the exact configs.
python -m venv venv
venv/bin/pip install -r app/requirements.txt # Windows: venv\Scripts\pip
cp .env.example .env # fill keys, set SESSION_HTTPS_ONLY=false
venv/bin/uvicorn app.main:app --reload --port 8001 # Windows: venv\Scripts\uvicornThen open http://localhost:8001.
| Variable | Required | Description |
|---|---|---|
PASTE_SECRET_KEY |
✅ | Fernet key used to encrypt paste content |
SESSION_SECRET_KEY |
✅ | Secret used to sign session cookies |
CSRF_SESSION_KEY |
✅ | Session key name for the CSRF token, e.g. csrf_token |
SESSION_HTTPS_ONLY |
— | true (default) for HTTPS, false for plain HTTP |
To generate keys:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" # PASTE_SECRET_KEY
python -c "import secrets; print(secrets.token_urlsafe(64))" # SESSION_SECRET_KEY| Method | Path | Description |
|---|---|---|
GET |
/ |
Home page: create a paste or open one by code |
POST |
/paste |
Create a paste (form: content, expires_in, id_type, password, csrf_token) |
POST |
/go |
Redirect to a paste by code |
GET |
/{id} |
View a paste (password prompt if protected) |
POST |
/{id}/unlock |
Unlock a protected paste for the current session |
POST |
/{id}/delete |
Delete a paste (a protected paste must be unlocked first) |
GET |
/raw/{id} |
Plain-text content, for example curl https://ctext.ir/raw/482913 |
Two files hold all the state:
sudo cp /var/www/ctext/.env ~/ctext-backup.env
sudo cp /var/www/ctext/pastes.db ~/ctext-backup.db
⚠️ Keep.envsafe. Without the originalPASTE_SECRET_KEY, existing pastes can't be decrypted.
To restore, copy both files back, run chown www-data:www-data on them, and then run systemctl restart ctext.
systemctl status ctext # service status
journalctl -u ctext -f # live logs
systemctl restart ctext # restart
nginx -t # test nginx config
certbot renew --dry-run # test SSL renewal
cat /var/www/ctext/ctext_cleanup.log # cleanup history| Problem | Cause / fix |
|---|---|
403 CSRF validation failed |
Site served over HTTP while SESSION_HTTPS_ONLY=true. Set it to false, or enable SSL. Then restart |
Cannot decrypt paste content |
PASTE_SECRET_KEY was changed. Restore the original .env |
502 Bad Gateway |
The app is not running. Check journalctl -u ctext -n 50 |
attempt to write a readonly database |
Run sudo chown -R www-data:www-data /var/www/ctext |
| Certbot fails | The domain's DNS doesn't point to this server yet, or port 80 is blocked |
| Wrong times | Times are stored as Asia/Tehran (LOCAL_TZ in main.py). The server timezone doesn't matter |
cText یک Pastebin سبک و خودمیزبانه. متن رو میچسبونی، تعیین میکنی چقدر بمونه، و یه کد کوتاه یا لینک برای بقیه میفرستی. محتوا رمزشده توی دیتابیس ذخیره میشه و بعد از انقضا خودکار پاک میشه. ثبتنام و ردیابی نداره. همهی فایلها، حتی فونت، روی خود سرور هستن و به CDN خارجی وابسته نیست.
- رمزنگاری: محتوای پیست قبل از ذخیره با Fernet (AES-128-CBC + HMAC) رمز میشه.
- رمز عبور اختیاری: با bcrypt هش میشه. رمزهای خیلی طولانی هم درست کار میکنن.
- انقضای خودکار: از ۱۰ دقیقه تا ۳۰ روز. پیست منقضی موقع باز شدن حذف میشه و یه cron شبانه بقیه رو پاک میکنه.
- کد کوتاه ۶ کاراکتری: فقط عدد، فقط حروف، یا ترکیبی.
- نمایشگر: شمارهی خط، شکستن خطوط بلند، نمایش Raw، دانلود
.txtو کپی با یک کلیک. - ویرایشگر: شمارندهی خط و کاراکتر، ارسال با
Ctrl + Enter. - ظاهر: حالت روشن و تیره، مناسب موبایل، و پشتیبانی کامل از متن فارسی با فونت وزیر.
- امنیت: محافظت CSRF روی همهی فرمها و کوکی session امضاشده.
- نصب یکدستوری: با
install.sh.
پوشهی پروژه رو روی سرور کپی کن (با git clone یا scp)، بعد داخلش این دستور رو بزن:
sudo bash install.shدو تا سؤال میپرسه:
- دامنه: مثلاً
ctext.ir. خالی بذاری، سایت روی IP سرور با HTTP بالا میاد. - ایمیل: برای گواهی SSL از Let's Encrypt. خالی بذاری، SSL نصب نمیشه.
نصب بدون سؤال:
sudo DOMAIN=ctext.ir EMAIL=you@example.com bash install.sh
sudo DOMAIN= bash install.sh # بدون دامنه، روی IP
sudo APP_PORT=8002 bash install.sh # پورت داخلی دلخواهقبل از گرفتن SSL، رکورد A دامنه باید به IP سرور اشاره کنه.
| مرحله | توضیح |
|---|---|
| پکیجها | python3، python3-venv، nginx، و certbot اگه SSL بخوای |
| فایلها | کپی پروژه به /var/www/ctext. فایل .env و دیتابیس موجود هیچوقت بازنویسی نمیشن |
| پایتون | ساخت virtualenv در /var/www/ctext/venv. دیگه --break-system-packages لازم نیست |
| کلیدها | فقط بار اول .env رو با کلیدهای تصادفی امن میسازه |
| سرویس | ctext.service در systemd روی 127.0.0.1:8001 با کاربر www-data و ریاستارت خودکار |
| nginx | reverse proxy، بهعلاوهی سرو مستقیم /static |
| SSL | گواهی با certbot، ریدایرکت HTTP به HTTPS و تمدید خودکار |
| Cron | پاک کردن پیستهای منقضی هر روز ساعت ۳ صبح |
فایلهای جدید رو جایگزین کن و دوباره همون دستور رو بزن. دادهها و کلیدها حفظ میشن.
sudo bash install.shsudo bash install.sh --uninstall # حذف سرویس، سایت nginx و cron
sudo rm -rf /var/www/ctext # اختیاری: پاک کردن فایلها و دیتابیسpython -m venv venv
venv/bin/pip install -r app/requirements.txt # ویندوز: venv\Scripts\pip
cp .env.example .env # کلیدها رو پر کن، SESSION_HTTPS_ONLY=false
venv/bin/uvicorn app.main:app --reload --port 8001بعد آدرس http://localhost:8001 رو باز کن.
| متغیر | ضروری | توضیح |
|---|---|---|
PASTE_SECRET_KEY |
✅ | کلید Fernet برای رمز کردن محتوای پیستها |
SESSION_SECRET_KEY |
✅ | کلید امضای کوکی session |
CSRF_SESSION_KEY |
✅ | اسم کلید توکن CSRF، مثلاً csrf_token |
SESSION_HTTPS_ONLY |
— | true (پیشفرض) برای HTTPS و false برای HTTP ساده |
| متد | مسیر | توضیح |
|---|---|---|
GET |
/ |
صفحهی اصلی |
POST |
/paste |
ساخت پیست |
POST |
/go |
رفتن به پیست با کد |
GET |
/{id} |
نمایش پیست |
POST |
/{id}/unlock |
باز کردن پیست رمزدار |
POST |
/{id}/delete |
حذف پیست |
GET |
/raw/{id} |
متن خام، مثلاً curl https://ctext.ir/raw/482913 |
کل دادهها توی دو فایله: /var/www/ctext/.env و /var/www/ctext/pastes.db. از هر دو بکآپ بگیر.
⚠️ مراقب.envباش. بدونPASTE_SECRET_KEYاصلی، پیستهای قبلی دیگه باز نمیشن.
برای بازگردانی، دو فایل رو برگردون سر جاشون، مالکشون رو www-data کن و بعد systemctl restart ctext بزن.
systemctl status ctext # وضعیت سرویس
journalctl -u ctext -f # لاگ زنده
systemctl restart ctext # ریاستارت
nginx -t # تست تنظیمات nginx
certbot renew --dry-run # تست تمدید SSL| مشکل | علت و راهحل |
|---|---|
خطای 403 CSRF validation failed |
سایت روی HTTP سادهست ولی SESSION_HTTPS_ONLY=true هست. بذارش false یا SSL فعال کن، بعد ریاستارت |
Cannot decrypt paste content |
کلید PASTE_SECRET_KEY عوض شده. .env اصلی رو برگردون |
502 Bad Gateway |
برنامه اجرا نیست. journalctl -u ctext -n 50 رو ببین |
readonly database |
دستور sudo chown -R www-data:www-data /var/www/ctext رو بزن |
| خطای certbot | DNS دامنه هنوز به سرور اشاره نمیکنه یا پورت ۸۰ بستهست |
| ساعت اشتباه | زمانها به وقت تهران ذخیره میشن. timezone سرور مهم نیست |
© 2026 cText.ir · Powered by URANIUM