SpyBara
Go Premium

self-hosted-environments-configuration.md 2026-10-01 23:59 UTC to 2026-10-02 22:59 UTC

This page contains 167 additions and 31 deletions.

2026
Fri 2 22:59

Sesuaikan sesi di lingkungan yang di-host sendiri

Sesuaikan sesi lingkungan yang di-host sendiri dengan skrip wrapper untuk kredensial per-sesi, hook siklus hidup, dan pemijahan runner sesuai permintaan.

Sebuah lingkungan yang di-host sendiri menjalankan Claude Code sesi cloud di infrastruktur Anda sendiri, dieksekusi oleh proses runner yang Anda deploy. Tanpa konfigurasi, runner itu mengkloning repositori sesi, memijahkan Claude Code, dan membersihkan. Halaman ini untuk insinyur platform yang mengoperasikan runner: ini mencakup titik ekstensi untuk ketika default itu tidak sesuai, dari penyediaan kredensial per-sesi hingga mengganti checkout sepenuhnya. Wrapper dan hook berjalan sebagai file yang dapat dieksekusi di host runner, yang merupakan Linux atau macOS, dan contoh di halaman ini mengasumsikan shell POSIX.

Beberapa environment variable hook di halaman ini masih menggunakan pool, seperti CLAUDE_RUNNER_POOL_ID; nama flag CLI dan env var menggunakan environment, seperti --environment-secret-file.

Skrip wrapper

Gunakan skrip wrapper ketika setiap sesi memerlukan setup yang tidak dapat dilakukan runner sendiri: penyediaan kredensial jangka pendek yang dibatasi untuk pembuat sesi, mengekspor rahasia khusus lingkungan, menyiapkan toolchain bahasa, atau menerapkan batas sumber daya di sekitar proses anak. Runner memulai wrapper Anda sebagai pengganti biner Claude Code, sekali per sesi. Akhiri wrapper dengan exec-ing ke $CLAUDE_RUNNER_CLAUDE_BIN, biner runner sendiri, sehingga sinyal dan kode keluar menyebar dengan benar.

Arahkan --exec-path, atau SELF_HOSTED_RUNNER_EXEC_PATH, ke wrapper ketika Anda memulai runner:

claude self-hosted-runner --environment-secret-file /etc/claude/environment-secret --exec-path /etc/claude/session-wrapper.sh

Runner menetapkan yang berikut di lingkungan wrapper:

Variabel Deskripsi
CLAUDE_CODE_SESSION_ACCESS_TOKEN JWT sesi, diawali dengan sk-ant-cc-. Klaim act mengidentifikasi pembuat sesi, dengan email pembuat ketika surface pembuatan merekamnya. Nilainya adalah token pada waktu pemijahan; penyegaran tiba melalui stdin anak, jadi wrapper hanya melihat nilai awal. Lihat Verify session identity.
CCR_SESSION_ACCOUNT_EMAIL Email pembuat sesi, pra-ekstrak oleh runner dari klaim act.email token tanpa verifikasi tanda tangan. Cocok untuk pelabelan, seperti trailer commit. Ketika email membuka akses penyediaan kredensial, verifikasi token dan baca klaim darinya; lihat Provision credentials scoped to the session creator. Tidak diatur ketika token tidak membawa email pembuat. Perlakukan sebagai informasi yang dapat diidentifikasi secara pribadi.
CLAUDE_RUNNER_CLIENT_PLATFORM Surface klien yang membuat sesi, seperti web_claude_ai, desktop_app, ios, claude_code_cli, atau scheduled_trigger. Anthropic merekam nilainya sekali saat pembuatan sesi, jadi wrapper dan setiap hook siklus hidup melihat nilai yang sama. Gunakan untuk analitik adopsi dan pelabelan saja, bukan sebagai sinyal otorisasi. Tidak diatur ketika sesi tidak memiliki surface yang tercatat atau dikenali, jadi referensikan sebagai ${CLAUDE_RUNNER_CLIENT_PLATFORM:-} di bawah set -u. Memerlukan Claude Code v2.1.229 atau lebih baru.
CLAUDE_RUNNER_CLAUDE_BIN Jalur absolut ke biner Claude Code runner sendiri. Akhiri wrapper Anda dengan exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@" untuk menyerahkan ke biner yang disematkan tanpa hardcoding jalur instalasi.
CLAUDE_CODE_REMOTE_SESSION_ID ID sesi dalam bentuk cse_... yang ditandai. Ini adalah sesi yang sama yang dilihat lifecycle hooks sebagai CLAUDE_RUNNER_SESSION_ID dalam bentuk session_...; variabel UUID cocok di kedua sisi, dan mengganti awalan cse_ dengan session_ menghasilkan ID yang ditampilkan di URL sesi.
CLAUDE_CODE_REMOTE_SESSION_UUID ID sesi yang sama dalam bentuk UUID kanonik, untuk sistem yang menggunakan UUID sebagai kunci.
CLAUDE_SESSION_INGRESS_TOKEN_FILE Jalur absolut ke file per-sesi yang menyimpan JWT sesi saat ini, tetap segar di seluruh penyegaran token. Subproses shell membacanya untuk header Authorization mereka saat mengunduh lampiran yang ditambahkan pengguna ke sesi. exec menyimpan variabel secara otomatis; wrapper yang membangun kembali lingkungan anak harus membawa variabel, atau unduhan lampiran berhenti bekerja diam-diam.
CLAUDE_CONFIG_DIR Direktori konfigurasi Claude per-sesi, ditulis saat awal sesi dari snapshot konfigurasi host runner yang ditangkap runner saat startup; lihat Permissions and tool approval. Penulisan di sini terisolasi ke sesi ini. Direktori tetap di bawah <base-dir>/_sessions/ setelah sesi berakhir kecuali Anda memulai runner dengan --remove-session-state; lihat Reuse a pre-warmed checkout.
ANTHROPIC_BASE_URL URL dasar API yang akan digunakan anak, dikirimkan oleh bidang kontrol per sesi dan biasanya https://api.anthropic.com. Jangan timpa: kredensial inferensi sesi adalah token OAuth yang dikeluarkan Anthropic yang tidak diterima penyedia lain.
CLAUDE_CODE_OAUTH_TOKEN Token akses OAuth jangka pendek yang digunakan anak untuk inferensi model, dibatasi untuk inferensi model dan unggahan file saja, dengan masa pakai sekitar 30 menit. Runner mencetak ulang sebelum kedaluwarsa dan mengirimkan rotasi melalui stdin anak, jadi wrapper yang tidak menjaga stdin terlampir hanya melihat nilai awal. Jangan andalkan allowlist IP organisasi Anda untuk membatasi penggunaan token ini: perlakukan sebagai kredensial pembawa yang tetap dapat digunakan selama kira-kira 30 menit jika bocor, dan jangan catat, tulis ke disk, atau teruskan di luar kontainer sesi.

Wrapper juga mewarisi sisa lingkungan anak yang dikelola, termasuk environment variable yang disediakan server. exec menyebarkan semuanya secara otomatis; jika wrapper Anda memijahkan anak dengan cara lain, teruskan lingkungan lengkap.

Jaga stdin dan file descriptor 3 terlampir

Stdin anak adalah saluran kontrol runner. Rotasi token dan sinyal akhir sesi tiba di atasnya. Runner juga membuka pipa pada file descriptor 3 dan membaca sinyal aktivitas anak darinya untuk mendorong timeout idle dan startup. exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@" biasa menyimpan keduanya secara otomatis.

Jika wrapper Anda menempatkan anak di latar belakang dengan & telanjang, itu memutus stdin anak: sesi terlihat sehat sampai masa pakai token OAuth awal sekitar 30 menit berakhir, kemudian setiap panggilan API gagal dengan 401 authentication_error. Jika wrapper Anda harus menempatkan anak di latar belakang, misalnya untuk menjaga perangkap pembongkaran tetap hidup, simpan stdin pada file descriptor 4 atau lebih tinggi dan lampirkan kembali secara eksplisit:

