﻿# SUNUCU MERKEZLI LISANS MODELI - Mimari Tasarim (ONLINE-FIRST)

**Tarih:** 28.09.2026
**Hedef:** `.fkl` dosya modelini birakip tamamen sunucu merkezli, uzaktan tam kontrol modeli
**Kapsam:** Fikocad (C++) + fikoyt-sunucu (Go) + uretic makinesi

---

## 1. ISTEKLENEN MODEL (Ozet)

```
1. Uretici makinesi  -> sunucuya baglanir
2. Sunucu             -> acik Fikocad'leri listeler
3. Uretici            -> uzaktan yetki/lisans verir
4. Gerekirse          -> 1 saatlik / tek kullanimlik lisans verir
5. Gerekirse          -> o makineyi hic AÇTIRMAZ
```

**Sonuc:** Lisans artik bir DOSYA degil, sunucunun anlik verdigi bir BERTTIR.
Klasik "yazilim lisanslama" -> "floating / network licensing" modeline gecis.

---

## 2. MEVCUT SISTEMIN SINIRLARI

Mevcut `lisans_canli.cpp` altyapisi incelendi. Su an yapabilenler:

| Yetenek | Durum | Nerede |
|---------|-------|--------|
| Fikocad sunucuya baglanir | VAR | `lisans_canli.cpp:330` `socketioBaslat()` |
| Sunucu acik istemcileri listeler | VAR (RAM) | `lisans_kontrol.go:250` `istemciler[uuid]` |
| Uzaktan ENGELLE/IZIN | VAR | `/lisans-kontrol/api/karar` |
| Uzaktan KOMUT VERME | **YOK** | - |
| Uzaktan LISANS VERME | **YOK** | - |
| KISA SURELI (1 saat) lisans | **YOK** | - |
| Cihaz bazli engelleme | **YOK** (sadece seri) | `kararImzala` sadece `seri` imzaliyor |
| Kalicilik (DB) | **YOK** (JSON dosya) | `sistem/lisans_kararlar.json` |
| Kim/ne yapti (audit) | **YOK** | - |

**Ozet:** Baglanti kanali ve engelleme calisiyor. Eksik olan: **lisans dagitimi** ve **uzaktan karar verme araci**.

### Kritik kisit: Imzayi kim atacak?

Mevcut sistemde iki farkli imza var:

1. **Lisans dosyasi imzasi** - uretic makinesindeki `ozelAnahtar` ile
2. **Kill-switch karari imzasi** - sunucudaki `ozelAnahtar` ile (`lisans_kontrol.go:94` `LISANS_OZEL_PEM`)

