Personalizzare le sessioni negli ambienti self-hosted
Personalizzare le sessioni degli ambienti self-hosted con script wrapper per credenziali per sessione, hook del ciclo di vita e spawning di runner su richiesta.
Gli ambienti self-hosted sono in beta pubblica sui piani Team ed Enterprise; un Owner li abilita attivando Allow self-hosted environments nella pagina di amministrazione Cloud environments. Questa pagina presuppone un runner funzionante; consulta la guida rapida per la configurazione e Deploy to production per le ricette della flotta.
Un ambiente self-hosted esegue le sessioni cloud di Claude Code sulla tua infrastruttura, eseguite da un processo runner che distribuisci. Senza configurazione, quel runner clona il repository della sessione, avvia Claude Code e pulisce. Questa pagina è per l'ingegnere della piattaforma che gestisce i runner: copre i punti di estensione per quando questi valori predefiniti non si adattano, dal provisioning delle credenziali per sessione alla sostituzione completa del checkout. I wrapper e gli hook vengono eseguiti come file eseguibili sull'host del runner, che è Linux o macOS, e gli esempi su questa pagina presuppongono una shell POSIX.
Alcune variabili d'ambiente degli hook su questa pagina utilizzano ancora pool, come CLAUDE_RUNNER_POOL_ID; i nomi dei flag CLI e delle variabili d'ambiente utilizzano environment, come --environment-secret-file.
Script wrapper
Usa uno script wrapper quando ogni sessione ha bisogno di una configurazione che il runner non può fare da solo: provisioning di credenziali di breve durata limitate al creatore della sessione, esportazione di segreti specifici dell'ambiente, preparazione di toolchain di linguaggio o applicazione di limiti di risorse attorno al processo figlio. Il runner avvia il tuo wrapper al posto del binario Claude Code, una volta per sessione. Termina il wrapper con exec in $CLAUDE_RUNNER_CLAUDE_BIN, il binario del runner stesso, in modo che i segnali e i codici di uscita si propaghino correttamente.
Punta --exec-path, o SELF_HOSTED_RUNNER_EXEC_PATH, al wrapper quando avvii il runner:
claude self-hosted-runner --environment-secret-file /etc/claude/environment-secret --exec-path /etc/claude/session-wrapper.sh
Il runner imposta quanto segue nell'ambiente del wrapper:
| Variabile | Descrizione |
|---|---|
CLAUDE_CODE_SESSION_ACCESS_TOKEN |
Il JWT della sessione, con prefisso sk-ant-cc-. Il suo claim act identifica il creatore della sessione, con l'email del creatore quando la superficie di creazione l'ha registrata. Il valore è il token al momento dello spawn; gli aggiornamenti arrivano sullo stdin del figlio, quindi un wrapper vede solo il valore iniziale. Consulta Verify session identity. |
CCR_SESSION_ACCOUNT_EMAIL |
L'email del creatore della sessione, pre-estratta dal runner dal claim act.email del token senza verifica della firma. Adatta per l'etichettatura, come i trailer dei commit. Quando l'email controlla il rilascio delle credenziali, verifica il token e leggi il claim da esso; consulta Provision credentials scoped to the session creator. Non impostata quando il token non contiene un'email del creatore. Trattala come informazione personale identificabile. |
CLAUDE_RUNNER_CLIENT_PLATFORM |
La superficie client che ha creato la sessione, come web_claude_ai, desktop_app, ios, claude_code_cli o scheduled_trigger. Anthropic registra il valore una volta alla creazione della sessione, quindi il wrapper e ogni hook del ciclo di vita vedono lo stesso valore. Usala solo per l'analisi dell'adozione e l'etichettatura, non come segnale di autorizzazione. Non impostata quando la sessione non ha una superficie registrata o riconosciuta, quindi fai riferimento ad essa come ${CLAUDE_RUNNER_CLIENT_PLATFORM:-} sotto set -u. Richiede Claude Code v2.1.229 o successivo. |
CLAUDE_RUNNER_CLAUDE_BIN |
Percorso assoluto al binario Claude Code del runner stesso. Termina il wrapper con exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@" per passare il controllo al binario bloccato senza hardcodificare un percorso di installazione. |
CLAUDE_CODE_REMOTE_SESSION_ID |
ID sessione nel formato taggato cse_.... Questa è la stessa sessione che gli hook del ciclo di vita vedono come CLAUDE_RUNNER_SESSION_ID nel formato session_...; le variabili UUID corrispondono in entrambi, e sostituire il prefisso cse_ con session_ produce l'ID mostrato nell'URL della sessione. |
CLAUDE_CODE_REMOTE_SESSION_UUID |
Lo stesso ID sessione nel formato UUID canonico, per i sistemi che si basano su UUID. |
CLAUDE_SESSION_INGRESS_TOKEN_FILE |
Percorso assoluto a un file per sessione che contiene il JWT della sessione corrente, mantenuto aggiornato negli aggiornamenti dei token. I sottoprocessi della shell lo leggono per il loro header Authorization quando scaricano gli allegati che l'utente ha aggiunto alla sessione. exec preserva la variabile automaticamente; un wrapper che ricostruisce l'ambiente del figlio deve riportare la variabile, altrimenti i download degli allegati smettono di funzionare silenziosamente. |
CLAUDE_CONFIG_DIR |
Directory di configurazione Claude per sessione, scritta all'inizio della sessione dallo snapshot della configurazione dell'host del runner che il runner acquisisce all'avvio; consulta Permissions and tool approval. Le scritture qui sono isolate a questa sessione. La directory rimane sotto <base-dir>/_sessions/ dopo la fine della sessione a meno che tu non avvii il runner con --remove-session-state; consulta Reuse a pre-warmed checkout. |
ANTHROPIC_BASE_URL |
L'URL di base dell'API che il figlio utilizzerà, fornito dal piano di controllo per sessione e normalmente https://api.anthropic.com. Non sovrascriverlo: la credenziale di inferenza della sessione è un token OAuth emesso da Anthropic che altri provider non accettano. |
CLAUDE_CODE_OAUTH_TOKEN |
Il token di accesso OAuth di breve durata che il figlio utilizza per l'inferenza del modello, limitato solo all'inferenza del modello e al caricamento di file, con una durata di circa 30 minuti. Il runner lo riemette prima della scadenza e fornisce la rotazione sullo stdin del figlio, quindi un wrapper che non mantiene stdin collegato vede solo il valore iniziale. Non fare affidamento sull'allowlist IP della tua organizzazione per limitare l'uso di questo token: trattalo come una credenziale bearer che rimane utilizzabile per circa 30 minuti se trapela, e non registrarlo nei log, non scriverlo su disco né inoltrarlo al di fuori del container della sessione. |
Il wrapper eredita anche il resto dell'ambiente gestito del figlio, incluse tutte le variabili d'ambiente fornite dal server. exec le propaga tutte automaticamente; se il tuo wrapper avvia il figlio in un altro modo, inoltra l'ambiente completo.
Mantenere stdin e il descrittore di file 3 collegati
Lo stdin del figlio è il canale di controllo del runner. Le rotazioni dei token e i segnali di fine sessione arrivano su di esso. Il runner apre anche una pipe sul descrittore di file 3 e legge da essa i segnali di attività del figlio per gestire i timeout di inattività e di avvio. Un semplice exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@" preserva entrambi automaticamente.
Se il tuo wrapper mette in background il figlio con un semplice &, interrompe lo stdin del figlio: la sessione sembra sana fino a quando non scade la durata di circa 30 minuti del token OAuth iniziale, dopodiché ogni chiamata API fallisce con 401 authentication_error. Se il tuo wrapper deve mettere in background il figlio, ad esempio per mantenere attivo un trap di teardown, salva stdin sul descrittore di file 4 o superiore e ricollegalo esplicitamente:
exec 4<&0
"$CLAUDE_RUNNER_CLAUDE_BIN" "$@" <&4 4<&- &
CHILD=$!
trap 'teardown' EXIT
wait "$CHILD"
Non chiudere né riutilizzare il descrittore di file 3 nel wrapper. Reindirizzare lo stdout e lo stderr del figlio va bene.
Passare i flag del prompt di sistema
Il prompt di sistema e il prompt di sistema aggiuntivo che il piano di controllo di Anthropic invia per una sessione raggiungono il tuo wrapper come percorsi di file, non come testo inline. Il runner scrive ogni prompt in un file nella directory di configurazione della sessione, CLAUDE_CONFIG_DIR, e ne passa il percorso negli argomenti che il tuo wrapper riceve, come --system-prompt-file <path> o --append-system-prompt-file <path>.
I runner su Claude Code v2.1.281 o successivo forniscono i prompt come file. Prima della v2.1.281, il runner li passava come --system-prompt <text> e --append-system-prompt <text>.
Nel tuo script wrapper o hook command, gestisci questi flag come segue:
- Passali così come sono: termina il wrapper con
exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@", che inoltra i flag dei file insieme a ogni altro argomento. Non eliminarli né riscriverli. Se una sessione perde un flag del file di prompt, viene eseguita senza le istruzioni che il piano di controllo ha inviato per essa. - Su un runner alla v2.1.281 o successiva, un flag di file che aggiungi sostituisce quello del server, non vi si aggiunge mai: ogni flag del file di prompt accetta un solo valore e Claude Code mantiene l'ultima occorrenza, quindi se aggiungi
--append-system-prompt-file <path>dopo"$@", il contenuto del tuo file sostituisce le istruzioni aggiuntive del server. Per aggiungere istruzioni oltre a quelle del server, inseriscile nelCLAUDE.mddell'immagine del runner, che il runner inserisce nella configurazione a livello utente di ogni sessione.
Provisioning di credenziali limitate al creatore della sessione
Usa il sottocomando decode-token per leggere i claim dal JWT della sessione. Legge il token da un argomento, da CLAUDE_CODE_SESSION_ACCESS_TOKEN o da stdin, in quest'ordine; consulta Verify the token inside the session per sapere cosa controlla. L'esempio seguente decodifica l'identità del creatore, la scambia con credenziali AWS di breve durata ed esegue exec in Claude Code:
#!/bin/bash
# Key on the stable Anthropic user ID and require a human creator.
CREATOR_SUB=$("$CLAUDE_RUNNER_CLAUDE_BIN" self-hosted-runner decode-token \
| jq -re '.act.sub // "" | select(startswith("user:"))') \
|| { echo "decode-token: verification failed or no human creator" >&2; exit 1; }
creds=$(your-sts-helper assume-role --subject "$CREATOR_SUB") \
|| { echo "credential exchange failed" >&2; exit 1; }
eval "$creds"
exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@"
Usa jq -re anziché jq -r quando il claim estratto controlla una decisione di autenticazione, in modo che un claim assente esca con codice diverso da zero invece di passare a valle la stringa letterale null. Le sessioni create da un'identità di servizio dell'organizzazione, come le sessioni di bot e agenti, portano un soggetto agent: anziché user:, quindi questo esempio le rifiuta; se il tuo ambiente serve quelle sessioni, decidi esplicitamente se il wrapper debba ricorrere a una credenziale predefinita per esse invece di uscire. Quando il tuo scambio di credenziali ha invece bisogno dell'email, leggi .act.email e gestiscine l'assenza: il token la contiene solo quando la superficie di creazione l'ha registrata, e una sessione inviata da CLI può esserne priva. Per il riferimento completo dei claim e la verifica da servizi al di fuori del runner, consulta Verify session identity.
Hook del ciclo di vita
Gli hook del ciclo di vita sostituiscono le fasi della pipeline per sessione del runner con i propri script. Puntare il runner a una directory di hook con --hooks-dir <path>, o SELF_HOSTED_RUNNER_HOOKS_DIR. Il runner cerca file eseguibili con nomi ben noti; qualsiasi hook che non è presente ricade nel comportamento integrato, quindi si scrivono solo quelli di cui si ha bisogno. Gli hook vengono eseguiti con i privilegi del runner stesso, e i figli della sessione condividono quel UID, quindi montare la directory degli hook in sola lettura, o cuocerla nell'immagine, in modo che il codice della sessione non possa modificarla; consultare la sezione di hardening.
Questi hook sono distinti dagli hook di Claude Code, che vengono eseguiti all'interno della sessione; gli hook del ciclo di vita vengono eseguiti sul runner, attorno alla sessione.
checkout
Viene eseguito una volta per repository, al posto del clone e del fetch integrati del runner. Usa l'hook per clonare da un mirror read-through, popolare un albero di lavoro da un archivio o applicare l'autenticazione git per sessione. Il runner imposta queste variabili, e può impostare altre variabili CLAUDE_RUNNER_ che la tabella non elenca:
| Variabile | Descrizione |
|---|---|
CLAUDE_RUNNER_REPO_URL |
URL del repository da clonare, dopo che --git-host-rewrite e --git-ssh-rewrite sono stati applicati |
CLAUDE_RUNNER_REPO_REF |
Revisione da controllare: ramo, tag o commit SHA come la sessione lo ha richiesto. Vuoto significa il ramo predefinito del repository. |
CLAUDE_RUNNER_CHECKOUT_PATH |
Percorso assoluto dove l'albero di lavoro deve essere lasciato |
CLAUDE_RUNNER_SESSION_ID |
ID sessione nel modulo taggato session_..., per la registrazione e la correlazione |
CLAUDE_RUNNER_SESSION_UUID |
Lo stesso ID sessione nel modulo UUID canonico |
CLAUDE_RUNNER_API_BASE_URL |
URL di base dell'API Anthropic per le chiamate limitate alla sessione |
CLAUDE_RUNNER_CLIENT_PLATFORM |
La superficie client che ha creato la sessione, come web_claude_ai, desktop_app o ios. Non impostato quando la sessione non ha una superficie registrata o riconosciuta. |
CLAUDE_CODE_SESSION_ACCESS_TOKEN |
Il token di accesso della sessione, per le chiamate API limitate alla sessione |
GIT_CONFIG_COUNT, GIT_CONFIG_KEY_n, GIT_CONFIG_VALUE_n |
Impostazioni git che il runner fissa per il git eseguito dal tuo hook. Configurazione git all'interno degli hook del ciclo di vita le descrive. Richiede Claude Code v2.1.280 o successivo. |
Lo script deve lasciare un albero di lavoro in CLAUDE_RUNNER_CHECKOUT_PATH controllato alla revisione richiesta. HEAD staccato va bene; il runner crea il ramo di lavoro della sessione in cima. Il runner verifica che il percorso contenga un .git in seguito; se l'hook materializza una fonte non-git come Perforce o un tarball scompattato, impostare CLAUDE_RUNNER_SKIP_GIT_VERIFY=1 nell'ambiente del runner per saltare quel controllo. I flussi basati su Git come la creazione del ramo di lavoro e il push dei risultati richiedono un checkout git, quindi esportare i risultati da alberi non-git con un hook post-session.
Il runner non passa una credenziale git all'hook. Invece, coniare una credenziale di clone per sessione dall'identità della sessione: verificare CLAUDE_CODE_SESSION_ACCESS_TOKEN con una libreria JWT standard rispetto all'endpoint JWKS sotto CLAUDE_RUNNER_API_BASE_URL, come descritto in Verify the token from your service, quindi fare in modo che il servizio di credenziale emetta una credenziale di clone di breve durata per l'identità nel claim act del token. CLAUDE_RUNNER_CLAUDE_BIN non è impostato nell'ambiente dell'hook di checkout, quindi il subcomando decode-token non è disponibile qui. Ricadere in qualsiasi autenticazione git che l'host ha già, come un agente SSH, un helper di credenziale o .netrc, è anche un'opzione.
Quando l'hook esce con codice diverso da zero, o esce 0 senza lasciare un checkout utilizzabile dietro, ciò che il runner fa dipende dal repository:
- Un repository a cui la sessione spinge i risultati: il runner fallisce la sessione e su un'uscita diversa da zero mostra la coda dello stderr dello script all'utente.
- Un repository che la sessione legge solo, come un repository aggiunto a una sessione in esecuzione: il runner registra una riga
[runner:warn]con il dettaglio del fallimento, pubblica un passoSkippedalla sessione, rimuove ciò che l'hook ha lasciato al percorso di checkout e continua con i repository rimanenti. Quando il runner non può rimuovere il percorso immediatamente, ritenta la rimozione alla fine della sessione. Se saltare lascia la sessione senza alcun repository, il runner fallisce comunque la sessione.
Prima della v2.1.228, il runner falliva la sessione su un fallimento dell'hook per qualsiasi repository, quindi un repository di sola lettura che l'hook non poteva servire falliva di nuovo la sessione su ogni nuovo runner su cui la sessione riprendeva.
Il runner rimuove il percorso di checkout dopo la fine della sessione.
post-session
Viene eseguito una volta per sessione, dopo che il figlio Claude Code è uscito e prima che il runner smantelli l'area di lavoro. Questo hook è la tua unica possibilità di salvare il lavoro non committato: a --capacity superiore a uno, il runner elimina i worktree per sessione subito dopo il ritorno dell'hook, e a --capacity 1 il clone canonico riutilizzato viene hard-reset quando la sessione successiva inizia, quindi i cambiamenti tracciati non committati non sopravvivono su nessuno dei due percorsi. Gli usi tipici sono il push di un ramo snapshot di cambiamenti non committati, l'archiviazione di log o l'emissione di un evento di fine sessione ai propri sistemi.
L'hook si attiva ad ogni fine sessione dove un processo figlio è stato generato, qualunque sia la causa; i valori CLAUDE_RUNNER_EXIT_REASON di seguito enumerano i casi. Non può attivarsi quando il runner termina bruscamente, come una preemption VM o una perdita di potenza; se hai bisogno di garanzie contro la terminazione brusca, fai uno snapshot periodicamente dall'interno della sessione con un hook Claude Code PostToolUse invece. Il runner imposta:
| Variabile | Descrizione |
|---|---|
CLAUDE_RUNNER_SESSION_ID |
ID sessione nel modulo taggato session_... |
CLAUDE_RUNNER_SESSION_UUID |
Lo stesso ID sessione nel modulo UUID canonico |
CLAUDE_RUNNER_EXIT_REASON |
Come la sessione è terminata; consultare i valori sotto la tabella |
CLAUDE_RUNNER_WORKSPACE_PATHS |
Percorsi assoluti separati da due punti degli alberi di lavoro della sessione. Vuoto per sessioni senza repository. |
CLAUDE_RUNNER_DEBUG_LOG_PATH |
Percorso al log di debug della sessione, ancora su disco mentre l'hook viene eseguito |
CLAUDE_RUNNER_API_BASE_URL |
URL di base dell'API Anthropic per le chiamate limitate alla sessione |
CLAUDE_RUNNER_CLIENT_PLATFORM |
La superficie client che ha creato la sessione, come web_claude_ai, desktop_app o ios. Non impostato quando la sessione non ha una superficie registrata o riconosciuta. Richiede Claude Code v2.1.229 o successivo. |
CLAUDE_CODE_SESSION_ACCESS_TOKEN |
Il token di accesso della sessione, per le chiamate API limitate alla sessione |
GIT_CONFIG_COUNT, GIT_CONFIG_KEY_n, GIT_CONFIG_VALUE_n |
Impostazioni git che il runner fissa per il git eseguito dal tuo hook. Configurazione git all'interno degli hook del ciclo di vita le descrive. Richiede Claude Code v2.1.280 o successivo. |
CLAUDE_RUNNER_EXIT_REASON assume uno di quattro valori:
completed: la sessione è terminata in modo pulito. Il processo Claude Code è uscito normalmente, oppure la sessione è stata archiviata o eliminata mentre era ancora in esecuzione.failed: il processo Claude Code è andato in crash, oppure la configurazione è fallita dopo l'avvio.interrupted: il runner ha interrotto la sessione. Ha rilasciato la sessione per liberare lo slot, la sessione è scaduta all'avvio, il server ha spostato la sessione da questo runner, il runner era in drenaggio, oppure la sessione ha superato il limite--kill-session-after-min.abandoned: riservato per una sessione che un altro runner ha rivendicato. L'hook attualmente non si attiva in quel caso.
I contatori del ciclo di vita della sessione contano un rilascio, un timeout di avvio e uno spostamento del server come completed piuttosto che interrupted, perché il runner ha restituito lo slot in modo pulito. Aspettati quella differenza se confronti le ricevute dell'hook con i contatori.
Lo stato di uscita dell'hook non influisce mai sul risultato della sessione; un fallimento viene registrato e ignorato. Il runner attende fino a --post-session-hook-timeout-sec, 60 secondi per impostazione predefinita, ad ogni fine sessione incluso l'arresto del runner. Questo esempio salva il lavoro non committato in un ramo di salvataggio:
#!/usr/bin/env bash
set -u
IFS=':'
# -c overrides beat repo-local settings, blocking session-written fsmonitor,
# hook-path, and gpg-program config from executing code with the hook's
# privileges. -c commit.gpgsign=false also leaves these rescue commits
# unsigned under --configure-git.
# Repo-local credential.helper and pushurl still apply, and on a runner
# before v2.1.280 so does core.sshCommand; if the hook holds credentials
# the session didn't, see the note below the script.
g() { git -c core.fsmonitor=false -c core.hooksPath=/dev/null \
-c commit.gpgsign=false "$@"; }
for ws in $CLAUDE_RUNNER_WORKSPACE_PATHS; do
cd "$ws" 2>/dev/null || continue
[ -z "$(g status --porcelain 2>/dev/null)" ] && continue
g add -A
g commit -q -m "runner snapshot: $CLAUDE_RUNNER_SESSION_ID ($CLAUDE_RUNNER_EXIT_REASON)" || continue
g push -q origin "HEAD:refs/heads/rescue/$CLAUDE_RUNNER_SESSION_ID" || true
done
L'hook esegue il push con le credenziali git disponibili nel proprio ambiente sull'host del runner. Con l'approccio senza credenziali nell'immagine, anche quando il clone integrato passa attraverso il proxy git di Anthropic, non ce ne sono, quindi genera una credenziale di push di breve durata all'interno dell'hook prima del push: scambia il token della sessione che l'hook riceve in CLAUDE_CODE_SESSION_ACCESS_TOKEN con il tuo servizio di token, verificandolo come descritto in Verificare l'identità della sessione. Quando l'hook dispone di una credenziale che la sessione non aveva, sostituisci origin con un URL fornito dall'operatore e passa -c credential.helper= insieme al tuo helper. Configurazione git all'interno degli hook del ciclo di vita descrive su cosa può ancora influire la configurazione scritta dalla sessione.
Hook timing quando il runner rilascia una sessione
Una sessione rilasciata può riprendere su un altro runner. Su un runner su v2.1.236 o successivo, ciò che la sessione stava facendo al rilascio decide se può riprendere prima che questo hook finisca:
- Inattivo dopo un turno, o timeout all'avvio: il runner ferma il figlio ed esegue questo hook fino al completamento. Solo allora rilascia la sessione. Un messaggio utente inviato mentre l'hook viene eseguito non può riprendere la sessione su un altro runner prima che l'hook finisca.
- In attesa che l'utente risponda a un prompt, come un prompt di autorizzazione: il runner rilascia la sessione per primo, quindi esegue questo hook. Un messaggio utente inviato mentre l'hook viene eseguito può riprendere la sessione su un altro runner prima che l'hook finisca.
Questo si applica ogni volta che il runner rilascia una sessione: al timeout di inattività, al momento --retire-at, e, su un runner su v2.1.260 o successivo, al limite --kill-session-after-min di una sessione. Una sessione il cui turno è terminato e che contiene solo attività in background conta come inattiva qui. Prima della v2.1.236, il runner rilasciava la sessione per primo e quindi eseguiva questo hook in entrambi i casi.
Durante un drenaggio SIGTERM, il runner mantiene il lease della sessione fino al completamento dell'hook; consultare Shutdown timing.
Configurazione git all'interno degli hook del ciclo di vita
Gli hook checkout e post-session vengono eseguiti con il token di accesso della sessione nel loro ambiente, e il git che eseguono legge file di configurazione che le sessioni possono scrivere, come ~/.gitconfig e il .git/config di un checkout. Prima dell'esecuzione di ciascuno dei due hook, il runner imposta nell'ambiente dell'hook alcune impostazioni git, tra cui quelle elencate sotto, come coppie GIT_CONFIG_COUNT/GIT_CONFIG_KEY_n/GIT_CONFIG_VALUE_n e variabili d'ambiente git. Git le considera prioritarie rispetto a ogni file di configurazione, e si applicano solo al git eseguito dai tuoi hook, non al git della sessione. All'avvio, il runner stampa una riga [runner:git] lifecycle hooks: che mostra il percorso degli hook, i protocolli consentiti, i programmi gpg e la modalità di firma in vigore. Richiede Claude Code v2.1.280 o successivo.
- Hook git: a meno che tu non fornisca un valore,
core.hooksPathè/dev/null, quindi git salta gli hook nella.git/hooksdi un repository e in qualsiasi directory di hook indicata da~/.gitconfig. Per fornirne uno, esportacore.hooksPathcome coppiaGIT_CONFIG_KEY_n/GIT_CONFIG_VALUE_nnell'ambiente del runner. Il runner legge anchecore.hooksPathdalla configurazione git di sistema, e la usa solo quando l'utente del runner non può scrivere in quel file, nella directory indicata o nei file di hook che contiene. Quando il runner ignora un valore, una riga[runner:warn]all'avvio indica il valore e il motivo. - Monitor del file system:
core.fsmonitorè vuoto, quindi il git nel tuo hook non esegue un programma di monitoraggio indicato da un file di configurazione. - Protocolli remoti:
GIT_ALLOW_PROTOCOLèhttps:http:ssh. Un clone, un fetch o un push che usa un percorso locale, un URLfile://o un URLgit://fallisce confatal: transport 'file' not allowedofatal: transport 'git' not allowed. - Comando SSH e richiesta di credenziali: il git nel tuo hook ignora
core.sshCommandecore.askPassdai file di configurazione. Per usare il tuo comando SSH, impostaGIT_SSH_COMMANDnell'ambiente del runner. Per usare un programma di richiesta delle credenziali, imposta lìGIT_ASKPASS. Le sessioni ereditano l'ambiente del runner, quindi entrambe le variabili raggiungono anche il git della sessione. Non inserire una credenziale in nessuna delle due. - Programmi gpg:
gpg.program,gpg.openpgp.program,gpg.x509.programegpg.ssh.programsono percorsi impostati dal runner, mai valori provenienti da un file di configurazione. - Firma dei commit: con
--configure-git, i commit che esegui da un hook vengono firmati come la sessione. Senza il flag,commit.gpgsignetag.gpgsignsonofalse.
Per modificare una di queste impostazioni, usa l'ambiente del runner o un'opzione git -c all'interno dell'hook:
- Coppie di configurazione: una coppia
GIT_CONFIG_KEY_n/GIT_CONFIG_VALUE_nche esporti nell'ambiente del runner sostituisce il valore del runner per la stessa chiave. Numera le tue coppie a partire da0e impostaGIT_CONFIG_COUNTsul loro numero. Quando manca l'ultima coppia indicata dal conteggio, il runner ignora tutte le tue coppie e registra una riga[runner:warn]all'avvio. - Variabili d'ambiente git: il runner lascia
GIT_ALLOW_PROTOCOL,GIT_SSH_COMMANDeGIT_ASKPASScome le imposti nel suo ambiente. - Opzioni
git -c: un'opzionegit -call'interno dell'hook sovrascrive una coppiaGIT_CONFIG_KEY_n, sia quella del runner sia la tua. Non modificaGIT_ALLOW_PROTOCOL,GIT_SSH_COMMANDoGIT_ASKPASS, che git legge prima di qualsiasi configurazione.
Il git nel tuo hook legge comunque ogni impostazione che il runner non imposta, come i credential helper, le riscritture url.*.insteadOf e i filter driver, da ogni file di configurazione, compresi quelli che le sessioni possono scrivere. Un credential helper o un filter driver indicato in uno di questi file viene eseguito come programma con i privilegi del tuo hook, e la configurazione in quei file può ancora cambiare la destinazione di un push dal tuo hook, incluso un push verso un URL che passi sulla riga di comando.
Prima della v2.1.280, il runner non impostava nessuna di queste impostazioni e, con --configure-git, un commit eseguito da un hook falliva a meno che l'hook non passasse -c commit.gpgsign=false.
command
Viene eseguito una volta per sessione dopo il checkout, al posto dello spawn del figlio integrato. L'hook riceve lo stesso ambiente di uno script wrapper e dovrebbe fare exec in "$CLAUDE_RUNNER_CLAUDE_BIN" allo stesso modo. Utilizzare l'hook command per mantenere tutta la personalizzazione in una directory di hook; utilizzare --exec-path quando il wrapper vive altrove. Se --exec-path è anche impostato, il flag ha la precedenza e l'hook command viene ignorato.
Sempre fare exec del binario del runner stesso piuttosto che di un claude risolto da PATH; altrimenti si sconfigge il pinning della versione.
Runner su richiesta
Invece di eseguire una flotta fissa, è possibile avviare un runner per sessione. L'orchestratore è un subcomando separato e senza stato che esegue il polling di Anthropic per le richieste di spawn, una per sessione in coda senza runner disponibile, ed esegue l'hook spawn-runner per ciascuna. L'hook invia un carico di lavoro alla propria piattaforma: un Kubernetes Job, un'istanza EC2, un dispatch Nomad.
I runner su richiesta migliorano l'igiene delle credenziali. Su una flotta fissa, il segreto dell'ambiente vive su ogni host del runner, che è lo stesso host che esegue le sessioni utente. Con l'orchestratore, il segreto dell'ambiente rimane solo sull'host dell'orchestratore, che non esegue mai il codice utente; ogni runner generato riceve un ordine di lavoro monouso che registra esattamente un runner e quindi scade.
Per avviare l'orchestratore, passare il segreto dell'ambiente e una directory di hook contenente uno script spawn-runner eseguibile:
claude self-hosted-runner orchestrator \
--environment-secret-file /etc/claude/environment-secret \
--hooks-dir /etc/claude/hooks
L'orchestratore non mantiene alcuno stato tra i poll, quindi è possibile eseguire due o più repliche rispetto allo stesso ambiente per la disponibilità. Ogni richiesta di spawn viene rivendicata lato server da esattamente una replica. Tutte le repliche devono utilizzare lo stesso valore --expected-spawn-seconds; consultare il contratto dell'hook.
L'hook spawn-runner
L'orchestratore esegue ${hooks-dir}/spawn-runner una volta per richiesta di spawn. L'hook deve inviare il lavoro in modo asincrono, senza attendere l'avvio del runner, e tornare entro --hook-timeout, 60 secondi per impostazione predefinita. L'hook riceve:
| Variabile | Descrizione |
|---|---|
CLAUDE_RUNNER_WORK_ORDER_FILE |
Percorso a un file temporaneo contenente il JWT dell'ordine di lavoro firmato con cui il nuovo runner si registra. Eliminato dopo l'uscita dell'hook. Non registrare il contenuto del file. |
CLAUDE_RUNNER_ORDER_ID |
Chiave di idempotenza opaca, unica per richiesta di spawn e sicura per i nomi delle risorse Kubernetes. Utilizzarla come chiave di dedup del provisioner. |
CLAUDE_RUNNER_SESSION_ID |
La sessione per cui è questa richiesta. Si ripete su ogni richiesta per la sessione, quindi utilizzarla per la registrazione e l'instradamento, non come chiave di dedup. Vuoto per le richieste di pre-warming, che avviano un runner standby prima di qualsiasi sessione specifica quando --min-idle è impostato, quindi non assumere che la variabile sia impostata. |
CLAUDE_RUNNER_SESSION_UUID |
Lo stesso ID sessione nel modulo UUID canonico. Vuoto per le richieste di pre-warming. |
CLAUDE_RUNNER_ATTEMPT |
Quante richieste di spawn questa sessione ha avuto. 0 per le richieste di pre-warming. |
CLAUDE_RUNNER_ORDER_SERVER_TIME |
Ora del server dalla risposta del poll header HTTP Date. Quando l'hook verifica l'exp del JWT dell'ordine di lavoro, confrontare rispetto a questo valore invece dell'orologio locale per tollerare lo skew. Vuoto quando il gateway ha omesso l'header. |
CLAUDE_RUNNER_POOL_ID |
L'ID dell'ambiente a cui il nuovo runner dovrebbe unirsi, nel modulo ccpool_... |
CLAUDE_RUNNER_ACCOUNT_ID |
ID taggato dell'account che ha accodato la sessione, per l'instradamento per account, la quota o il chargeback. Vuoto quando non disponibile, e sempre vuoto per le sessioni del canale Claude Tag, che nessun account accoda. |
CLAUDE_RUNNER_ACCOUNT_EMAIL |
Email dell'account che ha accodato la sessione. Vuoto quando non disponibile. Trattare l'email come informazioni personali identificabili e non registrarla. |
CLAUDE_RUNNER_PRIMARY_REPO_URL |
URL della prima fonte git della sessione, per l'instradamento a un runner con quel repository pre-riscaldato. Vuoto quando la sessione non ha fonti git. |
CLAUDE_RUNNER_PRIMARY_REPO_REVISION |
Revisione della prima fonte git della sessione: ramo, SHA o tag. Vuoto quando non specificato. |
CLAUDE_RUNNER_REPO_SOURCES |
Array JSON di {url, revision} per tutte le fonti git della sessione, per gli hook che instradano su un repository secondario. Vuoto quando non ci sono fonti. |
CLAUDE_RUNNER_CORRELATION_ID |
L'ID di correlazione fornito alla creazione della sessione, ripetuto in modo che l'hook possa mappare questo ordine di lavoro alla richiesta che ha creato la sessione. Vuoto quando la sessione non ne ha uno. |
CLAUDE_RUNNER_CLIENT_PLATFORM |
La superficie client che ha creato la sessione, come web_claude_ai, desktop_app, ios o scheduled_trigger, per l'analisi dell'adozione. Non impostato quando la sessione non ha una superficie registrata o riconosciuta, e per le richieste di pre-warming; controllarlo con [ -n "${CLAUDE_RUNNER_CLIENT_PLATFORM:-}" ], che rimane sicuro sotto set -u. |
Il runner generato si registra con l'ordine di lavoro al posto del segreto dell'ambiente:
- Avviarlo con l'ordine di lavoro: puntare
--environment-secret-filea un file contenente il JWT dell'ordine di lavoro, o impostareSELF_HOSTED_RUNNER_ENVIRONMENT_SECRETal valore JWT. - Copiare il JWT prima che l'hook esca: l'orchestratore elimina il file dell'ordine di lavoro dopo l'uscita dell'hook, quindi copiare il JWT nel carico di lavoro che si invia, come un Kubernetes Secret sul Job generato, piuttosto che passare il percorso del file.
- Utilizzare
--capacity 1sui runner generati: un ordine di lavoro legato alla sessione registra esattamente un runner legato a quella sessione, quindi una capacità più alta aggiunge slot che non ricevono mai lavoro, e il runner registra un avviso all'avvio. - Gli ordini di lavoro di pre-warming si registrano non legati: il runner standby non è legato a una sessione e rivendica il lavoro in coda come un runner di flotta fissa.
Il contratto ha quattro regole agnostiche del provisioner:
- Essere idempotenti su
CLAUDE_RUNNER_ORDER_ID. La rielaborazione della stessa richiesta deve generare al massimo un runner. Derivare un nome di risorsa deterministico dall'ID e lasciare che la propria piattaforma rifiuti il duplicato. Non chiave suCLAUDE_RUNNER_SESSION_IDinvece. Ogni richiesta per una sessione porta lo stesso ID sessione con un nuovo ID ordine, quindi un carico di lavoro denominato o deduplicato dall'ID sessione viene creato una volta e mai più per quella sessione. - Non ritentare il carico di lavoro. Un ID ordine significa al massimo un carico di lavoro creato. Se il runner non si registra mai, Anthropic richiede con un ID ordine fresco dopo
--expected-spawn-seconds. - Utilizzare il contratto del codice di uscita. Uscita 0 significa inviato. Uscita 1 significa fallimento ritentabile; la sessione si ritira e viene riottenuta. Uscita 2 o superiore significa non ritentabile; la sessione è bloccata dallo spawn di nuovo fino a quando un Owner non seleziona Retry su di essa nella scheda Activity dell'ambiente. Su uscita diversa da zero, la coda dello stderr dell'hook appare lì come motivo del fallimento, quindi scrivere l'errore azionabile su stderr e mai segreti. Per una richiesta di pre-warming non c'è sessione da fallire: l'orchestratore registra un'uscita diversa da zero localmente solo, e il server richiede di nuovo lo spawn dopo il lease.
- Impostare
--expected-spawn-secondsad almeno il tempo di avvio p99. Questo è il lease lato server. Tutte le repliche dell'orchestratore devono utilizzare lo stesso valore.
Tutto ciò che l'hook scrive su stdout o stderr appare nel log dell'orchestratore con le credenziali automaticamente redatte. Se le sessioni rimangono in coda, controllare il corpo /healthz dell'orchestratore per i conteggi della coda, quindi aprire la scheda Activity dell'ambiente sulla pagina di amministrazione Cloud environments: espandere una sessione fallita lì per il suo errore di spawn e selezionare Retry per richiederlo di nuovo.
Una sessione che rimane in coda senza errore di spawn nella scheda Activity può significare che l'hook è chiave sull'ID sessione. Per confermare, controllare se la propria piattaforma ha un carico di lavoro per la prima richiesta di spawn di quella sessione e nessuno per le richieste. Se così, chiave il carico di lavoro su CLAUDE_RUNNER_ORDER_ID invece.
Inviare le richieste al modello a Bedrock o ad Agent Platform
Se la tua organizzazione ha bisogno che le richieste al modello passino attraverso il proprio account AWS o Google Cloud, configura il runner per Amazon Bedrock o per Agent Platform di Google Cloud, in precedenza Vertex AI. Ogni sessione avviata da quel runner chiamerà quindi il modello nel tuo account cloud, con le tue credenziali cloud. Senza questa configurazione, le sessioni inviano le richieste al modello all'API di Anthropic.
Il runner continua a interrogare Anthropic per le sessioni, e ogni sessione continua a inviare il proprio flusso di eventi a api.anthropic.com. Il flusso di eventi contiene prompt, risposte e risultati degli strumenti. Il requisito relativo al piano e l'esclusione della Zero Data Retention descritti in Disponibilità e limitazioni restano validi.
Le sessioni vengono instradate a un ambiente, non a un runner, e una sessione rimessa in coda o ripresa può essere eseguita su un runner diverso. Configura allo stesso modo ogni runner dell'ambiente. Prima di iniziare, leggi cosa cambia con questi provider.
Prepara l'account cloud e le tue regole di egress
Configura l'accesso ai modelli, una policy o un ruolo con ambito ristretto e l'accesso alla rete:
- Amazon Bedrock: invia i dettagli del caso d'uso, quindi crea la policy descritta in Configurazione IAM, limitando
bedrock:InvokeModelebedrock:InvokeModelWithResponseStreamai profili di inferenza usati dalle tue sessioni e ai modelli di base che li supportano - Agent Platform: abilita l'API e richiedi l'accesso ai modelli, quindi crea il ruolo personalizzato descritto in Configurazione IAM, con il solo
aiplatform.endpoints.predict - Egress: consenti gli endpoint del tuo provider nelle tue regole di egress. Consulta Requisiti di rete. Se le sessioni non riescono a raggiungerli, Claude Code può continuare a riprovare per ore prima che la sessione mostri un errore.
Fornisci alle sessioni credenziali con ambito ristretto
Associa la policy o il ruolo del passaggio 1 a un'identità che non possa fare nient'altro. Per i metodi accettati da Claude Code, consulta Configurare le credenziali AWS e Configurare le credenziali GCP.
Chiunque riesca a far eseguire codice in una sessione, anche tramite prompt injection, può usare queste credenziali a tue spese finché restano valide. Claude Code viene eseguito all'interno della sessione, quindi la credenziale con cui chiama il modello deve essere leggibile lì.
I comandi shell eseguiti da Claude, i tuoi hook di Claude Code e i server MCP stdio ereditano l'ambiente della sessione e vengono eseguiti con lo stesso utente di Claude Code. Di conseguenza, possono leggere le variabili e i file delle credenziali.
Quando rafforzi il tuo deploy, tieni le credenziali dell'host fuori dalle sessioni, ma non puoi tenere fuori questa credenziale. Non concedere all'identità che la supporta nulla oltre alla policy o al ruolo del passaggio 1.
Verifica il metodo che scegli rispetto a questi comportamenti del runner:
- Endpoint dei metadati: se neghi del tutto alle sessioni l'accesso all'endpoint dei metadati cloud, nemmeno le credenziali fornite da esso, come un instance profile, raggiungono Claude Code. Un'identità web basata su file, come IAM Roles for Service Accounts (IRSA) su Amazon EKS o un file di credenziali di Workload Identity Federation, non dipende da esso.
- Rinnovo: una sessione può durare più a lungo di una credenziale, quindi usa un metodo che si rinnova da solo, come un'identità web basata su file
- Script wrapper: il runner avvia il tuo script wrapper una volta per sessione, quindi le credenziali che esporta non vengono rinnovate. Claude Code legge le credenziali AWS dal proprio ambiente, quindi se il tuo wrapper esporta già credenziali AWS per altri scopi, Claude Code può usarle per firmare le richieste al modello.
Imposta le variabili di un solo provider nell'ambiente del runner
Imposta le variabili di un solo provider nello stesso punto in cui imposti le altre variabili d'ambiente del runner, come la specifica del container o la service unit, quindi riavvia il runner. Gli esempi le mostrano come export della shell. Con i runner on demand, impostale sul workload avviato dal tuo hook spawn-runner.
Avvia questi runner con --confine-repo-settings enforce. Rifiuta le sessioni sui repository le cui impostazioni committate vengono segnalate, quindi esegui prima nella modalità predefinita warn e risolvi ciò che viene registrato nei log.
Sostituisci la regione con la tua:
export CLAUDE_CODE_USE_BEDROCK=1
export AWS_REGION=us-east-1
Per sapere come Claude Code determina la regione, consulta Configurare Claude Code. Per sapere quale prefisso del profilo di inferenza usa Claude Code per la tua regione, consulta Prefissi dei profili di inferenza cross-region.
Sostituisci la regione e l'ID del progetto con i tuoi:
export CLAUDE_CODE_USE_VERTEX=1
export CLOUD_ML_REGION=global
export ANTHROPIC_VERTEX_PROJECT_ID=YOUR-PROJECT-ID
Per scegliere una regione, consulta Configurazione della regione.
Verifica che le variabili abbiano raggiunto una sessione
La tua shell sull'host è un processo diverso, quindi esegui la verifica dall'interno di una sessione. Avviane una nell'ambiente e chiedi a Claude di eseguire questo comando:
env | grep -E 'CLAUDE_CODE_USE_(BEDROCK|VERTEX)'
Una riga che imposta CLAUDE_CODE_USE_BEDROCK o CLAUDE_CODE_USE_VERTEX su 1 indica che la variabile ha raggiunto la sessione. Se compaiono entrambe, Claude Code usa Amazon Bedrock. Nessun output significa che nessuna delle due l'ha raggiunta.
Il comando mostra la configurazione, non il traffico. Per confermare le richieste stesse, cercale nelle metriche o nei log delle richieste del tuo account cloud. Se invece il primo messaggio non va a buon fine, consulta la risoluzione dei problemi per Amazon Bedrock o per Agent Platform.
Cosa cambia rispetto alle sessioni sull'API di Anthropic
Una sessione che invia le richieste al modello ad Amazon Bedrock o ad Agent Platform di Google Cloud differisce da una sessione sull'API di Anthropic nei seguenti modi:
- Policy da claude.ai: le impostazioni gestite dal server non raggiungono queste sessioni. Nemmeno le policy dell'organizzazione che un Owner imposta nelle impostazioni di amministrazione di Claude Code le raggiungono, quindi Claude Code non le applica all'interno della sessione. Inserisci le regole su cui fai affidamento nel file delle impostazioni gestite dell'immagine del runner.
- File: i file che le persone allegano a una sessione in claude.ai o nell'app mobile o desktop non la raggiungono, e Claude non può inviare file indietro con lo strumento
SendUserFile. Inserisci invece i file di input nel repository o sul runner. - Selezione del modello: il control plane di Anthropic invia il modello di ogni sessione e, quando una sessione viene avviata senza un modello, Claude Code usa quello predefinito per il provider. Il runner rimuove
ANTHROPIC_MODELeANTHROPIC_DEFAULT_MODELdall'ambiente che passa alle sessioni. Gli esempi nelle pagine dei provider impostanoANTHROPIC_MODEL, ma nell'ambiente del runner nessuna delle due variabili ha effetto. Le variabili per famiglia descritte in Fissare le versioni dei modelli per Amazon Bedrock e per Agent Platform raggiungono invece le sessioni. Determinano a cosa si risolve un alias comeopus, non a cosa si risolve un ID completo del modello. - Modelli non serviti dal tuo account: una sessione può fallire su un messaggio con un errore che indica il modello. Abilita i modelli che i tuoi sviluppatori possono scegliere, il modello in background descritto in Fissare le versioni dei modelli e il modello del classificatore usato dalla modalità auto. Su Amazon Bedrock, consenti ciascuno di essi nella tua policy.
- Ricerca web e modalità veloce: la ricerca web non è disponibile su Amazon Bedrock e la modalità veloce non è disponibile su nessuno dei due provider. Per altre funzionalità che variano in base al provider, consulta Funzionalità della CLI che variano in base al provider.
Server MCP
Per rendere i server MCP disponibili in ogni sessione, aggiungili durante la build dell'immagine con lo stesso comando claude mcp add usato in un'installazione desktop. Se il tuo runner è un processo nudo anziché un container, esegui lo stesso comando come utente del runner sull'host, quindi riavvia il runner: legge la configurazione dell'host una sola volta all'avvio. Il flag --scope user è obbligatorio; l'ambito locale predefinito scrive sotto una chiave per directory che il runner non inserisce nelle sessioni. Ad esempio, nel tuo Dockerfile:
RUN claude mcp add --scope user sidecar -- /usr/local/bin/mcp-sidecar
RUN claude mcp add --scope user --transport http internal http://mcp-gateway.svc.cluster.local:8080
Il runner acquisisce uno snapshot della configurazione dell'host una sola volta all'avvio. Lo snapshot cattura la chiave mcpServers dal file .claude.json dell'host, che si trova accanto a ~/.claude/ anziché al suo interno, e il runner inserisce solo quella chiave nella configurazione isolata di ogni sessione; lo stato dell'account e la cronologia dei progetti vengono scartati. Per verificare che i server abbiano raggiunto le sessioni, avvia una sessione nell'ambiente e chiedi a Claude di elencare i suoi strumenti MCP; il runner registra inoltre un avviso all'avvio per ogni voce acquisita il cui type non riconosce e scarta la voce, così puoi vedere perché quel server manca dalle sessioni. Quando SELF_HOSTED_RUNNER_HOST_CONFIG_DIR è impostata, il runner legge invece .claude.json da quella directory, quindi puntare la variabile a una directory vuota disabilita anche l'inserimento dei server MCP.
Claude Code carica i server MCP anche da altre fonti:
- Il file MCP gestito con ambito enterprise nel suo percorso di sistema standard:
/etc/claude-code/managed-mcp.jsonsugli host runner Linux,/Library/Application Support/ClaudeCode/managed-mcp.jsonsugli host macOS. Usalo per flotte bloccate in cui possono essere caricati solo i server elencati dall'amministratore. Consulta controllo esclusivo con managed-mcp.json per le regole di precedenza. Quando questo file si trova sull'host del runner, Claude Code ignora i server MCP che il control plane di Anthropic fornisce a una sessione, inclusi i connettori di claude.ai, e li nomina in un avviso sullo stderr del processo figlio della sessione, che il runner registra al livello di logdebug. Prima della v2.1.229, quelle sessioni terminavano all'avvio conYou cannot dynamically configure MCP servers when an enterprise MCP config is present. - La chiave
managedMcpServersnelle impostazioni gestite sull'host del runner: fornisce server HTTP e SSE senza assumere il controllo esclusivo, quindi i server delle altre fonti vengono comunque caricati. Richiede Claude Code v2.1.259 o successiva. <repo>/.mcp.json: ambito di progetto. Esegui il commit del file nel repository; i suoi server vengono approvati automaticamente nelle sessioni cloud.
Quando la distribuzione dei connettori è abilitata per la tua organizzazione, il control plane di Anthropic fornisce i connettori che hai configurato su claude.ai alle sessioni create in modo interattivo tramite una configurazione MCP fornita dal server, instradata attraverso api.anthropic.com. Le sessioni create in modo programmatico, come i dispatch da CLI, non ricevono i connettori; fornisci loro i server MCP tramite una qualsiasi delle altre fonti elencate in questa sezione. Il token OAuth del processo figlio non include uno scope per recuperare direttamente i connettori, quindi il processo figlio non tenta autonomamente quel recupero; la distribuzione è gestita dal server.
settings.json non contiene definizioni di server MCP e nello schema delle impostazioni non esiste un campo mcpServers di primo livello. Nelle impostazioni gestite, fornisci invece i server con la chiave managedMcpServers.
Le sessioni ereditano l'ambiente del runner, quindi imposta lì ENABLE_TOOL_SEARCH per controllare la ricerca degli strumenti MCP per ogni sessione avviata da un runner; la pagina MCP descrive i valori.
Disattivare gli strumenti di sessione integrati
Il control plane di Anthropic collega alle sessioni cloud un proprio server MCP, chiamato Claude Code Remote. Claude usa gli strumenti del server per pianificare routine, avviare e guidare altre sessioni cloud, collegare altri repository e seguire l'attività delle pull request.
Per disattivare l'intero server, aggiungi una regola di negazione a livello di server alle tue impostazioni. Il control plane registra il server con uno di tre nomi, a seconda di come è stata creata la sessione. Claude Code confronta il nome in una regola in modo esatto, maiuscole e minuscole comprese, quindi scrivi la regola una volta per ogni nome come mostrato:
{
"permissions": {
"deny": [
"mcp__Claude_Code_Remote",
"mcp__claude-code-remote",
"mcp__bf7c680d-5fdc-5ef4-b4a0-abadb619bf0a"
]
}
}
Una regola che nomina l'intero server copre anche gli strumenti che il server acquisirà in seguito. Per disattivare un solo strumento e mantenere gli altri, aggiungi a ogni regola altri due trattini bassi e il nome dello strumento, come in mcp__Claude_Code_Remote__add_repo. Per impedire del tutto al server di connettersi anziché rimuoverne gli strumenti, aggiungi invece i tre nomi senza il prefisso mcp__ come voci serverName in deniedMcpServers.
Inserisci le regole nelle impostazioni gestite dal server per raggiungere le sessioni senza modificare il runner, oppure in ~/.claude/settings.json sul runner. Su un runner che invia le richieste al modello a Bedrock o Agent Platform, usa quel file, perché le impostazioni gestite dal server non raggiungono quelle sessioni. Permessi e approvazione degli strumenti spiega come le impostazioni sul runner raggiungono le sessioni.
Per verificare che le regole siano state applicate, avvia una sessione nell'ambiente e chiedi a Claude di elencare i suoi strumenti MCP. Claude Code rimuove uno strumento negato dal contesto di Claude, quindi gli strumenti negati non compaiono nella sua risposta.
Prompt delle sessioni per spingere il loro lavoro
Le sessioni ospitate da Anthropic eseguono un hook Stop, l'hook Claude Code che viene eseguito quando Claude finisce di rispondere, che richiede a Claude di committare e spingere il suo lavoro. Il runner non ne installa uno. Senza di esso, una sessione che termina con cambiamenti non committati lascia quel lavoro solo sul disco del runner, e il pulsante Create PR in claude.ai/code rimane inattivo fino a quando il ramo non esiste sul remoto.
L'implementazione di riferimento di seguito ha due parti. Unire il blocco delle impostazioni in ~/.claude/settings.json sull'host del runner, che il runner semina in ogni sessione, e salvare lo script come ~/.claude/hooks/stop-hook-nudge.sh sull'host del runner e renderlo eseguibile:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"timeout": 10,
"command": "\"$CLAUDE_CONFIG_DIR/hooks/stop-hook-nudge.sh\""
}
]
}
]
}
}
#!/bin/sh
# Implementazione di riferimento dello stop-hook per runner self-hosted.
#
# Spinge Claude una volta per turno se la directory del progetto ha cambiamenti
# non committati O commit non spinti, in modo che il lavoro non vada perso quando
# una sessione inattiva viene rilasciata e in modo che il pulsante "Create PR" su
# claude.ai/code si illumini.
#
# Livello runner (nessun cambio del repository): rilasciare questo file in ~/.claude/hooks/
# sull'host del runner e unire il blocco delle impostazioni dello stop-hook accompagnante
# in ~/.claude/settings.json — il runner semina entrambi in ogni sessione.
# Alternativa a livello di repository: committare a <repo>/.claude/hooks/ e cambiare il
# percorso del comando settings.json a $CLAUDE_PROJECT_DIR/.claude/hooks/.
#
# stdin: payload JSON dell'hook (consultare https://code.claude.com/docs/en/hooks)
# stdout: {"decision":"block","reason":"..."} per spingere, o nulla per consentire lo stop.
# Guardia di rientrata: l'imbracatura imposta stop_hook_active=true quando reinvoca
# lo stop hook dopo un blocco. Uscire in modo da spingere solo una volta per turno. L'
# imbracatura emette JSON compatto (nessuno spazio dopo i due punti), che questo
# pattern si basa; usare jq se hai bisogno di un controllo tollerante agli spazi bianchi.
in=$(cat)
case "$in" in *'"stop_hook_active":true'*) exit 0 ;; esac
d="$CLAUDE_PROJECT_DIR"
# Non un repository git → nulla da spingere.
git -C "$d" rev-parse --git-dir >/dev/null 2>&1 || exit 0
# Nessun remoto → "spingere al remoto" è insodisfacibile; uscire.
[ -z "$(git -C "$d" remote 2>/dev/null)" ] && exit 0
# Cambiamenti non committati (staged, unstaged o untracked). Escludere .claude/
# interamente — le impostazioni seminate dall'operatore e lo stato di runtime scritto da CLI
# (blocco dello scheduler, worktree, stato della routine) vivono lì e nessuno è
# "lavoro non committato" che il modello ha bisogno di spingere.
s=$(git -C "$d" status --porcelain -- . ':(exclude).claude/' 2>/dev/null)
if [ -n "$s" ]; then
printf '{"decision":"block","reason":"There are uncommitted changes in the repository. Please commit and push these changes to the remote branch."}'
exit 0
fi
# Commit non spinti. Contare i commit su HEAD non raggiungibili da alcun
# ref di tracciamento remoto o FETCH_HEAD. Questo funziona uniformemente per:
# - checkout init+fetch (runner predefinito: solo FETCH_HEAD esiste)
# - checkout basati su clone (origin/* esiste)
# - il runner predefinito: il figlio inizia sul ramo di risultato della sessione,
# che il runner crea dopo il checkout
# - HEAD staccato, quando una configurazione personalizzata salta quella creazione di ramo
# Senza alcun punto di riferimento (mai recuperato), rimanere silenzioso piuttosto che
# falso positivo su un turno di sola lettura.
base=""
git -C "$d" rev-parse --verify -q FETCH_HEAD >/dev/null && base="FETCH_HEAD"
if [ -z "$base" ] && [ -z "$(git -C "$d" for-each-ref --count=1 refs/remotes/origin 2>/dev/null)" ]; then
exit 0
fi
# shellcheck disable=SC2086 # $base è "" o "FETCH_HEAD", word-split intenzionale
unpushed=$(git -C "$d" rev-list HEAD --not $base --remotes=origin --count 2>/dev/null) || unpushed=0
if [ "$unpushed" -gt 0 ]; then
branch=$(git -C "$d" symbolic-ref --short -q HEAD)
if [ -n "$branch" ]; then
# $branch è influenzato dall'attaccante — git-check-ref-format(1) consente `"`
# nei nomi dei ref. `\` è vietato (regola 10) ma comunque sfuggito come difesa
# economica in profondità.
# Sfuggire ai metacaratteri JSON prima di interpolare nel payload costruito a mano
# in modo che un ramo come x","continue":false non possa iniettare chiavi nel
# JSON di output dell'hook che l'imbracatura analizza. $unpushed è sicuro — il
# guard -gt sopra rifiuta qualsiasi cosa che non sia un semplice intero.
branch_esc=$(printf '%s' "$branch" | sed 's/\\/\\\\/g; s/"/\\"/g')
printf '{"decision":"block","reason":"There are %s unpushed commit(s) on branch '\''%s'\''. Please push these changes to the remote repository."}' "$unpushed" "$branch_esc"
else
printf '{"decision":"block","reason":"There are %s unpushed commit(s) on a detached HEAD. Please create a branch and push it to the remote repository."}' "$unpushed"
fi
exit 0
fi
exit 0
L'hook spinge Claude a committare e spingere prima della fine della sessione, e rimane silenzioso quando la directory non è un repository git o non ha un remoto.
Autorizzazioni e approvazione degli strumenti
Una sessione self-hosted non ha un terminale allegato, quindi un prompt di autorizzazione senza risposta blocca il turno fino a quando l'utente non risponde nell'interfaccia utente. Il piano di controllo di Anthropic invia l'elenco degli strumenti di ogni sessione e le regole di autorizzazione con il payload di lavoro; la configurazione predefinita pre-approva le chiamate di routine, incluso Bash, e le sessioni cloud pre-approvano le modifiche ai file indipendentemente dalla modalità. Una chiamata che nulla pre-approva richiede attraverso l'interfaccia utente della sessione.
Pinare solo la modalità auto su un ambiente le cui sessioni contenitore vengono eseguite con default-deny network egress e il resto della sezione di hardening in atto. Le chiamate di routine, incluse le richieste di rete Bash, vengono eseguite senza un umano nel ciclo sia sul set di strumenti pre-approvati predefinito che in modalità auto, quindi il confine di rete è ciò che limita dove quelle chiamate possono raggiungere.
Per mantenere i prompt al minimo indipendentemente da ciò che il piano di controllo invia, pinare la modalità auto dal script wrapper o dall'hook command. La modalità auto consente alle sessioni di funzionare senza prompt di autorizzazione di routine: un modello di classificatore separato esamina le azioni prima che vengono eseguite e blocca quelle che rifiuta, e le regole di richiesta esplicita forzano comunque un prompt; la pagina delle modalità di autorizzazione copre ciò che il classificatore controlla. Il runner aggiunge flag calcolati dal server prima di invocare il wrapper, e per flag a valore singolo come --permission-mode il parser onora l'ultima occorrenza, quindi un flag che si aggiunge dopo "$@" sovrascrive il valore inviato dal server:
#!/bin/bash
exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@" --permission-mode auto
Per pre-approvare strumenti specifici, aggiungere --allowed-tools con le proprie regole, ad esempio --allowed-tools "Bash(bazel *) Bash(yarn *) mcp__internal__*". I flag di elenco come --allowed-tools e --disallowed-tools si accumulano tra le occorrenze piuttosto che sovrascrivere, quindi le proprie regole si applicano in cima a qualsiasi regola che il piano di controllo invia. Per restringere, aggiungere --disallowed-tools, che nega gli strumenti anche se un'altra regola li consente.
Come la configurazione di ogni sessione viene assemblata
Il runner fornisce a ogni sessione la propria directory di configurazione, seminata da uno snapshot in memoria di ~/.claude/ dell'host che il runner acquisisce una volta all'avvio: settings.json, CLAUDE.md, hook, agenti, comandi e skill nell'immagine del runner si applicano a ogni sessione come baseline a livello di utente. Se si modifica la configurazione su un host in esecuzione, la modifica ha effetto solo dopo il riavvio del runner.
Impostare SELF_HOSTED_RUNNER_HOST_CONFIG_DIR per seminare da un percorso diverso, o puntarlo a una directory vuota per disabilitare la semina.
Il .claude/settings.json committato nel repository si sovrappone come impostazioni del progetto. Le sessioni leggono anche managed-settings.json dal percorso di sistema standard nell'immagine del runner. Se le sue chiavi si applicano insieme alle impostazioni gestite dal server segue come Claude Code combina le fonti gestite: per impostazione predefinita, quando l'organizzazione fornisce qualsiasi chiave gestita dal server, le sessioni ignorano il file dell'immagine del runner a parte le chiavi che Claude Code legge da ogni fonte di amministrazione, come il blocco env, i blocchi sandbox, i percorsi binari sandbox e forceRemoteSettingsRefresh. Consultare settings precedence.
Quando il piano di controllo di Anthropic fornisce una sessione con hook Claude Code, il runner li installa insieme, non sopra, la propria configurazione. Richiede Claude Code v2.1.229 o successivo.
- Dove atterrano: il runner scrive ogni script di hook fornito in una sottodirectory riservata
hooks/.ccr-launcher/della directory di configurazione della sessione e registra gli script in un file di impostazioni separato che passa alla sessione con--settings, lasciando ilsettings.jsonseminato e i propri script inhooks/<name>intatti. Il runner ricrea la sottodirectory riservata per ogni sessione e non semina il contenuto dell'host in~/.claude/hooks/.ccr-launcher/nelle sessioni. - Chi li crea: il piano di controllo popola gli script da costanti fisse nella propria distribuzione, mai da input per sessione o di terze parti.
- Cosa ancora li governa: gli hook forniti attraverso
--settingsentrano nella configurazione ordinaria dell'hook unito, non nel livello gestito, quindi le impostazioni gestite si applicano ancora.disableAllHooksli disabilita, e non sono tra le categorie cheallowManagedHooksOnlymantiene caricate.
Al di fuori delle sessioni Claude Tag, una sessione in un ambiente self-hosted viene eseguita con la memoria automatica disattivata per impostazione predefinita. Per le istruzioni che devono valere tra una sessione e l'altra, usa il CLAUDE.md nell'immagine del runner o nel repository.
Lo snapshot di ~/.claude/ dell'host acquisito dal runner esclude la directory projects/. La posizione di archiviazione predefinita della memoria automatica si trova in quella directory. Se vi inserisci file di memoria, il runner non li copia nelle sessioni e questi non attivano la memoria automatica.
Regole di autorizzazione committate nel repository
Non mettere una voce "Edit", "Write" o "NotebookEdit" nuda in un permissions.allow committato nel repository. Una regola di strumento file nuda corrisponde allo strumento indipendentemente dal percorso, concedendo scritture ovunque sull'host piuttosto che solo l'area di lavoro, quindi la guardia di confinamento dell'ambito di scrittura del runner contrassegna la sessione; con --confine-repo-settings enforce rifiuta di generare la sessione invece di registrare e continuare. Consultare la sezione di hardening.
Un repository non ha bisogno di alcuna regola di strumento file: le sessioni cloud pre-approvano le modifiche ai file indipendentemente dalla modalità. Se si committano una regola, limitarla all'area di lavoro, come "Edit(/**)"; una singola barra iniziale è relativa alla radice del progetto, che è l'area di lavoro della sessione. Le regole di strumento file nude vanno bene nel settings.json a livello di host dell'operatore, poiché quel file non è committato nel repository.
Un defaultMode di auto è onorabile solo dal file di impostazioni a livello di immagine o a livello di utente, quindi un repository estratto non può concedere a se stesso la modalità auto. Per quali modalità le sessioni cloud accettano e la sintassi completa della regola, consultare permission modes.
Cosa c'è dopo
- Reference: ogni flag CLI, variabile di ambiente e metrica
- Verify session identity: convalidare il token della sessione da servizi al di fuori del runner