exec 4<&0
"$CLAUDE_RUNNER_CLAUDE_BIN" "$@" <&4 4<&- &
CHILD=$!
trap 'teardown' EXIT
wait "$CHILD"

Jangan tutup atau gunakan kembali file descriptor 3 di wrapper. Mengalihkan stdout dan stderr anak tidak apa-apa.

Teruskan flag system prompt

System prompt dan system prompt tambahan yang dikirim bidang kontrol Anthropic untuk sebuah sesi sampai ke wrapper Anda sebagai jalur file, bukan sebagai teks inline. Runner menulis setiap prompt ke file di direktori konfigurasi sesi, CLAUDE_CONFIG_DIR, dan meneruskan jalurnya dalam argumen yang diterima wrapper Anda, sebagai --system-prompt-file <path> atau --append-system-prompt-file <path>.

Runner pada Claude Code v2.1.281 atau lebih baru mengirimkan prompt sebagai file. Sebelum v2.1.281, runner meneruskannya sebagai --system-prompt <text> dan --append-system-prompt <text>.

Di skrip wrapper atau hook command Anda, tangani flag ini sebagai berikut:

  • Teruskan flag tersebut: akhiri wrapper dengan exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@", yang meneruskan flag file bersama setiap argumen lainnya. Jangan hapus atau tulis ulang flag tersebut. Jika sebuah sesi kehilangan flag file prompt, sesi berjalan tanpa instruksi yang dikirim bidang kontrol untuknya.
  • Pada runner v2.1.281 atau lebih baru, flag file yang Anda tambahkan menggantikan milik server, tidak pernah menambahinya: setiap flag file prompt hanya menerima satu nilai dan Claude Code mempertahankan kemunculan terakhir, jadi jika Anda menambahkan --append-system-prompt-file <path> setelah "$@", isi file Anda menggantikan instruksi tambahan dari server. Untuk menambahkan instruksi di atas instruksi server, letakkan di CLAUDE.md pada image runner, yang disemai runner ke konfigurasi tingkat pengguna setiap sesi.

Sediakan kredensial yang dibatasi untuk pembuat sesi

Gunakan subperintah decode-token untuk membaca klaim dari JWT sesi. Ini membaca token dari argumen, dari CLAUDE_CODE_SESSION_ACCESS_TOKEN, atau dari stdin, dalam urutan itu; lihat Verify the token inside the session untuk apa yang diperiksa. Contoh di bawah ini mendekode identitas pembuat, menukarnya dengan kredensial AWS jangka pendek, dan exec ke 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" "$@"

Gunakan jq -re daripada jq -r ketika klaim yang diekstrak membuka keputusan auth, sehingga klaim yang hilang keluar bukan nol daripada melewatkan string literal null ke hilir. Sesi yang dibuat oleh identitas layanan organisasi, seperti sesi bot dan agent, membawa subjek agent: daripada user:, jadi contoh ini menolak mereka; jika lingkungan Anda melayani sesi tersebut, putuskan secara eksplisit apakah wrapper kembali ke kredensial default untuk mereka daripada keluar. Ketika pertukaran kredensial Anda justru memerlukan email, baca .act.email dan tangani ketidakhadirannya: token membawanya hanya ketika surface pembuatan merekamnya, dan sesi yang dikirim CLI dapat tidak memilikinya. Untuk referensi klaim lengkap dan verifikasi dari layanan di luar runner, lihat Verify session identity.

Lifecycle hooks

Lifecycle hooks mengganti tahap pipeline per-sesi runner dengan skrip Anda sendiri. Arahkan runner ke direktori hook dengan --hooks-dir <path>, atau SELF_HOSTED_RUNNER_HOOKS_DIR. Runner mencari file yang dapat dieksekusi dengan nama yang terkenal; hook apa pun yang tidak ada jatuh ke perilaku bawaan, jadi Anda hanya menulis yang Anda butuhkan. Hook berjalan dengan hak istimewa runner sendiri, dan anak sesi berbagi UID itu, jadi pasang direktori hook hanya-baca, atau panggang ke dalam gambar, sehingga kode sesi tidak dapat memodifikasinya; lihat bagian hardening.

Hook ini berbeda dari Claude Code hooks, yang berjalan di dalam sesi; lifecycle hooks berjalan di runner, di sekitar sesi.

checkout

Berjalan sekali per repositori, sebagai pengganti klon dan fetch bawaan runner. Gunakan hook untuk mengkloning dari mirror read-through, mengisi working tree dari arsip, atau menerapkan autentikasi git per-sesi. Runner menetapkan variabel-variabel ini, dan dapat menetapkan variabel CLAUDE_RUNNER_ lain yang tidak tercantum dalam tabel:

Variabel Deskripsi
CLAUDE_RUNNER_REPO_URL URL repositori untuk mengkloning, setelah --git-host-rewrite dan --git-ssh-rewrite apa pun telah diterapkan
CLAUDE_RUNNER_REPO_REF Revisi untuk checkout: cabang, tag, atau commit SHA seperti yang diminta sesi. Kosong berarti cabang default repositori.
CLAUDE_RUNNER_CHECKOUT_PATH Jalur absolut di mana pohon kerja harus ditinggalkan
CLAUDE_RUNNER_SESSION_ID ID sesi dalam bentuk session_... yang ditandai, untuk logging dan korelasi
CLAUDE_RUNNER_SESSION_UUID ID sesi yang sama dalam bentuk UUID kanonik
CLAUDE_RUNNER_API_BASE_URL URL dasar API Anthropic untuk panggilan yang dibatasi sesi
CLAUDE_RUNNER_CLIENT_PLATFORM Permukaan klien yang membuat sesi, seperti web_claude_ai, desktop_app, atau ios. Tidak diatur ketika sesi tidak memiliki permukaan yang tercatat atau dikenali.
CLAUDE_CODE_SESSION_ACCESS_TOKEN Token akses sesi, untuk panggilan API yang dibatasi sesi
GIT_CONFIG_COUNT, GIT_CONFIG_KEY_n, GIT_CONFIG_VALUE_n Pengaturan git yang ditetapkan runner untuk git yang dijalankan hook Anda. Konfigurasi Git di dalam lifecycle hooks menjelaskannya. Memerlukan Claude Code v2.1.280 atau lebih baru.

Skrip harus meninggalkan pohon kerja di CLAUDE_RUNNER_CHECKOUT_PATH yang diperiksa pada revisi yang diminta. Detached HEAD tidak apa-apa; runner membuat cabang kerja sesi di atasnya. Runner memverifikasi jalur berisi .git sesudahnya; jika hook Anda mewujudkan sumber non-git seperti Perforce atau tarball yang dibuka, atur CLAUDE_RUNNER_SKIP_GIT_VERIFY=1 di lingkungan runner untuk melewati pemeriksaan itu. Alur berbasis Git seperti pembuatan cabang kerja dan hasil push memerlukan checkout git, jadi ekspor hasil dari pohon non-git dengan hook post-session.

Runner tidak melewatkan kredensial git ke hook. Sebaliknya, cetak kredensial klon per-sesi dari identitas sesi: verifikasi CLAUDE_CODE_SESSION_ACCESS_TOKEN dengan perpustakaan JWT standar terhadap titik akhir JWKS di bawah CLAUDE_RUNNER_API_BASE_URL, seperti yang dijelaskan dalam Verify the token from your service, kemudian buat layanan kredensial Anda mengeluarkan kredensial klon jangka pendek untuk identitas dalam klaim act token. CLAUDE_RUNNER_CLAUDE_BIN tidak diatur di lingkungan checkout-hook, jadi subperintah decode-token tidak tersedia di sini. Kembali ke apa pun autentikasi git yang sudah dimiliki host, seperti agen SSH, pembantu kredensial, atau .netrc, juga merupakan pilihan.

