PushizoDocs
Operasyon

Pushizo — Yayına alma (adım adım)

Bu belge, tek sunucuda (Docker Compose) ilk canlı kurulum içindir. Ayrıntılı kontrol listesi GO_LIVE_CHECKLIST.md, altyapı ayrıntıları infra/README.md. Kubernetes için docs/runbooks/helm-production.md.

Bu belge, tek sunucuda (Docker Compose) ilk canlı kurulum içindir. Ayrıntılı kontrol listesi GO_LIVE_CHECKLIST.md, altyapı ayrıntıları infra/README.md. Kubernetes için docs/runbooks/helm-production.md.

0. Hazırlanacaklar (ürün sahibi)

#NeNeredenNot
1Alan adıHerhangi bir kayıt firmasıÖr. pushizo.com
2SunucuHetzner / DigitalOcean vb.Ubuntu 24.04, başlangıç için 4 vCPU / 8 GB RAM / 80 GB SSD (plan 08)
3DNS A kayıtlarıAlan adı paneliapi, app, status, docs, grafana, storage alt alan adları + kök/www (tanıtım sitesi) → sunucu IP'si
4Sistem e-postasıSMTP / Amazon SES / ResendKayıt doğrulama ve parola sıfırlama e-postaları için zorunlu; SPF/DKIM/DMARC kayıtları
5Apple pushApple Developer → Keys.p8 anahtarı, Key ID, Team ID, Bundle ID (panelde app kurulumunda girilir)
6Android pushFirebase konsoluServis hesabı JSON'u (panelde app kurulumunda yüklenir)
7Yasal metinlerHukukGizlilik, Kullanım Şartları, KVKK Aydınlatma, Çerez — apps/web/content/legal/ (tanıtım sitesi)
8(İsteğe bağlı) ÖdemeStripe veya iyzicoYoksa ücretli plana geçiş kapalıdır; platform ücretsiz planla ve admin panelinden atanan planlarla çalışır

Push ve e-posta kimlik bilgileri sunucuya değil, panelden app başına girilir (şifreli saklanır).

