# Postojeće stanje (provereno na serveru 2026-07-24, read-only) ## 1. Server 151 — pristup i raspored - **SSH:** `ssh root@157.180.119.151` (host se javlja kao `MCLaud`, Ubuntu; ključ `~/.ssh/id_ed25519` sa razvojne mašine; samo root). Hetzner, van LAN-a. - Na ISTOM serveru žive i drugi sistemi — **ne dirati**: - `terminia` SaaS platforma za klubove (portovi 8091/8092) — IME SLIČNO, ali NIJE naš proizvod; - Gitea; - ESIR backend (fiskalizacija). ## 2. `dal-license-server` (naš deo) - **Kod:** `/root/projects/dal-license-server` — Go, bez frameworka, MySQL. - **Port:** 8090 lokalno; javno preko nginx sajta **`arv`**. - **Javni URL (ovo firmware već koristi):** `https://157.180.119.151/license/api/v1/` - HTTPS 443, cert `/etc/nginx/ssl/arv.crt`, `server_name` = IP → **self-signed** (zato firmware koristi `setInsecure()`; odluku štiti RSA potpis odgovora). - Na portu 80 je izložen samo validator API; ostalo redirect na 443. - **Produkcioni obim:** 3 proizvoda (ESIR, ARV, LIGHT_TICKET — seed u `migrations/002_seed_products.sql`), ~140 licenci, ~19 aktivacija. **ŽIVO.** ### Kripto - **RSA-2048**, potpis **RSA-SHA256**. - Privatni ključ: na serveru (`crypto/` + `.env` konfiguracija) — **NE čitati, NE kopirati, NE logovati.** - Javni ključ: `crypto/public.pem` — VEĆ ugrađen u firmware (`licenca.cpp`, `LIC_PUBKEY`). Ako se ključ ikad rotira, MORA i novi firmware — to je krupna odluka, ne radi se usput. - Potpis stiže kao string: `"signature": "RSA-SHA256:"` uz `license` objekat. **Potpisuje se serijalizacija license objekta kakvu server pošalje** — klijent verifikuje SIROVE bajtove (ne re-serijalizuje). Svaka izmena serijalizacije na serveru lomi sve klijente u polju. ### Klijentski API (bez autentikacije, rate-limited) | Ruta | Telo zahteva | Odgovor | |---|---|---| | `POST /api/v1/activate` | `{license_key, machine_fingerprint, app_version, os, hostname}` | `{license:{... expires_at, grace_days, limits, features, customer ...}, signature}` | | `POST /api/v1/validate` | `{license_key, machine_fingerprint}` | `{valid, expires_at, revoked}` | | `POST /api/v1/deactivate` | (transfer/oslobađanje) | — | Greške (JSON `error` polje): `INVALID_KEY`, `ALREADY_ACTIVATED`, `KEY_EXPIRED`, `KEY_REVOKED`, `PRODUCT_MISMATCH`. ### Admin API - Header `X-API-Key` (ključ u `.env` — od vlasnika se traži ključ ili da on sam klikne u dashboardu; agent NE vadi ključ iz `.env` bez izričite dozvole). - Kreira proizvode i licence. Postoji i htmx admin dashboard. ## 3. Firmware klijent — GOTOV (od FW 0.10.0) Fajlovi u firmware repou (`firmware/10_terminia/`): | Šta | Gde | |---|---| | Modul | `licenca.h` / `licenca.cpp` | | Ugrađen javni ključ | `licenca.cpp` → `LIC_PUBKEY` (PEM) | | NVS | namespace `lic`: `raw` (sirovi license JSON) + `sig`; ključ/URL u configu: `licKey`, `licUrl` (podrazumevano `https://157.180.119.151/license/api/v1`) | | Web API (SPA) | `/api/license` (status), `/api/license-save` (ključ/URL), `/api/license-activate`, `/api/license-check` — sve samo admin | | UI | SPA → Podešavanja → Uređaj → kartica „Licenca" (status, izdanje, korisnik, važi do, otisak uređaja, unos ključa, Aktiviraj, Proveri na serveru) | ### Kako klijent radi (bitno za server stranu) 1. **Otisak:** `machine_fingerprint = "sha256:" + sha256hex(MAC)` gde je MAC oblika `AA:BB:CC:DD:EE:FF` (velika slova, dvotačke). ⚠️ **PRETPOSTAVKA** — proveriti u Go kodu servera kako računa fingerprint za poređenje; ako se razlikuje, uskladiti NA SERVERU dokumentaciju/verifikaciju za ASP proizvod ILI javiti pa menjamo FW (zadatak Z2). 2. **Aktivacija:** POST `/activate`; iz odgovora isecₐ SIROVE bajtove `license` objekta (brojanje zagrada, string-safe), verifikuje `signature` RSA‑SHA256 javnim ključem, čuva sirovo+potpis u NVS. 3. **Svaki start (offline):** ponovo verifikuje potpis iz NVS + poredi `machine_fingerprint` iz licence sa svojim + proverava `expires_at` (poređenje po datumu `YYYY-MM-DD`; prazno = trajna; nepouzdan sat → grejs, nikad pad). 4. **Statusi na uređaju:** `nema / vazi / grejs / los-potpis / drugi-uredjaj / istekla`. 5. **ENFORCEMENT NE POSTOJI** — uređaj samo prikazuje status. Gašenje funkcija bez licence je odluka vlasnika (zadatak Z4). ### Polja koja klijent čita iz `license` objekta `edition`, `customer` (ili `customer_name`), `expires_at`, `license_id` (ili `key`), `grace_days`, `machine_fingerprint`. Nepoznata dodatna polja ne smetaju (parsira se ciljano). `limits`/`features` se JOŠ ne čitaju — planirano uz enforcement. ## 4. Šta NE postoji (i pripada zadacima) - Proizvod **„ASP-Terminia"** na serveru — ne postoji (zadatak Z1). - Nijedan ASP ključ — ne postoji (Z1/Z2). - Domen `licenca.terminia.net` + Let's Encrypt — ne postoji (Z3; domen kupuje vlasnik). - Enforcement na uređaju + `features`/`limits` semantika za ASP (Z4/Z5). - FW potpisivanje `.bin` + katalog firmvera (Z6; preduslov za auto-OTA, vidi i ASP repo: `serviseri/03-provisioning-i-fw-katalog.md`).