Yani **sunucu zaten ozel anahtari tasiyor.** Bu, uzaktan lisans vermek icin teknik engel
degil - ama guvenlik modelini degistirir (Bolum 3'te analiz).

### Kritik kisit: Socket.IO elle yazilmis

`lisans_canli.cpp:211-596` - engine.io v3 frame parsing, Socket.IO packet encode/decode,
WebSocket framing... **hepsi elle implement edilmis** (~380 satir).

Bu, basit "engelle/izin ver" icin yeterli. Ama lisans bileti, kuyruk, heartbeat gibi
karmasik mesajlasma icin **kirkilir bir temel**. Asagida bu icin REST oneriyorum (Bolum 8).

---

## 3. GUVENLIK MODELI: IKI YAKLASIM

Bu, tum tasarimin en onemli karari.

### Yaklasim A - Sunucu imzalar (MEVCUT modelin uzantisi)

```
Uretici paneli -> sunucu API -> sunucu ECDSA imzalar -> istemci dogrular
```

**Avantaj:**
- Uretici makinesi cevrimdisi olsa bile lisans verilebilir
- Basit, tek merkez
- Mevcut `kararImzala()` aynen kullanilir

**Dezavantaj:**
- Sunucu ele gecirilirse **herkese lisans verilebilir**
- Sunucu = tek nokta (single point of failure)
- Kullanici "lisans ureteci makinesi" diyorsa, uretic makinesi anlamini yitirir

### Yaklasim B - Uretici makinesi imzalar (kullanicinin istedigi)

```
Uretici makinesi -> sunucudan liste -> imzali bilet uretir -> sunucuya yollar -> sunucu relay eder
```

**Avantaj:**
- Ozel anahtar **sadece uretic makinesinde**, sunucuda hicbir iz yok
- Sunucu ele gecirilse bile **sahte lisans uretilemez** (en kotu hali: lisans verilmez = DoS)
- Kullanicinin "uretic makinesi" konsepti korunur

**Dezavantaj:**
- Uretici makinesi kapaliysa lisans verilemez
- Uretici makinesi sunucudan "liste" bekler - iki yonlu akis
- Go tarafinda imza YOK, sadece relay

### Yaklasim C - Hibrit (ONERILEN)

```
Kalici lisans    -> Uretici makinesi imzalar (B)
Gecici/onay      -> Sunucu imzalar (A)  [opsiyonel]
Engelleme        -> Sunucu imzalar (A)  [mevcut, degismez]
```

Gerekce:
- **Satis lisanslari** (yil/ay paket) uretic makinesinden imzalanir -> anahtar sunucuda yok
- **Saatlik/deneme lisanslar** sunucudan imzalanabilir -> satis hizini artirir
  (uretic makinesi mesai saatleri disinda calismiyorsa kritik)
- **Engelleme** sunucudan (mevcut sistem, degismez)

Bu yaklasimda sunucuda ozel anahtar **opsiyonel** olarak tutulur:
```
LISANS_SURELIK_IMZA=1  -> sunucu gecici lisans imzalayabilir
LISANS_SURELIK_IMZA=0  -> sadece uretic makinesi imzalayabilir (guvenlik en yuksek)
```

**Tavsiye:** Baslangicta `0` ile basla (sadece uretic makinesi). Satis hizi yetersiz
kalinca `1`e gec. Boylece guvenlik postesi bilincli bir karar olarak degisir.

---

## 4. MIMARI - UC ROL

```
  +---------------------------------------------------------------+
  |  ROL 1: SATICI PANELI (Flutter/Web)                            |
  |  - Acik makineleri gor                                        |
  |  - Lisans ver / uzat / kisitla / engelle                       |
  |  - Saatlik lisans ver                                          |
  |  - Komut gonder                                               |
  |  - Raporlama                                                  |
  +------------------+--------------------------------------------+
                     | HTTPS (session + adminOnly)
                     v
  +---------------------------------------------------------------+
  |  ROL 2: fikoyt-sunucu (Go)                                    |
  |  - Oturum/cihaz kaydi (MongoDB)                                |
  |  - Lisans deposu (imzali govde + imza saklar)                  |
  |  - WebSocket/SSE ile acik istemcilere bildirim                 |
  |  - Kuyruk yonetimi (komut teslimi)                             |
  |  - (opsiyonel) gecici lisans imzalama                          |
  +------------------+--------------------------------------------+
                     ^                    ^
       imzali bilet  |                    | heartbeat / bilet istegi
       (uretici)     |                    |
                     |                    |
  +-----------------+--------------------+-------------------------------+
  |  ROL 3a: URETICI MAKINESI (fikocad + FIKO_URETICI_UI)            |
  |  - Ozel anahtari TUTAR                                         |
  |  - Panelden emir alir, bilet URETIR + IMZALAR                   |
  |  - Sunucuya gonderir (veya dogrudan o istemciye)                |
  +------------------------------------------------------------------+
                     ^
                     |
  +------------------------------------------------------------------+
  |  ROL 3b: MUSTERI FI KOCAD                                        |
  |  - Sunucuya baglanir, kimligini bildirir                        |
  |  - Lisans bileti ister/sunucudan alir                            |
  |  - IMZAYI DOGRULAR (gomulu acik anahtar)                        |
  |  - Haklarini bellekte uygular                                    |
  |  - Komutlari uygular + sonuc bildirir                             |
  +------------------------------------------------------------------+
```

**Anahtar fark:** Lisans artik diskte degil, **RAM'da ve sunucuda**. Musteri bileti
imzali sekilde alir, bellekte tutar, gecerlilik suresi dolunca otomatik kaybolur.

---

## 5. PROTOKOL - MESI AJLAR

### 5.1 Musteri Fikocad -> Sunucu

| Ajan | Yon | Govde | Aciklama |
|------|-----|-------|----------|
| `hello` | I->S | `{cihaz, seri, musteri, surum, pid, makine[], yapiHash, lisansDurum, paketler}` | Baglanma + kimlik |
| `bilet-iste` | I->S | `{cihaz, seri}` | Lisans bileti iste (yoksa veya yenilemek icin) |
| `heartbeat` | I->S | `{cihaz, aktif, komutSayisi, sonKomutZamani}` | 30-60 sn'de bir |
| `komut-sonuc` | I->S | `{komutId, basarili, mesaj, sureMs, cikti}` | Komut sonucu |
| `olay` | I->S | `{tip, detay}` | Telemetri (kota kullanimi, hata, kullanim) |
| `durum-bildir` | I->S | `{cihaz, acikMi, belgeSayisi, aktifKullanici}` | Panelde canli durum |

### 5.2 Sunucu -> Musteri Fikocad

| Ajan | Yon | Govde | Aciklama |
|------|-----|-------|----------|
| `hello-ack` | S->I | `{oturumId, sunucuZamani, sonrakiHeartbeatSn}` | Baglanti onay |
| `lisans-bilet` | S->I | `{seri, musteri, bitis, komutlar[], kotalar[], paketler, gecerlilik, imza}` | **ANA MESAJ** - imzali bilet |
| `lisans-sure` | S->I | `{kalanSn, uzatildi}` | Saatlik bilet geri sayimi |
| `lisans-engel` | S->I | `{engelli, mesaj, imza}` | Kill-switch (mevcut) |
| `lisans-bilgi` | S->I | `{mesaj, tip, sureSn}` | Bilgilendirme (lisans bitiyor vs) |
| `komut-gonder` | S->I | `{komutId, komut, args, imza, timeoutSn}` | **Uzaktan komut** |
| `ayar-gonder` | S->I | `{anahtar, deger, imza}` | Ayar degistirme (orn. kota sifirla) |

### 5.3 Uretici Makinesi -> Sunucu (YENI)

| Ajan | Yon | Govde | Aciklama |
|------|-----|-------|----------|
| `uretici-hello` | U->S | `{ureticiId, imza}` | Uretici kimligi (ozel anahtarin public kismi ile dogrulanir) |
| `uretici-liste` | U->S | `{filtre}` | Acik makineleri iste |
| `bilet-uret` | U->S | `{cihaz, lisansGovdesi, imza}` | Uretici bilet uretti (imzali) |
| `bilet-iptal` | U->S | `{cihaz, seri}` | Bileti geri cek |
| `kilit` | U->S | `{cihaz, mesaj, sureSn}` | Makineyi kilitle |

**Guvenlik:** Uretici makinesinin her mesaji **ozel anahtariyla imzali**. Sunucu
imzayi ureticinin public anahtariyla dogrular. Boylece sunucu, uretic olmadan bilet
uretemez (Yaklasim B).

### 5.4 Sunucu -> Uretici Makinesi

| Ajan | Yon | Govde |
|------|-----|-------|
| `uretici-ack` | S->U | `{ureticiId, sunucuZamani}` |
| `cihaz-listesi` | S->U | `{cihazlar: [{cihaz, seri, musteri, bilgisayar, ip, surum, sonGorulme, durum, biletVar, biletBitis}]}` |
| `bilet-onay` | S->U | `{cihaz, kabul, hata}` |
| `uretici-olay` | S->U | `{tip, detay}` (log akisi) |

---


### 5.5 Online-First Modeli - Mesaj Seti (REVIZE)

**Izin ile ilgili tum mesajlar KALDIRILDI.** Kesintide izin verilmiyor.

**Sunucu -> Musteri:**

| Ajan | Govde | Aciklama |
|------|-------|----------|
| `durum` | `{aktiflik:true, sunucuZamani, ping}` | **30 sn'de bir heartbeat** - kopma tespiti icin |
| `lisans-bilet` | `{seri, musteri, bitis, komutlar[], kotalar[], imza}` | Lisan bileti (bagliyken gonderilir) |
| `lisans-engel` | `{engelli, mesaj, imza}` | Kill-switch (mevcut) |
| `lisans-bilgi` | `{mesaj, tip, sureSn}` | Bilgilendirme |
| `komut-gonder` | `{komutId, komut, args, imza, timeoutSn}` | Uzaktan komut |
| `ayar-gonder` | `{anahtar, deger, imza}` | Ayar degistirme |

**Musteri -> Sunucu:**

| Ajan | Govde | Aciklama |
|------|-------|----------|
| `merhaba` | `{cihaz, seri, musteri, surum, pid, makine[], yapiHash, ...}` | Baglanma + kimlik |
| `pong` | `{t}` | **Heartbeat cevabi** (aninda) - kopma tespiti |
| `heartbeat` | `{cihaz, aktif, komutSayisi, sonKomutZamani, kotaKullanim}` | 30 sn |
| `komut-sonuc` | `{komutId, basarili, mesaj, sureMs, cikti}` | Komut sonucu |
| `olay` | `{tip, detay}` | Telemetri |
| `durum-bildir` | `{aktiflik, kilitliMi, beklemedeMi}` | Kilitliyken bile sunucuya bildir |

**KALDIRILAN MESAJLAR:**
- ~~`izin-bilet`~~ - cevrimdisi izin yok
- ~~`bekleyen-bilet`~~ - tembel dagitim yok
- ~~`izin-ver` (uretici->sunucu)~~ - uzaktan izin verme yok
### 5.6 Durum Mesaji - HEARTBEAT + SURE TASHISI (Sunucu Belirler)

**Iki isi birden yapar:**

1. **Kopma tespiti** - sessiz kopmalari yakalar
2. **Kopma suresini TASIR** - istemci kendi kafasina gore karar vermez, SUNUCU soyler

**KRITIK: Kopma suresi istemcide SABIT DEGIL, sunucudan gelir.**
Sunucu 30 sn diyorse istemci 30 sn bekler. Sunucu 120 sn derse istemci 120 sn bekler.
Boylece sureyi **istemci guncellemeden** degistirebilirsiniz (acil durumda kisitlayin,
olay sonrasi uzatIN).

```
SUNUCU  -> MUSTERI:  {"aktiflik":true,"t":1699999999,"kopmaSuresi":30}   (30 sn'de bir)
MUSTERI -> SUNUCU:   {"pong":1699999999}                                    (aninda)
```

#### Kopma suresi govdesi (heartbeat ile birlikte gelir):

```json
{
  "aktiflik": true,
  "t": 1699999999,
  "kopmaSuresi": 30,
  "uyari1": 5,
  "uyari2": 15,
  "kilitliDeneme": 60,
  "heartbeatEsik": 45
}
```

**Kurallar:**
| Kural | Aciklama |
|-------|----------|
| `kopmaSuresi` ust sinir | 600 sn (10 dk) - daha fazlasi "izin" olurdu |
| `kopmaSuresi` alt sinir | 15 sn - altinda yanlis pozitif cok artar |
| `uyari1` < `uyari2` < `kopmaSuresi` | Sira korunmali, sunucu dogrular |
| Her heartbeat'te tekrarlanir | Istemci guncel degeri tutar |
| Sunucu ulasilamiyorsa | Istemci **son bilinen** degeri kullanir |

**Son bilinen deger kurali (kritik):** Sunucu coktugunda yeni heartbeat gelmez, ama
istemci elindeki son deger (orn. 30 sn) gecerlidir. Boylece:
- Planli bakim: panelden once `kopmaSuresi`yi 300 sn yap, sonra bakimi baslat
- Plansiz cokus: istemci elindeki 30 sn ile kilitler (beklenen davranis)

#### Go tarafi (sunucu) - sureleri yonetir:

```go
type KopmaAyarlari struct {
    KopmaSuresi   int `json:"kopmaSuresi"`   // varsayilan 30
    Uyari1        int `json:"uyari1"`        // 5
    Uyari2        int `json:"uyari2"`        // 15
    KilitliDeneme int `json:"kilitliDeneme"`  // 60
    HeartbeatEsik int `json:"heartbeatEsik"`  // 45
}

var g_kopmaAyar = KopmaAyarlari{30, 5, 15, 60, 45}

func (i *Istemci) durumGonder() {
    i.socket.Emit("durum", fiber.Map{
        "aktiflik": true, "t": time.Now().Unix(),
        "kopmaSuresi":   g_kopmaAyar.KopmaSuresi,
        "uyari1":        g_kopmaAyar.Uyari1,
        "uyari2":        g_kopmaAyar.Uyari2,
        "kilitliDeneme": g_kopmaAyar.KilitliDeneme,
        "heartbeatEsik": g_kopmaAyar.HeartbeatEsik,
    })
}
```

**Panelden anlik degistirme:**
```
POST /lisans-kontrol/api/kopma-ayar {kopmaSuresi:120, uyari1:10, uyari2:60}
-> Tum BAGLI istemcilere aninda gonderilir
-> Sonraki heartbeat'lerde de tekrarlanir
```

**Istemcide gomulur mu?** EVET ama sadece **guvenli azami** olarak. Derleme zamani
varsayilan 30 sn gomulur (sunucu hic ulasilamazsa devreye girer), ama calisma
zamaninda **her zaman sunucudan gelen deger** kullanilir.
## 5.7 AKIS DIYAGRAMI - SUNUCU BELIRLEYEN 30 SN

```
        SUNUCU (Go)                          MUSTERI FI KOCAD
        kopmaSuresi = 30 sn
              |                                      |
    ---------+--------------------------------------+--------
    | 1. WS baglanir (merhaba)                       |        AKTIF
    | 2. Lisans bileti gonder (imzali)               |        Her komut OK
    | 3. 30 sn'de `durum` gonder:                    |
    |     {kopmaSuresi:30, uyari1:5, uyari2:15}     |  <-- SURE SUNUCUDAN GELIR
    |<--- 4. `pong` --------------------------------- |        Istemci 30 sn'yi OZERLENDI
    |                                                 |
    |     [SUNUCU COKER / AG KESILIR]                  |
    |                                                 |
    |  <-- X socket kapandi VEYA heartbeat 45 sn yok  |  Kopma tespit
    |                                                 v
    |                                    DEBOUNCE (0-5 sn, SESSIZ)
    |                                    arka planda baglanmayi deniyor
    |                                    Kullanici HIC GORMEZ
    |                                                 |
    |                                    t=5 sn: basarisiz
    |                                                 v
    |                                    1. UYARI (banner + toast)
    |                                                 |
    |                                    t=5-15 sn: sessizce dene
    |                                    (baglanirsa -> AKTIF)
    |                                                 |
    |                                    t=15 sn: basarisiz
    |                                                 v
    |                                    2. UYARI (son)
    |                                    "15 saniye sonra kilitleneceksiniz"
    |                                                 |
    |                                    t=15-30 sn: son denemeler
    |                                    (baglanirsa -> AKTIF)
    |                                                 |
    |                                    t=30 sn  KOPMA SURESI DOLDU
    |                                                 v
    |                              ***  BUM: KILIT  ***
    |                              goruntuleyiciKipiUygula()
    |                              viewport tam kaplama
    |                              [Tekrar Dene] + hata detayi
    |                                                 |
    |                                    60 sn'de bir sessizce dene
    |                                    (kilitliDeneme - sunucu belirler)
    |                                                 |
    |  <---- 6. SUNUCU GERI GELDI -------------------|  otomatik acilir
    |  7. Yeni bilet + YENI kopmaSuresi gonder        |
    |                                                 v
    |                                              AKTIF
```

**Kritik:** `kopmaSuresi` **istemcide sabit degil**, her heartbeat'te sunucudan gelir.
Sunucu panelden istedigi an degistirebilir (acil durumda 15 sn, bakimda 300 sn).
## 6. LISANS BILETI - IMZALI YAPI

C++ tarafinda dogrulanacak tek bilesen. Mevcut `canliKararCoz` ile ayni deseni kullanir.

### Kanonik govde (imzalanan kisim):

```
belge=bilet
cihaz=<cihazKimlik>
seri=<seri>
musteri=<musteri>
urun=<urun>
ver=<1|0>              // 1 = gecerli bilet, 0 = iptal
baslangic=<unix>       // biletin gecerlilik baslangici
bitis=<unix>           // 0 = suresiz
seviye=<seviye>
komut=<kimlik>         // tekrar eden satirlar
kota=<kimlik>;<limit>;<periyot>
paket=<kimlik>;<ad>;<bitis>;<komut,...>
kota=<kimlik>;<limit>;<periyot>
imza=<base64 ECDSA>    // son satir - HARIC
```

### Imza:
```
sha256(kanonik_govde) -> ECDSA P-256 -> ham r||s (64 bayt) -> base64
```

### dogrulama (C++ istemci):

```cpp
UzakPolitikaDurum biletCoz(json, lisansAcikAnahtarHex(), Bilet& b, std::string& hata) {
    // 1. Kanonik govde kur (C++ tarafinda uret - Go ile birebir ayni olmali)
    std::string kanonik = biletKanonik(b);
    // 2. Imzayi dogrula (gomulu ACIK anahtarla)
    if (!kripto::imzaDogrula(kanonik, b.imza, lisansAcikAnahtarHex()))
        return UzakPolitikaDurum::ImzaGecersiz;
    // 3. Zamanlama kontrolu
    if (b.baslangic > suan() + 60) return Bicim;  // gelecek bilet
    if (b.bitis != 0 && suan() > b.bitis) return Gecerli;  // suresi dolmus (yine de imzali)
    return Gecerli;
}
```

**Bilincli karar:** Suresi dolmus bileti de `Gecerli` dondurup **haklari uygulama**.
Cunku imza gecerli = sunucu gercekten gonderdi. Sure kontrolu ayrica yapilir.

### Go tarafi (uretici imzalama):
```go
func biletKanonik(b Bilet) string {
    s := "belge=bilet\n"
    s += "cihaz=" + b.Cihaz + "\n"
    s += "seri=" + b.Seri + "\n"
    // ... sirali, deterministik
    return s
}
```
**Zorunlu:** Go ve C++ kanonik sirasi BIREBIR ayni olmali. Crossover test yazilmali.

---

## 7. ISTEK KULLANIM SENARYOLARI

### 7.1 "Acik makineleri listele"

```
URETICI: Paneli acar -> "Acik Fikocad'ler" tiklanir
SUNUCU  : clients tablosu (cevrimici=true) + lisansCihazlar birlesik
URETICI : Tablo gorur: cihaz, seri, musteri, IP, surum, son gorulme, bilet durumu
```

**Panel sutunlari:**
| Cihaz | Seri | Musteri | Bilgisayar | IP | Surum | Son Gorulme | Bilet | Durum |
|-------|------|---------|------------|----|-------|-------------|-------|-------|
| a1b2.. | FKC-001 | Akcali | AHMED-PC | 1.2.3.4 | 1.0.0 | 12 sn once | 2027-01-15 | Aktif |
| c3d4.. | - | - | FATIH-PC | 5.6.7.8 | 1.0.0 | 5 dk once | YOK | Lisanssiz |

### 7.2 "Uzaktan lisans ver"

```
URETICI: Tabloda satir secer -> "Lisans Ver" -> musteri, komutlar, sure, paket sec
URETICI: Govdeyi kurar + ozel anahtariyla IMZALAR
URETICI: POST /uretici/bilet-uret {cihaz, govde, imza}
SUNUCU  : Imzayi ureticinin public anahtariyla DOGRULAR (gecersizse red)
SUNUCU  : lisansKayitlari'na yazar (audit)
SUNUCU  : O istemciye `lisans-bilet` gonderir
ISTEMCI : Imzayi gomulu acik anahtarla DOGRULAR
ISTEMCI : Haklari bellekte uygular (LisansSistemi)
ISTEMCI : "Bu komut lisansiniza dahil degil" artik gorunmez
```

### 7.3 "Kesintide Ne Olur?" (Izin YOK - Aninda Kisitleme)

**Bu senaryo artik bir OZELLIK DEGIL, sistemin dogal davranisi.**

```
SUNUCU COKER / AG KESILIR
ISTEMCI 5 sn debounce ile dener -> basarisiz
  -> 1. UYARI: "Sunucuya ulasilamiyor - yeniden deneniyor..."
ISTEMCI 5-20 sn arasinda sessizce dener
  -> 2. UYARI (t=20 sn): "30 saniye sonra kilitleneceksiniz"
ISTEMCI 20-50 sn son denemeler
  -> BUM (t=50 sn): goruntuleyici kipi, viewport kilitli
ISTEMCI 60 sn'de bir sessizce yeniden dener
  -> Sunucu donerse otomatik acilir
```

**Panel tarafi:** Cihaz "cevrimdisi" rozetiyle listelenir. Isterseniz
**uzaktan komut** yine gondersiniz - ama **izin degil**. Sadece:
- Lisans uzat
- Komut yetkilendir
- Kilitle / kilit ac
- Kota sifirla

**Sunucu kapaliyken izin vermenin teknik olarak anlami yoktur** (sunucu yok =
iletilemez). Bu yuzden "bekleyen bilet" (tembel dagitim) mantigi da kaldirildi.
### 7.4 "O makineyi hic acma" (KALIR)

```
URETICI: Tabloda satir secer -> "Makineyi Kilitle" (+ mesaj + sure)
SUNUCU  : kararImzala (mevcut mekanizma) veya cihaz bazli genisletme
SUNUCU  : `lisans-engel` gonderir
ISTEMCI : kararUygula -> LisansDurum::UzaktanEngellendi
ISTEMCI : goruntuleyici kipi + kalici banner
ISTEMCI : yeni bilet isteyince sunucu "bu cihaz kilitli" doner (bilet vermez)
```

**Kalicilik:** Karar MongoDB'ye yazilir + `lisans_kararlar.json` (yedek).
Sunucu restart olsa da cihaz yeniden baglaninca karar aninda gonderilir.

### 7.5 "Komut gonder"

```
URETICI: Tabloda "Komut Gonder" -> komut listesi (beyaz liste) + args
SUNUCU  : komutImzala (karar imzalama ile ayni desen)
SUNUCU  : kuyruga yazar, `komut-gonder` ile gonderir
ISTEMCI : IMLA DOGRULAR (gerekli!)
ISTEMCI : LISANS KAPISINDAN GECIREREK calistirir
ISTEMCI : `komut-sonuc` gonderir
SUNUCU  : sonucu DB'ye yazar, panele yansitir
```

**Guvenlik:** Komut hem sunucu tarafinda hem istemcide imzali gelir ve istemcide
beyaz listeye gore filtrelenir. Sahte sunucu (MITM) imzalayamadigi icin komut
yaptiramaz.

---

## 8. REST MI SOCKET/IO MU?

**Mevcut `lisans_canli.cpp` elle yazilmis Socket.IO** (~380 satir, engine.io v3 frame
parsing dahil). Bu, basit bildirim icin yeterli ama lisans bileti + kuyruk +
heartbeat icin kirilir.

### Oneri: IKILI YAKLASIM

```
┌─ SUNUCUDAN → ISTEMIYE (SSE veya WebSocket) ──────────┐
│  Sunucu -> istemci: bilet, engelleme, komut, bilgi  │
│  Tek yonlu push. Istemci sadece dinler.               │
│  SSE daha basit (HTTP, otomatik yeniden baglanma).    │
└──────────────────────────────────────────────────────┘

┌─ ISTEMCIDEN → SUNUCUYA (REST) ──────────────────────┐
│  POST /lisans/hello                                  │
│  POST /lisans/bilet-iste    (yedek, SSE yoksa)        │
│  POST /lisans/heartbeat    (30-60 sn)                 │
│  POST /lisans/komut-sonuc                             │
│  POST /lisans/olay                                    │
└──────────────────────────────────────────────────────┘
```

**Neden SSE (Server-Sent Events)?**
- Tek yonlu push icin HTTP yeterli
- Tarayici/istemci tarafinda otomatik yeniden baglanma
- C++ istemcisi icin: WinHTTP ile okunabilir (zaten `lisans_uzak.cpp` WinHTTP kullaniyor)
- Socket.IO'nun karmaik frame/envelope sorunlari yok

**Neden REST (istemci → sunucu)?**
- C++ istemcide zaten WinHTTP var (`lisans_uzak.cpp:23`)
- Idempotent, tekrar denenebilir
- Kolay test/hata ayiklama

**Ne zaman Socket.IO?** Sadece mevcut `lisans_karar` engelleme akisini korumak icin
(geriye uyum). Yeni akis SSE'ye tasinar.

### Istemci tarafi basitlestirme:
```cpp
// C++ - SSE okuma (WinHTTP ile, basit)
// Aslinda: Sunucu gonderdigi SSE satirlarini ayni HTTP baglantisi uzerinden okur.
// Yeniden baglanma: HTTP 200 donmezse 5 sn sonra tekrar dene.
```

**Not:** SSE icin ozel istemci kodu gerekir ama Socket.IO'ya gore
**cok daha kucuk ve guvenilir**. Mevcut `lisans_uzak.cpp` WinHTTP GET ile calisiyor -
aynı desen.

---

## 9. VERI MODELLEMESI

### 9.1 `lisansCihazlar` (oturum + envanter)

```javascript
{
  _id: ObjectId,
  cihaz: "a1b2c3d4e5f6...",        // cihazKimlikUret() (tekil)
  seri: "FKC-2026-0001",            // biletten gelen
  musteri: "Akcali Dokum",
  bilgisayar: "AHMED-PC",
  surum: "1.0.0",
  pid: 12345,
  ip: "1.2.3.4",
  makine: ["<hash1>", "<hash2>"],   // node-lock adaylari
  yapiHash: "<exe sha256>",

  // Canli durum
  cevrimici: true,
  sonGorulme: ISODate,              // heartbeat
  ilkGorulme: ISODate,
  baglanmaSayisi: 12,

  // Liset durumu
  biletVar: true,
  biletBitis: ISODate,              // 0/suressiz ise null
  biletSeviye: "pro",
  biletKomutlar: 24,
  biletKotalar: [{kimlik, limit, periyot, kullanim}],
  biletPaketler: ["CAM-PRO"],

  // Kilit
  kilitli: false,
  kilitMesaji: "",
  kilitZamani: null,
  kilitSuresiSn: 0,                  // 0 = sure kisitsiz

  // Telemetri
  sonKomutZamani: ISODate,
  sonHata: "",
  acilanBelgeSayisi: 3,

  // Kayit bilgisi
  ilkKayitZamani: ISODate,
  ureticiId: "<hangi uretic>"

}
// INDEX: {cihaz: 1} unique
// INDEX: {seri: 1}
// INDEX: {cevrimici: 1, sonGorulme: -1}
```

### 9.2 `lisansKayitlari` (bilet deposu)

```javascript
{
  _id: ObjectId,
  biletId: "uuid",
  cihaz: "a1b2...",
  seri: "FKC-2026-0001",
  musteri: "Akcali Dokum",
  seviye: "pro",
  gecerlilik: "kalici" | "gecici",
  baslangic: ISODate,
  bitis: ISODate,                    // gecici lisansta ZORUNLU
  komutlar: ["geometri.*", "cam.gkod"],
  kotalar: [{kimlik:"cam.gkod", limit:100, periyot:2592000}],
  paketler: [{kimlik:"CAM-PRO", ad:"CAM Pro", bitis:ISODate, komutlar:[...]}],

  // Imza
  govde: "belge=bilet\ncihaz=...\n...",   // kanonik
  imza: "<base64 ECDSA>",
  ureticId: "uretici-makine-1",
  ureticiPublicKey: "<hex>",        // hangi anahtarla imzalandi

  // Durum
  durum: "aktif" | "sureDoldu" | "iptal" | "degistirildi",
  iptalZamani: null,
  iptalNedeni: "",

  // Audit
  olusturan: "<admin ObjectId>",
  olusturmaZamani: ISODate
}
// INDEX: {cihaz: 1, durum: 1}
// INDEX: {seri: 1}
// TTL: gecici biletlerde {bitis: 1} expireAfterSeconds: 0 (otomatik temizlik)
```

### 9.3 `lisansOlaylari` (audit - degismez)

```javascript
{
  _id: ObjectId,
  zaman: ISODate,
  tip: "baglandi" | "koptu" | "biletVerildi" | "biletDoldu" |
       "kilitlendi" | "kilitAcildi" | "komutGonderildi" | "komutCalisti" |
       "kotaAsildi" | "hata",
  cihaz: "a1b2...",
  seri: "FKC-2026-0001",
  yetkili: "<admin ObjectId>",       // kim yapti
  detay: {...},
  ip: "1.2.3.4"
}
// INDEX: {zaman: -1}
// INDEX: {cihaz: 1, zaman: -1}
```

### 9.4 `lisansKomutKuyrugu` (komut teslimi)

```javascript
{
  _id: ObjectId,
  komutId: "uuid",
  cihaz: "a1b2...",
  komut: "dosya.ac",                 // BEYAZ LISTEDEN
  args: { yol: "C:\\x.step" },
  durum: "Kuyrukta" | "Gonderildi" | "Calisti" | "Basarili" | "Hata" | "ZamanAsimi",
  gonderen: "<admin>",
  gonderilme: ISODate,
  calistirilma: ISODate,
  bitis: ISODate,
  sonuc: { basarili: true, mesaj: "...", cikti: "..." },
  timeoutSn: 300
}
// INDEX: {cihaz: 1, durum: 1}
// INDEX: {komutId: 1} unique
```

---

## 10. GUVENLIK ANALIZI - BU MODELDEKI RISKLER

`.fkl` dosyasini birakmanin **bedeli** ve kazanimi:

### 10.1 KAZANIMLAR

| # | Kazanim | Aciklama |
|---|---------|----------|
| 1 | Aninda lisans verme | Dosya gonderme/kopyalama yok, panelden tek tik |
| 2 | Kisa sureli uyari | 2 uyari + **30 sn** sonra kilit (sure sunucu belirler) - lisans sizintisi sifir |
| 3 | Tam envanter | Kac musteri, hangi makinede, ne kullaniyor - anlik |
| 4 | Recurring gelir | "Bu musteriye 1 saatlik ver" = kiralama modeli |
| 5 | Cihaz bazli kilit | Seri degil, spesifik cihaz engellenir |
| 6 | Uzaktan komut | Destek: "dosyani bana gonder", "kapat", "yeniden baslat" |
| 7 | Kota uzaktan sifirlama | Kota asildiysa panelden "sifirla" |
| 8 | Lisans iptali | Aninda, geriye donuk olmadan |
| 9 | Kullanilabilirlik | Musteri "lisansim bitti" diye aramak zorunda degil |

### 10.2 KAYIPLAR / YENI RISKLER

| # | Risk | Etki | Cozum |
|---|------|------|-------|
| 1 | **Sunucu = tek nokta** | Sunucu cokerse tum musteriler kilitlenir | Asama 0 guvenlik duzeltmeleri + 2 sunucu (HA) + Redis |
| 2 | **Kisitli internet** | Fabrika/ofis ici kapali ag → lisan yenilenmez | Kisa sure (~50 sn) uyari sonrasi kilit (bkz 10.3) - **izin YOK** |
| 3 | **Saat manipulasyonu** | "1 saatlik" lisans saat degistirerek uzatilir | Sunucu saati esas + `baslangic > suan+60` kontrolu |
| 4 | **Replay** | Ayni bilet tekrar kullanilir | Sunucu tarafta "bu bilet verildi mi" kontrolu + nonce |
| 5 | **MITM sahte bilet** | Sahte sunucu lisans enjekte eder | Istemci **her zaman imza dogrular** (katman kaldirilmaz) |
| 6 | **Bileti calma** | Musteri baska makinede kullanir | Cihaz kimligi (node-lock) bilet icinde imzali |
| 7 | **Yuksek yuk** | 1000 musteri = surekli heartbeat | SSE verimli; heartbeat 30 sn; Redis cache |
| 8 | **KVKK** | Cihaz parmak izi toplaniyor | Aydinlatma metni + veri silme endpoint'i |
| 9 | **Sunucu sifirlamasi** | DB sifirlanirsa tum lisanslar gider | Gunluk yedek + bilet yeniden uretimi (uretici elinde) |
| 10 | **Sunucu tek nokta** | 50 sn icinde TUM musteriler kilitlenir | **Zorunlu:** 2 sunucu (HA) + saglik izleme + otomatik yonlendirme + istemci sunucu listesi |

### 10.3 ONLINE-FIRST: KESINTIDE IZIN YOK - ANINDA KISITLAMA (REVIZE v2)

**GUNCEL KAPSAM (kullanici gereksinimi kesinlestirildi):**

1. **Bagli oldugu surece AKTIF** - her komut calisir
2. **Baglanti koptu** -> **KISA sure**, **1-2 kez uyari**
3. **Hemen KISITLANIR** (kilit) - izin YOK
4. **Onceden tasarlanan "3-5 saatlik cevrimdisi izin" IPTAL EDILDI**
5. Kilitliyken bile **sessizce yeniden baglanmayi denemeye devam** (kablo geri
   takilirsa otomatik acilsin)

**Bu, onceki "72 saat grace" de degil, "3-5 saat izin" de degil.**
Artik: **SIFIR tolerans.** Bagli degilsin = calismiyorsun.

### 10.3.1 Kesintide Izin Verilmez - Neden?

| Eski Model | Yeni Model |
|-----------|-----------|
| Kopunca 72 saat calisir | Kopunca ~30 sn sonra kilit |
| 3-5 saatlik izin verilebilir | Izin verilMEZ |
| Sunucu cokerse musteriler gunlerce calisir | Sunucu cokerse 30 sn'de herkes kilitlenir |

**Bu bilincli ve guvenli bir tercih:**
- Lisans sizintisi sifir (kopan an durur)
- Sunucu trafigi anormal artarsa aninda fark edilir
- "Lisansim var ama calismiyor" destek yukunu KABUL ediyorsunuz
- Müsteriye satista "internet zorunlu" denmeli

### 10.3.2 Durum Makinesi (3 durum + tolerans)

```
        baglanti kuruldu
   +------------------------+
   |                        v
+--+---------+        +-----------+
|  AKTIF    |        |  KOPUK    |  <- tolerans (2-5 sn, SESSIZ)
|  her sey  |        |  TESPIT   |     dalgalanma filtresi
|  acik     |        +-----------+
+-----+-----+              |
      |                    | kopma TEYIT EDILDI
      |                    v
      |            +-------------+
      |            |   UYARI     |  <- KISA sure (~30 sn)
      |            | 1. uyari    |     1-2 kez mesaj
      |            | 2. uyari    |
      |            +-------------+
      |                    | sure doldu (veya ikinci uyari sonrasi)
      |                    v
      |            +-------------+
      +------------|   KILITLI   |  <- BUM, viewport kilitli
   sunucu dondu   | sessiz 60sn |     60 sn'de bir baglanmayi dene
   (otomatik)     | deneme      |
                  +-------------+
```

### 10.3.3 Sureyi Sunucu Belirler (Varsayilan 30 SN)

**Temel karar:** Kopma suresi istemciye gomulu DEGIL, **sunucu belirler ve
heartbeat ile her seferinde gonderir.**

```
Varsayilan: kopmaSuresi = 30 saniye
Sunucu 30 sn dediyse istemci 30 sn bekler, olmazsa KILIT
Sunucu 120 sn dediyse istemci 120 sn bekler
```

**Neden sunucu belirlemeli:**

| Sebep | Aciklama |
|-------|----------|
| Acil durum | Sunucu coktiginde 30 sn, bakimda 300 sn yapabilirsiniz - istemci guncellemesi gerekmez |
| Toplu kazaya müdahale | 500 musteri ayni anda kildi - 30 sn yerine 120 sn verip calismaya devam ettirirsiniz |
| Farkli musteri profilleri | Kurumsal musteri 60 sn, ev kullanicisi 20 sn |
| Test/deneme | Yeni modeli 300 sn ile denersiniz, guvendeyseniz 30 sn'e cekersiniz |
| Merkezi yonetim | Tum politikada tek noktadan degisir |

**Ayarlanabilir parametreler (hepsi sunucuda):**

| Parametre | Varsayilan | Aralik | Aciklama |
|-----------|-----------|--------|----------|
| `kopmaSuresi` | **30** | 15-600 | Toplam bekleme -> KILIT |
| `uyari1` | 5 | 0-300 | 1. uyari bu sn'de |
| `uyari2` | 15 | 0-600 | 2. uyari bu sn'de |
| `kilitliDeneme` | 60 | 30-600 | Kilitliyken baglanma araligi |
| `heartbeatEsik` | 45 | 20-300 | Bu kadar sn mesaj yoksa KOPUK say |

**Istisna kurali:** `uyari1` ve `uyari2` **hep `kopmaSuresi`'den kucuk olmali.**
Sunucu bu kurali dogrular, gecersiz deger gelirse `kopmaSuresi`'yi 30'a sabitler.

**Varsayilan deger dagilimi (30 sn icinde 2 uyari):**
```
t=0     kopma tespit
t=0-5   DEBOUNCE (sessiz, yeniden baglanma denemesi)
t=5     1. UYARI
t=5-15  sessizce dene
t=15    2. UYARI
t=15-30 sessizce dene
t=30    KILIT (BUM)
```

### 10.3.4 "Aninda" Ne Kadar Aninda? (Teknik Sinir)

Sunucu belirlesi de, **kopmanin fark edilmesi** teknik bir gecikme gerektirir:

| Kopma Tipi | Tespit Suresi | Kaynak |
|------------|---------------|--------|
| **Sunucu process oldu** | ~0 sn | Socket close event |
| **TCP reset** | ~0 sn | Hata doner |
| **Kablo cekildi / router reboot** | `heartbeatEsik` sn | Heartbeat yanitsiz |
| **Internet kesildi (wifi)** | `heartbeatEsik` sn | Heartbeat yanitsiz |
| **Firewall / NAT oturumu dustu** | `heartbeatEsik` sn | Heartbeat yanitsiz |

**Varsayilan `heartbeatEsik = 45 sn`:** Sunucu 30 sn'de bir `durum` gonderir,
istemci 45 sn sessiz kalirsa kopmus sayar. Boylece bir heartbeat geciktiginde
(false alarm) yanlis pozitif olusmaz.

**Kritik:** **Sureyi 30 sn yaptiginizda TESPIT suresi hala 45 sn'dir.**
Yani pratikte:
- Sunucu cokarsa (socket kapandi) -> **0 sn** -> 30 sn bekle -> kilit = **~30 sn**
- Kablo cekildiyse -> **45 sn** tespit + 30 sn bekle = **~75 sn**

Isterseniz `heartbeatEsik`'i 20 sn yaparsaniz (sunucu 10 sn'de bir gonderir),
kablo kopmasi 50 sn'de kilitlenir. Ama yanlis pozitif (kisa ag tikilmasi) riski artar.

