Jak dać agentowi kodującemu AI sekret, nie wpisując go do promptu
Agent potrzebuje klucza Stripe albo hasła do bazy, żeby uruchomić swoją komendę. Wklejasz go do czatu i ląduje w transkrypcie na twoim dysku, u dostawcy modelu, czasem w commicie. Zasada, do której doszliśmy po trzech własnych błędach: sekret dociera do agenta przez zmienną środowiskową i przez nic innego.
Sytuacja jest zawsze taka sama. Agent jest w połowie zadania, musi wywołać twoje testowe API Stripe, uruchomić migrację na bazie staging albo wypchnąć coś do prywatnego rejestru. Prosi o klucz. Masz klucz w menedżerze haseł, agent czeka, a najszybszą rzeczą na świecie jest wkleić go do czatu.
To wklejenie jest momentem, w którym klucz wymyka ci się spod kontroli. Nie dlatego, że model jest złośliwy, ale dlatego, że prompt to obiekt zapisywany i przekazywany dalej: trafia do pliku transkryptu na twoim dysku, trafia do dostawcy modelu i jest kopiowany przez każde ponowienie i każdego subagenta, który dziedziczy rozmowę.
Budujemy AgentsRoom, aplikację desktopową, która uruchamia agentów kodujących, więc musieliśmy rozwiązać to u siebie. We wrześniu 2026 pomyliliśmy się trzy razy. Oto zasada, do której doszliśmy, trzy błędy, które ją zrodziły, i jak ją stosować niezależnie od tego, czy używasz naszej aplikacji.
Krótka odpowiedź
- Sekret dociera do agenta przez zmienną środowiskową i przez nic innego. Nie przez wiadomość, nie przez ticket, nie przez skill, nie przez flagę wiersza poleceń.
- Agent używa
$NAME, nigdy wartości. Powłoka podstawia ją w chwili uruchomienia komendy. - Zapisz wartość raz, poza repozytorium, zaszyfrowaną pękiem kluczy systemu, i wszędzie indziej odwołuj się do niej po nazwie.
- Sekret, który przeszedł przez rozmowę, jest spalony. Wymień go, potem posprzątaj.
- Maskowanie to siatka bezpieczeństwa, nie plan. Łapie to, co wygląda jak klucz, a nie hasło do bazy, które czyta się jak zwykłe słowo.
Dlaczego to wklejenie jest gorsze, niż wygląda
Trzy rzeczy dzieją się naraz i tylko pierwsza jest widoczna.
Trafia do dostawcy modelu. To część, którą widzą wszyscy. Twój klucz jest teraz w ciele żądania na cudzej infrastrukturze, wewnątrz rozmowy, której nie możesz edytować.
Trafia na twój dysk, czystym tekstem. Claude Code, Codex, Antigravity CLI i reszta trzymają transkrypt rozmowy, żebyś mógł ją wznowić. Ten plik leży w twoim katalogu domowym, niezaszyfrowany, tak długo, jak CLI przechowuje swoją historię. Przeszukaj raz swoje transkrypty pod kątem ciągu sk- i zobacz, co wróci.
Rozchodzi się na boki. Rozmowa jest pamięcią agenta, więc wszystko, co kopiuje rozmowę, kopiuje klucz: wznowiona sesja, sforkowany agent, subagent uruchomiony z kontekstem rodzica, podsumowanie zapisane w pliku projektu. Widzieliśmy, jak klucz wędrował z wiadomości na czacie do opisu ticketu, a stamtąd do pierwszej wiadomości drugiego agenta, i nikt nie zrobił nic złego.
A zwykła siatka bezpieczeństwa nie wytrzymuje. Maskowanie wyjścia działa na wzorcach: sk-…, glpat-…, kształt JWT. Nie może wiedzieć, że thursday-harbour-71 to hasło do twojej bazy staging. W naszej własnej aplikacji maskujemy rozpoznane wartości w wyjściu terminala i i tak traktujemy to jako siatkę pod trapezem, a nie jako trapez.
Zasada jednych drzwi
Oto ona, w jednym zdaniu: dane uwierzytelniające docierają do agenta przez zmienną środowiskową procesu, w którym agent działa, i żadnym innym kanałem.
Cała reszta to tekst dosłowny. Wiadomość, którą piszesz, opis ticketu, plik skilla, prompt systemowy, flaga wiersza poleceń: to wszystko czyta człowiek albo model, więc sekret zapisany tam jest sekretem opublikowanym.
Wiersz poleceń zasługuje na osobne zdanie, bo wydaje się bezpieczniejszy niż wiadomość na czacie, a nie jest. Wartość w --api-key=… jest widoczna przez ps dla każdego procesu na maszynie, ląduje w historii twojej powłoki, jest logowana przez wszystko, co zapisuje uruchomioną komendę, i pojawia się w narzędziach diagnostycznych, które wyświetlają ci z powrotem linię procesu. Zmienna środowiskowa jednego procesu jest czytelna dla tego procesu i dla roota. Mniejsza powierzchnia, ta sama wygoda.
Agent tymczasem nigdy nie potrzebuje wartości. Potrzebuje napisać to:
curl -H "Authorization: Bearer $STRIPE_SECRET_KEY" https://api.stripe.com/v1/charges
Powłoka podstawia $STRIPE_SECRET_KEY przy wykonaniu. Agent napisał siedemnaście znaków i znak dolara. Jego transkrypt zawiera siedemnaście znaków i znak dolara.
Jak to działa w naszej aplikacji
Mechanizm liczy się mniej niż kształt, ale kształt łatwiej skopiować, gdy widzisz jedno wdrożenie.
Jeden sejf, jeden plik. AgentsRoom trzyma sekrety w jednym pliku w twoim katalogu domowym, w trybie 0600, zaszyfrowanym przez pęk kluczy systemu operacyjnego, potwierdzanym przy każdym zapisie. Nic nie jest synchronizowane z serwerem i nic nie przechodzi na inny komputer: sekret należy do maszyny, która go przechowuje. Jeśli pęk kluczy jest niedostępny, zapis kończy się błędem, zamiast schodzić do czystego tekstu.
Zapisujesz wartość raz. Polecenia i połączenia > Sekrety, w dowolnym projekcie. Nazwa (litery, cyfry, kropka, myślnik, podkreślenie, 64 znaki, żeby nadawała się na nazwę zmiennej środowiskowej) i wartość, wpisana w pole hasła. Wiersz, który się pojawia, pokazuje nazwę i jej odwołanie, {{secret:STRIPE_SECRET_KEY}}, oraz przycisk kopiujący odwołanie. Nie ma przycisku pokazywania ani kopiowania wartości, bo nie istnieje ścieżka w kodzie, która zwracałaby wartość do interfejsu.
Odwołujesz się do niego tam, gdzie budowane jest środowisko. Edytuj agenta > Zaawansowane > Zmienne środowiskowe, albo ustawienia projektu, albo ustawienia providera, albo komenda deweloperska:
STRIPE_SECRET_KEY={{secret:STRIPE_SECRET_KEY}}
Przy starcie proces główny podstawia wartość do środowiska terminala, który zaraz utworzy. Interfejs nigdy jej nie widzi, agent nigdy jej nie widzi, telefon nigdy jej nie widzi. Strona Menedżera sekretów pokazuje kolejne ekrany.
Agent odkrywa nazwy, nie wartości. Agent podłączony do naszych narzędzi MCP może wywołać secrets_list i dostać listę nazw obecnych na tej maszynie. To wystarczy, żeby był użyteczny: może powiedzieć „masz zapisany STRIPE_SECRET_KEY, dodaj STRIPE_SECRET_KEY={{secret:STRIPE_SECRET_KEY}} do środowiska tego agenta, a ja użyję $STRIPE_SECRET_KEY". Nie może odczytać wartości, nawet wykonując komendę, która z niej korzysta.
Trzy błędy, które wypuściliśmy
Zasada powyżej brzmi oczywiście, gdy już jest spisana. To blizna po trzech błędach, wszystkich z września 2026, wszystkich zgłoszonych przez użytkowników.
Odwołanie zacytowane w tickecie wracało rozwiązane
19 września. Robert udokumentował mapowanie w opisie ticketu, całkowicie rozsądne STRIPE_SECRET_KEY={{secret:STRIPE_SECRET_KEY}}, żeby ten, kto weźmie ticket, wiedział, co ustawić. Odczytany przez API ticket zwracał odwołanie nietknięte. Ale agent uruchomiony z tego ticketu dostawał wartość w swojej pierwszej wiadomości.
Przyczyną był szczegół instalacji z bardzo realnym skutkiem. Brief ticketu staje się pierwszym promptem agenta, a ten podróżował wewnątrz zmiennej środowiskowej komendy uruchamiającej. Nasz resolver podstawiał każdą wartość środowiska, więc podstawił też prompt. Klucz poszedł na dysk i do dostawcy modelu. Wymiana klucza, nie dało się tego obejść.
Poprawka polegała na tym, żeby komenda uruchamiająca agenta była dosłowna: gdy środowisko niesie identyfikator agenta, zmienna z pierwszym promptem zostaje nietknięta. Komenda deweloperska używająca tej samej zmiennej nie ma identyfikatora agenta i rozwiązuje się jak wcześniej. Świadomie przyjęliśmy efekt uboczny: odwołanie wpisane w opcje CLI agenta też nie jest już rozwiązywane, co jest poprawne, bo wiersz poleceń i tak ląduje w ps i w logach.
Lekcja: drzwi muszą być wąskie celowo. „Podstawiać wszędzie, gdzie mogłoby się przydać" to dokładnie sposób, w jaki sekret ląduje tam, gdzie nigdy nie powinien.
Odwołanie wklejone do wiadomości, które nigdy się nie rozwiązuje
22 września. Cs040618 zgłosił odwrotny problem: „wklejam odwołanie do rozmowy, a agent nie potrafi zrozumieć, czym jest ten klucz".
To nie był błąd. Wiadomość jest dosłownym tekstem z założenia, co jest dokładnie poprawką sprzed trzech dni. Ale nic w aplikacji tego nie mówiło w momencie tego gestu, a agent, dostając {{secret:…}} w wiadomości, nie miał pojęcia, co odpowiedzieć.
Trzy poprawki, wszystkie o tym, żeby powiedzieć ludziom, gdzie są drzwi: podpowiedź, która pojawia się, gdy wklejasz odwołanie do pola wiadomości, i kieruje do zmiennych środowiskowych, tekst pomocy nad listą sekretów, który nazywa trzy pola, gdzie odwołanie jest podstawiane, i mówi „nigdy w wiadomości", oraz linijka w instrukcjach samego agenta, mówiąca mu, co odpowiedzieć, gdy odwołanie przychodzi w wiadomości.
„Ustawienia > Sekrety" nie istniały
28 września. Agent Mohammada złożył zgłoszenie: użytkownik mówi, że dodał OTPIQ_API_KEY w Ustawieniach > Sekrety, zapisał go kilka razy w ciągu trzydziestu minut, a secrets_list nadal zwraca te same trzy stare sekrety.
Ścieżka zapisu działała dobrze. Sekret nigdy nie dotarł do sejfu, bo nie ma strony Sekrety w ustawieniach. Sześć tekstów pomocy w aplikacji desktopowej, strona na tej witrynie i instrukcje agenta wysyłały ludzi na ekran, który nie istniał. W ustawieniach istnieje natomiast blok zmiennych środowiskowych, więc to tam wpisał token, w nawiasach klamrowych, co nie jest odwołaniem: nigdy nie podstawiane, wysłane do agenta bez zmian i synchronizowane z projektem, więc token opuścił maszynę.
Poprawiliśmy teksty, żeby nazywały prawdziwą ścieżkę, sprawiliśmy, że narzędzia skierowane do agentów mówią, gdzie dodaje się sekret, i dodaliśmy kontrolę bloku środowiska, która sygnalizuje dane uwierzytelniające wpisane surowo, nazwę w nawiasach klamrowych, która nie jest odwołaniem, oraz odwołanie do sekretu nieistniejącego na tej maszynie.
Lekcja nie dotyczy tu w ogóle sekretów: zła ścieżka w tekście pomocy to błąd bezpieczeństwa. Przesuwa użytkownika do najbliższego pola, które wygląda właściwie.
Jeśli nie używasz AgentsRoom
Zasada podróżuje. Wybierz to, co pasuje do twojej konfiguracji; wszystkie kończą w tym samym miejscu, na zmiennej środowiskowej w procesie, w którym działa agent.
- CLI menedżera haseł.
op run -- claudez 1Password albo odpowiednik w Bitwarden i Doppler: narzędzie czyta odwołania z szablonu, wstrzykuje prawdziwe wartości do środowiska procesu potomnego i nic nie trafia na dysk. direnvz plikiem.envrcw gitignore. Proste, lokalne, osobne dla każdego katalogu. Dodaj.envrci.env*do.gitignore, zanim wejdzie tam pierwsza wartość, a nie potem.- Plik
.env, którego agent nigdy nie czyta. Trzymaj go poza repozytorium i ładuj w powłoce, która uruchamia agenta. Jeśli plik leży w repozytorium, agent prędzej czy później go otworzy, zacytuje w wiadomości i wrócisz do punktu wyjścia. - Dane uwierzytelniające do chmury: bierz krótkożyciowe. GitHub OIDC w CI zamiast długowiecznych kluczy AWS i tożsamość obciążenia zamiast pliku klucza konta serwisowego.
Cokolwiek wybierzesz, wpisz zasadę do instrukcji agenta, żeby przestał pytać. Wystarczy jeden akapit w CLAUDE.md albo AGENTS.md: nigdy nie pytać o wartość danych uwierzytelniających, nigdy nie zapisywać ich do pliku, używać $NAME i powiedzieć użytkownikowi, którą zmienną ustawić. Publikujemy mały skill, który mówi dokładnie to, i ma pięć linijek.
Jeśli klucz już przeszedł przez rozmowę
Po kolei, a pierwszy krok nie jest opcjonalny.
- Wymień go. Promptu nie da się cofnąć. Załóż, że wartość jest skompromitowana, i wystaw nową.
- Znajdź transkrypty. CLI trzymają pliki rozmów w twoim katalogu domowym. Poszukaj w nich starej wartości i usuń albo utnij to, co pasuje.
- Sprawdź miejsca, które kopiują tekst. Historia menedżera schowka, ticket albo issue, w którym agent dostał brief, każde podsumowanie, które agent zapisał w repozytorium, i
git log -p, jeśli agent commitował, trzymając klucz. - I dopiero wtedy zbuduj drzwi. Nowa wartość za zmienną środowiskową, odwołanie po nazwie i zaktualizowane instrukcje agenta, żeby następny nie pytał.
Krok 4 na końcu, bo czysty mechanizm wokół wyciekłego klucza to tylko lepiej posprzątany wyciek.
Pytania, które ludzie naprawdę zadają
Dlaczego klucz API nigdy nie powinien trafiać do promptu wysyłanego agentowi AI?
Bo prompt jest zapisywany i przekazywany dalej. Claude Code, Codex i pozostałe CLI zapisują rozmowę do pliku transkryptu na twoim dysku, a sama wiadomość trafia do dostawcy modelu. Stamtąd klucz jest też w historii twojego schowka, w każdym logu, który powtarza wiadomość, oraz w ponowieniach i subagentach, które kopiują rozmowę. Maskowanie oparte na wzorcach łapie rzeczy w kształcie sk- albo glpat-, a nie hasło do bazy, które wygląda jak zwykłe słowo.
Jak agent kodujący AI używa sekretu, którego nie może przeczytać?
Przez zmienną środowiskową. Wkładasz wartość do zmiennej środowiskowej procesu, w którym działa agent, a agent pisze w komendzie $STRIPE_SECRET_KEY zamiast wartości. Powłoka podstawia ją, gdy komenda się wykonuje. Agent nigdy nie widzi tych znaków, więc nigdy nie trafiają one do jego rozmowy ani do jego transkryptu.
Czy mogę zamiast tego przekazać sekret jako argument wiersza poleceń?
Możesz i nie powinieneś. Wiersz poleceń jest widoczny przez ps dla każdego procesu na maszynie, trafia do historii powłoki, do logów procesów i do każdego narzędzia diagnostycznego, które odczytuje uruchomioną komendę. Zmienna środowiskowa pojedynczego procesu jest czytelna dla tego procesu i dla roota, a to znacznie mniejsza powierzchnia.
Gdzie AgentsRoom przechowuje sekrety, z których korzystają agenci?
W jednym pliku w twoim katalogu domowym, w trybie 0600, zaszyfrowanym przez pęk kluczy twojego systemu operacyjnego, wyłącznie na tej maszynie. Nic nie jest synchronizowane z serwerem i nie ma przycisku, który pokazałby wartość ponownie. Aplikacja pokazuje nazwy, agent widzi nazwy, a wartość jest odszyfrowywana tylko w procesie głównym, w chwili startu terminala albo komendy.
Co się stanie, jeśli wkleję odwołanie do sekretu w wiadomości do agenta?
Nic nie zostanie podstawione i jest to celowe. Wiadomość, opis ticketu, skill i prompt systemowy to tekst dosłowny, więc odwołanie tam zapisane dociera do agenta jako tekst. Podstawiana jest tylko wartość zmiennej środowiskowej. Nauczyliśmy się tego boleśnie: odwołanie zacytowane w opisie ticketu wracało kiedyś rozwiązane w pierwszej wiadomości agenta uruchomionego z tego ticketu.
Klucz już przeszedł przez rozmowę. Co mam zrobić?
Najpierw go wymień, przed wszystkim innym, bo nie da się go cofnąć: jest po stronie dostawcy i w pliku transkryptu na twoim dysku. Potem usuń albo utnij transkrypty, które go zawierają, sprawdź menedżera schowka i przeszukaj repozytorium na wypadek, gdyby agent zapisał go do pliku. Dopiero wtedy umieść nową wartość za zmienną środowiskową.
Krótka wersja
Agent nie musi czytać twojego klucza. Potrzebuje procesu, którego środowisko już go zawiera.
Zapisz wartość raz, poza repozytorium, zaszyfrowaną przez system. Odwołuj się do niej po nazwie tam, gdzie budowane jest środowisko. Pozwól agentowi napisać $NAME. Zachowaj dosłowność każdego innego kanału, także tych, w których podstawianie byłoby wygodne, bo wygodne kanały to dokładnie te, które są zapisywane i przekazywane dalej.
A kiedy się pomylisz, bo się pomylisz, najpierw wymień klucz, potem posprzątaj.
Pobierz AgentsRoom
Uruchamiaj wszystkich swoich agentów AI, we wszystkich projektach, z jednego okna.
Aplikacja towarzysząca: monitoruj agentów w podróży
Użyj Claude, Codex, Antigravity CLI lub innego dostawcy AI.
Wysyłaj bugi i prośby bezpośrednio do swojego publicznego backlogu.
Czytaj dalej
Jak skalować agentów AI do kodowania w zespole deweloperskim
Jeden deweloper z agentem kodującym to historia o produktywności. Pięciu deweloperów z dwudziestoma agentami to problem koordynacji. Oto co psuje się jako pierwsze, gdy zespół się rozrasta, oraz konfiguracja, która działa: zobowiązane pliki kontekstowe, jasna własność plików, przegląd według promienia eksplozji i koszty, które naprawdę widać.
Czytaj artykułJak Uruchomić od 3 do 8 Agentów Kodujących Równolegle Bez Utraty Kontroli
Uruchomienie wielu agentów Claude Code lub Codex jednocześnie jest proste. Kontrola jest tym, gdzie wszystko się rozpada. Oto metoda, która faktycznie działa.
Czytaj artykułJedno konto Claude na dwóch komputerach albo dla dwóch osób: co działa
Na jedno konto Claude możesz się zalogować na każdym swoim urządzeniu, a wszystkie korzystają z tych samych limitów 5-godzinnego i tygodniowego, Claude i Claude Code razem. Dwie osoby na jednym koncie to naruszenie warunków Anthropic, a planu rodzinnego nie ma. Co sprawdziliśmy 25 października 2026 i jak zobaczyć, który komputer zużył ile.
Czytaj artykuł