Pushizo için durum (2026-10-16)

  • Alan adı pushizo.com (DNS Cloudflare'de). Sistem e-postası bildirim@pushizo.com, SMTP mta1.mail.tecrube.com:465 (SSL).
  • Depo kökünde (git dışı, .gitignore) .env.prod hazırlandı: pushizo.com adresleri, SMTP ve rastgele üretilmiş gizli değerler. Sunucuya kopyalanır (chmod 600); Stripe/iyzico yoksa BILLING_PROVIDER=auto ile ücretli plana geçiş kapalı kalır.
  • GitHub değişkenleri: PROD_APP_URL=https://app.pushizo.com, PROD_API_URL=https://api.pushizo.com, PROD_SITE_URL=https://pushizo.com, PROD_DOCS_URL=https://docs.pushizo.com.

E-posta teslim edilebilirliği (spam'e düşmeme)

Mevcut DNS'te SPF (-all), DKIM (default._domainkey, 2048 bit), DMARC (p=none) ve sunucu PTR'si (mta1.mail.tecrube.com) doğru. Spam'in ana nedeni alan adının yeni olmasıdır (kayıt: 2026-10-16); Gmail/Outlook yeni alan adına itibar oluşana kadar (genellikle 2–4 hafta) temkinli davranır. Yapılacaklar:

  1. Gmail'de gelen iletide Orijinali göster → SPF, DKIM, DMARC üçü de PASS olmalı. DKIM PASS değilse barındırma firmasından pushizo.com için DKIM imzalamanın açılmasını isteyin.
  2. DMARC ve TLS-RPT raporları kendi alan adına alınır (2026-10-16 Cloudflare'de ayarlandı): _dmarc → v=DMARC1; p=none; rua=mailto:dmarc@pushizo.com; fo=1, _smtp._tls → v=TLSRPTv1; rua=mailto:dmarc@pushizo.com. dmarc@pushizo.com kutusu açık olmalıdır. Raporlar Pushizo admin panelinde E-posta güvenliği sayfasında izlenir (aşağıda "DMARC raporları"); SPF/DKIM sürekli geçince panel p=quarantine'i önerir.
  3. Google Postmaster Tools'a pushizo.com ekleyip doğrulayın (alan adı itibarı ve spam oranı buradan izlenir).
  4. İlk haftalarda düşük hacimle, gerçek alıcılara gönderin; gelen spam kutusundaki iletileri Spam değil olarak işaretleyin.
  5. Bülten (haber@pushizo.com) için ayrı alt alan adı önerilir (ör. haber.pushizo.com); toplu gönderim itibarı sistem e-postalarını (doğrulama, parola sıfırlama) etkilemesin.

DMARC raporları ve politika (ADR-0035)

DMARC toplu raporları ve TLS-RPT dmarc@pushizo.com adresine gelir. Pushizo bunları saklar, gönderen kaynakları yetkili / yetkisiz olarak sınıflar, yeni yetkisiz kaynakta veya doğrulama oranı düştüğünde platform adminlerine e-posta gönderir (kaynak/uyarı başına günde bir) ve 14 gün + %98 doğrulama sonrasında politikayı bir adım sıkılaştırmayı önerir (none → quarantine → reject). Panel: https://app.pushizo.com/admin/email-security.

Raporları almak için iki yoldan birini (veya ikisini) kurun:

A) Postal HTTP Endpoint (önerilen; pushizo.com MX'i Postal'dadır):

  1. Belirteç üretin: openssl rand -hex 24 (48 karakter). .env.prod → DMARC_INBOUND_TOKEN=<belirteç>; API'yi yeniden başlatın (docker compose --env-file .env.prod -f infra/compose/docker-compose.prod.yml up -d api worker).
  2. Postal paneli (panel.mail.tecrube.com) → pushizo.com sunucusu → Routing → HTTP Endpoints → Add: URL https://api.pushizo.com/hooks/dmarc/<belirteç>, Encoding: Body as JSON, Format: Raw message, Strip replies kapalı.
  3. Routing → Routes → Add route: Name dmarc, Domain pushizo.com, Endpoint = 2. adımdaki HTTP endpoint, Mode Endpoint. Böylece dmarc@pushizo.com için ayrı posta kutusu gerekmez.
  4. Postal'ın gelen posta spam denetimini açık tutun (sunucu ayarı Spam checking: SpamAssassin, eşik ~5). Rapor olmayan iletiler (spam) Pushizo tarafından 200 ile kabul edilip atılır; Postal yeniden denemez. Gövde sınırı 25 MB, hız sınırı IP başına dakikada 120 istek; yanlış belirteç 404.
  5. Doğrulama: curl -s -o /dev/null -w '%{http_code}' -X POST https://api.pushizo.com/hooks/dmarc/yanlis -H 'content-type: application/json' -d '{}' → 404. İlk raporlar 24–48 saat içinde gelir (Google, Microsoft, Yahoo).

B) IMAP posta kutusu (Postal route kurulamazsa):

  1. Barındırma panelinde dmarc@pushizo.com posta kutusunu oluşturun (güçlü parola).
  2. .env.prod → DMARC_IMAP_URL=imaps://dmarc%40pushizo.com:<URL kodlu parola>@<imap-sunucusu>:993 (parolada @ : / ? # % varsa URL kodlayın: node -e "console.log(encodeURIComponent(process.argv[1]))" '<parola>'). Worker'ı yeniden başlatın.
  3. Worker kutuyu 15 dakikada bir tarar (Redis kilidiyle tek çalıştırma): raporlar okunup Processed klasörüne (yoksa oluşturulur), rapor olmayan iletiler çöp kutusuna taşınır. Panelde Şimdi tara ile hemen tetiklenebilir.

Cloudflare ile tek tıkla politika (isteğe bağlı): Cloudflare → My Profile → API Tokens → Create Token → "Edit zone DNS" şablonu, Zone Resources = yalnızca pushizo.com. .env.prod → CLOUDFLARE_API_TOKEN=<belirteç>, CLOUDFLARE_ZONE_ID=\<pushizo.com Overview sayfasındaki Zone ID>; API'yi yeniden başlatın. Panelde Cloudflare ile uygula yalnızca p= etiketini değiştirir (rua, fo korunur), işlem platform denetim kaydına yazılır ve Geri al ile önceki değere dönülür. Cloudflare yapılandırılmazsa panel girilecek tam TXT değerini gösterir; kaydı Cloudflare'de elle güncelleyin.

1. GitHub ayarları (bir kez)

Settings → Secrets and variables → Actions (depo özel: github.com/exec0de/pushizo):

  • Secrets: PROD_SSH_HOST (sunucu IP'si veya adı), PROD_SSH_USER (dağıtım kullanıcısı, ör. deploy), PROD_SSH_KEY (bu kullanıcının özel SSH anahtarı; genel anahtarı sunucuda ~/.ssh/authorized_keys).
  • Variables: PROD_APP_URL=https://app.pushizo.com, PROD_API_URL=https://api.pushizo.com, PROD_SITE_URL=https://pushizo.com, PROD_DOCS_URL=https://docs.pushizo.com, DEPLOY_PATH=/opt/pushizo. İsteğe bağlı: PROD_INDEXNOW_KEY, PROD_CONTACT_EMAIL (sitede iletişim adresi, ör. bildirim@pushizo.com).
  • Staging: ayrı staging sunucusu yoksa hiçbir şey tanımlamayın; STAGING_ENABLED tanımsızken staging işi atlanır ve üretim dağıtımı doğrudan onaya düşer. Staging kullanılacaksa STAGING_ENABLED=true + STAGING_SSH_HOST/USER/KEY secret'ları + STAGING_APP_URL, STAGING_API_URL değişkenleri.
  • Settings → Environments → production: zorunlu onaylayıcı (prod dağıtımı elle onaylanır).

2. Sunucu hazırlığı (bir kez)

# Sunucuda (root veya sudo):
apt-get update && apt-get install -y docker.io docker-compose-v2 git ufw
# Güvenlik duvarı (yalnızca Plesk OLMAYAN sunucu): SSH, HTTP, HTTPS. Plesk'li sunucuda ufw kullanmayın: §9.1.
ufw allow 22/tcp && ufw allow 80/tcp && ufw allow 443/tcp && ufw --force enable
# 8 GB RAM altı sunucularda takas alanı (Postgres/ClickHouse olmayan tek sunucu için 4 GB yeterli)
fallocate -l 4G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
# Dağıtım kullanıcısı (GitHub Actions bu kullanıcıyla SSH yapar)
adduser --disabled-password --gecos "" deploy && usermod -aG docker deploy
install -d -o deploy -g deploy -m 700 /home/deploy/.ssh
# PROD_SSH_KEY'in genel anahtarını /home/deploy/.ssh/authorized_keys dosyasına ekleyin (izin 600, sahibi deploy).
install -d -o deploy -g deploy /opt/pushizo
```text
Depo özel olduğu için klonlama **deploy key** ile, imajlar **GHCR girişi** ile alınır (ikisi de `deploy` kullanıcısıyla):
```bash
sudo -iu deploy
# 1) Salt okunur deploy key: genel anahtarı GitHub → depo → Settings → Deploy keys'e ekleyin ("Allow write access" kapalı).
ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519 -C "pushizo-deploy" && cat ~/.ssh/id_ed25519.pub
ssh-keyscan github.com >> ~/.ssh/known_hosts
git clone git@github.com:exec0de/pushizo.git /opt/pushizo && cd /opt/pushizo
# 2) GHCR: yalnızca `read:packages` kapsamlı bir GitHub PAT (classic) ile; parola stdin'den okunur, geçmişe yazılmaz.
docker login ghcr.io -u <GITHUB_KULLANICI_ADINIZ> --password-stdin
# 3) Ortam dosyası
cp infra/compose/.env.prod.example .env.prod && chmod 600 .env.prod
```text
`.env.prod` içinde doldurulacaklar: `SMTP_URL` (parola URL kodlu), gizli değerler (`MASTER_KEY` 32 bayt base64,
`SESSION_SECRET` en az 32 karakter, `POSTGRES_*`, `REDIS_PASSWORD`, `STORAGE_ROOT_*`, `S3_ACCESS_KEY`/`S3_SECRET_KEY`,
`GRAFANA_ADMIN_PASSWORD`) ve yedekleme için ayrı `BACKUP_S3_ACCESS_KEY`/`BACKUP_S3_SECRET_KEY`. Rastgele değer:
`openssl rand -hex 32` (yalnızca `0-9a-f`). `POSTGRES_PASSWORD` ve `REDIS_PASSWORD` `DATABASE_URL`/`REDIS_URL` adreslerine
gömülür: base64 çıktısındaki `/ + =` (ya da `@ : ? # %`) adresi bozar ve `deploy.sh` bu durumda dağıtımı açık bir mesajla
durdurur (değer yazdırılmaz). `MASTER_KEY` istisnadır: 32 bayt base64 olmalıdır (örnek dosyadaki komut). Alan adları örnekte pushizo.com olarak hazırdır. `PROVIDER_MODE=real` ve
`ALLOW_MOCK_PROVIDERS=false` kalmalıdır; `ADMIN_MFA_REQUIRED` varsayılan `true`'dur (kapatmak bilinçli karar;
`GO_LIVE_CHECKLIST.md`). Üretimde kısa `SESSION_SECRET`, geliştirme S3 parolası ve `localhost` SMTP adresi reddedilir
(uygulama açılmaz).

Alarm e-postası (Alertmanager) için SMTP parolası ayrı bir gizli dosyaya yazılır (deploy betikleri dosya yoksa boş
oluşturur; boşken alarm e-postası gitmez). Slack isteğe bağlıdır: `secrets/slack_webhook_url` doluysa Slack de etkinleşir.
```bash
install -d -m 700 infra/compose/secrets
read -rs -p "SMTP parolası: " P && printf '%s' "$P" > infra/compose/secrets/smtp_password; unset P; echo
```text
Dizin 700'dür; dosya izni betiklerce 644 yapılır (Alertmanager konteyneri `nobody` kullanıcısıyla okur).

**Kurulu sistemde URL güvenli olmayan parolayı değiştirme:** Postgres rol parolası yalnızca ilk kurulumda (boş veri
dizini) ayarlanır; `.env.prod`'u değiştirmek tek başına yetmez. Yeni değeri `openssl rand -hex 32` ile üretip önce
veritabanında, sonra dosyada güncelleyin (parola komut satırına/geçmişe yazılmaz):
```bash
cd /opt/pushizo
read -rs -p "Yeni pushizo parolası: " P; echo
printf "alter role pushizo password '%s';\n" "$P" | docker compose --env-file .env.prod -f infra/compose/docker-compose.prod.yml \
  exec -T postgres psql -U postgres -d pushizo -v ON_ERROR_STOP=1 >/dev/null; unset P
# .env.prod → POSTGRES_PASSWORD=<aynı değer> (nano/vim ile), ardından: IMAGE_TAG=<sürüm> ./infra/scripts/deploy.sh prod
```text
`REDIS_PASSWORD` yalnızca `.env.prod`'da değiştirilir; `deploy.sh` Redis ve uygulamaları yeni değerle yeniden oluşturur.

## 3. DNS
Cloudflare'de (pushizo.com) aşağıdaki **A** kayıtlarını sunucu IP'sine yönlendirin; proxy durumu **DNS only (gri bulut)**
olmalıdır (Let's Encrypt HTTP doğrulaması ve nginx TLS'i doğrudan sunucuda çalışır):

| Ad | Tür | Değer |
|----|-----|-------|
| `pushizo.com` (`@`) | A | sunucu IP'si |
| `www` | A | sunucu IP'si (nginx kök alan adına 301 yönlendirir) |
| `app`, `api`, `docs`, `status`, `grafana`, `storage` | A | sunucu IP'si |

Mevcut MX/SPF/DKIM/DMARC kayıtlarına dokunmayın. Yayılmayı doğrulayın: `dig +short app.pushizo.com` sunucu IP'sini dönmeli.

## 4. Sürüm etiketi → imajlar
Geliştirici makinesinde (sürüm `package.json`, `apps/api/src/version.ts` ve `CHANGELOG.md` ile eşleşmeli):
```bash
git tag v1.0.0 && git push origin v1.0.0
```text
`release.yml` imajları GHCR'ye koyar (`ghcr.io/exec0de/pushizo-*:1.0.0`, etiket `v` önekisiz) ve GitHub Release oluşturur.
Üretim dağıtımı işi `production` onayında bekler: **5. adım bitmeden onaylamayın** (sertifikalar yokken nginx açılmaz).

## 5. TLS ve ilk dağıtım (sunucuda, `deploy` kullanıcısı)
```bash
cd /opt/pushizo && git fetch --tags && git checkout -q v1.0.0
IMAGE_TAG=1.0.0 ./infra/scripts/certbot-init.sh   # tüm alan adları + www için Let's Encrypt sertifikası
IMAGE_TAG=1.0.0 ./infra/scripts/deploy.sh prod    # migrate → api-1/api-2 → worker'lar → dashboard/docs/web → nginx
```text
Ardından GitHub Actions'ta üretim dağıtımını onaylayın: aynı sürümü yeniden uygular (idempotent) ve duman testini
(`/healthz` sürümü, status.json, dashboard) çalıştırır. Sonraki sürümlerde yalnızca etiket itilir ve onay verilir.

## 6. İlk giriş ve yönetici
1. `https://app.pushizo.com/register` ile hesap aç, e-postayı doğrula.
2. Hesabı platform yöneticisi yap (sunucuda, bir kez):
   ```bash
   docker compose --env-file .env.prod -f infra/compose/docker-compose.prod.yml exec postgres \
     psql -U postgres -d pushizo -c "update accounts set is_platform_admin = true where lower(email) = lower('SENIN@EPOSTAN');"
   ```text
3. `https://app.pushizo.com/admin` (MFA açıksa önce Hesabım → iki aşamalı doğrulama).

## 7. İlk app ve uygulamaları bağlama
Panelde **Yeni app** → platform seç → `.p8` / Firebase JSON / site adresi → doğrulama → verilen SDK kodunu uygulamana ekle →
ilk abone gelince **test bildirimi**. OneSignal'dan geçişte aboneler **Veri → İçe aktar → OneSignal CSV** ile aktarılır;
birkaç gün iki sistem paralel çalıştırılıp sonra OneSignal kapatılır.

## 8. Yayın sonrası
- `https://status.pushizo.com` ve Grafana panoları (`https://grafana.pushizo.com`); alarmlar `infra/monitoring` (e-posta: `bildirim@pushizo.com`).
- Yedek: `sudo bash infra/scripts/install-cron.sh` (bir kez; idempotent) → `/etc/cron.d/pushizo` her gün 03:30
  `backup.sh` (PostgreSQL `pg_dump` + RustFS nesne depolama aynası → sunucu dışı kova; wal-g açıksa 03:00 `walg-backup.sh`),
  günlük `/var/log/pushizo-backup.log` (logrotate haftalık, 8 kopya), tazelik metriği
  `/var/lib/node_exporter/textfile/pushizo_backup_*.prom` → 26 saati aşınca `PushizoBackupStale` alarmı. AWS CLI ana
  makineye kurulmaz (`amazon/aws-cli` sabit sürüm imajı, `AWS_CLI_IMAGE`). İlk yedeği hemen deneyin:
  `sudo /opt/pushizo/infra/scripts/backup.sh` ve bir geri yükleme tatbikatı yapın (`docs/runbooks/restore.md`).
- Arama motorları: `docs/guides/seo.md` (Search Console, sitemap).

## 9. Plesk'li sunucu (EDGE_MODE=plesk)
80/443 ve TLS Plesk'in nginx'indedir; Pushizo nginx konteyneri yalnızca `127.0.0.1:EDGE_PORT` (18080) dinler
(`infra/compose/docker-compose.plesk.yml`). Alan adları ve sertifikalar `sudo ./infra/scripts/plesk-setup.sh` ile kurulur
(yükleme sınırı 64 MB, Pushizo nginx'iyle aynı). `LOCAL_BUILD=1` ise imajlar sunucuda derlenir; `deploy.sh` başarılı
dağıtımdan sonra bir haftadan eski kullanılmayan imajları ve 10 GB üzerindeki derleme önbelleğini temizler. GitHub'da
`LOCAL_BUILD=true` değişkeni tanımlanırsa dağıtım GHCR imajlarına bağlı kalmaz (`release.yml`).

### 9.1 Güvenlik duvarı (Plesk Firewall)
Plesk sunucusunda `ufw`/`iptables` kurallarını elle yazmayın: Plesk Firewall kurallarını ezer ya da onlarla çakışır ve
posta/panel erişimi kesilebilir. **Tools & Settings → Firewall** (Plesk Firewall eklentisi) üzerinden yönetin:

| Port | Hizmet | Durum |
|------|--------|-------|
| 22/tcp (ya da özel SSH portu, ör. 1981) | SSH | açık; mümkünse yalnızca yönetici IP'lerine |
| 80/tcp, 443/tcp | HTTP/HTTPS (Plesk nginx → Pushizo) | açık |
| 8443/tcp | Plesk paneli | açık; mümkünse yalnızca yönetici IP'lerine |
| 25, 465, 587/tcp | SMTP (gelen posta, gönderim) | açık kalmalı |
| 993, 143/tcp | IMAP (SSL / düz) | açık kalmalı |
| 995, 110/tcp | POP3 (SSL / düz) | açık kalmalı |
| 4190/tcp | ManageSieve (posta filtreleri) | açık kalmalı |

`EDGE_PORT` (18080) ve konteyner portları (Postgres, Redis, Grafana, Prometheus) dışarı açılmaz: compose bunları
yayınlamaz, nginx yalnızca `127.0.0.1`'e bağlanır. Docker kendi iptables zincirlerini yönetir; Plesk Firewall'da
"Docker" ile ilgili kuralları silmeyin. Değişikliği uygularken Plesk 60 sn içinde onay ister; erişim kesilirse kurallar
otomatik geri alınır.

### 9.2 SSH sertleştirme (elle; betikler SSH ayarını değiştirmez)
1. Önce anahtarla girişin çalıştığını **ayrı bir oturumda** doğrulayın (`ssh -i ~/.ssh/<anahtar> <kullanıcı>@<sunucu>`);
   `deploy` kullanıcısının `~/.ssh/authorized_keys` dosyasında GitHub Actions genel anahtarı olmalıdır.
2. `/etc/ssh/sshd_config.d/99-pushizo.conf` dosyasına:
   ```text
   PasswordAuthentication no
   KbdInteractiveAuthentication no
   PermitRootLogin prohibit-password
   PubkeyAuthentication yes
   ```text
   Ardından `sshd -t && systemctl reload ssh`. Mevcut oturumu kapatmadan yeni bir oturumla girişi tekrar deneyin.
3. Özel SSH portu kullanılıyorsa (ör. 1981) Plesk Firewall'da açın, GitHub'daki `PROD_SSH_HOST` için `~/.ssh/config`
   ya da iş akışında `-p` gerekir; port değişikliği yapılmadıysa 22 kalır.
4. **fail2ban:** Plesk → Tools & Settings → IP Address Banning (Fail2Ban) → **Enable intrusion detection**; `plesk-ssh`
   (ya da `ssh`) jail'i açık olmalı. GitHub Actions çıkış IP'leri değişkendir: başarısız girişler engellenirse
   jail'i kontrol edin (`fail2ban-client status sshd`), yönetici IP'nizi "Trusted IP Addresses" listesine ekleyin.

### 9.3 Yedek ve izleme
- `sudo bash infra/scripts/install-cron.sh` → `/etc/cron.d/pushizo` (03:30 yedek) ve `/etc/logrotate.d/pushizo`; Plesk'in
  kendi "Scheduled Tasks" ekranında görünmez, `cat /etc/cron.d/pushizo` ile kontrol edilir.
- Prometheus iç nginx probu (`http://nginx/nginx-health`) Plesk modunda da çalışır (konteyner içi 80 dinlenir, dışarı
  yayınlanmaz); dış problar `www`, `grafana`, `storage` dahil tüm alan adlarını ve sertifika bitişini (Plesk Let's
  Encrypt) izler.

Bu sayfada