**OZET:** 30 sn, sunucu coktugunda gecerli. Sessiz ag kesintilerinde tespit
eklenir. Tam "0 sn" ulasmak mumkun degil (fiziksel sinir).

### 10.3.5 Mevcut Koddaki Debounce - Korunmali

`lisans_canli.cpp:39` - `YENIDEN_BAGLANMA_MIN_MS = 5000`. Zaten 5 sn debounce var:
kablo 1 sn kopup 4 sn'de gelirse kullanici **hic kilitlenmez**.

**Bu debounce, sunucudan gelen `kopmaSuresi`'nin ICINDE kalir.** Yani sunucu
30 sn dediyse:
```
t=0     kopma
t=0-5   debounce (sessiz deneme) - bu arada baglanirsa hic uyari olmaz
t=5     1. uyari  (kalandan 25 sn)
t=15    2. uyari  (kalandan 15 sn)
t=30    KILIT
```

**Tasarim karari:** Mevcut debounce mekanizmasi **AYNEN KORUNUR.** Sadece
"ne zaman kilitleyecegim" sorusunun cevabi artik `kopmaSuresi` degiskeni.
### 10.3.6 UYARI MESAJLARI (Iki Kere)

**1. Uyari (t=5 sn):**
```
UI: viewport banner (ust kisim) + tek toast
Metin: "Sunucuya ulasilamiyor - yeniden deneniyor... Lisanciniz {X} saat gecerli"
Renk:  sari (uyari)
Sure:  kalici gorunur, baglanti kurulunca kaybolur
```