Ketika hook keluar bukan nol, atau keluar 0 tanpa meninggalkan checkout yang dapat digunakan di belakang, apa yang dilakukan runner tergantung pada repositori:

  • Repositori yang sesi dorong hasil ke: runner gagal sesi, dan pada keluar bukan nol permukaan ekor stderr skrip ke pengguna.
  • Repositori yang hanya dibaca sesi, seperti repositori yang ditambahkan ke sesi yang berjalan: runner mencatat baris [runner:warn] dengan detail kegagalan, memposting langkah Skipped ke sesi, menghapus apa pun yang ditinggalkan hook di jalur checkout, dan melanjutkan dengan repositori yang tersisa. Ketika runner tidak dapat menghapus jalur segera, itu mencoba penghapusan lagi saat akhir sesi. Jika melewati meninggalkan sesi tanpa repositori sama sekali, runner gagal sesi pula.

Sebelum v2.1.228, runner gagal sesi pada kegagalan hook untuk repositori apa pun, jadi repositori hanya-baca yang tidak dapat dilayani hook gagal sesi lagi pada setiap runner segar yang dilanjutkan sesi.

Runner menghapus jalur checkout setelah sesi berakhir.

post-session

Berjalan sekali per sesi, setelah anak Claude Code keluar dan sebelum runner merobohkan ruang kerja. Hook ini adalah satu-satunya kesempatan Anda untuk menyimpan pekerjaan yang tidak dikomit: pada --capacity di atas satu, runner menghapus worktree per-sesi tepat setelah hook kembali, dan pada --capacity 1 klon kanonik yang digunakan kembali di-hard-reset ketika sesi berikutnya dimulai, jadi perubahan terlacak yang tidak dikomit tidak bertahan di jalur mana pun. Penggunaan khas adalah mendorong cabang snapshot perubahan yang tidak dikomit, mengarsipkan log, atau memancarkan acara akhir sesi ke sistem Anda sendiri.

Hook menyala pada setiap akhir sesi di mana proses anak dipijahkan, apa pun penyebabnya; nilai CLAUDE_RUNNER_EXIT_REASON di bawah menghitung kasus. Itu tidak dapat menyala ketika runner berhenti tiba-tiba, seperti preemption VM atau kehilangan daya; jika Anda memerlukan jaminan terhadap penghentian tiba-tiba, snapshot secara berkala dari dalam sesi dengan hook Claude Code PostToolUse sebagai gantinya. Runner menetapkan:

Variabel Deskripsi
CLAUDE_RUNNER_SESSION_ID ID sesi dalam bentuk session_... yang ditandai
CLAUDE_RUNNER_SESSION_UUID ID sesi yang sama dalam bentuk UUID kanonik
CLAUDE_RUNNER_EXIT_REASON Bagaimana sesi berakhir; lihat nilai di bawah tabel
CLAUDE_RUNNER_WORKSPACE_PATHS Jalur absolut yang dipisahkan titik dua dari pohon kerja sesi. Kosong untuk sesi tanpa repo.
CLAUDE_RUNNER_DEBUG_LOG_PATH Jalur ke log debug sesi, masih di disk saat hook berjalan
CLAUDE_RUNNER_API_BASE_URL URL dasar API Anthropic untuk panggilan yang dibatasi sesi
CLAUDE_RUNNER_CLIENT_PLATFORM Permukaan klien yang membuat sesi, seperti web_claude_ai, desktop_app, atau ios. Tidak diatur ketika sesi tidak memiliki permukaan yang tercatat atau dikenali. Memerlukan Claude Code v2.1.229 atau lebih baru.
CLAUDE_CODE_SESSION_ACCESS_TOKEN Token akses sesi, untuk panggilan API yang dibatasi sesi
GIT_CONFIG_COUNT, GIT_CONFIG_KEY_n, GIT_CONFIG_VALUE_n Pengaturan git yang ditetapkan runner untuk git yang dijalankan hook Anda. Konfigurasi Git di dalam lifecycle hooks menjelaskannya. Memerlukan Claude Code v2.1.280 atau lebih baru.

CLAUDE_RUNNER_EXIT_REASON mengambil salah satu dari empat nilai:

  • completed: sesi berakhir dengan bersih. Proses Claude Code keluar secara normal, atau sesi diarsipkan atau dihapus saat masih berjalan.
  • failed: proses Claude Code mogok, atau setup gagal setelah dimulai.
  • interrupted: runner menghentikan sesi. Itu merilis sesi untuk membebaskan slot, sesi timeout saat startup, server memindahkan sesi dari runner ini, runner sedang mengalirkan, atau sesi melampaui batas --kill-session-after-min nya.
  • abandoned: dicadangkan untuk sesi yang diklaim runner lain. Hook saat ini tidak menyala dalam kasus itu.

Penghitung siklus hidup sesi menghitung rilis, timeout startup, dan perpindahan server sebagai completed daripada interrupted, karena runner menyerahkan slot kembali dengan bersih. Harapkan perbedaan itu jika Anda membandingkan penerimaan hook dengan penghitung.

Status keluar hook tidak pernah mempengaruhi hasil sesi; kegagalan dicatat dan diabaikan. Runner menunggu hingga --post-session-hook-timeout-sec, 60 detik secara default, pada setiap akhir sesi termasuk shutdown runner. Contoh ini menyimpan pekerjaan yang tidak dikomit ke cabang penyelamatan:

#!/usr/bin/env bash
set -u
IFS=':'
# Override -c mengalahkan pengaturan lokal repo, memblokir konfigurasi fsmonitor,
# hook-path, dan gpg-program yang ditulis sesi agar tidak mengeksekusi kode dengan
# hak istimewa hook. -c commit.gpgsign=false juga membuat commit penyelamatan ini
# tidak ditandatangani di bawah --configure-git.
# credential.helper dan pushurl lokal repo masih berlaku, dan pada runner
# sebelum v2.1.280 begitu pula core.sshCommand; jika hook menyimpan kredensial
# yang tidak dimiliki sesi, lihat catatan di bawah skrip.
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

Hook melakukan push dengan kredensial git apa pun yang tersedia di lingkungannya sendiri di host runner. Di bawah postur tanpa-kredensial-dalam-image, termasuk ketika klon bawaan melewati proxy git Anthropic, tidak ada kredensial, jadi buat kredensial push jangka pendek di dalam hook sebelum melakukan push: tukarkan token sesi yang diterima hook di CLAUDE_CODE_SESSION_ACCESS_TOKEN dengan layanan token Anda sendiri, dengan memverifikasinya seperti yang dijelaskan di Verify session identity. Ketika hook menyimpan kredensial yang tidak dimiliki sesi, ganti origin dengan URL yang disediakan operator dan teruskan -c credential.helper= ditambah helper Anda sendiri. Konfigurasi Git di dalam lifecycle hooks menjelaskan apa yang masih dapat dipengaruhi oleh konfigurasi yang ditulis sesi.

Hook timing when the runner releases a session

Sesi yang dirilis dapat dilanjutkan di runner lain. Di runner pada v2.1.236 atau lebih baru, apa yang dilakukan sesi saat rilis memutuskan apakah itu dapat dilanjutkan di runner lain sebelum hook ini selesai:

  • Idle setelah giliran, atau timeout saat startup: runner menghentikan anak dan menjalankan hook ini hingga selesai. Hanya kemudian itu merilis sesi. Pesan pengguna yang dikirim saat hook berjalan tidak dapat melanjutkan sesi di runner lain sebelum hook selesai.
  • Menunggu pengguna menjawab prompt, seperti prompt izin: runner merilis sesi terlebih dahulu, kemudian menjalankan hook ini. Pesan pengguna yang dikirim saat hook berjalan dapat melanjutkan sesi di runner lain sebelum hook selesai.

Ini berlaku setiap kali runner merilis sesi: pada timeout idle, pada waktu --retire-at, dan, di runner pada v2.1.260 atau lebih baru, pada batas --kill-session-after-min sesi. Sesi yang giliran telah berakhir dan yang hanya menyimpan tugas latar belakang dihitung sebagai idle di sini. Sebelum v2.1.236, runner merilis sesi terlebih dahulu kemudian menjalankan hook ini di kedua kasus.

Selama drain SIGTERM, runner menyimpan sewa sesi sampai hook selesai; lihat Shutdown timing.

Konfigurasi Git di dalam lifecycle hooks

