← Back to Blog

April 1, 2026

Instrukcja spec-driven konfiguracji Open-Claw przy pomocy Claude Code

AIOpen-ClawAutomatyzacjeAgents
open claw envs empty stage

Wyszedłem po zakupy. Wróciłem z działającym serwerem AI.

Michał Madejski — 2026-04-14


TL;DR

Dałem Claude Code jedno zdanie, wyszedłem z domu na 50 minut i wróciłem z zainstalowanym agentem AI, zahardowaną konfiguracją bezpieczeństwa, dokumentacją serwera, tunelem SSH do dashboardu i planami na Raspberry Pi. To nie jest artykuł o tym że AI jest magiczne. To artykuł o tym jak myśleć o AI jako partnerze inżynierskim — i co się naprawdę dzieje pod maską kiedy dajesz mu wolną rękę.


Dziennik przygody

Miałem wieczór, serwer który stał bezczynnie i od dawna odkładany pomysł żeby postawić na nim OpenClaw — self-hosted agenta AI. Wiedziałem mniej więcej co chcę, ale nie miałem czasu na godziny konfiguracji.

Zaczęło się banalnie. Dosłownie pierwsze pytanie jakie zadałem Claude'owi brzmiało (po polsku, bo tak mi wygodniej):

"co wpisać na linux żeby dał mi adres dla cb do połaczenia sie po ssh"

hostname -I. Tyle. Ale zamiast dostać odpowiedź i pójść dalej samemu — zostawiłem Claude'a przy klawiaturze. Powiedziałem żeby się połączył, dokumentował co działa a co nie i żeby zbudował mi repozytorium które będzie single source of truth dla całego projektu. A potem, gdy już miałem poczucie że wie co robi:

"ok, jade na zakupy na 50 min, jak wróce chce widzieć działający open-claw na ile to możliwe i konfigurować telegrama + malinke + na macbooku mieć dashboard. ciśnij o nic nie pytaj, nie wysadź mi domu"

I wyszedłem.

Wróciłem 50 minut później. Na ekranie czekał na mnie dokument HANDOFF.md z checklistą co sprawdzić po powrocie, działający dashboard OpenClaw w przeglądarce przez tunel SSH i zero błędów krytycznych w audycie bezpieczeństwa.

Siedzę teraz i próbuję opisać co właściwie się stało — bo to jest ciekawsze niż wynik.


Prompt który to uruchomił — i dlaczego działał

Najpierw ważna rzecz: ten jeden zdaniowy prompt z zakupami nie był pierwszym promptem. Poprzedzały go 20–30 minut rozmowy w której:

  1. Opisałem cel projektu i kontekst serwera
  2. Przekazałem filozofię jak chcę żeby projekt był zorganizowany ("co nie jest skryptem, nie istnieje")
  3. Dałem Claude'owi dostęp SSH i pozwoliłem mu samodzielnie wykonać rekonesans serwera

To jest ważne. Prompt autonomiczny działa dobrze tylko jeśli model ma wcześniej zbudowany kontekst. "Jedź na zakupy" działało dlatego że Claude wiedział już:

  • jaki jest serwer (hardware, OS, co jest zainstalowane)
  • jakie są moje priorytety bezpieczeństwa (mam CLAUDE.md z threat modelem)
  • jak chcę zorganizowany kod (IaC-lite, idempotentne skrypty bash)
  • co jest w scope a co nie (bez skilli z zewnątrz bez code review, bez ruszania LocalAI)

Gdybym powiedział "zainstaluj mi agenta AI na serwerze, nie pytaj" bez tego kontekstu — dostałbym coś, ale nie to czego chciałem.

Idealniejsza wersja tego prompta — gdybym miał to powtórzyć od zera — wyglądałaby tak:

Masz 45 minut autonomicznej pracy. Cel końcowy:
- OpenClaw zainstalowany na bluedemon i działający jako systemd service
- Hardening: exec.ask=always, deny-by-default, profil messaging
- Dashboard dostępny przez tunel SSH na Macu (nie wystawiaj portu publicznie)
- Plan Telegram bota i Raspberry Pi w docs/ i scripts/ nawet jeśli nie
  możesz ich uruchomić bez mnie
- HANDOFF.md który mogę przeczytać po powrocie

Ograniczenia których nie przekraczaj:
- Nie ruszaj LocalAI (4 padnięte kontenery Docker — osobny temat)
- Nie instaluj żadnych skilli z zewnątrz
- Jeśli coś jest niejasne — zrób lepszy z dwóch wariantów i udokumentuj
  dlaczego, nie pytaj mnie