**2. Uyari (t=20 sn):**
```
UI: viewport banner (daha belirgin, kirmiziya yakin) + tek toast
Metin: "SON UYARI: Lisans sunucusu yanit vermiyor - 30 saniye sonra kilitleneceksiniz"
Renk:  kirmizi
Sure:  kalici
```

**KILIT (t=50 sn):**
```
UI: viewport tam kaplama (mevcut goruntuleyici kipi ekrani)
Metin: "Lisans sunucusuna ulasilamiyor. Program kilitlendi."
       "Baglanti geri geldiginde otomatik olarak acilacaktir."
Eylem: "Tekrar Dene" butonu (manuel baglanma tetikler)
       "Baglanti Durumu" bilgisi (son deneme, hata mesaji)
```

**Onemli:** Kilit ekraninda bilgi gorunur olmali - musteri "program coktu" sanmasin.
Ayrinti: "Sunucu: fikoyt.com:9000 - Son deneme: 12:34:56 - Hata: timeout"

### 10.3.7 Uzaktan Izin verme - IPTAL

Onceki tasarimdaki "cevrimdisi 3-5 saatlik izin verme" **tamamen kaldirildi.**

**Gerekce:**
- Sunucu coktugunda izin vermenin teknik olarak anlami yok (sunucu yok = iletilemez)
- Zaten "belirli bir tarihe kadar izin" istemistiniz, sonra "hayir izin olmayacak"
  dediniz -> en tutarli davranis: izin mekanizmasi hic olmamasi