Hook checkout dan post-session berjalan dengan token akses sesi di lingkungannya, dan git yang dijalankannya membaca file konfigurasi yang dapat ditulis oleh sesi, seperti ~/.gitconfig dan .git/config milik checkout. Sebelum salah satu hook berjalan, runner menetapkan pengaturan git di lingkungan hook, termasuk yang di bawah ini, sebagai pasangan GIT_CONFIG_COUNT/GIT_CONFIG_KEY_n/GIT_CONFIG_VALUE_n dan environment variable git. Git menempatkan pengaturan tersebut di atas setiap file konfigurasi, dan pengaturan itu hanya berlaku untuk git yang dijalankan hook Anda, bukan untuk git milik sesi itu sendiri. Saat startup, runner mencetak baris [runner:git] lifecycle hooks: yang menampilkan hooks path, protokol yang diizinkan, program gpg, dan mode penandatanganan yang berlaku. Memerlukan Claude Code v2.1.280 atau lebih baru.

  • Git hook: kecuali Anda menyediakan nilai, core.hooksPath adalah /dev/null, sehingga git melewati hook di .git/hooks repositori dan direktori hook apa pun yang disebutkan ~/.gitconfig. Untuk menyediakan nilai, ekspor core.hooksPath sebagai pasangan GIT_CONFIG_KEY_n/GIT_CONFIG_VALUE_n di lingkungan runner. Runner juga membaca core.hooksPath dari konfigurasi git sistem, dan menggunakannya hanya ketika pengguna runner tidak dapat menulis file tersebut, direktori yang disebutkannya, atau file hook di dalamnya. Ketika runner mengabaikan suatu nilai, baris [runner:warn] saat startup menyebutkan nilai tersebut dan alasannya.
  • Monitor sistem file: core.fsmonitor kosong, sehingga git di hook Anda tidak menjalankan program monitor yang disebutkan oleh file konfigurasi.
  • Protokol remote: GIT_ALLOW_PROTOCOL adalah https:http:ssh. Clone, fetch, atau push yang menggunakan jalur lokal, URL file://, atau URL git:// gagal dengan fatal: transport 'file' not allowed atau fatal: transport 'git' not allowed.
  • Perintah SSH dan prompt kredensial: git di hook Anda mengabaikan core.sshCommand dan core.askPass dari file konfigurasi. Untuk menggunakan perintah SSH Anda sendiri, atur GIT_SSH_COMMAND di lingkungan runner. Untuk menggunakan program prompt kredensial, atur GIT_ASKPASS di sana. Sesi mewarisi lingkungan runner, sehingga kedua variabel juga mencapai git milik sesi itu sendiri. Jangan letakkan kredensial di salah satunya.
  • Program gpg: gpg.program, gpg.openpgp.program, gpg.x509.program, dan gpg.ssh.program adalah jalur yang ditetapkan runner, tidak pernah nilai dari file konfigurasi.
  • Penandatanganan commit: dengan --configure-git, commit yang Anda buat dari hook ditandatangani sebagai sesi. Tanpa flag tersebut, commit.gpgsign dan tag.gpgsign bernilai false.

Untuk mengubah salah satu pengaturan ini, gunakan lingkungan runner atau opsi git -c di dalam hook:

  • Pasangan konfigurasi: pasangan GIT_CONFIG_KEY_n/GIT_CONFIG_VALUE_n yang Anda ekspor di lingkungan runner menggantikan nilai runner untuk kunci yang sama. Beri nomor pasangan Anda mulai dari 0 dan atur GIT_CONFIG_COUNT ke jumlah pasangan tersebut. Ketika pasangan terakhir yang diumumkan oleh hitungan tidak ada, runner mengabaikan semua pasangan Anda dan mencatat baris [runner:warn] saat startup.
  • Environment variable git: runner membiarkan GIT_ALLOW_PROTOCOL, GIT_SSH_COMMAND, dan GIT_ASKPASS sebagaimana Anda mengaturnya di lingkungannya.
  • Opsi git -c: opsi git -c di dalam hook menimpa pasangan GIT_CONFIG_KEY_n, baik milik runner maupun milik Anda. Opsi ini tidak mengubah GIT_ALLOW_PROTOCOL, GIT_SSH_COMMAND, atau GIT_ASKPASS, yang dibaca git sebelum konfigurasi apa pun.

Git di hook Anda tetap membaca setiap pengaturan yang tidak ditetapkan runner, seperti credential helper, rewrite url.*.insteadOf, dan filter driver, dari setiap file konfigurasi, termasuk yang dapat ditulis oleh sesi. Credential helper atau filter driver yang disebutkan di salah satu file tersebut berjalan sebagai program dengan hak istimewa hook Anda, dan konfigurasi dalam file tersebut masih dapat mengubah tujuan push dari hook Anda, termasuk push ke URL yang Anda teruskan di baris perintah.

Sebelum v2.1.280, runner tidak menetapkan satu pun dari pengaturan ini, dan di bawah --configure-git commit yang dibuat dari hook gagal kecuali hook meneruskan -c commit.gpgsign=false.

command

Berjalan sekali per sesi setelah checkout, sebagai pengganti pemijahan anak bawaan. Hook menerima lingkungan yang sama dengan skrip wrapper dan harus exec ke "$CLAUDE_RUNNER_CLAUDE_BIN" dengan cara yang sama. Gunakan hook command untuk menyimpan semua kustomisasi dalam satu direktori hooks; gunakan --exec-path ketika wrapper tinggal di tempat lain. Jika --exec-path juga diatur, flag mengambil prioritas dan hook command diabaikan.

Selalu exec biner runner sendiri daripada claude yang diselesaikan PATH; jika tidak, Anda mengalahkan version pinning.

Runner on-demand

Alih-alih menjalankan fleet tetap, Anda dapat boot satu runner per sesi. Orchestrator adalah subperintah terpisah yang stateless yang menanyai Anthropic untuk permintaan pemijahan, satu per sesi yang antri tanpa runner tersedia, dan menjalankan hook spawn-runner Anda untuk masing-masing. Hook Anda mengirimkan beban kerja ke platform Anda: Kubernetes Job, instans EC2, dispatch Nomad.

Runner on-demand meningkatkan kebersihan kredensial. Pada fleet tetap, rahasia lingkungan tinggal di setiap host runner, yang merupakan host yang sama yang menjalankan sesi pengguna. Dengan orchestrator, rahasia lingkungan tetap hanya di host orchestrator, yang tidak pernah menjalankan kode pengguna; setiap runner yang dipijahkan menerima pesanan kerja sekali pakai yang mendaftarkan tepat satu runner dan kemudian kedaluwarsa.

Untuk memulai orchestrator, lewatkan rahasia lingkungan dan direktori hooks yang berisi skrip spawn-runner yang dapat dieksekusi:

claude self-hosted-runner orchestrator \
  --environment-secret-file /etc/claude/environment-secret \
  --hooks-dir /etc/claude/hooks

Orchestrator tidak menyimpan status antara polling, jadi Anda dapat menjalankan dua atau lebih replika terhadap lingkungan yang sama untuk ketersediaan. Setiap permintaan pemijahan diklaim server-side oleh tepat satu replika. Semua replika harus menggunakan nilai --expected-spawn-seconds yang sama; lihat kontrak hook.

Hook spawn-runner

Orchestrator menjalankan ${hooks-dir}/spawn-runner sekali per permintaan pemijahan. Hook harus mengirimkan pekerjaan secara asinkron, tanpa menunggu runner boot, dan kembali dalam --hook-timeout, 60 detik secara default. Hook menerima:

