Miesiąc po wdrożeniu produkcyjnym klient wraca z pytaniami z audytu bezpieczeństwa. „Czy system jest naprawdę lokalny?” – typowe pierwsze pytanie. „Bo widzimy ruch sieciowy”. Zaczynamy grzebać. Docker ściąga obrazy z Hub, system sprawdza certyfikaty przez OCSP, czas leci przez NTP, monitoring wypycha metryki gdzieś na zewnątrz. Niby żaden z tych calli nie dotyczy danych dokumentów, ale audytora to nie interesuje – system lokalny, a jednak coś gada do świata.
Tak to wygląda. Hasło „100% lokalne przetwarzanie” to właściwie marketing, rzadko kiedy wpisuje się w spójną definicję techniczną. Handlowcy gadają o „complete data isolation”, devy sprawdzają tylko czy nie dzwonią do AI API, security po stronie klienta myśli: zero egress, zero kompromisów, najlepiej true air-gap. Każdy mówi o lokalnym systemie, ale co innego ma na myśli.
Definicja "lokalności" to nie jest akademicki problem
Jest ogromna różnica między „nie wysyłamy dokumentów do OpenAI” a „zero network egress”. Pierwsze podejście proste – nie dzwonisz do API, robisz inference na własnych maszynach. Ale jak klient oczekuje prawdziwego air-gap, nagle okazuje się, że update”y modeli, monitoring, nawet zarządzanie certyfikatami trzeba ogarnąć zupełnie inaczej.
Widziałem systemy, które szły jako „w pełni lokalne”, a w tle: co 6 godzin ping do endpointu dostawcy po aktualizacje modelu OCR, crash dumpy lecą do Sentry, serwer licencji sprawdzany online, czas przez publiczny NTP, niby nic groźnego, ale każdy z tych calli to side-channel, potencjalny problem przy timing attacks i najczęściej niezgodność z wymaganiami compliance tam, gdzie klient ma zablokowany egress.
Zabawnie robi się w bankach, szpitalach, instytucjach publicznych. Tu compliance to nie jakaś miękka sugestia a twardy wymóg z audytem i checklistą. I wtedy się okazuje, że twój „lokalny” system to musisz przeorać prawie od zera, bo klient chce mieć express allowlist na każdy możliwy zewnętrzny endpoint, a czasem najlepiej w ogóle brak jakiejkolwiek łączności na zewnątrz.
"Są projekty, w których największym problemem nie jest OCR. Jest nim odkrycie wszystkich rzeczy, które od lat zakładały, że internet zawsze będzie po drugiej stronie kabla."
Trzy poziomy lokalności - konkret, nie marketing
Po paru wdrożeniach w różnych środowiskach security, zaczynasz widzieć trzy poziomy lokalności. To nie są abstrakcyjne klasy – to faktyczne opcje deploymentu, które wprost przekładają się na architekturę.
Tier 1: True air-gap. Żadnej sieci na zewnątrz w runtime. System chodzi w środowisku całkiem odizolowanym od internetu i corporate network, poza na przykład jednym VLAN-em do przesyłania outputu. Update”y modeli OCR dowożone fizycznie: pendrive, secure laptop. Monitoring lokalny, logi zgrywasz offline, walidacja certyfikatów wyłącznie przez lokalny store aktualizowany ręcznie. NTP? Też lokalny serwer czasu.
Może się wydawać, że to overkill, ale defence contractors czy instytucje bankowe tak właśnie pracują – nie „bo lubią mieć pod górę”, tylko threat model zakłada aktywnego, zaawansowanego atakującego, który może słuchać każdego pakietu sieciowego. Każdy outbound packet to potencjalny wyciek.
Koszt operacyjny tier 1? Masakryczny. Update modelu OCR = fizyczna wizyta lub mega formalna procedura na przepisanie przez wetted media. Debugowanie production? Horrory – żadnej telemetrii na żywo, logi analizujesz offline. A/B testy modeli? Zapomnij, chyba że bank ma cierpliwość na procedurę trwającą miesiące.
Tier 2: Restricted egress na allowlisty. Tu system może gadać na zewnątrz, ale wyłącznie na explitnie zatwierdzone adresy/domeny. Typowe przykłady: prywatne repo z modelami, firmowy NTP, wewnętrzna CA, monitoring na wewnętrzny endpoint. Zero publicznych API, żadnych cloudowych usług, nic po za tym, co jest wymagane i zatwierdzone.
To standard w healthcare i dużych enterprise”ach pod RODO. Klient rozumie, że system operuje w jakimś ekosystemie, ale chce mieć gwarancję, że żadne dane same nie wychodzą. Kluczowa różnica do tier 1 – możesz zdalnie aktualizować modele i masz monitoring na żywo, ale musisz wdrożyć egress control: firewall, allowlisty na poziomie kodu, najlepiej jedno i drugie.
Trzeba logować każde połączenie na zewnątrz i podnosić alert, jak system próbuje wyjechać gdzie indziej. To nie jakaś paranoja – tylko praktyka. Raz ktoś wrzucił zależność, która ni z gruchy odpaliła call na domyślny endpoint – taki pest control wykryjesz tylko monitoringiem i hard policy.
Tier 3: Standard local czyli „nie używamy cloud AI, cała reszta dozwolona”. Możesz użyć publicznego DNS, z czasem gadać przez publicznego NTP, monitoring czy telemetria lecą do dostawcy, aktualizacje sprawdzasz przez internet. Jedyne ograniczenie: żadnych wywołań do zewnętrznych AI APIs typu OpenAI, Anthropic, Google Vision. Samo przetwarzanie dokumentów idzie lokalnie.
Zazwyczaj to się sprawdza w standardowych B2B – klient ma pewność, że faktury nie lądują na trenowaniu LLM, a vendor nie musi walczyć z brakiem update”ów i monitoringu. Nakład pracy wdrożeniowej jest zauważalnie niższy niż w tier 2 czy – nie mówiąc o – tier 1.
Konkret: te poziomy to nie arbitralne ograniczenia. Wynikają z realnych threat modelów i wymogów compliance. Tier 1: ochrona przed bardzo zaawansowanymi atakami. Tier 2: ochrona przed cloud data breach. Tier 3: ochrona przed „przy okazji wyślemy wasze dane do GPT-5”. Różny koszt, różny sens – raz to compliance, raz zdrowy rozsądek.
Side-channel leaks - niewidoczne dopóki nie wybuchnie audyt
DNS queries to klasyka, jeśli chodzi o przecieki w „lokalnych” systemach. Masz bibliotekę OCR, która dociąga dependency w locie, i nagle każda domena oddaje informację na zewnątrz, że właśnie przetwarzasz szwedzki format faktury. Przykład z życia: library parsujące faktury sprawdza schema wywołując lookup do „swedish-invoice-schemas.vendor.com” tylko wtedy, gdy wykryje taki typ dokumentu. DNS jest widoczny dla każdego, kto patrzy na sieć i masz leak. Brzmi jak teoria – a w praktyce są klienci, którzy zakazują jakiegokolwiek DNS poza korporacyjnym resolverem, dokładnie przez takie edge case”y.
Certificate validation przez OCSP – kolejny side-channel. Za każdym razem weryfikacja przez OCSP leci poza organizację, przekazując m.in. serial certyfikatu. Jeśli podpisujesz dokumenty lub używasz po TLS między komponentami – każda walidacja to outgoing call. W tier 1 nie wchodzi w grę, tu zostaje tylko CRL dystrybuowany offline.
Package manager to pole minowe. Pip, npm, apt – domyślnie zawsze patrzą na publiczne repozytoria. Zrobisz „pip install” lub cokolwiek podobnego na produkcji (zdarza się częściej niż by się chciało) i już leci call na zewnątrz. Nawet Docker – może mieć całe dependencies w obrazie, ale i tak sprawdzi up-to-date na publicznym registry. W tier 1 – musisz mieć private registry i kategorycznie wyłączyć auto-update.
Error reporting i crash dumpy – podstępny wyciek. Większość frameworków domyślnie łączy się do zewnętrznych serwisów typu Sentry czy Rollbar przy każdym poważniejszym błędzie aplikacji. Stack trace nierzadko łapie fragmenty danych wejściowych – jeśli processing padnie na „fakturze od [Company X]”, masz biznesowy leak. W tej kategorii albo wyłączasz crash reporting całkiem (tier 1), albo każdy report przechodzi przez sanitizację (minimum w tier 2/3).
Debugowanie takich niespodzianek jest dramatem, bo one zwykle nie wychodzą w testach funkcjonalnych – zależność odpali HTTP call tylko pod konkretnym edge case”em na produkcji. Dopiero packet capture pokazuje, co się stało. Dlatego w poważnych scenariuszach default-deny egress i jawna allowlista na każdy endpoint. Jak coś próbuje wyjść, po prostu drop i alert.
Local OCR kontra Cloud OCR - brutalne porównanie wydajności, które każdy wypiera
Lokalne OCR jest wolniejsze i mniej dokładne niż Google Vision API czy AWS Textract. Nie ma tu co ściemniać, wystarczy porównać wyniki. Cloudowy provider ma własne farmy TPU, mega zoptymalizowane modele, cachowanie na każdym etapie. Na miejscu masz najwyżej CPU lub – rzadziej – mocniejsze GPU i open-source model.
Konkret: Tesseract, najpopularniejszy open-source OCR, na CPU przerzuca stronę A4 w ok. 2-3 sekundy z accuracy 85-90% dla czystych skanów. Google Vision? 400-600ms, accuracy 95-98%. Textract AWS jeszcze szybszy na batchach, wyciąga z faktury od razu netto/brutto/VAT.
GPU daje przyspieszenie 3-4x, więc spadasz przy Tesseract do 500-800ms na stronę – ale to i tak wolniej niż cloud, gdzie jadą na dedykowanym hardware i optimization strictly pod ich modele. I od razu koszt: każdy deployment musi mieć GPU, czyli droższa maszyna w chmurze albo własny sprzęt.
Osobny temat – cold start. Cięższe modele, typu transformer-based OCR, ładują się 10-30 sekund na start. Cloud nie ma tego problemu, bo u nich model zawsze w pamięci. W praktyce twój lokalny system musi mieć stale żyjące procesy, zero serverless, co utrudnia skalowanie i deployment.
Przepustowość? Cloud API zniesie setki czy tysiące dokumentów na sekundę, lokalny deployment ogranicza cię do własnego hardware. 8-core CPU realistycznie przetrzyma 3-4 dokumenty jednocześnie (reszta dla OS i overheadu). Większy load? Dokup kolejne maszyny lub postaw drogie multi-GPU.
Model kosztowy odwrócony: Cloud płacisz per document, AWS Textract to $0.015 za stronę, AnalyzeExpense $0.05; 10 tys. stron na miesiąc to $150-500. Lokalnie masz koszt stały – sprzęt, prąd, utrzymanie – break-even gdzieś 50-200 tysięcy stron/miesiąc, zależnie od sprzętu i stopnia wykorzystania. Poniżej tej skali chmura jest tańsza, powyżej – lokalka wychodzi na swoje, pod warunkiem ~stałego dużego ruchu.
Nieprzyjemna prawda: accuracy cloud vs open OCR najbardziej widać w edge case”ach. Cloud trenowany na większym zróżnicowaniu, radzi sobie z nietypowymi fontami, rotacją, dziwnym layoutem czy kiepskimi skanami – Tesseract się tu wykłada, szczególnie na dokumentach z watermarkiem, teksturą, czy z scanów faksów. Możesz się ratować preprocessingiem (deskew, denoise, binarization), ale procesowanie wydłuża się i wzrasta complexity.
Przykład z życia: Klient – 200 dostawców, 50 tys. faktur miesięcznie, restrykcje pod RODO (żadnych danych poza EU). Chcieli „najlepsze OCR”. Test: ten sam batch przez Google Vision (95% accuracy, 45s) kontra lokalny Tesseract + GPU (88%, 4 minuty). 7% różnicy = dużo więcej manualnej weryfikacji. Po przekalkulowaniu kosztów ludzi wyszło, że nawet z niższą accuracy lokalny model im się opłaca, jeśli zaakceptują dłuższy czas przetwarzania. I to był explicit decision/trade-off, nie domysł.
Provisioning hardware pod lokalne OCR - tu się zaczynają prawdziwe dylematy
CPU-only to default. Tesseract na 16-core CPU przetwarza ok. 6-8 stron na sekundę (przy 80% obciążeniu, reszta dla OS). Daje ci to ok. 700 tys. stron/dzień przy pracy 24/7 – teoretycznie sporo. W praktyce problem pojawia się przy pikach, typu zamknięcie miesiąca księgowego i lawina dokumentów. Wtedy CPU nie domaga, kolejki rosną, SLA się sypie.
GPU przyspiesza 3-4x, ale nie za darmo. RTX 3090 = $1500, wymaga PCIe (serwer albo drogi cloud), AWS p3.2xlarge ok. $3/h (i to za minimum). Większy problem: Tesseract z GPU nie wykorzystuje GPU tak jak można by oczekiwać – dużo rzeczy idzie na CPU, GPU się nudzi. Jeśli chcesz realnie użyć GPU, przejdź na engine typu PaddleOCR czy EasyOCR, które faktycznie korzystają z akceleracji. To oznacza rewrite pipeline.
Model size i memory – im lepszy model (TrOCR, Donut itp.), tym wyższa skuteczność dla kiepskich skanów, ale wtedy modele ważą kilkaset MB do ponad gigabajta. Masz na przykład kartę z 11GB VRAM (RTX 2080Ti), to zmieścisz 1 duży model lub kilka mniejszych. Jak pipeline wymaga kilku modeli (OCR + layout + NER), na standardowym hardwarze szybko kończy się VRAM, zaczyna się przepinanie modeli, latency leci w górę.
Batch processing to podstawa dla throughput. Każda strona osobno = większość czasu marnuje się na I/O, loading, context switch. Batch 16 stron na raz podbija wykorzystanie GPU nawet do 70-80%. Trzeba tylko pogodzić się z tym, że wzrasta latency pojedynczego dokumentu – czekasz, aż uzbiera się batch. Dla nocnych jobów jest OK, dla interaktywnych (user uploaduje dokument, chce wynik) – trzeba to mocno policzyć.
On-premise deployment to kolejne wyzwania – w chmurze dokładam instancje, na własnym sprzęcie muszę dobrać capacity z kilku(nasto-)miesięcznym wyprzedzeniem. Przeszarżuję – marnuję sprzęt. Zaniżę – nie dowożę peak loadów. Dochodzi fizyczna przestrzeń, prąd, chłodzenie, awarie sprzętu, spares… Software’owy problem zamienia się w kłopot rodem z datacenter.
"Produkcja ma ciekawy zwyczaj. Im bardziej jesteś przekonany, że nic nie wychodzi na zewnątrz, tym większa szansa, że jakaś zależność właśnie wykonuje HTTP request, o którym nikt nie pamiętał."
Aktualizacje modeli w air-gap - najtrudniejszy operacyjnie fragment
Versioning w air-gap to duży ból. Przy cloudowym podejściu – jeden pull i deploy, nowa wersja jest. W trybie air-gap: eksportujesz model na nośnik po security scan, fizycznie przenosisz go na dedykowany komputer, transferujesz do środowiska produkcyjnego (przez zabezpieczony VLAN albo tool typu data diode, czasem z wydrukiem kontrolnym), instalujesz i testujesz. Kilka dni minimum, nieraz tygodnie.
A/B testów na air-gap praktycznie nie da się zrobić na żywym ruchu – bo nie podzielisz requestów, rollback trwa wieki. Możesz jedynie offline puścić historyczne dane przez nową wersję, porównać wyniki i podjąć decyzję, czy w ogóle wchodzić z update’em.
Procedury rollback muszą działać na twardo – nie masz marginesu na szybki hotfix rodem z CI/CD. Trzymasz na miejscu poprzednie wersje modeli (więcej storage), dokumentujesz pełen flow rollbacku i okresowo drillujesz na testach, bo rollback przez pendrive bywa mniej intuicyjny niż się wydaje.
Manual workflow pod kątem update”ów zawsze kończy się w formalnym changemgmencie. Każda zmiana przechodzi przez request, approval, okno serwisowe, deployment oficjalnie potwierdzony. To nie jest „release as you go”, to są cykle kwartalne. Co za tym idzie: update’y są zbiorcze, poprawka krytyczna – czeka na okno. Przy bugach wydłuża się do absurdalnych czasów.
Praktyka: deployment dla defence contractor, model miał błąd w parsowaniu formatów daty (eu vs us). Błąd zgłoszony w drugim tygodniu, poprawka przygotowana w trzecim, deploy możliwy dopiero w czternastym. 11 tygodni obsługa ręcznie klepała daty dokument po dokumencie.
Matrix decyzji - który tier i na jakiej podstawie?
Podstawa to regulacje. RODO nie wymaga ani air-gap, ani nawet tier 2 – można klepać OCR w chmurze, jeśli masz DPA, szyfrowanie, ogarnięte security. Ale są branże z ostrzejszymi przepisami: healthcare (HIPAA, krajowe regulacje UE), finanse (PCI-DSS, bankowość), sektor publiczny (dane z nadanymi klauzulami). Jeśli dotykasz classified, tier 1 wychodzi nie z decyzji technicznej, a ustawowej.
Najważniejsze to threat model, nie compliance. Odpowiedz sobie: czego faktycznie się boisz? Jeśli po prostu chcesz uniknąć wycieku do OpenAI czy Anthropic, tier 3 wystarczy. Jeśli nie ufasz cloud providerom, tier 2. Jeśli boisz się państwowego aktora i masz skrajne security – tylko tier 1.
Tier 1 (air-gap): Privacy 10/10, Performance 4/10, Koszt 9/10, Złożoność ops 10/10
Tier 2 (restricted): Privacy 8/10, Performance 6/10, Koszt 6/10, Złożoność ops 7/10
Tier 3 (standard local): Privacy 6/10, Performance 7/10, Koszt 4/10, Złożoność ops 3/10
Chmura: Privacy 3/10, Performance 10/10, Koszt 2/10, Złożoność ops 1/10
Liczby umowne, ale jasny framework. Z każdym wdrożeniem decydujesz, co jest dla klienta rzeczywiście krytyczne. Performance istotny? Tier 1 odpada. Privacy nie do negocjacji? Chmura odpada. 99% przypadków to kompromis między 2/3.
Czułość na side-channels to oddzielne pole. Jedni klienci zaakceptują DNS czy NTP out (tier 3), inni chcą logować i approveować każdy outbound packet (tier 2), jeszcze inni – 0 egress must be zero (tier 1). Nie zawsze idzie to w parze z danymi – czasem masz deployment z mało wrażliwymi dokumentami, ale security team narzuca tier 1, bo tak.
Przy tier 2/3 – niezbędne: allowlista outbound endpointów w dokumencie wdrożeniowym, disable autoupdate w runtime, sanitizacja error reports, log każdego outboundu do audytu, rotacja secretów lokalnie bez zewnętrznych zależności. Przy tier 1: dystrybucja modeli wyłącznie offline, lokalny monitoring (Prometheus/Grafana lokalnie), lokalny time server, private package mirror.
Najczęstszy fuckup? Zakładanie tieru bez rozmowy z klientem. Developer twierdzi, że wystarczy tier 3 („nikomu nie przeszkadza NTP”), handlowiec obiecuje tier 2 („nic nigdzie nie wychodzi”), klient oczekuje tier 1 („zero połączeń na zewnątrz”). Produkcja wszystko weryfikuje – później poprawki kosztują miesiące. Tier musi być zdefiniowany na papierze i w dokumentacji architektury, najlepiej z przykładami pre-approved/forbidden wywołań.
Klient zmienia wymagania w trakcie? Co wtedy
Przeskok z tier 3 do tier 2 jest kosztowny, ale realny. Przepisujesz kontrolę egressu, wprowadzasz allowlist, usuwasz zależności z połączeniem na świat, tuningujesz monitoring. To do zrobienia w tygodnie/miesiące zależnie od bazy kodu. Z tier 2 do 3 prosto – rozluźniasz reguły.
Ale migracja do tier 1 to zupełnie inny świat technicznej ekwilibrystyki. Musisz pozbyć się update’ów po sieci, wszystko przenieść na offlinowy delivery, monitoring na lokalną stackę, package registry wewnętrzny mirror. To nie refactor, to redesign.
Jeśli klient zaskoczy cię wymaganiem tier 1 w środku projektu – jesteś w czarnej d… Chyba że już od początku masz wyciągniętą warstwę egress abstraction. Czyli business logic nigdy nie robi sam HTTP call, a outbound traffic idzie przez własną warstwę. W tier 3 abstraction layer robi call, w tier 2 sprawdza allowlist, w tier 1 wrzuca wyjątek „egress not allowed”. Da się wtedy przełączyć tier przez konfigurację. Nie masz layer – czeka cię cholernie bolesny rewrite.
Był u nas taki case: Połowa projektu, klient deklaruje tier 3. Nowy compliance officer – od teraz tier 2. Uratowało nas to, że całość egressów była już wyciągnięta do abstraction layer. Dołożyliśmy tylko allowlist logic, monitoring przełączyliśmy na wewnętrzny endpoint, mirrorowaliśmy package repos. Całość: 3 tygodnie. Bez tej warstwy: rework na kwartał.
"Najtrudniejsze migracje rzadko dotyczą kodu. Zwykle polegają na przekonaniu systemu, żeby przestał zakładać, że po drugiej stronie zawsze czeka internet."
Podsumowanie
Produkcja nie wybacza reklamowych przechwałek. Hasło „100% lokalne” kończy się kłótnią z firewallem i nieprzyjemnym raportem z audytu. Różnica między tier 3 a tier 1 to nie tylko zawiłości techniczne – to inna filozofia granicy systemu. Przy tier 3 liczysz na wygodę i rozsądne ograniczenia privacy. Tier 1? Tu każdy pakiet, każde połączenie, każdy call może ci wywrócić wdrożenie do góry nogami. Większość zespołów rozumie to dopiero po pierwszym audycie, niestety z reguły za późno na tanią naprawę.
Notatka redaktorska: Największa bolączka draftu to korporacyjny, „AI-contentowy” ton i przeładowanie tekstu wygładzonymi, powtarzalnymi frazami typu „warto zauważyć”, „kluczowa rola”, czy kompletnie zbędnymi podsumowaniami każdego akapitu. Przekształciłem tekst na styl naturalny, techniczny, surowy, z krótkimi zdaniami i konkretami, usunąłem cały zbędny „puszysty” język, zachowując wszystkie merytoryczne szczegóły i detale architektoniczne.
Zobacz powiązane case studies i analizy
Procesy, architektura i workflow powiązane z tematami poruszanymi w tym materiale – od integracji i realtime systems po automatyzacje operacyjne.