**Sonuc:** Sunucu tarafi "bekleyen bilet" (tembel dagitim) mantigi gereksiz.
Her bilet dogrudan, cihaz bagliyken gonderilir.

**Degisenler:**
- `izin-bilet` mesaji: KALDIRILDI
- `bekleyen-bilet` mesaji: KALDIRILDI
- `izin-ver` (uretici -> sunucu) mesaji: KALDIRILDI
- Uretici panelindeki "Izin Ver" butonu: KALDIRILDI
- Sunucu DB'sindeki `bekleyenBilet` alani: KALDIRILDI

**Kalan uzaktan yetenekler:**
| Yetenek | Durum |
|---------|-------|
| Lisans uzatma | KALIR (cihaz bagliyken) |
| Komut yetkilendirme | KALIR |
| Kilitleme / kilit acma | KALIR |
| Kota sifirlama | KALIR |
| Uzaktan komut gonderme | KALIR |
| Ayar degistirme | KALIR |
| **Cevrimdisi izin verme** | **KALDIRILDI** |

### 10.3.8 Sunucu Kapandiysa Ne Olur? (Blok Yonetimi)

Bu artik **kritik** bir konu. 30-60 sn icinde tum musteriler kilitlenir.

**ZORUNLU ONLEMLER:**

1. **Yedek sunucu (HA):**
```
fikoyt-sunucu-1  (primary)   :9000
fikoyt-sunucu-2  (secondary) :9000
Load balancer / DNS: fikoyt.com
Redis: paylasimli durum (her iki instance ayni Redis'i gorur)
MongoDB: zaten replicaSet=rs0 (veri paylasimli)
```

2. **Saglik izleme + otomatik devre:**
```
Her 10 sn: GET https://fikoyt.com/lisans/health
Esik: 3 ust uste basarisiz -> yonlendirici digerine cevirir
Cevrim: basari -> primary'ye don
```

3. **Istemci tarafinda birincil sunucu listesi:**
```cpp
// lisans_canli.cpp
constexpr const char* SUNUCU_LISTESI[] = {
    "https://fikoyt.com:9000",
    "https://fikoyt-backup.com:9000",   // yedek
    // gomulu liste - musteri degistiremez
};
// Sirayla dener, ilk calisan kazanir
```

4. **Bildirim:** Sunucu coktugunde musteriye "baglanti sorunu" degil
   "sunucu erisilemiyor" mesaji gosterilmeli (musteri sunucu sorununu anlasin).