Variabel Deskripsi
CLAUDE_RUNNER_WORK_ORDER_FILE Jalur ke file temp yang berisi JWT pesanan kerja yang ditandatangani yang didaftarkan runner baru. Dihapus setelah hook keluar. Jangan catat isi file.
CLAUDE_RUNNER_ORDER_ID Kunci idempotency yang buram, unik per permintaan pemijahan dan aman untuk nama sumber daya Kubernetes. Gunakan hanya order ID sebagai kunci dedup provisioner Anda.
CLAUDE_RUNNER_SESSION_ID Sesi yang diminta ini untuk. Ini berulang pada setiap permintaan ulang untuk sesi, jadi gunakan untuk logging dan routing, bukan sebagai kunci dedup. Kosong untuk permintaan pre-warming, yang boot runner standby sebelum sesi spesifik apa pun ketika --min-idle diatur, jadi jangan asumsikan variabel diatur.
CLAUDE_RUNNER_SESSION_UUID ID sesi yang sama dalam bentuk UUID kanonik. Kosong untuk permintaan pre-warming.
CLAUDE_RUNNER_ATTEMPT Berapa banyak permintaan pemijahan yang dimiliki sesi ini. 0 untuk permintaan pre-warming.
CLAUDE_RUNNER_ORDER_SERVER_TIME Waktu server dari header HTTP Date respons polling. Ketika hook memverifikasi exp JWT pesanan kerja, bandingkan terhadap nilai ini daripada jam lokal untuk mentoleransi skew. Kosong ketika gateway menghilangkan header.
CLAUDE_RUNNER_POOL_ID ID lingkungan yang harus diikuti runner baru, dalam bentuk ccpool_...
CLAUDE_RUNNER_ACCOUNT_ID ID yang ditandai dari akun yang antri sesi, untuk routing per-akun, kuota, atau chargeback. Kosong ketika tidak tersedia, dan selalu kosong untuk sesi saluran Claude Tag, yang tidak ada akun antri.
CLAUDE_RUNNER_ACCOUNT_EMAIL Email akun yang antri sesi. Kosong ketika tidak tersedia. Perlakukan email sebagai informasi yang dapat diidentifikasi secara pribadi dan jangan catat.
CLAUDE_RUNNER_PRIMARY_REPO_URL URL sumber git pertama sesi, untuk routing ke runner dengan repositori itu pre-warmed. Kosong ketika sesi tidak memiliki sumber git.
CLAUDE_RUNNER_PRIMARY_REPO_REVISION Revisi sumber git pertama sesi: cabang, SHA, atau tag. Kosong ketika tidak ditentukan.
CLAUDE_RUNNER_REPO_SOURCES Array JSON dari {url, revision} untuk semua sumber git sesi, untuk hook yang route pada repositori sekunder. Kosong ketika tidak ada sumber.
CLAUDE_RUNNER_CORRELATION_ID ID korelasi yang disediakan saat pembuatan sesi, digemakan kembali sehingga hook dapat memetakan pesanan kerja ini ke permintaan yang membuat sesi. Kosong ketika sesi tidak memiliki satu.
CLAUDE_RUNNER_CLIENT_PLATFORM Permukaan klien yang membuat sesi, seperti web_claude_ai, desktop_app, ios, atau scheduled_trigger, untuk analitik adopsi. Tidak diatur ketika sesi tidak memiliki permukaan yang tercatat atau dikenali, dan untuk permintaan pre-warming; periksa dengan [ -n "${CLAUDE_RUNNER_CLIENT_PLATFORM:-}" ], yang tetap aman di bawah set -u.

Runner yang dipijahkan mendaftarkan dengan pesanan kerja sebagai pengganti rahasia lingkungan:

  • Mulai dengan pesanan kerja: arahkan --environment-secret-file ke file yang berisi JWT pesanan kerja, atau atur SELF_HOSTED_RUNNER_ENVIRONMENT_SECRET ke nilai JWT.
  • Salin JWT sebelum hook keluar: orchestrator menghapus file pesanan kerja setelah hook keluar, jadi salin JWT ke dalam beban kerja yang Anda kirimkan, seperti Kubernetes Secret di Job yang dipijahkan, daripada melewatkan jalur file.
  • Gunakan --capacity 1 di runner yang dipijahkan: pesanan kerja yang terikat sesi mendaftarkan tepat satu runner yang terikat ke sesi itu, jadi kapasitas lebih tinggi menambah slot yang tidak pernah menerima pekerjaan, dan runner mencatat peringatan saat startup.
  • Pesanan kerja pre-warming mendaftarkan tidak terikat: runner standby tidak terikat ke sesi dan mengklaim pekerjaan antri seperti runner fleet tetap.

Kontrak memiliki empat aturan yang agnostik provisioner:

  1. Jadilah idempoten pada CLAUDE_RUNNER_ORDER_ID. Pengiriman ulang permintaan yang sama harus memijahkan paling banyak satu runner. Turunkan nama sumber daya deterministik dari order ID dan biarkan platform Anda menolak duplikat. Jangan kunci pada CLAUDE_RUNNER_SESSION_ID sebagai gantinya. Setiap permintaan ulang untuk sesi membawa ID sesi yang sama dengan order ID baru, jadi beban kerja yang dinamai atau dideduplikasi oleh ID sesi dibuat sekali dan tidak pernah lagi untuk sesi itu.
  2. Jangan coba ulang beban kerja. Satu ID pesanan berarti paling banyak satu beban kerja yang dibuat. Jika runner tidak pernah mendaftar, Anthropic meminta ulang dengan ID pesanan segar setelah --expected-spawn-seconds.
  3. Gunakan kontrak kode keluar. Keluar 0 berarti dikirimkan. Keluar 1 berarti kegagalan yang dapat dicoba ulang; sesi mundur dan ditawarkan kembali. Keluar 2 atau lebih tinggi berarti tidak dapat dicoba ulang; sesi diblokir dari pemijahan lagi sampai Owner memilih Retry di atasnya di tab Activity lingkungan. Pada keluar bukan nol, ekor stderr hook muncul di sana sebagai alasan kegagalan, jadi tulis kesalahan yang dapat ditindaklanjuti ke stderr dan jangan pernah rahasia. Untuk permintaan pre-warming tidak ada sesi untuk gagal: orchestrator mencatat keluar bukan nol secara lokal saja, dan server meminta ulang pemijahan setelah sewa.
  4. Atur --expected-spawn-seconds ke setidaknya waktu boot p99 Anda. Ini adalah sewa server-side. Semua replika orchestrator harus menggunakan nilai yang sama.

Semua yang ditulis hook ke stdout atau stderr muncul dalam log orchestrator dengan kredensial secara otomatis diredaksi. Jika sesi tetap antri, periksa badan /healthz orchestrator untuk hitungan antrian, kemudian buka tab Activity lingkungan Anda di halaman admin Cloud environments: perluas sesi yang gagal di sana untuk kesalahan pemijahan, dan pilih Retry untuk memintanya ulang.

Sesi yang tetap antri tanpa kesalahan pemijahan di tab Activity dapat berarti hook dikunci pada ID sesi. Untuk mengonfirmasi, periksa apakah platform Anda memiliki beban kerja untuk permintaan pemijahan pertama sesi itu dan tidak ada untuk permintaan ulang. Jika demikian, kunci beban kerja pada CLAUDE_RUNNER_ORDER_ID sebagai gantinya.

Mengirim permintaan model ke Bedrock atau Agent Platform

Jika organisasi Anda mengharuskan permintaan model melewati akun AWS atau Google Cloud milik organisasi sendiri, konfigurasikan runner untuk Amazon Bedrock atau Google Cloud's Agent Platform, sebelumnya Vertex AI. Setiap sesi yang dimulai oleh runner tersebut kemudian memanggil model di akun cloud Anda, dengan kredensial cloud Anda. Tanpa konfigurasi ini, sesi mengirim permintaan model ke Anthropic API.

Runner tetap melakukan polling ke Anthropic untuk mendapatkan sesi, dan setiap sesi tetap mengirim aliran peristiwanya ke api.anthropic.com. Aliran peristiwa tersebut memuat prompt, respons, dan hasil tool. Persyaratan paket dan pengecualian Zero Data Retention dalam Ketersediaan dan batasan tetap berlaku.

Sesi dirutekan ke sebuah environment, bukan ke sebuah runner, dan sesi yang dimasukkan ulang ke antrean atau dilanjutkan dapat berjalan di runner yang berbeda. Konfigurasikan setiap runner di environment tersebut dengan cara yang sama. Sebelum memulai, baca apa yang berbeda pada penyedia ini.