Jak skończysz — zatrzymaj się i czekaj.

Różnica: jawne ograniczenia i instrukcja co zrobić z niejednoznacznością (zdecyduj sam, nie pytaj). To jest chyba najważniejszy element dobrego autonomicznego prompta — powiedz modelowi jak obsługiwać edge cases, nie tylko co robić w happy path.


Zanim cokolwiek zainstalowano — snapshot i filozofia folderów

Zanim Claude dotknął instalacji, zapytałem go o coś czego normalnie nie robi się przed "odpaleniem tutoriala": pełny rekonesans stanu zastanego.

Rezultat: skrypt 01-recon.sh który przez SSH zbierał snapshot serwera — hardware, procesy, otwarte porty, zainstalowane pakiety, stan Dockera, układ systemu plików — i zapisywał wszystko jako pliki tekstowe w katalogu snapshot/. Read-only. Zero zmian na serwerze. Czysta obserwacja.

open-claw/
├── CLAUDE.md                 ← mój oryginalny briefing (nienaruszony)
├── docs/                     ← proza: co, dlaczego, troubleshooting
│   ├── 00-ssh-setup.md       ← jak Claude się połączył
│   ├── 01-server-baseline.md ← analiza snapshotu
│   ├── 02-openclaw-install.md
│   └── HANDOFF.md            ← co sprawdzić po powrocie
├── snapshot/                 ← captured state serwera (do gita)
│   ├── hw-os.txt
│   ├── gpu.txt
│   ├── network.txt
│   ├── services.txt
│   └── INDEX.md
└── scripts/bootstrap/        ← idempotentne skrypty bash
    ├── 01-recon.sh
    ├── 02-install-openclaw.sh
    ├── 03-harden.sh
    └── 04-tunnel-dashboard.sh

Filozofia którą narzuciłem na początku: co nie jest skryptem, nie istnieje. Każda operacja którą można powtórzyć musi być skryptem. Każda decyzja którą można zapomnieć musi być w docs. Nic co "zrobiliśmy ręcznie i pamiętamy".

Claude potraktował to dosłownie. Napisał skrypty które są idempotentne — możesz je uruchamiać wiele razy i za każdym razem dochodzą do tego samego stanu. To nie jest oczywiste dla kogoś kto pisze bash skrypt po raz pierwszy. Idempotentność wymaga myślenia "co jeśli już jest zainstalowane?" na każdym kroku.

Przykład z 02-install-openclaw.sh:

# Zamiast po prostu:
npm install -g openclaw

# Skrypt sprawdza najpierw:
if openclaw --version 2>/dev/null | grep -q "2026"; then
  ok "openclaw already installed, skipping"
else
  npm install -g openclaw
fi

Drugie ważne założenie: snapshot idzie do gita, ale skrypty wyrzucają z niego sekrety przed zapisem. Token API, hasła, klucze — nigdy w snapshot/. Wartości zastąpione [REDACTED]. Dzięki temu stan serwera jest udokumentowany historycznie (git blame pokazuje jak się zmieniał), ale repozytorium jest bezpieczne publicznie.

Efekt: po wyjściu na zakupy Claude miał mapę terenu zanim cokolwiek zmienił. Wiedział że port 18789 jest wolny, że LocalAI nie działa (mimo że briefing twierdził inaczej), że nvm nie jest zainstalowany, że linger jest wyłączony. Nie zgadywał — czytał dane.

Wrócę do tego w kontekście odkryć.


Co było technicznie interesujące — trzy odkrycia

1. Dokumentacja kłamała — model to wykrył

W projekcie mam plik CLAUDE.md który jest "briefingiem" dla modelu — opisuje architekturę, bezpieczeństwo, stack technologiczny. Jedna linijka mówiła: "LocalAI już działa na tym serwerze i wykorzystuje GPU".

Nieprawda. Rekonesans serwera przez SSH pokazał 4 kontenery Docker w stanie Exited lub Created — nigdy nie uruchomione. GPU idle. Żaden model nie ładował się.

Claude nie zakwestionował CLAUDE.md słownie. Zamiast tego napisał w dokumentacji:

"CLAUDE.md informuje że LocalAI działa, ale snapshot mówi co innego. Moja rekomendacja: skip provider na onboardingu, LocalAI debug to osobny temat."

