Blog · Car Brain / Expo / Appwrite / Architektura

Twój klucz API jest prywatny. Dopóki nie wyślesz go z aplikacją.

Działający odczyt VIN umieścił klucz dostawcy po niewłaściwej stronie granicy klient–serwer. Funkcja Appwrite, jej koszty i usunięcie starej ścieżki.

Tomasz Stanisz4 min czytania

Odczyt VIN działał. To była łatwiejsza część.

W Car Brain numer VIN pozwala uzupełnić dane pojazdu bez ręcznego wpisywania każdego pola. Pierwsza implementacja odpytywała auto.dev bezpośrednio z aplikacji mobilnej. Integrację napisał agent; za jej przegląd odpowiadałem ja.

Problem krył się w miejscu, z którego pobierała klucz dostawcy: EXPO_PUBLIC_AUTO_DEV_API_KEY.

W Expo używane w kodzie zmienne EXPO_PUBLIC_ trafiają do paczki JavaScript aplikacji. Usunięcie wartości z Gita nie usuwa jej z aplikacji. Poprawna odpowiedź API potwierdza, że integracja działa. Nie potwierdza, że klucz znajduje się po właściwej stronie.

Trzeba przyznać: nazwa EXPO_PUBLIC_ niczego nie ukrywa.

Z konfiguracji zrobiła się decyzja architektoniczna

Zmienne środowiskowe odpowiadają na pytanie „skąd pochodzi ta wartość?”. Nie rozstrzygają automatycznie, kto będzie mógł ją odczytać.

Publiczny adres API może być częścią konfiguracji klienta. Sekretny klucz, który pozwala wykonywać żądania na moim koncie u dostawcy, już nie. Podczas przeglądu oba wyglądają jak zwykłe ciągi znaków. Konsekwencje są zupełnie inne.

Dokumentacja Expo wprost ostrzega przed umieszczaniem sekretów w zmiennych z publicznym prefiksem. Sama zmiana nazwy też nie wystarczy: jeśli aplikacja nadal potrzebuje klucza do wywołania dostawcy, problem architektoniczny zostaje.

Tego samego dnia przeniosłem wywołanie do funkcji Appwrite. Klient wysyła VIN; funkcja pobiera klucz ze swojego środowiska po stronie serwera i odpytuje auto.dev.

Przed: aplikacja mobilna zawiera klucz dostawcy i wywołuje auto.dev bezpośrednio. Po: funkcja Appwrite sprawdza użytkownika i dane wejściowe, a następnie wywołuje auto.dev z kluczem przechowywanym na serwerze. Koszt to dodatkowy krok, wdrożenie i obsługa błędów.
Schemat przepływu żądań. Otwórz diagram w pełnym rozmiarze

Najważniejsza zmiana dotyczy granicy dostępu do klucza. Car Brain już korzystał z Appwrite, więc funkcja pozwoliła przenieść tę operację na serwer bez dokładania osobnego serwisu API.

Funkcja ma więcej do zrobienia niż schowanie klucza

Serwis mobilny wywołuje teraz API wykonywania funkcji Appwrite i interpretuje wynik. Dostępem do zewnętrznego dostawcy zajmuje się funkcja.

W kodzie sprawdzonym do tego artykułu ścieżka dekodowania najpierw sprawdza obecność tożsamości wywołującego przekazanej przez Appwrite. Waliduje też format VIN, ogranicza czas żądania do dostawcy i rozróżnia błędy, między innymi niepoprawne dane, brak danych pojazdu oraz błąd dostawcy.

Te obowiązki są ze sobą związane: kto może zlecić operację, jakie dane przyjmujemy i co robimy, kiedy zewnętrzne API zawiedzie. Klient nadal odpowiada za stan ładowania i czytelny komunikat. Przeniesienie wykonania nie przenosi całego doświadczenia użytkownika.

Ważne jest jeszcze jedno rozróżnienie: ukrycie klucza i kontrolowanie jego użycia to dwa osobne problemy. Klucz na serwerze nadal może finansować niechciane żądania, jeśli dostęp do operacji jest zbyt szeroki. Uwierzytelnianie to jedna warstwa; limity użycia, częstotliwości i wydatków wymagają osobnych decyzji. Samo przeniesienie wywołania nie dowodzi, że wszystkie te mechanizmy działają.

Funkcja wyznacza granicę. Nie jest miniaturowym działem bezpieczeństwa.

Co daje dodatkowy krok i ile kosztuje

DecyzjaZyskKoszt
Wywołanie dostawcy w funkcjiMobilna ścieżka VIN nie potrzebuje klucza dostawcyDodatkowy krok wykonania i wdrożenie funkcji
Walidacja i obsługa błędów na serwerzeJedno miejsce kontroli tej integracjiOdpowiedzi błędów stają się kontraktem dla klienta
Wykorzystanie istniejącego AppwriteBez osobnego serwisu API dla tej operacjiZależność od modelu wykonania i ograniczeń platformy

Zimny start funkcji i opóźnienia dostawcy to możliwe koszty, a nie zmierzone tutaj pogorszenie wydajności. Nie mam benchmarku, który wykazywałby przewagę szybkości lub ceny nad innym backendem.

Wróciłbym do tej decyzji, gdyby integracje wymagały na tyle dużo wspólnej logiki lub koordynacji, że uzasadniałoby to osobny serwis. Dla pojedynczego, ograniczonego zadania oznaczałby on dziś dodatkowy obowiązek operacyjny.

Analiza danych pojazdu z Gemini również korzysta z osobnej funkcji Appwrite. To kolejne zastosowanie tej granicy, a nie powód, żeby zmienić tekst w przegląd całej architektury AI. Cache i koszty generowania odpowiedzi zasługują na własny artykuł.

Usunąć skrót, zachować lekcję

Pierwotny dekoder pozostał w kliencie jako nieużywany kod, mimo że aktywne wywołanie przeniesiono wcześniej. Został usunięty 12 października. Obecny serwis VIN w aplikacji mobilnej korzysta ze ścieżki przez funkcję.

To istotna różnica. „Dziś nikt tego nie wywołuje” daje mniej pewności niż usunięcie starej drogi, którą kolejna zmiana mogłaby przywrócić. Uzasadnienie decyzji można zachować w ADR bez zachowywania wykonywalnego skrótu.

Przy kolejnej integracji chcę sprawdzić:

  • Czy klucz służy publicznej identyfikacji, czy daje uprzywilejowany dostęp do konta u dostawcy?
  • Dokąd trafia: do paczki klienta, środowiska serwera czy obu?
  • Kto może uruchomić operację po stronie serwera?
  • Co się dzieje, gdy dostawca nie odpowie, odrzuci żądanie lub wyczerpie limit?

To pytania do przeglądu, nie deklaracja, że każdą odpowiedź wymusza automatyczny test.

Wniosek wykracza poza kod generowany przez AI. Za tę granicę nadal odpowiadam ja — niezależnie od tego, czy pierwszą wersję napisał agent, znalazłem ją w tutorialu, czy podpowiedział mi ją mój własny optymizm po północy.

Stan na 12 października 2026: artykuł opisuje kod i zapisy decyzji w repozytorium, nie nowy test działającego Appwrite. Nie stwierdza publicznego wykorzystania klucza, strat finansowych ani wydania aplikacji z przejętymi zabezpieczeniami.

Kontekst produktu znajdziesz w tekście dlaczego zbudowałem Car Brain.