1

Siapkan akun cloud dan aturan egress Anda

Siapkan akses model, kebijakan atau peran dengan cakupan sempit, dan akses jaringan:

  • Amazon Bedrock: kirimkan detail kasus penggunaan, lalu buat kebijakan di konfigurasi IAM, dengan membatasi bedrock:InvokeModel dan bedrock:InvokeModelWithResponseStream pada inference profile yang digunakan sesi Anda serta foundation model di baliknya
  • Agent Platform: aktifkan API dan minta akses model, lalu buat peran kustom yang dijelaskan dalam konfigurasi IAM, hanya dengan aiplatform.endpoints.predict
  • Egress: izinkan endpoint penyedia Anda melalui aturan egress Anda. Lihat Persyaratan jaringan. Jika sesi tidak dapat menjangkaunya, Claude Code dapat terus mencoba ulang selama berjam-jam sebelum sesi menampilkan error.
2

Berikan sesi kredensial dengan cakupan sempit

Lampirkan kebijakan atau peran dari langkah 1 ke sebuah identitas yang tidak dapat melakukan hal lain. Untuk metode yang diterima Claude Code, lihat Mengonfigurasi kredensial AWS dan Mengonfigurasi kredensial GCP.

Periksa metode yang Anda pilih terhadap perilaku runner berikut:

  • Metadata endpoint: jika Anda sepenuhnya menolak akses sesi ke cloud metadata endpoint, kredensial yang disajikan darinya, seperti instance profile, juga tidak akan sampai ke Claude Code. Web identity berbasis file, seperti IAM Roles for Service Accounts (IRSA) di Amazon EKS atau file kredensial Workload Identity Federation, tidak bergantung padanya.
  • Pembaruan: sebuah sesi dapat bertahan lebih lama daripada kredensial, jadi gunakan metode yang memperbarui dirinya sendiri, seperti web identity berbasis file
  • Skrip wrapper: runner memulai skrip wrapper Anda sekali per sesi, sehingga kredensial yang diekspornya tidak diperbarui. Claude Code membaca kredensial AWS dari environment-nya, jadi jika wrapper Anda sudah mengekspor kredensial AWS untuk pekerjaan lain, Claude Code dapat menandatangani permintaan model dengan kredensial tersebut.
3

Tetapkan variabel satu penyedia di environment runner

Tetapkan variabel untuk tepat satu penyedia di tempat Anda menetapkan environment variable runner lainnya, seperti spesifikasi container atau service unit, lalu mulai ulang runner. Contoh-contoh menampilkannya sebagai ekspor shell. Dengan runner sesuai permintaan, tetapkan variabel tersebut pada workload yang dimulai oleh hook spawn-runner Anda.

Mulai runner ini dengan --confine-repo-settings enforce. Flag ini menolak sesi pada repositori yang pengaturan ter-commit-nya ditandai, jadi jalankan terlebih dahulu dalam mode default warn dan selesaikan apa yang dicatatnya di log.

Ganti region dengan milik Anda:

export CLAUDE_CODE_USE_BEDROCK=1
export AWS_REGION=us-east-1

Untuk cara Claude Code menentukan region, lihat Mengonfigurasi Claude Code. Untuk prefiks inference profile yang digunakan Claude Code untuk region Anda, lihat Prefiks inference profile lintas region.

4

Periksa bahwa variabel telah sampai ke sesi

Shell Anda sendiri di host adalah proses yang berbeda, jadi periksa dari dalam sesi. Mulai sebuah sesi di environment tersebut dan minta Claude menjalankan perintah ini:

env | grep -E 'CLAUDE_CODE_USE_(BEDROCK|VERTEX)'

Baris yang menetapkan CLAUDE_CODE_USE_BEDROCK atau CLAUDE_CODE_USE_VERTEX ke 1 berarti variabel tersebut telah sampai ke sesi. Jika keduanya muncul, Claude Code menggunakan Amazon Bedrock. Tidak ada output berarti tidak satu pun yang sampai.

Perintah tersebut menampilkan konfigurasi, bukan lalu lintas. Untuk memastikan permintaan itu sendiri, cari permintaan tersebut di metrik atau log permintaan akun cloud Anda. Jika pesan pertama justru gagal, lihat pemecahan masalah untuk Amazon Bedrock atau Agent Platform.

Perbedaan dari sesi di Anthropic API

Sesi yang mengirim permintaan model ke Amazon Bedrock atau Google Cloud's Agent Platform berbeda dari sesi di Anthropic API dalam hal-hal berikut:

  • Kebijakan dari claude.ai: pengaturan yang dikelola server tidak sampai ke sesi ini. Begitu pula kebijakan organisasi yang ditetapkan Owner di pengaturan admin Claude Code, sehingga Claude Code tidak memberlakukannya di dalam sesi. Letakkan aturan yang Anda andalkan di file pengaturan terkelola pada image runner.
  • File: file yang dilampirkan orang ke sesi di claude.ai atau aplikasi seluler atau desktop tidak sampai ke sesi tersebut, dan Claude tidak dapat mengirim file kembali dengan tool SendUserFile. Sebagai gantinya, letakkan file input di repositori atau di runner.
  • Pemilihan model: control plane Anthropic mengirimkan model setiap sesi, dan ketika sesi dimulai tanpa model, Claude Code menggunakan model default untuk penyedia tersebut. Runner menghapus ANTHROPIC_MODEL dan ANTHROPIC_DEFAULT_MODEL dari environment yang diteruskannya ke sesi. Contoh di halaman penyedia menetapkan ANTHROPIC_MODEL, tetapi di environment runner tidak satu pun variabel tersebut berpengaruh. Variabel per keluarga model di Menyematkan versi model untuk Amazon Bedrock dan Agent Platform memang sampai ke sesi. Variabel tersebut menentukan ke mana alias seperti opus diarahkan, bukan ke mana ID model lengkap diarahkan.
  • Model yang tidak dilayani akun Anda: sebuah sesi dapat gagal pada suatu pesan dengan error yang menyebutkan nama model. Aktifkan model yang dapat dipilih developer Anda, model latar belakang yang dijelaskan dalam Menyematkan versi model, dan model pengklasifikasi yang digunakan auto mode. Di Amazon Bedrock, izinkan masing-masing model tersebut dalam kebijakan Anda.
  • Pencarian web dan fast mode: pencarian web tidak tersedia di Amazon Bedrock, dan fast mode tidak tersedia di kedua penyedia. Untuk kemampuan lain yang berbeda menurut penyedia, lihat Kemampuan CLI yang bervariasi menurut penyedia.

Server MCP

Agar server MCP tersedia di setiap sesi, tambahkan server tersebut saat build image dengan perintah claude mcp add yang sama seperti yang digunakan pada instalasi desktop. Jika runner Anda berupa proses biasa dan bukan container, jalankan perintah yang sama sebagai pengguna runner di host, lalu mulai ulang runner: runner membaca konfigurasi host satu kali saat startup. Flag --scope user wajib digunakan; cakupan local default menulis di bawah kunci per direktori yang tidak disemai oleh runner ke dalam sesi. Misalnya, di Dockerfile Anda:

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

Runner mengambil snapshot konfigurasi host satu kali saat startup. Snapshot tersebut menangkap kunci mcpServers dari .claude.json milik host, yang berada di sebelah ~/.claude/ dan bukan di dalamnya, dan runner hanya menyemai kunci tersebut ke dalam konfigurasi terisolasi setiap sesi; status akun dan riwayat proyek dibuang. Untuk memastikan server telah mencapai sesi, mulai sesi pada environment tersebut dan minta Claude untuk membuat daftar tool MCP-nya; runner juga mencatat peringatan startup untuk setiap entri yang ditangkap yang type-nya tidak dikenali dan membuang entri tersebut, sehingga Anda dapat melihat mengapa server itu tidak ada di sesi. Ketika SELF_HOSTED_RUNNER_HOST_CONFIG_DIR diatur, runner membaca .claude.json dari direktori tersebut sebagai gantinya, sehingga mengarahkan variabel ke direktori kosong juga menonaktifkan penyemaian MCP.

