Author Archives: Ondřej Václavek

Pomalé buildy v Claude Code na MacOS

Pokud na macOS pracuješ s Claude Code a ještě nemáš zapnutý sandbox, zapni si ho. Je to jediná smysluplná obrana, jak izolovat agenta od zbytku stroje — filesystem, síť, syscally — a riziko vážného průšvihu klesne řádově. Praktický návod, jak to nakonfigurovat, je třeba v článku Sandboxing Claude Code on macOS. Tenhle text předpokládá, že už ten krok máš za sebou — a popisuje jeden konkrétní side-effect, na který jsem narazil.

Symptom

Začalo to nenápadně — všiml jsem si, že Claude Code mi začal nezvykle dlouho trvat i jednoduché úkoly. Drobné refaktory, na které byl dřív zvyklý odpovědět během chvíle, najednou zabraly desítky minut. Když jsem se podíval pořádně, co se uvnitř session vlastně děje, ukázalo se, že agent opakovaně spouští dotnet build a každý jednotlivý běh mu trvá pět minut. Lokálně, ve vedlejším terminálu, ten samý build doběhne za sekundu.

Výstup z těch dlouhých běhů vypadal pokaždé identicky:

  Determining projects to restore...

Build FAILED.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:05:00.81

Pět minut, FAILED, a přitom nula chyb a nula varování. Build failed s 0 errors? Co to vůbec je? Buď build selhal a má nám sdělit proč, nebo neselhal — ale tyhle dvě věci dohromady jsou nesmysl.

Diagnostika

Nechal jsem to vyřešit samotného agenta — zajímalo mě, jakou cestou se k příčině dostane on. Co následuje, je jeho úvaha nahlas.

První rozumný tip byl NuGet restore — síť uvnitř sandboxu je omezená a v logu svítilo „Determining projects to restore“. Zkusil tedy --no-restore. Stejný výsledek. Tak možná zápisová práva do ~/.dotnet nebo ~/.nuget cache? Ne. Přidal --no-dependencies -v:m, nic. Pro jistotu pak pustil ještě jeden běh úplně mimo sandbox přes dangerouslyDisableSandbox: true — a build doběhl za 1,02 sekundy. Tím bylo jasné, že problém je 100 % sandbox.

Když běžná podezření zradila, sáhl po -v:diag — to MSBuild zaplaví výstup úplně vším, co se uvnitř děje. Hned na prvních pár desítkách řádků se objevilo:

(eval):1: nice(5) failed: operation not permitted

Příčina

Claude Code na macOS pouští každý Bash tool call přes Apple Seatbelt sandbox, který blokuje syscall setpriority() (běžně volaný přes nice). MSBuild defaultně forkuje worker nody (-maxcpucount), každý worker si při startu chce snížit svou prioritu přes nice(), sandbox volání odmítne — a worker se zacyklí v silent retry/respawn smyčce. Žádná chyba na stdout, žádný non-zero exit, žádné varování. Jen čekání, až dotnet sám zabije proces vlastním 5-minutovým watchdogem.

Klíčové na tomhle failure mode: MSBuild považuje nice failure za non-fatal, a worker fork stack se snaží vzpamatovat. Sandbox tedy nezablokuje build — jen mu odebere mechanismus terminace.

Řešení

Sandbox nenabízí granular syscall allow (Seatbelt na macOS to neumí filtrovat per syscall pro běžné uživatelské profily). Co Claude Code umí, je vyloučit konkrétní commands ze sandboxování:

{
"sandbox": {
"excludedCommands": ["dotnet *"]
}
}

V ~/.claude/settings.json. Pozor na syntaxexcludedCommands používá Claude Code permission-rule syntax, tedy prefix s wildcardem. Plain ["dotnet"] se s ničím nematchne a problém přetrvá. Po změně settings je potřeba session restart.

Výsledek: dotnet build v sandboxu 0,62 s, žádný nice retry-loop.

HTTPS do každé rodiny [Ondřej Václavek, Vzdělávací okénko, 18.11.2020]

Záznam ze Vzdělávacího okénka HAVIT, kde Ondra Václavek povídal o HTTPS, Let’s Encrypt, ACME a dalších tématech z oblasti.

CORS a WebAPI

V rámci solution máme dva webové projekty – Web (javascriptová single-page aplikace) a WebAPI (backend s API). v ASP.NET Core 2.0. Řešili jsme problém, že nám nefungoval spolehlivě CORS – javascriptové requesty z Webu nedostávaly správné odpovědi od WebAPI. Chovalo se to velmi podivně i přesto, že lokálně vše fungovalo, a to i když aplikace běžely na různých portech.

Konkrétně:

  • Některé GET requesty fungovaly i v testovacím prostředí, ale POST, PUT a DELETE requesty nikdy.
  • GET requesty browser někdy zdvojoval (zejména v Safari na Macu), první vždy prošel (status code 200) a druhý neprošel (status code 403)
  • V Developer tools v Chrome, ani v Safari nejsou vidět autorizační hlavičky i přesto, že uživatel je přihlášený (při odchycení requestu Fiddlerem tam skutečně jsou). Ovšem na localhostu jsou vidět autorizační hlavičky vždy.

CORS v aplikaci máme nastaven celkem standardně a nepříliš restriktivně:

2018-04-10-CORS-settings

Kde byl(y) problém(y):

Nejdříve jsme neměli povolené všechny hlavičky (pozor zejména na Authorization, která není v Chrome Developer tools defaultně vidět). Nakonec jsme stejně skočili u AllowAnyHeader().

Důležité je mít povoleno AllowCredentials(), pokud řešíte přihlašování

Na co nám nejdéle trvalo přijít je, že je potřeba povolit anonymní autentifikaci i když v aplikaci žádné anonymní požadavky nejsou potřeba (povoluje se to v IIS na obrázku níže). Je potřeba na to ovšem myslet a v aplikaci si ošetřit, že uživatel musí být přihlášen při běžném přístupu do aplikace.

2018-04-10-CORS-IIS

Na pozadí CORS funguje tak, že vzdálený klient nejdříve udělá tzv. preflight request metodou OPTIONS a v hlavičce pošle informace, na jakou URL chce jakou metodou kdo přistupovat. Pokud není povolena anonymní autentifikace, OPTIONS požadavek selže (vrátí 401), protože v hlavičce preflight requestu se dle specifikace https://fetch.spec.whatwg.org/#cors-preflight-fetch autorizační hlavička neposílá. V takovém případě vzdálený klient není schopný zjistit, zda bude mít oprávnění udělat skutečný požadavek, a tak celá komunikace selže (nebo se chová nepředvídatelně – viz podivnosti výše).

Technické podrobnosti např. zde: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

Nakonec pro přehlednost ještě schéma posílání CORS požadavků:2018-04-10-CORS-schema