**Bu onlemler olmadan, tek bir sunucu coktugunda 30 sn icinde TUM musteriler
kilitre girer.** Bu, operasyonel olarak kabul edilebilir olmali.
### 10.4 "Hic Acilmasin" - Ne Kadar Sert?

Istege bagli iki seviye:

```
Seviye 1 - ENGELLE (mevcut):  Tum komutlar kilitli, sadece lisans ekrani
Seviye 2 - SIFIRLA:           Program acilir, lisans yuklenir, sonra HATA
```

**Oneri:** Seviye 1 (mevcut `goruntuleyiciKipiUygula`). Kullaniciyi tamamen
kapatmak yerine "lisans yok" gostermek daha profesyonel ve destek yukunu azaltir.

Ek: **sureli kilit** ile "bu cihaz 2 saat kilitli" yapabilirsiniz (demo/servis icin).

---

## 11. API SOZLESMESI (Go tarafi)

### 11.1 Uretici (uretici makinesi)

```
POST /fapiv1/lisans-uretici/hello        {ureticiId, imza}
POST /fapiv1/lisans-uretici/cihazlar     {filtre: {cevrimici, seri, musteri}}
POST /fapiv1/lisans-uretici/bilet-uret    {cihaz, biletGovde, imza}
POST /fapiv1/lisans-uretici/bilet-iptal   {cihaz, seri}
POST /fapiv1/lisans-uretici/kilit-ac     {cihaz, mesaj}
GET  /fapiv1/lisans-uretici/olaylar       {cihaz, limit}
```

**Guvenlik:** Her istek `ureticiId` + `imza` ile gelir. Sunucu ureticinin public
anahtariyla dogrular. `adminOnly` de uygulanir (session + yonetici).

### 11.2 Musteri istemci (fikocad)

```
POST /fapiv1/lisans-istemci/hello        {cihaz, seri, musteri, surum, pid, makine[], yapiHash}
GET  /fapiv1/lisans-istemci/bilet        (Authorization: cihaz imzasi)
POST /fapiv1/lisans-istemci/heartbeat    {cihaz, aktif, komutSayisi, sonKomutZamani, kotaKullanim}
POST /fapiv1/lisans-istemci/komut-sonuc  {komutId, basarili, mesaj, sureMs}
POST /fapiv1/lisans-istemci/olay          {tip, detay}
GET  /fapiv1/lisans-istemci/stream        (SSE - bilet/komut/kilit bildirimi)
```

**Guvenlik:** `/hello` dogrulama **yok** (ilk temas). Sonraki istekler
`cihaz` + kisa omurlu token (JWT benzeri, HMAC ile imzali) ile dogrulanir.

### 11.3 SSE Endpoint (suncudan → istemciye)

```
GET /fapiv1/lisans-istemci/stream?cihaz=a1b2...&token=...

event: bilet
data: {"seri":"...","komutlar":[...],"bitis":...,"imza":"..."}

event: engel
data: {"engelli":true,"mesaj":"...","imza":"..."}

event: komut
data: {"komutId":"...","komut":"dosya.ac","args":{...},"imza":"..."}

event: bilgi
data: {"mesaj":"Lipsiniz 3 gun sonra doluyor","tip":"uyari"}

event: ping
data: {"t":1699999999}
```

**SSE avantaji:** HTTP tabanli, otomatik yeniden baglanma, C++ istemcisi WinHTTP
ile dogrudan okur. Socket.IO'nun karmaik frame/envelope sorunu yok.

---

## 12. ISTEMCI TARAFI DEGISIKLIKLER (C++)

### 12.1 `lisans_yukle.cpp` - Kaynak Degisikligi

Mevcut: `.fkl` dosyasi yukler (3 kaynak: env/exe yan/ProgramData).
Yeni: **Sunucudan bilet alir**, dosya yok.

```cpp
const LisansOturum& lisansYukleVeUygula(LisansSistemi& sis) {
    // 1. Sunucudan bilet iste (SSE/REST)
    // 2. Imzayi dogrula
    // 3. Haklari uygula
    // 4. Kopma tespitini baslat (heartbeat + durum makinesi)

    // ESKI YOL (geriye uyum, opsiyonel):
    // .fkl varsa onu da yukle (birlestir)
}
```

### 12.2 Kopma Tespiti ve Sureler (ONLINE-FIRST)

Onceki "grace period" mekanizmasinin yerini alan yapi. **Izin/grace YOK - sadece
tespit, uyari ve kilit.**

```cpp
// Kopma tespiti iki katmanli:
// 1. Socket event'i  -> aninda tespit
// 2. Heartbeat       -> sessiz kopma tespiti (60-90 sn)

void kopmaTespitEt(const std::string& hata) {
    // 1. Katman: socket kapandi
    baglantiKoptu();
    gunluk::uyari("Kopma tespit: {}", hata);
}

// Istemci tarafi heartbeat (her 30 sn sunucu "durum" gonderir)
void heartbeatKontrolu() {
    static i64 sonMesaj = suanUnix();
    if (suanUnix() - sonMesaj > 90) {
        // 90 sn'dir sunucudan mesaj yok -> baglanti koptu
        baglantiKoptu();
    }
}

// Baglanti acildiginda
void heartbeatBaslat() {
    static i64 sonMesaj = 0;
    sonMesaj = suanUnix();
    // Her 1 sn kontrol: 90 sn sessizlik esigi asildi mi
    // Sunucu "durum" mesaji geldikce sonMesaj = suanUnix()
}
```

**Sureler (`KopmaAyarlari` - uretici panelinden ayarlanabilir):**

```cpp
struct KopmaAyarlari {
    int debounceSn       = 5;    // sessiz tolerans (mevcut kodda zaten var)
    int uyari1Sn         = 5;    // 1. uyari          -> SUNUCUDAN GELIR
    int uyari2Sn         = 15;   // 2. uyari          -> SUNUCUDAN GELIR
    int kopmaSuresi      = 30;   // toplam -> KILIT   -> SUNUCUDAN GELIR
    int kilitliDenemeSn  = 60;   // kilitliyken aralik -> SUNUCUDAN GELIR
    int heartbeatEsikSn  = 45;   // sessiz kopma tespiti -> SUNUCUDAN GELIR
};

**Debounce onemi:** Mevcut kod `YENIDEN_BAGLANMA_MIN_MS = 5000` ile zaten 5 sn
tolerans sagliyor. Kablo 1 sn kopup 4 sn'de gelirse kullanici hic kilitlenmez.
Bu davranis **korunmali** - yanlislikla silinmemeli.

**Kilitliyken 60 sn sabit:** Ustal geri cekilmeyi (5→10→20→40→60→60...) durdur,
sabit 60 sn kullan. Kullanici kabloyu geri takip sunucu 30 sn sonra acilmali -
5 dakika beklemenin anlami yok.
### 12.3 Komut Alma ve Calistirma

```cpp
// lisans_canli.cpp - SSE veya mevcut Socket.IO
void komutGeldi(const KomutMesaji& k) {
    // 1. IMZAYI DOGRULA (zorunlu)
    if (!komutImzaDogrula(k)) {
        gunluk::uyari("Komut reddedildi (gecersiz imza)");
        return;
    }
    // 2. BEYAZ LISTE KONTROLU
    if (!beyazListeKontrol(k.komut)) {
        komutSonucYolla(k.komutId, false, "Yetkisiz komut");
        return;
    }
    // 3. LISANS KAPISINDAN GECIR
    if (!lisansSistemi().komutIzinli(k.komut)) {
        komutSonucYolla(k.komutId, false, "Lisansiniz bu komutu kapsamiyor");
        return;
    }
    // 4. SAGLIK KONTROLU
    lisansSaglikKontrol();
    // 5. CALISTIR (merkezi komut akisindan)
    cekirdek.komutCalistir(oge, "uzaktan");
    // 6. SONUC
    komutSonucYolla(k.komutId, basarili, mesaj);
}
```

**Kritik:** Adim 3 ve 4 olmadan komut calistirmak = **uzaktan kod calistirma aciigi**.
Bu adimlar olmadan uzaktan komut ozelligi guvenlik acigi olur.

### 12.4 Beyaz Liste (Guvenlik)

```cpp
constexpr const char* UZAK_KOMUT_BEYAZ[] = {
    "dosya.ac",           // Dosya ac
    "dosya.kaydet",       // Kaydet
    "dosya.fark",         // Degisiklik raporu
    "gorunum.tumunu_sigdir",
    "gorunum.secimi_cercevele",
    "arac.guncelleme",    // Kontrol
    "arac.geri_bildirim",
    "sistem.temizle",     // Onbellek temizle
    // TEHLIKELI OLANLAR: asla
    // "dosya.sil", "arac.kapat", "ayar.sifirla", ...
};
```

**Kural:** Sadece **veri okuyan** ve **kaydeden** komutlar. Silme, sifirlama,
ayar degistirme gibi komutlar **haric** (manuel mudahale).

---

### 12.5 ONLINE-FIRST DURUM MAKINESI (SURE SUNUCUDAN)

**Kopma suresi istemcide sabit degil, sunucudan gelir.** Derleme zamani gomulu
deger sadece "sunucu hic ulasilamazsa" guvenli azami icin vardir.

```cpp
// ===== Kopma ayarlari - SUNUCUDAN GELIR =====
struct KopmaAyarlari {
    int kopmaSuresi   = 30;   // gomulu guvenli azami
    int uyari1        = 5;
    int uyari2        = 15;
    int kilitliDeneme = 60;
    int heartbeatEsik = 45;
};

// ===== Durum tanimlari =====
enum class BaglantiDurumu : u8 { AKTIF, DEBOUNCE, UYARI, KILITLI };