Claude Code juga memuat server MCP dari sumber lain:

  • File MCP terkelola cakupan enterprise pada jalur sistem standarnya: /etc/claude-code/managed-mcp.json pada host runner Linux, /Library/Application Support/ClaudeCode/managed-mcp.json pada host macOS. Gunakan file ini untuk armada yang dikunci ketat di mana hanya server yang didaftarkan administrator yang boleh dimuat. Lihat kontrol eksklusif dengan managed-mcp.json untuk aturan prioritas. Ketika file ini ada di host runner, Claude Code melewati server MCP yang dikirimkan control plane Anthropic ke sesi, termasuk konektor claude.ai, dan menyebutkan namanya dalam peringatan di stderr proses anak sesi, yang dicatat runner pada level log debug. Sebelum v2.1.229, sesi tersebut keluar saat startup dengan You cannot dynamically configure MCP servers when an enterprise MCP config is present.
  • Kunci managedMcpServers dalam pengaturan terkelola pada host runner: menyediakan server HTTP dan SSE tanpa mengambil kontrol eksklusif, sehingga server dari sumber lain tetap dimuat. Memerlukan Claude Code v2.1.259 atau yang lebih baru.
  • <repo>/.mcp.json: cakupan project. Lakukan commit file ini ke repositori; server-servernya disetujui secara otomatis di sesi cloud.

Ketika pengiriman konektor diaktifkan untuk organisasi Anda, control plane Anthropic mengirimkan konektor yang telah Anda konfigurasikan di claude.ai ke sesi yang dibuat secara interaktif melalui konfigurasi MCP yang disediakan server, yang dirutekan melalui api.anthropic.com. Sesi yang dibuat secara terprogram, seperti dispatch CLI, tidak menerima pengiriman konektor; berikan server MCP kepada sesi tersebut melalui sumber lain yang tercantum di bagian ini. Token OAuth proses anak tidak membawa scope untuk mengambil konektor secara langsung, sehingga proses anak tidak mencoba pengambilan tersebut sendiri; pengiriman digerakkan oleh server.

settings.json tidak memuat definisi server MCP, dan tidak ada field mcpServers tingkat atas dalam skema pengaturan. Dalam pengaturan terkelola, sediakan server dengan kunci managedMcpServers sebagai gantinya.

Sesi mewarisi environment runner, jadi atur ENABLE_TOOL_SEARCH di sana untuk mengontrol pencarian tool MCP bagi setiap sesi yang dijalankan runner; halaman MCP menjelaskan nilai-nilainya.

Matikan tool sesi bawaan

Control plane Anthropic melampirkan server MCP miliknya sendiri, bernama Claude Code Remote, ke sesi cloud. Claude menggunakan tool server tersebut untuk menjadwalkan routine, memulai dan mengarahkan sesi cloud lain, melampirkan repositori tambahan, dan mengikuti aktivitas pull request.

Untuk mematikan seluruh server, tambahkan aturan deny tingkat server ke pengaturan Anda. Control plane mendaftarkan server dengan salah satu dari tiga nama, tergantung pada cara sesi dibuat. Claude Code mencocokkan nama dalam aturan secara persis, termasuk huruf besar-kecil, jadi tulis aturan satu kali untuk setiap nama seperti yang ditunjukkan:

{
  "permissions": {
    "deny": [
      "mcp__Claude_Code_Remote",
      "mcp__claude-code-remote",
      "mcp__bf7c680d-5fdc-5ef4-b4a0-abadb619bf0a"
    ]
  }
}

Aturan yang menyebutkan seluruh server juga mencakup tool yang ditambahkan ke server di kemudian hari. Untuk mematikan satu tool dan mempertahankan sisanya, tambahkan dua garis bawah lagi dan nama tool ke setiap aturan, seperti pada mcp__Claude_Code_Remote__add_repo. Untuk memblokir server agar tidak terhubung sama sekali alih-alih menghapus tool-nya, tambahkan ketiga nama tanpa awalan mcp__ sebagai entri serverName di bawah deniedMcpServers sebagai gantinya.

Letakkan aturan tersebut di pengaturan yang dikelola server untuk menjangkau sesi tanpa perubahan pada runner, atau di ~/.claude/settings.json pada runner. Pada runner yang mengirimkan permintaan model ke Bedrock atau Agent Platform, gunakan file tersebut, karena pengaturan yang dikelola server tidak menjangkau sesi tersebut. Izin dan persetujuan tool menjelaskan bagaimana pengaturan pada runner menjangkau sesi.

Untuk memastikan aturan telah berlaku, mulai sesi pada environment tersebut dan minta Claude untuk membuat daftar tool MCP-nya. Claude Code menghapus tool yang ditolak dari konteks Claude, sehingga tool yang ditolak tidak muncul dalam jawabannya.

Prompt sesi untuk mendorong pekerjaan mereka

Sesi yang di-host Anthropic menjalankan hook Stop, hook Claude Code yang berjalan ketika Claude selesai merespons, yang mendorong Claude untuk melakukan komit dan mendorong pekerjaannya. Runner tidak memasang satu. Tanpa itu, sesi yang berakhir dengan perubahan yang tidak dikomit meninggalkan pekerjaan itu hanya di disk runner, dan tombol Create PR di claude.ai/code tetap tidak aktif sampai cabang ada di remote.

Implementasi referensi di bawah memiliki dua bagian. Gabungkan blok pengaturan ke ~/.claude/settings.json di host runner, yang ditanam runner ke dalam setiap sesi, dan simpan skrip sebagai ~/.claude/hooks/stop-hook-nudge.sh di host runner dan buat dapat dieksekusi:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "timeout": 10,
            "command": "\"$CLAUDE_CONFIG_DIR/hooks/stop-hook-nudge.sh\""
          }
        ]
      }
    ]
  }
}
#!/bin/sh
# Implementasi referensi Stop-hook untuk runner yang di-host sendiri.
#
# Mendorong Claude sekali per giliran jika direktori proyek memiliki perubahan
# yang tidak dikomit ATAU komit yang tidak didorong, sehingga pekerjaan tidak
# hilang ketika sesi idle dirilis dan sehingga tombol "Create PR" di claude.ai/code
# menyala.
#
# Tingkat runner (tidak ada perubahan repo): jatuhkan file ini di ~/.claude/hooks/
# di host runner dan gabungkan blok pengaturan Stop-hook yang menyertai ke
# ~/.claude/settings.json — runner menabur keduanya ke dalam setiap sesi.
# Alternatif tingkat repo: komit ke <repo>/.claude/hooks/ dan ubah jalur perintah
# settings.json ke $CLAUDE_PROJECT_DIR/.claude/hooks/.
#
# stdin: payload JSON hook (lihat https://code.claude.com/docs/en/hooks)
# stdout: {"decision":"block","reason":"..."} untuk mendorong, atau tidak ada untuk memungkinkan stop.

# Penjaga re-entry: harness menetapkan stop_hook_active=true ketika menginvokasi
# ulang hook Stop setelah blok. Keluar sehingga kami hanya mendorong sekali per
# giliran. Harness memancarkan JSON kompak (tidak ada spasi setelah titik dua),
# yang pola ini andalkan; gunakan jq jika Anda memerlukan pemeriksaan yang toleran
# terhadap spasi.
in=$(cat)
case "$in" in *'"stop_hook_active":true'*) exit 0 ;; esac

d="$CLAUDE_PROJECT_DIR"

# Bukan repo git → tidak ada yang didorong.
git -C "$d" rev-parse --git-dir >/dev/null 2>&1 || exit 0

# Tidak ada remote → "dorong ke remote" tidak dapat dipenuhi; keluar.
[ -z "$(git -C "$d" remote 2>/dev/null)" ] && exit 0

# Perubahan yang tidak dikomit (staged, unstaged, atau untracked). Kecualikan
# .claude/ sepenuhnya — pengaturan yang ditanam operator dan status runtime
# yang ditulis CLI (kunci penjadwal, worktree, status rutin) tinggal di sana
# dan tidak ada yang merupakan "pekerjaan yang tidak dikomit" yang perlu didorong
# model.
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