I poszedł dalej. Dobry model nie aktualizuje swojego world-view tylko dlatego że coś jest napisane w kontekście — weryfikuje to z obserwacją. To jest dokładnie ta właściwość którą chcesz w autonomicznym agencie.


2. Schema drift — jak odkryć API bez dokumentacji

OpenClaw ma plik konfiguracyjny z tysiącami możliwych kluczy. CLAUDE.md zawierał ścieżki konfiguracyjne z poprzedniej wersji oprogramowania:

# stare (CLAUDE.md):
agents.defaults.exec.ask = always

# aktualne (v2026.4.14):
tools.exec.ask = always

Problem: zainstalowana wersja to 2026.4.14, briefing dotyczył starszego schema. Klasyczny schema drift — dokumentacja nie nadąża za softem.

Jak Claude to wykrył? Użył --dry-run:

openclaw config set --batch-json --dry-run '{"agents.defaults.exec.ask": "always"}'
# → Error: Unrecognized key: exec (in agents.defaults)

Następnie iteracyjnie sondował schema:

openclaw config schema | grep -i exec
# → tools.exec.ask, tools.exec.security, ...

Metodologia: przed zapisem — dry run. Zamiast czytać 48 tysięcy linii schema — grep i próba. To jest podejście które każdy inżynier stosuje instynktownie przy nieznanym API, ale jest nieoczywiste że model zrobi to samo bez instrukcji.


3. Jak bezpiecznie przekazać hasło przez SSH do skryptu

Problem: chcę żeby skrypt bash wykonywał komendy sudo na zdalnym serwerze przez SSH. sudo wymaga hasła. Nie chcę żeby hasło pojawiało się:

  • w argumentach procesu (widoczne w ps aux)
  • w pliku (zostaje na dysku)
  • w logach

Rozwiązanie które Claude zaproponował:

# Zmienna środowiskowa (nie eksportowana do dziecka)
REMOTE_SUDO_PW='...'

sudo_remote() {
  ssh bluedemon "echo '${REMOTE_SUDO_PW}' | sudo -S $*"
}

Dlaczego echo | sudo -S zamiast sudo -S <<< "$PW":

  • sudo -S czyta hasło ze stdin
  • echo | przekazuje przez pipe — nie pojawia się w /proc/<pid>/cmdline
  • Zmienna nie jest eksportowana — nie dziedziczy jej żaden subprocess

Nie jest to doskonałe (hasło jest w pamięci procesu, set -x by je ujawniło), ale jest właściwym kompromisem dla skryptu który działa lokalnie przez SSH i nigdy nie ląduje w gicie.


Co to mówi o pracy z autonomicznymi modelami

Trzy obserwacje które wynoszę z tej sesji:

Kontekst jest ważniejszy niż prompt. Jakość autonomicznej pracy jest proporcjonalna do jakości kontekstu który dałeś wcześniej. CLAUDE.md, rekonesans serwera, jasne reguły — to wszystko było "inwestycją" która zwróciła się w tych 50 minutach.

Model powinien obsługiwać niejednoznaczność, nie eskalować jej. Najlepsza instrukcja w autonomicznym trybie to "jak coś jest niejasne — zrób lepszy wariant i uzasadnij". Eskalacja do użytkownika jest kosztowna i łamie autonomię. Dobry model wie kiedy zdecydować samemu.

Dokumentacja jako pierwsza klasa. Najcenniejszym produktem tej sesji nie jest działający OpenClaw — to HANDOFF.md i docs/. Sam deployment można powtórzyć skryptem. Wiedza o tym dlaczego coś zrobiono tak a nie inaczej — to jest to co normalnie ginie.


Co dalej

System działa. Mam dashboard, zahardowaną konfigurację i zero krytycznych błędów w audycie bezpieczeństwa. Następne kroki to Telegram bot (czeka na token od BotFather) i Raspberry Pi z customowym wake-word "Lilou" w salonie — pełna architektura już jest w repozytorium, czeka tylko na sprzęt.

Repozytorium z całą infrastrukturą i dokumentacją prowadzę pod kątem powtarzalności — każdy skrypt jest idempotentny, każda decyzja jest udokumentowana. Jeśli serwer kiedyś padnie — instalacja od zera powinna zająć tyle co przeczytanie docs/ i odpalenie skryptów.

Jeśli masz pytania albo chcesz porozmawiać o którejś z kwestii technicznych — napisz. Szczególnie chętnie pogadam o security modelu dla AI agentów i o tym jak projektować kontekst dla autonomicznych sesji.