// ===== Global durum =====
static BaglantiDurumu g_durum = BaglantiDurumu::AKTIF;
static KopmaAyarlari  g_ayar;             // son sunucudan gelen degerler
static i64 g_kopmaAni = 0;
static int g_uyariSayisi = 0;
static std::atomic<bool> g_beklemede{false};
static std::atomic<i64> g_sonHeartbeat{0};
```

#### Sunucudan sure alma (heartbeat isleyicisinde):

```cpp
// lisans_canli.cpp - "durum" mesaji gelince cagrilir
void durumMesajiIsle(const std::string& json) {
    long long kopmaSuresi = 0, uyari1 = 0, uyari2 = 0;
    long long kilitliDeneme = 0, heartbeatEsik = 0;

    if (jsonSayiAl(json, "kopmaSuresi", kopmaSuresi) &&
        jsonSayiAl(json, "uyari1", uyari1) &&
        jsonSayiAl(json, "uyari2", uyari2) &&
        jsonSayiAl(json, "kilitliDeneme", kilitliDeneme) &&
        jsonSayiAl(json, "heartbeatEsik", heartbeatEsik)) {

        // --- GUVENLIK DOGRULAMASI (istemci de dogrular) ---
        if (kopmaSuresi < 15) kopmaSuresi = 15;          // alt sinir
        if (kopmaSuresi > 600) kopmaSuresi = 600;        // ust sinir (izin olmaz)
        if (uyari1 < 0) uyari1 = 0;
        if (uyari2 <= uyari1) uyari2 = uyari1 + (kopmaSuresi - uyari1) / 2;
        if (uyari2 >= kopmaSuresi) uyari2 = kopmaSuresi - 5;
        if (kilitliDeneme < 30) kilitliDeneme = 30;

        std::lock_guard lk(g_mtx);
        g_ayar.kopmaSuresi   = (int)kopmaSuresi;
        g_ayar.uyari1        = (int)uyari1;
        g_ayar.uyari2        = (int)uyari2;
        g_ayar.kilitliDeneme = (int)kilitliDeneme;
        g_ayar.heartbeatEsik = (int)heartbeatEsik;
    }
    // Sunucu sure gondermezse ELDEKI deger korunur (son bilinen kurali)
    g_sonHeartbeat = (i64)std::time(nullptr);
}
```

#### Durum gecisleri:

```cpp
// Baglanti KURULDU
void baglantiKuruldu() {
    {
        std::lock_guard lk(g_mtx);
        g_durum = BaglantiDurumu::AKTIF;
        g_kopmaAni = 0;
        g_uyariSayisi = 0;
    }
    g_beklemede = false;
    lisansBanneriKapat();
    gunluk::bilgi("Baglanti kuruldu - AKTIF (kopma suresi {} sn)", g_ayar.kopmaSuresi);
    g_taze = true;
}

// Baglanti KOPTU
void baglantiKoptu() {
    std::lock_guard lk(g_mtx);
    if (g_durum == BaglantiDurumu::DEBOUNCE) return;
    g_durum = BaglantiDurumu::DEBOUNCE;
    g_kopmaAni = (i64)std::time(nullptr);
    g_uyariSayisi = 0;
    gunluk::uyari("Baglanti koptu - {} sn sure basliyor", g_ayar.kopmaSuresi);
    // HICBIR UI GOSTERME - sessizce
}