# Komit yang tidak didorong. Hitung komit di HEAD yang tidak dapat dijangkau dari
# ref pelacakan remote apa pun atau FETCH_HEAD. Ini bekerja secara seragam untuk:
#   - checkout init+fetch (default runner: hanya FETCH_HEAD ada)
#   - checkout berbasis klon (origin/* ada)
#   - default runner: anak dimulai di cabang hasil sesi, yang dibuat runner
#     setelah checkout
#   - detached HEAD, ketika setup kustom melewati pembuatan cabang itu
# Tanpa titik referensi sama sekali (tidak pernah diambil), tetap diam daripada
# false-positive pada giliran hanya-baca.
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 adalah "" atau "FETCH_HEAD", pemisahan kata yang disengaja
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 dipengaruhi penyerang — git-check-ref-format(1) memungkinkan `"`
    # dalam nama ref. `\` dilarang (aturan 10) tetapi lolos pula sebagai pertahanan
    # kedalaman murah.
    # Lolos karakter meta JSON sebelum interpolasi ke payload yang dibangun tangan
    # sehingga cabang seperti x","continue":false tidak dapat menyuntikkan kunci ke
    # JSON output-hook yang diurai harness. $unpushed aman — penjaga -gt di atas
    # menolak apa pun yang bukan integer biasa.
    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

Hook mendorong Claude untuk melakukan komit dan mendorong sebelum sesi berakhir, dan tetap diam ketika direktori bukan repositori git atau tidak memiliki remote.

Izin dan persetujuan alat

Sesi yang di-host sendiri tidak memiliki terminal yang terlampir, jadi prompt izin yang tidak dijawab menghentikan giliran sampai pengguna merespons di UI. Bidang kontrol Anthropic mengirimkan daftar alat setiap sesi dan aturan izin dengan muatan kerja; konfigurasi default pre-menyetujui panggilan alat rutin, termasuk Bash, dan sesi cloud pre-menyetujui edit file terlepas dari mode. Panggilan yang tidak ada yang pre-menyetujui mendorong melalui UI sesi.

Untuk menjaga prompt ke minimum terlepas dari apa yang dikirimkan bidang kontrol, pin mode auto dari skrip wrapper atau hook command Anda. Mode auto memungkinkan sesi berjalan tanpa prompt izin rutin: model pengklasifikasi terpisah meninjau tindakan sebelum mereka berjalan dan memblokir yang ditolaknya, dan aturan ask eksplisit masih memaksa prompt; halaman mode izin mencakup apa yang diperiksa pengklasifikasi. Runner menambahkan flag yang dihitung server sebelum menginvokasi wrapper, dan untuk flag nilai tunggal seperti --permission-mode parser menghormati kemunculan terakhir, jadi flag yang Anda tambahkan setelah "$@" menimpa nilai yang dikirim server:

#!/bin/bash
exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@" --permission-mode auto

Untuk pre-menyetujui alat spesifik sebagai gantinya, tambahkan --allowed-tools dengan aturan Anda, misalnya --allowed-tools "Bash(bazel *) Bash(yarn *) mcp__internal__*". Flag daftar seperti --allowed-tools dan --disallowed-tools terakumulasi di seluruh kemunculan daripada menimpa, jadi aturan Anda berlaku di atas aturan apa pun yang dikirimkan bidang kontrol. Untuk mempersempit, tambahkan --disallowed-tools, yang menolak alat bahkan jika aturan lain memungkinkannya.

Bagaimana konfigurasi setiap sesi dirakit

Runner memberikan setiap sesi direktori konfigurasinya sendiri, ditanam dari snapshot ~/.claude/ host yang ditangkap runner sekali saat startup: settings.json, CLAUDE.md, hooks, agents, commands, dan skills di gambar runner Anda berlaku untuk setiap sesi sebagai baseline tingkat pengguna. Jika Anda mengubah konfigurasi di host yang berjalan, perubahan hanya berlaku setelah Anda memulai ulang runner.

Atur SELF_HOSTED_RUNNER_HOST_CONFIG_DIR untuk menabur dari jalur berbeda, atau arahkan ke direktori kosong untuk menonaktifkan penanaman.

.claude/settings.json yang dikomit repositori berlapis di atas sebagai pengaturan proyek. Sesi juga membaca managed-settings.json dari jalur sistem standar di gambar runner Anda. Apakah kuncinya berlaku bersama pengaturan yang dikelola server mengikuti bagaimana Claude Code menggabungkan sumber yang dikelola: secara default, ketika organisasi Anda mengirimkan kunci yang dikelola server apa pun, sesi mengabaikan file gambar runner terlepas dari kunci yang dibaca Claude Code dari setiap sumber admin, seperti blok env, kunci sandbox, jalur biner sandbox, dan forceRemoteSettingsRefresh. Lihat prioritas pengaturan.

Ketika bidang kontrol Anthropic memasok sesi dengan Claude Code hooks, runner memasangnya bersama, bukan di atas, konfigurasi Anda sendiri. Memerlukan Claude Code v2.1.229 atau lebih baru.

  • Di mana mereka mendarat: runner menulis setiap skrip hook yang disediakan ke subdirektori hooks/.ccr-launcher/ yang dicadangkan dari direktori konfigurasi sesi dan mendaftarkan skrip dalam file pengaturan terpisah yang dilewatkan ke sesi dengan --settings, meninggalkan settings.json yang ditanam dan skrip Anda sendiri di hooks/<name> tidak tersentuh. Runner membuat ulang subdirektori yang dicadangkan untuk setiap sesi dan tidak menabur konten host di ~/.claude/hooks/.ccr-launcher/ ke dalam sesi.
  • Siapa yang menulisnya: bidang kontrol mengisinya dari konstanta tetap dalam deployment-nya sendiri, tidak pernah dari input per-sesi atau pihak ketiga.
  • Apa yang masih mengaturnya: hook yang dikirimkan melalui --settings memasuki konfigurasi hook yang digabungkan biasa, bukan tingkat yang dikelola, jadi pengaturan yang dikelola Anda masih berlaku. disableAllHooks menonaktifkannya, dan mereka bukan di antara kategori yang allowManagedHooksOnly tetap dimuat.

Di luar sesi Claude Tag, sesi dalam lingkungan yang di-host sendiri berjalan dengan auto memory nonaktif secara default. Untuk instruksi yang harus terbawa antar sesi, gunakan CLAUDE.md di image runner Anda atau di repositori.

Snapshot runner atas ~/.claude/ host tidak menyertakan direktori projects/. Lokasi penyimpanan default auto memory berada di bawah direktori tersebut. Jika Anda meletakkan file memori di sana, runner tidak menanamkannya ke dalam sesi, dan file tersebut tidak mengaktifkan auto memory.

Aturan izin yang dikomit repositori

Jangan letakkan entri "Edit", "Write", atau "NotebookEdit" telanjang dalam permissions.allow yang dikomit repositori. Aturan alat file telanjang cocok dengan alat terlepas dari jalur, memberikan penulisan di mana saja di host daripada hanya ruang kerja, jadi penjaga confine cakupan penulisan runner menandai sesi; dengan --confine-repo-settings enforce itu menolak untuk memijahkan sesi daripada mencatat dan melanjutkan. Lihat bagian hardening.

Repositori tidak memerlukan aturan alat file sama sekali: sesi cloud pre-menyetujui edit file terlepas dari mode. Jika Anda melakukan komit aturan, batasi ke ruang kerja, seperti "Edit(/**)"; garis miring tunggal di depan relatif terhadap akar proyek, yang merupakan ruang kerja sesi. Aturan alat file telanjang baik-baik saja dalam settings.json tingkat host operator, karena file itu tidak dikomit repositori.

defaultMode dari auto hanya dihormati dari file pengaturan tingkat gambar atau tingkat pengguna, jadi repositori yang diperiksa tidak dapat memberikan dirinya mode auto. Untuk mode mana sesi cloud terima dan sintaks aturan lengkap, lihat mode izin.

Apa selanjutnya