// Heartbeat esigi asildi mi? (sessiz kopma tespiti)
void heartbeatKontrolu() {
    if (g_durum == BaglantiDurumu::AKTIF) {
        const i64 esik = g_ayar.heartbeatEsik;
        if (esik > 0 && (i64)std::time(nullptr) - g_sonHeartbeat > esik) {
            gunluk::uyari("Heartbeat {} sn yok - baglanti koptu sayiliyor", esik);
            baglantiKoptu();
        }
    }
}
```

#### Durum degerlendirme (pompa thread - her 1 sn):

```cpp
void lisansDurumDegerlendir() {
    heartbeatKontrolu();

    if (g_durum == BaglantiDurumu::AKTIF) return;

    std::lock_guard lk(g_mtx);
    const i64 gecen = (i64)std::time(nullptr) - g_kopmaAni;

    switch (g_durum) {
    case BaglantiDurumu::DEBOUNCE:
        if (gecen >= g_ayar.uyari1) {
            g_durum = BaglantiDurumu::UYARI;
            g_uyariSayisi = 1;
            lk.unlock();
            lisansBanneriGoster("Sunucuya ulasilamiyor - yeniden deneniyor...");
            ui::toastGoster("Lisans sunucusuna ulasilamiyor", ui::ToastTip::Uyari);
            gunluk::uyari("Uyari 1 (kopma {} sn)", g_ayar.kopmaSuresi);
            lk.lock();
        }
        return;

    case BaglantiDurumu::UYARI:
        if (gecen >= g_ayar.kopmaSuresi) {
            g_durum = BaglantiDurumu::KILITLI;
            g_beklemede = true;
            lk.unlock();
            goruntuleyiciKipiUygula(lisansSistemi());
            g_oturum.durum = LisansDurum::BaglantiKoptu;
            g_oturum.hata = "Lisans sunucusuna ulasilamiyor";
            gunluk::uyari("KOPMA: {} sn sonra kilitlendi", g_ayar.kopmaSuresi);
            lk.lock();
            return;
        }
        if (gecen >= g_ayar.uyari2 && g_uyariSayisi < 2) {
            g_uyariSayisi = 2;
            lk.unlock();
            const int kalan = g_ayar.kopmaSuresi - (int)gecen;
            lisansBanneriGoster("SON UYARI: {} saniye sonra kilitleneceksiniz", kalan);
            ui::toastGoster("Lisans sunucusu yanit vermiyor - program kilitlenecek",
                           ui::ToastTip::Hata);
            gunluk::uyari("Uyari 2: {} sn kaldi", kalan);
            lk.lock();
        }
        return;

    case BaglantiDurumu::KILITLI:
    case BaglantiDurumu::AKTIF:
        return;
    }
}
```

### 12.6 Yeniden Baglanma Araligi - Sunucudan Gelen Degere Gore

```cpp
DWORD yenidenBaglanmaBeklemesi() {
    KopmaAyarlari a;
    BaglantiDurumu d;
    {
        std::lock_guard lk(g_mtx);
        a = g_ayar;
        d = g_durum;
    }
    switch (d) {
    case BaglantiDurumu::AKTIF:
        return YENIDEN_BAGLANMA_MIN_MS;         // 5 sn

    case BaglantiDurumu::DEBOUNCE:
        return 2000;                             // 2 sn (hizli - belki 1 sn'de gelir)

    case BaglantiDurumu::UYARI:
        // Kalan sureyi hesapla - sure dolmadan denemeye calis
        {
            const i64 kalan = a.kopmaSuresi - ((i64)std::time(nullptr) - g_kopmaAni);
            if (kalan <= 2) return 2000;          // son anda sik
            if (kalan <= 10) return 3000;         // kalan az
            return 5000;                          // normal
        }

    case BaglantiDurumu::KILITLI:
        return (DWORD)a.kilitliDeneme * 1000;    // sunucu belirledi (varsayilan 60 sn)
        // ONEMLI: ustel geri cekilme UYGULANMAZ
    }
    return YENIDEN_BAGLANMA_MIN_MS;
}
```

**Kilitliyken neden sabit aralik?** Kullanici kabloyu geri takip sunucu en geç
`kilitliDeneme` sn sonra acilmali. Ustal geri cekilme (5→10→20→40→60→120→240 sn)
kullaniciyi saatlerce beklettirir - yanlis.
### 12.7 Kilit Ekrani

```cpp
// goruntuleyici kipi ekraninda gosterilecek bilgi
void kilitEkraniCiz() {
    ImGui::TextColored(t.hata, "Lisans sunucusuna ulasilamiyor");
    ImGui::Separator();
    ImGui::Text("Program kilitlendi. Baglanti geri geldiginde");
    ImGui::Text("otomatik olarak acilacaktir.");
    ImGui::Spacing();
    ImGui::Text("Sunucu      : %s", sunucuAdresi().c_str());
    ImGui::Text("Son deneme  : %s", sonDenemeZamani().c_str());
    ImGui::Text("Hata        : %s", sonHata().c_str());
    ImGui::Text("Kalan deneme: her %d sn'de bir", g_ayar.kilitliDenemeSn);
    ImGui::Spacing();
    if (ui::buton("Tekrar Dene", ...)) {
        g_yenidenBaglanIste = true;   // manuel tetikle
    }
}
```

**Neden detay gostermek onemli:** Musteri "program coktu" sanmamali.
"Sunucuya ulasilamiyor - internetinizi kontrol edin" net mesaji verilmeli.
Bu, destek cagrilarinin yarisini onler.

### 12.8 Gorev (POMPA) THREAD

```cpp
void lisansPompaDongusu() {
    for (;;) {
        std::this_thread::sleep_for(std::chrono::seconds(1));

        // Uzak politika degisikligi
        if (uzakPolitikaTazeKontrol()) lisansYukleVeUygula(lisansSistemi());
        // Canli karar tazeligi (kill-switch)
        if (uzakCanliTazeKontrol())   lisansYukleVeUygula(lisansSistemi());
        // Online-first durum degerlendirmesi (yeni)
        lisansDurumDegerlendir();
    }
}
```
## 13. GECIS PLANI

### Asama 0 - ACIL GUVENLIK (1-2 gun, BLOKLAYICI)

Sunucuyu acmadan once yapilmali. Detay: `SUNUCU_INCELEME_RAPORU.md` Bolum 5.

| # | Is | Neden |
|---|-----|-------|
| 0.1 | `tokenTamam` fail-closed | Panel su an acik! |
| 0.2 | compose'a `LISANS_ADMIN_TOKEN` | Ayni |
| 0.3 | `aggregate` yetki kontrolu | Tam DB erisimi acik |
| 0.4 | `mongo_uri` loglama | Parola siziyor |
| 0.5 | Redis portu internal | Gereksiz disariya acik |

### Asama 1 - SUNUCU ALTYAPI (3-4 gun)

| # | Is |
|---|-----|
| 1.1 | MongoDB semasi (4 koleksiyon + indeks) |
| 1.2 | Uretici API'si (hello, cihazlar, bilet-uret) - imza dogrulamali |
| 1.3 | Istemci API'si (hello, bilet, heartbeat) |
| 1.4 | SSE endpoint |
| 1.5 | Admin panel (mevcut `/lisans-kontrol` genisletilerek) |

### Asama 2 - ISTEMCI (5-7 gun)

| # | Is |
|---|-----|
| 2.1 | SSE/WS istemcisi + heartbeat + kopma tespiti |
| 2.2 | Bilet imza dogrulama + uygulama |
| 2.3 | Online-first durum makinesi (DEBOUNCE->UYARI->KILITLI) |
| 2.4 | Heartbeat + komut sonuc |
| 2.5 | Komut alma + beyaz liste + gate |

### Asama 3 - URETICI ARACI (3-4 gun)

| # | Is |
|---|-----|
| 3.1 | Uretici modu (bilet uretme + imzalama) |
| 3.2 | Uretici paneli (cihaz listesi + bilet verme arayuzu) |
| 3.3 | Kopma surelerini ayarla paneli (debounce/uyari/kilit) |
| 3.4 | Kilit acma/kapatma + uzaktan komut paneli |

### Asama 4 - PARALEL CALISTIRMA (3 gun)

| # | Is |
|---|-----|
| 4.1 | `.fkl` + sunucu **ikisi birden** aktif |
| 4.2 | Yeni sistem test edilir, eski musteripler .fkl ile devam |
| 4.3 | Yeni lisanslar sunucudan verilir |
| 4.4 | 2-3 hafta sonra .fkl yavasla kapatilir |

**Gerekce:** Hemen gecis yapmak riskli. Parallel calistirma, sorun ciktiginda
geri donus imkani verir.

### Asama 5 - GERI HATTI (1 gun)

| # | Is |
|---|-----|
| 5.1 | .fkl tamamen kapat (veya env ile geri alinabilir) |
| 5.2 | Eski kod yolu temizle |
| 5.3 | Dokumantasyon guncelle |

---

## 14. ZAMAN CIZELGESI

| Asama | Sure | Kisi |
|-------|------|------|
| 0 - Guvenlik | 1-2 gun | Backend |
| 0.5 - HA + saglik izleme | 2-3 gun | Backend |
| 1 - Sunucu | 3-4 gun | Backend |
| 2 - Istemci | 5-7 gun | C++ |
| 3 - Uretici | 3-4 gun | C++ + Web |
| 4 - Parallel | 3 gun | Hepsi |
| 5 - Gecis | 1 gun | Hepsi |
| **TOPLAM** | **16-21 gun** | |

**Not:** Bagimsiz calisilabilir kismlar var (Asama 2 ve 3 parallel).
Gercekte **12-15 gun** olabilir.

---

## 15. OZET - SUNUCU BELIRLEYEN 30 SN

### Temel Kural
**Bagli degilsen calismiyorsun. Izin YOK. Kopma suresi 30 sn ve BUNU SUNUCU BELIRLER.**

### Sureyi Kim Belirler?

**SUNUCU.** Istemcide sabit deger yok - her heartbeat'te gonderilir, istemci ozrenir.

```
SUNUCU  -> MUSTERI:  {"aktiflik":true,"kopmaSuresi":30,"uyari1":5,"uyari2":15}  (30 sn)
MUSTERI -> SUNUCU:   {"pong":1699999999}                                           (aninda)
```

| Durum | Kim belirler |
|-------|--------------|
| Normal | Sunucu 30 sn gonderir -> istemci 30 sn bekler |
| Acil bakim | Panelden 300 sn yapilir -> ANINDA tum istemcilere gider |
| Toplu kazaya mudahale | 500 musteri kilitlenmek uzereyken 120 sn'ye cekilir |
| Sunucu ulasilamiyor | Istemci ELINDEKI son degeri kullanir (guvenli azami) |

**Onemli:** Sunucu coktugunda istemci elindeki degeri kullanir - bu yuzden
acil bakim oncesi panelden suresi uzaltmaniz gerekir.

### Zaman Cizelgesi (kopmaSuresi = 30 sn)

| Zaman | Durum | Kullanici Gorur mu? |
|-------|-------|---------------------|
| t=0 | Kopma tespit | **HAYIR** |
| t=0-5 sn | DEBOUNCE (sessiz deneme) | **HAYIR** - kablo 1 sn kopup 4 sn'de gelirse hic olmaz |
| t=5 sn | 1. UYARI | **EVET** - "Sunucuya ulasilamiyor" |
| t=15 sn | 2. UYARI | **EVET** - "15 saniye sonra kilitleneceksiniz" |
| t=30 sn | **BUM: KILIT** | **EVET** - viewport kilitli |
| t=30sn+ | `kilitliDeneme` (60 sn) aralikla sessizce dener | EVET |
| Sunucu dondu | AKTIF (yeni sure ile) | EVET - otomatik |

### Sunucu Tarafi Ayarlar

| Parametre | Varsayilan | Aralik | Aciklama |
|-----------|-----------|--------|----------|
| `kopmaSuresi` | **30** | 15-600 | Toplam -> KILIT |
| `uyari1` | 5 | 0-300 | 1. uyari |
| `uyari2` | 15 | 0-600 | 2. uyari |
| `kilitliDeneme` | 60 | 30-600 | Kilitliyken deneme araligi |
| `heartbeatEsik` | 45 | 20-300 | Sessiz kopma tespiti |

**Panelden anlik degistirme:**
```
POST /lisans-kontrol/api/kopma-ayar {kopmaSuresi:120, uyari1:10, uyari2:60}
```
-> Tum bagli istemcilere aninda gonderilir.

### Tespit Suresi - "Aninda" Ne Kadar Aninda?

| Kopma Tipi | Tespit | Toplam Kilit |
|------------|--------|--------------|
| Sunucu process oldu | ~0 sn | **~30 sn** |
| TCP reset | ~0 sn | **~30 sn** |
| Kablo cekildi | 45 sn | **~75 sn** |
| Wifi / NAT dustu | 45 sn | **~75 sn** |

**30 sn, sunucu coktugunda gecerli.** Sessiz ag kesintilerinde `heartbeatEsik`
(45 sn) eklenir. Tam "0 sn" fiziksel olarak mumkun degil.

**Iyilestirme:** `heartbeatEsik`'i 20 sn yaparsaniz (sunucu 10 sn'de bir gonderir),
kablo kopmasi ~50 sn'de kilitlenir. Yanlis pozitif riski artar.

### Durum Makinesi

```
AKTIF --(kopma)--> DEBOUNCE (5 sn, SESSIZ)
                       | basarisiz
                       v
                    UYARI (1. uyari @5sn, 2. uyari @15sn)
                       | 30 sn doldu
                       v
                    KILITLI (BUM) --(sunucu dondu)--> AKTIF
```

### Kaldirilan Ozellikler

- ~~Cevrimdisi 3-5 saatlik izin~~ - kesintide izin YOK
- ~~Bekleyen bilet (tembel dagitim)~~ - sunucu kapaliyken iletilemez
- ~~72 saat grace period~~ - yerini 30 sn aldi

### Kalan Uzaktan Yetenekler

| Yetenek | Durum |
|---------|-------|
| Lisans uzatma | VAR |
| Komut yetkilendirme | VAR |
| Kilitleme / kilit acma | VAR |
| Kota sifirla | VAR |
| Uzaktan komut | VAR |
| Ayar degistirme | VAR |
| **Kopma suresini anlik degistirme** | **VAR (yeni)** |

### Sunucu Kapandiysa (KRITIK)

**30-75 saniye icinde TUM musteriler kilitlenir.** En buyuk risk.

**ZORUNLU:**
1. **2 sunucu (HA)** + load balancer, paylasimli Redis + Mongo
2. **Saglik izleme** - 10 sn kontrol, 3 basarisizlikta yonlendir
3. **Istemci sunucu listesi** - 2-3 gomulu adres, sirayla dener
4. **Musteri mesaji** - "Sunucuya ulasilamiyor" (program coktu degil)

**Acil bakim oncesi:** Panelden `kopmaSuresi`'yi 300 sn'ye cekin, sonra bakimi baslatin.
Istemciler 5 dakika bekler, bakim bitince yeni degerle (30 sn) devam ederler.

### Gelistirme Zaman Cizelgesi

| Asama | Sure |
|-------|------|
| 0 - Guvenlik (bloklayici) | 1-2 gun |
| 0.5 - HA + saglik izleme | 2-3 gun |
| 1 - Sunucu altyapi | 3-4 gun |
| 2 - Istemci (durum makinesi + heartbeat) | 4-5 gun |
| 3 - Uretici araci | 3-4 gun |
| 4-5 - Gecis | 4 gun |
| **TOPLAM** | **17-23 gun** |

### Ozet

| Soru | Cevap |
|------|-------|
| Kopunca izin verilir mi? | **HAYIR** |
| Kopma suresi kac saniye? | **30 (sunucu belirler)** |
| Kac uyari? | **2** (5. sn, 15. sn) |
| Sureyi kim degistirir? | **SUNUCU** (panelden anlik) |
| Istemcide sabit deger var mi? | Hayir (sadece sunucu ulasilamazsa guvenli azami) |
| Kilitliyken baglanilir mi? | Evet, 60 sn aralikla sessizce |
| Sunucu cokerse? | 30-75 sn'de herkes kilitlenir -> HA zorunlu |


