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.
[!NOTE]
Lingkungan yang di-host sendiri berada dalam beta publik pada paket Team dan Enterprise; seorang Owner mengaktifkannya dengan mengaktifkan Allow self-hosted environments di halaman admin Cloud environments. Halaman ini mengasumsikan runner yang berfungsi; lihat mulai cepat untuk setup dan Deploy to production untuk resep fleet.
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:
- Menyediakan kredensial jangka pendek yang dibatasi untuk pembuat sesi
- Mengekspor rahasia khusus lingkungan
- Menyiapkan toolchain bahasa
- Menerapkan batas sumber daya di sekitar proses anak
Runner memulai wrapper Anda sebagai pengganti biner Claude Code setiap kali memulai sesi, termasuk ketika sesi dilanjutkan atau dimasukkan kembali ke antrean. 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, dengan mengganti <writable-dir> seperti yang dijelaskan dalam mulai cepat:
claude self-hosted-runner --environment-secret-file /etc/claude/environment-secret --base-dir '<writable-dir>' --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 sebagai gantinya. Lihat Provision credentials scoped to the session creator. Tidak diatur ketika token tidak membawa email pembuat, misalnya pada sesi yang dibuat oleh identitas layanan organisasi Anda. 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. 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_CODE_REMOTE_SLACK_THREAD_URL |
Untuk sesi Claude Tag yang dimiliki oleh satu thread Slack, tautan ke thread tersebut. Tidak diatur untuk sesi lain, dan dapat juga tidak diatur untuk sesi thread. |
CLAUDE_CODE_REMOTE_SLACK_THREAD_TS |
Untuk sesi Claude Tag yang dimiliki oleh satu thread Slack, timestamp Slack thread tersebut, seperti 1700000000.000100. Dapat tidak diatur, dan dapat diatur ketika CLAUDE_CODE_REMOTE_SLACK_THREAD_URL tidak diatur, jadi periksa setiap variabel secara terpisah. |
CLAUDE_CODE_REMOTE_SLACK_CHANNEL_ID |
Untuk sesi Claude Tag, ID channel Slack atau pesan langsung yang dilayani sesi tersebut atau tempat thread-nya berada, seperti C0123ABCDE. Timestamp Slack mengidentifikasi pesan di dalam satu channel, jadi catat ID ini bersama dengan CLAUDE_CODE_REMOTE_SLACK_THREAD_TS. Tidak diatur untuk sesi lain, dan dapat juga tidak diatur untuk sesi Claude Tag. |
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. |
CLAUDE_CODE_REMOTE_MCP_CONFIG |
Jalur ke file di CLAUDE_CONFIG_DIR yang menyimpan server MCP yang dikirimkan bidang kontrol. Jika wrapper Anda membangun kembali lingkungan anak, bawa variabel ini, CLAUDE_CODE_REMOTE, dan CLAUDE_CODE_ENVIRONMENT_KIND tanpa diubah; runner menetapkan ketiganya. Jika CLAUDE_CODE_REMOTE sampai tetapi variabel ini atau CLAUDE_CODE_ENVIRONMENT_KIND tidak, anak menghentikan server stdio, HTTP, dan SSE yang ditambahkan wrapper Anda dengan --mcp-config yang tidak disertakan dalam pembaruan server yang dikirimkan. Memerlukan Claude Code v2.1.299 atau lebih baru. |
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, 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. |
CLAUDE_RUNNER_ALIVE_EVERY_MS |
Runner menetapkan seberapa sering, dalam milidetik, anak melaporkan bahwa ia masih hidup ketika giliran dimulai sebelum anak selesai diinisialisasi. Jika wrapper Anda membangun kembali lingkungan anak, bawa variabel ini, atau runner dapat melepaskan sesi selama giliran tersebut dan menulis no child output for <n> — releasing ke log. Memerlukan v2.1.309 atau lebih baru pada runner dan anak. |
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.
Anda dapat membaca variabel CLAUDE_CODE_REMOTE_SLACK_* di wrapper Anda atau hook command, serta di apa yang dijalankan sesi, seperti perintah shell, hook git, dan hook Claude Code. Hook checkout, post-session, dan spawn-runner tidak menerimanya.
Berikan nilai default untuk variabel yang dapat tidak diatur
Jika skrip Anda menggunakan set -u, ekspansikan CCR_SESSION_ACCOUNT_EMAIL, CLAUDE_RUNNER_CLIENT_PLATFORM, CLAUDE_CODE_REMOTE_SLACK_THREAD_URL, CLAUDE_CODE_REMOTE_SLACK_THREAD_TS, dan CLAUDE_CODE_REMOTE_SLACK_CHANNEL_ID dengan nilai default, seperti "${CCR_SESSION_ACCOUNT_EMAIL:-}". Masing-masing dapat tidak diatur, dan di bawah set -u Bash berhenti dengan unbound variable ketika mengekspansi variabel yang tidak diatur.
Beri tanda kutip pada argumen dan variabel yang diekspansi skrip Anda
Beri tanda kutip pada setiap argumen dan variabel yang diteruskan runner ke wrapper Anda atau hook command. Beberapa nilai berisi karakter yang diproses shell, karena nilai tersebut menyimpan JSON, kalimat utuh, atau aturan izin seperti Bash(npm run test *).
- Argumen: teruskan sebagai
"$@" - Variabel: beri tanda kutip pada masing-masing, seperti dalam
"${CLAUDE_CODE_REMOTE_SLACK_THREAD_URL:-}". Tautan thread Slack, misalnya, dapat berisi?dan&. - String
evaldansh -c: jangan substitusikan nilai ke dalam string yang dijalankanevalataush -c, bahkan di dalam tanda kutip. Buat string tersebut mereferensikan variabelnya sebagai gantinya.
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 yang menggunakan token tersebut 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 9 dan lampirkan kembali secara eksplisit:
exec 9<&0 # the runner uses 3, and from v2.1.309 reserves 4 and 5
"$CLAUDE_RUNNER_CLAUDE_BIN" "$@" <&9 9<&- &
CHILD=$!
trap 'teardown' EXIT
wait "$CHILD"
Anda dapat mengalihkan stdout anak. Jaga file descriptor 3 dan stderr tetap terlampir ke runner:
- File descriptor 3: membawa sinyal aktivitas anak ke runner. Jangan tutup atau gunakan kembali di wrapper.
- File descriptor 4 dan 5: runner pada Claude Code v2.1.309 atau lebih baru mencadangkannya untuk anak. Jangan tutup atau gunakan kembali di wrapper. Jika wrapper Anda menggunakan salah satunya, misalnya untuk menyimpan stdin, pindahkan penggunaan tersebut ke descriptor 6 hingga 9, yang juga dibiarkan bebas oleh versi sebelumnya.
- stderr: ketika wrapper atau anak keluar dengan kode bukan nol, runner mengirimkan baris-baris terakhir stderr ke sesi dan mencetaknya di log-nya sendiri. Pengguna sesi melihat baris-baris tersebut, jadi jangan cetak rahasia ke stderr, dan hapus
set -xsebelum Anda men-deploy wrapper. Jika Anda mengalihkan stderr, sesi tetap berjalan, tetapi runner melaporkan kegagalan hanya dengan exit code.
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 diCLAUDE.mdpada image runner, yang disemai runner ke konfigurasi tingkat pengguna setiap sesi.
Perbaiki Argument list too long saat awal sesi
Di Linux, jika Anda menggabungkan prompt bidang kontrol dan teks Anda sendiri ke dalam satu argumen --append-system-prompt, runner menulis baris seperti ini ke log setelah argumen tersebut mencapai 128 KB:
Session <id> finished: failed — reason: child exited 126: /opt/wrapper.sh: line 9: /usr/local/bin/claude: Argument list too long
Linux membatasi setiap argumen tunggal hingga 32 halaman memori, atau 128 KB dengan ukuran halaman umum 4 KB. Angka dari getconf ARG_MAX, sering kali 2 MB, adalah batas terpisah untuk semua argumen dan lingkungan secara bersamaan.
Pada Claude Code v2.1.283 atau lebih baru, biarkan flag file tetap di "$@" dan teruskan hanya teks Anda sendiri sebagai --append-system-prompt. Claude Code menggunakan keduanya, prompt bidang kontrol terlebih dahulu, lalu teks Anda sendiri:
exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@" --append-system-prompt "$(cat /opt/team-instructions.md)"
Di Linux, jika teks Anda sendiri mencapai 128 KB, pindahkan ke CLAUDE.md pada image runner, karena teks tersebut tetap merupakan satu argumen.
Pada v2.1.281 atau v2.1.282, perintah tersebut keluar dengan Cannot use both --append-system-prompt and --append-system-prompt-file. Pada versi sebelumnya, teks Anda menggantikan prompt bidang kontrol tanpa error. Pada versi mana pun di antaranya, perbarui terlebih dahulu, atau pindahkan teks Anda sendiri ke CLAUDE.md.
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 untuk setiap repositori setiap kali runner memulai sesi, termasuk ketika sesi dilanjutkan atau dimasukkan kembali ke antrean, sebagai pengganti clone dan fetch bawaan runner. Gunakan hook untuk melakukan clone dari mirror read-through yang Anda jangkau melalui HTTPS atau SSH, 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 di-checkout, sebagaimana diminta oleh sesi: branch, tag, commit SHA, atau nama referensi lengkap seperti refs/heads/<branch> atau refs/pull/<number>/head. Kosong berarti branch default repositori. |
CLAUDE_RUNNER_CHECKOUT_PATH |
Jalur absolut tempat working tree harus ditinggalkan. Lihat Nama folder checkout. |
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 |
Surface klien yang membuat sesi, seperti web_claude_ai, desktop_app, atau ios. Tidak diatur ketika sesi tidak memiliki surface yang tercatat atau dikenali, jadi rujuk sebagai "${CLAUDE_RUNNER_CLIENT_PLATFORM:-}" di bawah set -u. 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. |
Skrip harus meninggalkan working tree di CLAUDE_RUNNER_CHECKOUT_PATH yang di-checkout pada revisi yang diminta. Detached HEAD dapat digunakan, karena runner membuat branch kerja sesi di atasnya.
Setelah hook Anda selesai, runner memverifikasi bahwa CLAUDE_RUNNER_CHECKOUT_PATH berisi .git. Jika hook Anda mewujudkan sumber non-git seperti Perforce atau tarball yang telah dibongkar, atur CLAUDE_RUNNER_SKIP_GIT_VERIFY=1 di lingkungan runner untuk melewati pemeriksaan itu. Alur berbasis Git seperti pembuatan branch kerja dan push hasil memerlukan checkout git, jadi ekspor hasil dari tree non-git dengan hook post-session.
Nama folder checkout
CLAUDE_RUNNER_CHECKOUT_PATH diakhiri dengan folder bernama <repo-owner>-<repo>, seperti acme-api. Dua repositori dalam satu sesi tidak dapat berbagi folder, bahkan ketika namanya hanya berbeda pada huruf besar/kecil. Misalnya, jika sesi mencantumkan acme/api lalu Acme/API, yang pertama mendapatkan acme-api dan yang kedua mendapatkan Acme-API-2.
Mendapatkan kredensial git di dalam hook
Runner tidak meneruskan kredensial git ke hook. Subperintah decode-token juga tidak tersedia di sini, karena CLAUDE_RUNNER_CLAUDE_BIN tidak diatur di lingkungan checkout-hook. Sebagai gantinya, buat kredensial clone per-sesi dari identitas sesi, atau gunakan autentikasi git milik host sendiri sebagai alternatif:
- Kredensial clone per-sesi: verifikasi
CLAUDE_CODE_SESSION_ACCESS_TOKENdengan library JWT standar terhadap endpoint JWKS di bawahCLAUDE_RUNNER_API_BASE_URL, seperti yang dijelaskan dalam Verify the token from your service. Kemudian minta layanan kredensial Anda menerbitkan kredensial clone berumur pendek untuk identitas dalam klaimacttoken. Kaitkan kredensial tersebut denganact.sub, dan jangan mewajibkanact.email. - Autentikasi git host: gunakan autentikasi git apa pun yang sudah dimiliki host, seperti SSH agent, credential helper, atau
.netrc.
Ketika hook gagal
Hook gagal ketika keluar dengan kode bukan nol, atau keluar dengan 0 tanpa meninggalkan checkout yang dapat digunakan:
-
Repositori tempat sesi melakukan push hasil: runner menggagalkan sesi, dan pada keluar dengan kode bukan nol menampilkan bagian akhir stderr skrip kepada pengguna. Tulis alasannya ke stderr sebagai satu baris singkat, tulis di bagian paling akhir, lalu keluar dengan kode bukan nol. Jangan mencetak rahasia ke stderr, dan hapus
set -xsebelum Anda men-deploy hook.Jika
couldn't find remote refdari git muncul dalam kira-kira 4 KB terakhir stderr hook, runner melaporkan branch yang telah dihapus sebagai gantinya. Pesan error sesi kemudian menyatakan bahwa branch tersebut tidak lagi ada di remote dan meminta pengguna untuk memulihkannya. Jika branch tersebut ada, lihat Sesi gagal denganno longer exists on the remoteataucouldn't find remote ref. -
Repositori yang hanya dibaca sesi, seperti repositori yang ditambahkan ke sesi yang sedang berjalan: runner melewatinya dan melanjutkan dengan repositori yang tersisa. Runner memposting langkah
Skippedke sesi dan menghapus apa pun yang ditinggalkan hook di jalur checkout.Ketika langkah
Skippedmenyebutkan checkout hook, temukan detail kegagalan pada baris[runner:warn] checkout hook faileddi log runner. Ketika langkah tersebut menyatakan bahwa ref sumberno longer exists on remote, lihat Sesi gagal denganno longer exists on the remoteataucouldn't find remote ref. Jika proses melewati ini membuat sesi tidak memiliki repositori sama sekali, runner tetap menggagalkan sesi.
Ketika hook berhasil, runner menghapus jalur checkout setelah sesi berakhir.
post-session
Berjalan setelah anak Claude Code keluar dan sebelum runner merobohkan workspace. Hook ini adalah satu-satunya kesempatan Anda untuk menyimpan pekerjaan yang belum di-commit, karena perubahan terlacak yang belum di-commit tidak bertahan di jalur mana pun:
- Pada
--capacitydi atas satu: runner menghapus worktree per-sesi tepat setelah hook kembali - Pada
--capacity 1: klon kanonik yang digunakan kembali di-hard-reset ketika sesi berikutnya dimulai
Penggunaan khas adalah melakukan push branch snapshot dari perubahan yang belum di-commit, mengarsipkan log, atau memancarkan event 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 |
Surface klien yang membuat sesi, seperti web_claude_ai, desktop_app, atau ios. Tidak diatur ketika sesi tidak memiliki surface yang tercatat atau dikenali, jadi rujuk sebagai "${CLAUDE_RUNNER_CLIENT_PLATFORM:-}" di bawah set -u. 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 keluar dengan sendirinya setelah sesi diarsipkan atau dihapus.failed: proses Claude Code mogok, atau setup gagal setelah dimulai.interrupted: runner menghentikan sesi, dalam salah satu kasus berikut:- Runner melepaskan sesi untuk membebaskan slot.
- Sesi mengalami timeout saat startup.
- Server memindahkan sesi dari runner ini.
- Polling runner mendeteksi pengarsipan atau penghapusan sebelum proses keluar.
- Runner sedang melakukan drain.
- Sesi melampaui batas
--kill-session-after-minmiliknya.
abandoned: dicadangkan untuk sesi yang diklaim runner lain. Hook saat ini tidak menyala dalam kasus itu.
Jika Anda membandingkan catatan penerimaan hook dengan penghitung siklus hidup sesi, perkirakan beberapa catatan interrupted dihitung sebagai completed di sana. Penghitung menghitung pelepasan, timeout startup, perpindahan server, serta pengarsipan atau penghapusan yang lebih dulu dideteksi polling runner sebagai completed, karena runner mengembalikan slot dengan bersih.
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
export GIT_ALLOW_PROTOCOL=${GIT_ALLOW_PROTOCOL:-https:http:ssh}
IFS=':'
rc=0 # becomes 1 when a step fails; a non-zero exit makes the runner log it
# -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; see the note below the script
# before you give this push a credential.
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" || { rc=1; continue; }
g add -A || rc=1
g diff-index --cached --quiet HEAD && continue # nothing to save
g commit -q -m "runner snapshot: $CLAUDE_RUNNER_SESSION_ID ($CLAUDE_RUNNER_EXIT_REASON)" || { rc=1; continue; }
g push -q origin "HEAD:refs/heads/rescue/$CLAUDE_RUNNER_SESSION_ID" || rc=1
done
exit "$rc"
Baris GIT_ALLOW_PROTOCOL dalam skrip membatasi git pada remote HTTPS, HTTP, dan SSH. Jika lingkungan runner sudah menetapkan daftar GIT_ALLOW_PROTOCOL sendiri yang tidak kosong, skrip mempertahankan daftar tersebut.
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.
Perlakukan setiap kredensial yang diberikan hook Anda kepada git sebagai kredensial yang dapat diperoleh sesi, dan buat kredensial tersebut sehingga tidak dapat melakukan lebih dari push ini. Git di hook Anda membaca file konfigurasi yang dapat ditulis oleh sesi, dan credential helper atau filter driver yang disebutkan di salah satunya berjalan dengan hak istimewa hook Anda. Pengaturan dalam file tersebut juga dapat mengubah tujuan push, remote apa pun yang Anda sebutkan. Untuk pengaturan git yang ditetapkan runner di hook Anda dan pengaturan yang diserahkan ke file tersebut, lihat Konfigurasi Git di dalam lifecycle hooks.
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.hooksPathadalah/dev/null, sehingga git melewati hook di.git/hooksrepositori dan direktori hook apa pun yang disebutkan~/.gitconfig. Untuk menyediakan nilai, eksporcore.hooksPathsebagai pasanganGIT_CONFIG_KEY_n/GIT_CONFIG_VALUE_ndi lingkungan runner. Runner juga membacacore.hooksPathdari 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.fsmonitorkosong, sehingga git di hook Anda tidak menjalankan program monitor yang disebutkan oleh file konfigurasi. - Protokol remote:
GIT_ALLOW_PROTOCOLadalahhttps:http:ssh. Clone, fetch, atau push yang menggunakan jalur lokal, URLfile://, atau URLgit://gagal denganfatal: transport 'file' not allowedataufatal: transport 'git' not allowed. - Perintah SSH dan prompt kredensial: git di hook Anda mengabaikan
core.sshCommanddancore.askPassdari file konfigurasi. Untuk menggunakan perintah SSH Anda sendiri, aturGIT_SSH_COMMANDdi lingkungan runner. Untuk menggunakan program prompt kredensial, aturGIT_ASKPASSdi 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, dangpg.ssh.programadalah 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.gpgsigndantag.gpgsignbernilaifalse.
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_nyang Anda ekspor di lingkungan runner menggantikan nilai runner untuk kunci yang sama. Beri nomor pasangan Anda mulai dari0dan aturGIT_CONFIG_COUNTke 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, danGIT_ASKPASSsebagaimana Anda mengaturnya di lingkungannya. - Opsi
git -c: opsigit -cdi dalam hook menimpa pasanganGIT_CONFIG_KEY_n, baik milik runner maupun milik Anda. Opsi ini tidak mengubahGIT_ALLOW_PROTOCOL,GIT_SSH_COMMAND, atauGIT_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 setelah checkout setiap kali sesi dimulai atau dilanjutkan, 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 tersebut diutamakan 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 menjadi tujuan permintaan ini, sebagai ID session_... di URL sesi. 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 |
Penghitung per sesi untuk digunakan dalam logging. Ini bukan jumlah retry maupun jumlah permintaan. 0 untuk permintaan pre-warming, meskipun permintaan untuk suatu sesi juga dapat membawa 0. |
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 mengantrikan sesi. Gunakan untuk routing per-akun, kuota, atau chargeback, bukan sebagai kunci dedup. Kosong ketika tidak tersedia. |
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: branch, SHA, tag, atau nama referensi lengkap. 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. |
CLAUDE_RUNNER_SPAWN_REASON |
Alasan Anthropic mengirim permintaan pemijahan ini, seperti runner_lost atau resumed. Tidak diatur ketika permintaan tidak membawa alasan yang terbentuk dengan baik, jadi baca sebagai "${CLAUDE_RUNNER_SPAWN_REASON:-}". Memerlukan Claude Code v2.1.318 atau yang lebih baru di orchestrator |
Runner yang dipijahkan mendaftarkan dengan pesanan kerja sebagai pengganti rahasia lingkungan:
- Mulai dengan pesanan kerja: arahkan
--environment-secret-fileke file yang berisi JWT pesanan kerja, atau aturSELF_HOSTED_RUNNER_ENVIRONMENT_SECRETke 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 1di 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, di platform mana pun hook Anda melakukan provisioning:
-
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 padaCLAUDE_RUNNER_SESSION_IDsebagai gantinya. Setiap permintaan ulang untuk sesi membawa ID sesi yang sama dengan order ID baru. Beban kerja yang dinamai atau dideduplikasi oleh ID sesi tidak dibuat lagi selama beban kerja pertama masih ada. -
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. -
Gunakan kontrak exit code. Keluar dengan status yang sesuai dengan hasilnya:
- Keluar 0: dikirimkan.
- Keluar 1, atau masih berjalan saat
--hook-timeoutberakhir: kegagalan yang dapat dicoba ulang. Sesi mundur dan ditawarkan kembali. Setelah 10 permintaan gagal berturut-turut, sesi diblokir seperti setelah keluar 2 atau lebih tinggi. - Keluar 2 atau lebih tinggi: kegagalan yang tidak dapat dicoba ulang. Sesi diblokir dari pemijahan lagi sampai pengguna mengirimkan pesan baru ke sesi tersebut atau Owner memilih Retry di atasnya di tab Activity lingkungan.
Selama sesi mundur, sesi tersebut menampilkan Queued di tab Activity tanpa kesalahan pemijahan, jadi baca output hook di log orchestrator. Setelah diblokir, sesi menampilkan Failed. Perluas sesi tersebut untuk membaca kesalahan pemijahan, yang diambil dari 512 karakter terakhir stderr hook. Tulis kesalahan yang dapat ditindaklanjuti ke stderr, dan jangan sertakan rahasia. Dalam hook shell, pertahankan kegagalan sementara agar dapat dicoba ulang.
Permintaan pre-warming tidak memiliki sesi untuk gagal: orchestrator mencatat keluar bukan nol secara lokal saja, dan server meminta ulang pemijahan setelah sewa
--expected-spawn-secondskedaluwarsa. -
Atur
--expected-spawn-secondske setidaknya waktu p99 Anda dari permintaan pemijahan hingga pendaftaran runner. Ukur sejak orchestrator menerima permintaan pemijahan, dan sertakan waktu tunggu untuk kapasitas di platform Anda serta waktu boot. Nilai ini adalah sewa server-side, dan pesanan kerja kedaluwarsa bersamanya, sehingga runner yang beban kerjanya membutuhkan waktu lebih lama tidak dapat mendaftar. 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.
Perbaiki sesi yang dibuka kembali yang tetap Queued atau menampilkan Failed
Jika sesi yang dibuka kembali tetap Queued atau menampilkan Failed, atau hook Anda mengalami timeout pada sesi tersebut, salah satu penyebabnya adalah hook yang menamai atau mendeduplikasi beban kerja berdasarkan ID sesi. Sesi dibuka kembali ketika penggunanya mengirim pesan ke sesi tersebut setelah runner-nya keluar. Sebagai contoh, ambil hook yang membangun nama setiap beban kerja seperti ini:
name="runner-${CLAUDE_RUNNER_SESSION_ID}"
- Pada permintaan pemijahan pertama sesi, hook membuat beban kerja dengan nama tersebut, dan sebuah runner dimulai
- Runner keluar, misalnya setelah
--release-idle-session-min, dan pengguna kemudian membuka kembali sesi tersebut - Permintaan pemijahan berikutnya untuk sesi tersebut membawa ID sesi yang sama dan order ID baru, sehingga hook membangun nama yang sama
- Jika platform Anda masih memiliki beban kerja dari permintaan pertama, karena tidak ada yang menghapusnya ketika runner-nya keluar, hook tidak membuat beban kerja baru dan tidak ada runner yang dimulai
- Setiap permintaan pemijahan berikutnya untuk sesi tersebut berakhir dengan cara yang sama
Buka hook Anda sendiri dan baca apa yang dilakukannya ketika beban kerja sudah ada, misalnya ketika platform Anda menolak nama duplikat. Temukan perilaku tersebut di kolom pertama untuk melihat apa yang ditampilkan sesi yang dibuka kembali di tab Activity:
| Ketika beban kerja sudah ada, hook Anda | Queued berubah menjadi Failed | Kesalahan pemijahan |
|---|---|---|
| Keluar dengan 0 tanpa membuat apa pun | Setelah 10 permintaan semacam itu berturut-turut | runner did not register within ETA after max attempts |
| Keluar dengan 1 | Setelah 10 permintaan semacam itu berturut-turut | Bagian akhir stderr hook Anda, atau exit 1 jika hook tidak menulis apa pun ke stderr |
| Keluar dengan 2 atau lebih tinggi | Segera | Bagian akhir stderr hook Anda, atau exit beserta kodenya, seperti exit 2, jika hook tidak menulis apa pun ke stderr |
Menunggu sampai --hook-timeout berakhir |
Setelah 10 permintaan semacam itu berturut-turut | Bagian akhir stderr hook Anda, atau spawn-runner hook timed out after 60000ms jika hook tidak menulis apa pun ke stderr |
Dalam kesalahan pemijahan di baris terakhir, 60000 adalah --hook-timeout Anda dalam milidetik. Ketika hook menunggu sampai --hook-timeout berakhir, log orchestrator juga mendapatkan baris timed out after.
Untuk memperbaiki hook, bangun setiap nama atau kunci dari CLAUDE_RUNNER_ORDER_ID, seperti yang diwajibkan aturan 1 kontrak hook. Dalam contoh tersebut, namanya menjadi:
name="runner-${CLAUDE_RUNNER_ORDER_ID}"
Jika hook Anda menunggu, buat juga hook tersebut keluar setelah mengirimkan beban kerja, karena hook yang menunggu menunda hook berikutnya dari orchestrator-nya. Setelah Anda mengubah hook, periksa status setiap sesi yang macet di tab Activity:
- Queued: sesi mendapatkan permintaan pemijahan berikutnya dengan sendirinya
- Failed: pilih Retry pada sesi tersebut sebagai Owner, atau minta penggunanya mengirim pesan baru
Buat runner on-demand keluar saat idle
Secara default, runner yang dimulai oleh hook Anda tidak keluar ketika giliran sesinya selesai, sehingga beban kerja yang Anda kirimkan tetap berjalan. Agar runner keluar, minta hook Anda meneruskan flag runner berikut:
| Flag, dengan contoh nilai | Teruskan ke | Fungsinya |
|---|---|---|
--release-idle-session-min 30 |
Setiap runner yang dimulai oleh hook Anda | Runner melepaskan sesinya setelah idle selama jumlah menit tersebut lalu keluar dengan 0. Sesi dilanjutkan ketika penggunanya mengirim pesan berikutnya, tetapi pekerjaan yang belum di-push akan hilang. Tahan sesi untuk proses yang dibiarkannya tetap berjalan. |
--kill-session-after-min 480 |
Setiap runner yang dimulai oleh hook Anda | Membatasi setiap sesi hingga jumlah menit tersebut, sebagai cadangan untuk sesi yang tidak dihitung sebagai idle, jadi atur nilainya di atas sesi terpanjang yang Anda perkirakan |
--exit-if-unused-min 60 |
Runner standby, yang dimulai oleh hook Anda untuk permintaan pre-warming | Runner standby yang tidak pernah mengambil sesi akan keluar setelah jumlah menit tersebut. Jika Anda mengatur --min-idle, Anda akan mendapatkan pengganti kecuali standby tersebut berlebih, jadi gunakan nilai yang jauh di atas waktu boot Anda. |
Hapus beban kerja ketika runner keluar, misalnya dengan mengatur ttlSecondsAfterFinished pada Kubernetes Job atau dengan membuat skrip start VM menghapus VM tersebut setelahnya.
Kirim output proses latar belakang ke file
Jika hook spawn-runner Anda membiarkan suatu proses berjalan di latar belakang, kirim output proses tersebut ke file yang dapat dibuat oleh pengguna hook. Misalnya, tambahkan >> "$HOME/claude-runner.log" 2>&1 ke perintah yang memulai proses tersebut, sebelum & apa pun. Baris hook ini menunjukkan letak pengalihan tersebut, dengan your-background-command sebagai pengganti perintah yang dijalankan hook Anda di latar belakang:
your-background-command >> "$HOME/claude-runner.log" 2>&1 &
Tanpa pengalihan, proses menulis ke stdout dan stderr hook:
- Sekitar satu detik setelah hook keluar, orchestrator mencatat
exited, but a process it started still has the hook's stdout or stderr open. Sejak baris tersebut, orchestrator mencatat hingga 64 KB tambahan dari output tersebut di bawah[runner:hook:spawn-runner:after-exit jti=<order id>]lalu membuang sisanya. - Setelah orchestrator keluar, penulisan proses ke sana akan gagal.
Runner yang dimulai sendiri oleh hook Anda menjalankan sesi di host orchestrator, yang menyimpan rahasia lingkungan.
Sebelum v2.1.305, hook yang sudah keluar tetap mencapai --hook-timeout selama proses yang dimulainya masih membuka stdout atau stderr hook, yang ditampilkan sebagai:
- Tab Activity, dalam kesalahan pemijahan sesi yang gagal:
spawn-runner hook timed out after 60000ms, atau baris-baris terakhir di stderr hook. - Log orchestrator:
timed out after 60000ms, sending SIGTERM to process tree.
Keluar dari hook tanpa menunggu runner
Jika setiap runner on-demand berhenti sekitar 60 detik setelah hook-nya dimulai, dan hook Anda berjalan lagi untuk sesi yang sama, periksa apakah hook menunggu runner. 60 detik tersebut adalah --hook-timeout default, jadi jika Anda mengatur nilai sendiri, runner berhenti selama waktu tersebut setelah hook dimulai.
Hook yang memulai claude self-hosted-runner di host orchestrator dan menunggunya, di foreground atau dengan wait, masih berjalan ketika --hook-timeout berakhir. Orchestrator kemudian menghentikan hook beserta proses yang berjalan di bawahnya, termasuk runner tersebut dan proses Claude milik sesi, sehingga giliran yang masih berjalan akan terputus.
- Untuk mengonfirmasi bahwa timeout-lah yang menghentikan runner Anda: cari
timed out after 60000ms, sending SIGTERM to process treedi log orchestrator pada setiap permintaan pemijahan.60000adalah--hook-timeoutAnda dalam milidetik. - Untuk memperbaikinya: minta hook mengirimkan runner ke platform Anda dan keluar dengan 0. Jika Anda menaikkan
--hook-timeoutsebagai gantinya, runner hanya berhenti lebih lambat.
Pertahankan kegagalan sementara agar dapat dicoba ulang dalam hook shell
Dalam hook shell yang menggunakan set -e, kegagalan yang sebenarnya dapat teratasi dengan retry dapat memblokir sesi. Hook berhenti pada perintah yang gagal dan keluar dengan status milik perintah itu sendiri, dan orchestrator menerapkan kontrak exit code pada status tersebut. Banyak kegagalan mengembalikan status 2 atau lebih tinggi, seperti 127 ketika suatu perintah tidak terinstal dan 22 dari curl --fail pada kesalahan HTTP, sehingga kegagalan tersebut memblokir sesi pada kegagalan pertamanya.
Sesi yang telah diblokir oleh hook tetap diblokir sampai pengguna mengirimkan pesan baru ke sesi tersebut atau Owner memilih Retry di atasnya di tab Activity lingkungan.
Untuk mengubah kegagalan semacam itu menjadi keluar 1, letakkan baris-baris berikut tepat di bawah baris #! hook, di atas apa pun yang dapat gagal:
set -e
PERMANENT=; permanent() { printf '%s\n' "$*" >&2; PERMANENT=1; exit 2; }
trap 'rc=$?; [ "$rc" -eq 0 ] || [ -n "${PERMANENT:-}" ] || exit 1' EXIT
Baris-baris ini mengubah perilaku bagian hook lainnya, jadi periksa hook untuk setiap pola berikut setelah Anda menambahkannya:
-
exit 2atau lebih tinggi secara langsung: dengan trap terpasang, ini menjadi keluar 1. Untuk kesalahan yang tidak dapat diperbaiki oleh retry apa pun, panggilpermanentdengan alasannya sebagai gantinya, sepertipermanent "namespace claude-runners does not exist". Panggil di shell utama, bukan di dalam$( ),( ), atau pipe. -
exec: jangan memulai perintah terakhir hook denganexec, karenaexecmenggantikan shell dan trap tidak berjalan. -
Trap
EXITkedua:trap ... EXITkedua menggantikan yang pertama, jadi gabungkan keduanya menjadi satu trap. Letakkan perintah pembersihan Anda tepat setelahrc=$?;dan akhiri masing-masing dengan|| true;. Pembersihan kemudian berjalan saat gagal maupun saat berhasil, dan perintah pembersihan yang gagal tidak menetapkan status keluar hook. Trap gabungan ini menunjukkan bentuknya, denganyour-cleanup-commandsebagai pengganti perintah Anda sendiri:trap 'rc=$?; your-cleanup-command || true; [ "$rc" -eq 0 ] || [ -n "${PERMANENT:-}" ] || exit 1' EXIT -
Perintah yang boleh gagal: jika hook sebelumnya tidak menggunakan
set -e, kini hook berhenti pada perintah pertama yang mengembalikan nilai bukan nol, seperti pencarian yang tidak menemukan apa pun. Jika hook bertindak berdasarkan hasilnya, jadikan perintah tersebut kondisi dari sebuahif. Jika hook mengabaikan hasilnya, tambahkan|| truesetelah perintah tersebut. -
Perintah yang mengirimkan beban kerja: jangan tambahkan
|| truesetelahnya. Dengan|| true, hook keluar dengan 0 bahkan ketika platform Anda menolak pengiriman, misalnya karena melebihi kuota, dan sesi menunggu runner yang tidak pernah dimulai.Beri nama beban kerja berdasarkan
CLAUDE_RUNNER_ORDER_ID, seperti yang dinyatakan aturan pertama hook. Jawaban already-exists kemudian merupakan pengiriman ulang permintaan yang sama, jadi perlakukan hanya jawaban tersebut sebagai keberhasilan. Untuk jawaban lainnya, cetak jawaban tersebut ke stderr dan keluar dengan 1, atau panggilpermanentjika tidak ada retry yang dapat memperbaikinya. Contoh ini melakukan hal tersebut untukkubectl create, denganjob.yamlyang hanya berisi beban kerja:if ! out=$(kubectl create -f job.yaml 2>&1); then case "$out" in *AlreadyExists*) ;; *) printf '%s\n' "${out:-kubectl create failed}" >&2; exit 1 ;; esac fiLog orchestrator kemudian menampilkan pesan tersebut. Jika hook membuat objek lain, beri nama masing-masing berdasarkan order ID, berikan perintahnya sendiri, dan perlakukan jawaban already-exists-nya sebagai keberhasilan dengan cara yang sama. Untuk objek yang menyimpan rahasia, cetak pesan tetap sebagai pengganti output perintah.
Untuk mengonfirmasi bahwa trap berfungsi, tambahkan baris tepat di bawah baris trap yang memanggil perintah yang tidak ada, seperti no-such-command. Jalankan file hook dari shell Anda dan periksa bahwa echo $? mencetak 1, lalu hapus baris tersebut.
Cari tahu mengapa permintaan pemijahan dikirim
Hook Anda menerima alasan setiap permintaan pemijahan di CLAUDE_RUNNER_SPAWN_REASON, sehingga Anda dapat mencatat mengapa hook berjalan, bersama dengan CLAUDE_RUNNER_SESSION_UUID. Memerlukan Claude Code v2.1.318 atau yang lebih baru di orchestrator. Baca variabel ini sebagai "${CLAUDE_RUNNER_SPAWN_REASON:-}", karena variabel ini dapat tidak diatur, dan mulai runner apa pun nilainya. Jika hook Anda bercabang berdasarkan variabel ini, pertahankan cabang default, karena Anthropic dapat menambahkan nilai baru.
Untuk menemukan alasan permintaan sebelumnya, cari session= dan CLAUDE_RUNNER_SESSION_UUID milik sesi tersebut, bukan ID session_...-nya, di log setiap replika orchestrator. Baris ok session= dan nack session= menampilkan nilai CLAUDE_RUNNER_SPAWN_REASON setelah reason=, atau reason=- jika variabel tersebut tidak diatur:
[runner:orchestrator] ok session=: hook keluar dengan 0[runner:orchestrator] nack session=: orchestrator melaporkan permintaan pemijahan yang gagal ke Anthropic
Setiap nilai hanya memberikan penyebab terakhir yang dicatat Anthropic untuk sesi tersebut, bukan riwayatnya:
| Nilai | Apa yang terjadi | Apa yang perlu diperiksa |
|---|---|---|
session_created |
Ini adalah permintaan pemijahan pertama untuk sesi baru | Tidak ada. Jangan mengandalkannya untuk mengenali permintaan pertama suatu sesi, karena permintaan pertama yang dilihat hook Anda dapat membawa runner_lost setelah runner fleet tetap atau runner standby memegang sesi tersebut. |
no_runner_registered |
Tidak ada runner yang mendaftar untuk permintaan sebelumnya dalam --expected-spawn-seconds, dan Anthropic tidak mencatat laporan kegagalan untuknya |
stop: no nack for session= atau nack failed session= di antara baris-baris sesi sebelumnya berarti tidak ada laporan kegagalan yang dicatat. Baca kesalahan hook apa pun sebelumnya, dan jika orchestrator sedang berhenti, lihat Hentikan atau mulai ulang orchestrator. Setelah baris ok session=, periksa bahwa runner-nya dimulai dan mendaftar dalam --expected-spawn-seconds. Jika tidak ada replika yang mencatat permintaan tersebut, periksa orchestrator Anda untuk restart dan baris poll failed. |
hook_failed |
Orchestrator Anda melaporkan bahwa permintaan sebelumnya gagal dan dapat dicoba ulang, misalnya karena hook Anda keluar dengan 1 atau masih berjalan saat --hook-timeout berakhir. Nilainya sama ketika orchestrator Anda mengembalikan permintaan ke Anthropic tanpa menjalankan hook, misalnya saat orchestrator sedang berhenti. |
Temukan baris [runner:orchestrator] nack session= atau stop: handing back session= terakhir milik sesi tersebut sebelum permintaan ini. Kesalahan pemijahan yang dikirim orchestrator mengikuti nilai reason= dan dapat berlanjut ke baris-baris berikutnya. Baris handing back session= berarti orchestrator sedang berhenti dan tidak menjalankan hook, sehingga tidak ada yang perlu diperbaiki pada hook. |
runner_lost |
Runner yang memegang sesi berhenti melapor ke Anthropic, membatalkan pendaftaran, atau dihapus | Temukan apa yang menghentikan runner atau memutusnya dari Anthropic, seperti eviction node, kill karena kehabisan memori, spot reclaim, atau rollout atau scale-down Anda sendiri |
runner_reported_failure |
Runner sebelumnya melaporkan bahwa sesi gagal di runner tersebut, misalnya saat checkout | Baca log runner sebelumnya. Jika banyak sesi mendapatkan nilai ini, periksa image dan penyiapan repositori. |
resumed |
Sesi dijeda dan kini membutuhkan runner lagi. Misalnya, runner-nya melepaskan sesi tersebut karena --release-idle-session-min, lalu kemudian sesi menerima pesan baru. |
Tidak ada |
requeued |
Sesi dimasukkan kembali ke antrian, misalnya ketika Owner memilih Retry atau sesi yang diblokir menerima pesan baru | Jika sesi yang sama terus mendapatkan nilai ini, baca kesalahan di baris nack session=-nya. Jika hook-nya keluar dengan 0, baca log runner. |
server_retry |
Anthropic menyerah pada permintaan sebelumnya atau pada memulai sesi di runner-nya, dan sedang mencoba ulang. Hook Anda tidak gagal. | Tidak ada untuk satu permintaan. Jika nilai ini terus muncul, pada satu sesi atau di banyak sesi, laporkan ke tim akun Anthropic Anda. |
prewarm |
Ini adalah permintaan pre-warming, belum ada sesi | Tidak ada |
Hentikan atau mulai ulang orchestrator
Untuk menghentikan atau memulai ulang orchestrator tanpa memutus hook spawn-runner yang sedang berjalan, mulai orchestrator dengan --shutdown-drain-sec. Saat menerima SIGTERM, orchestrator kemudian memberi hook yang sedang berjalan waktu hingga jumlah detik yang Anda atur untuk selesai. Untuk memilih nilainya dan menyiapkan process supervisor Anda agar sesuai, lihat Tentukan ukuran --shutdown-drain-sec dan stop timeout orchestrator.
Perintah start ini memberikan waktu 70 detik:
claude self-hosted-runner orchestrator \
--environment-secret-file /etc/claude/environment-secret \
--hooks-dir /etc/claude/hooks \
--shutdown-drain-sec 70
Orchestrator keluar setelah polling yang sudah dikirim dan setiap hook selesai. Ketika detik --shutdown-drain-sec habis, hook yang masih berjalan menerima SIGTERM dan orchestrator keluar. SIGTERM kedua atau SIGINT apa pun melakukan hal yang sama tanpa menunggu. Baris log yang berisi [runner:orchestrator] stop summary: menghitung hook dan permintaan pemijahan berdasarkan cara masing-masing berakhir.
Tanpa --shutdown-drain-sec, orchestrator langsung berhenti saat menerima SIGTERM atau SIGINT. Setiap hook yang sedang berjalan menerima SIGTERM, dan Anthropic tidak diberi tahu apa pun. Sesi yang runner-nya tidak pernah dimulai kemudian dapat tetap berada di antrian sampai Anthropic memintanya ulang, yang terjadi setelah --expected-spawn-seconds berlalu, 120 detik secara default.
[!NOTE]
--shutdown-drain-secmemerlukan Claude Code v2.1.299 atau yang lebih baru. Versi yang lebih lama keluar saat startup denganerror: unknown flag --shutdown-drain-sec.
Tentukan ukuran --shutdown-drain-sec dan stop timeout orchestrator
Pada pengaturan default, gunakan --shutdown-drain-sec 70 dengan stop timeout 85 detik, yaitu terminationGracePeriodSeconds: 85 di Kubernetes atau TimeoutStopSec=85 di bawah systemd. Jika Anda mengubah --hook-timeout, sesuaikan keduanya:
--shutdown-drain-sec: setidaknya--hook-timeoutditambah 10 detik, sehingga hook yang dimulai tepat sebelumSIGTERMmasih dapat selesai- Stop timeout: setidaknya
--shutdown-drain-secditambah 15 detik. Orchestrator mencatat angka ini saat startup: cari baris yang berisishutdown drain:.
Minta process supervisor Anda mengirim satu SIGTERM saja, hanya ke proses orchestrator, karena SIGTERM kedua memotong waktu tunggu --shutdown-drain-sec. Jika Anda membiarkan KillMode=control-group default milik systemd, hook juga menerima SIGTERM, dan hook dapat mati sebelum mengirimkan beban kerjanya. Sebelum Anda mengubah KillMode, periksa cara hook spawn-runner Anda memulai runner:
- Jika hook Anda mengirimkan beban kerjanya ke platform lalu keluar: jalankan orchestrator sebagai proses utama unit dan atur
KillMode=mixed, yang mengirimSIGTERMhanya ke proses tersebut. - Jika hook Anda memulai runner sebagai proses di dalam unit milik orchestrator sendiri:
KillMode=mixedtidak mengirimSIGTERMke runner tersebut. Sebaliknya, systemd kemudian mengirimi runner tersebutSIGKILL, sehingga runner keluar tanpa menuntaskan sesinya atau menjalankan hookpost-session-nya.
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:InvokeModeldanbedrock:InvokeModelWithResponseStreampada 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.
[!WARNING]
Siapa pun yang dapat menjalankan kode di dalam sesi, termasuk melalui prompt injection, dapat menggunakan kredensial ini dengan biaya yang Anda tanggung selama kredensial tersebut masih valid. Claude Code berjalan di dalam sesi, sehingga kredensial yang digunakannya untuk memanggil model harus dapat dibaca di sana.
Perintah shell yang dijalankan Claude, hook Claude Code Anda, dan server MCP stdio mewarisi environment sesi dan berjalan sebagai pengguna yang sama dengan Claude Code. Akibatnya, semuanya dapat membaca variabel kredensial dan file kredensial.
Saat Anda memperkuat deployment Anda, Anda menjauhkan kredensial host dari sesi, tetapi Anda tidak dapat menjauhkan kredensial ini. Jangan berikan identitas di baliknya apa pun selain kebijakan atau peran dari langkah 1.
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: nilai kredensial yang diekspor oleh skrip wrapper Anda tidak diperbarui selama Claude Code berjalan, jadi untuk sesi yang panjang, gunakan metode yang memperbarui dirinya sendiri. 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.
[tab] Amazon Bedrock
Ganti region dengan milik Anda:
export CLAUDE_CODE_USE_BEDROCK=1 export AWS_REGION=us-east-1Untuk 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.
[tab] Google Cloud's Agent Platform
Ganti region dan ID proyek dengan milik Anda:
export CLAUDE_CODE_USE_VERTEX=1 export CLOUD_ML_REGION=global export ANTHROPIC_VERTEX_PROJECT_ID=YOUR-PROJECT-IDUntuk memilih region, lihat Konfigurasi 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.
- Skill akun: sesi ini tidak mengunduh skill yang diaktifkan untuk akun claude.ai seseorang. Lihat Bagaimana konfigurasi setiap sesi disusun.
- 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. Anda tidak dapat memilih model dengan
ANTHROPIC_MODELatauANTHROPIC_DEFAULT_MODELdi environment runner, tetapi Anda dapat menyematkan ke mana sebuah alias diarahkan:ANTHROPIC_MODELdanANTHROPIC_DEFAULT_MODEL: runner menghapus keduanya dari environment yang diteruskannya ke sesi, meskipun contoh di halaman penyedia menetapkanANTHROPIC_MODEL.- Variabel penyematan per keluarga model: variabel di Menyematkan versi model untuk Amazon Bedrock dan Agent Platform memang sampai ke sesi. Variabel tersebut menentukan ke mana alias seperti
opusdiarahkan, 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.jsonpada host runner Linux,/Library/Application Support/ClaudeCode/managed-mcp.jsonpada 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 logdebug. Sebelum v2.1.229, sesi tersebut keluar saat startup denganYou cannot dynamically configure MCP servers when an enterprise MCP config is present. - Kunci
managedMcpServersdalam 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. Di luar thread project, lakukan commit file ini ke repositori, dan server-servernya disetujui secara otomatis di sesi cloud. Dalam sesi dengan beberapa repositori, paling banyak file dari satu repositori yang dimuat.- File
--mcp-configdari skrip wrapper Anda: tambahkan flag tersebut setelah"$@", seperti padaexec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@" --mcp-config /etc/claude/mcp.json. Agar server dari file tersebut tetap berjalan ketika server yang dikirimkan ke sesi diperbarui di tengah sesi, pastikan wrapper Anda meneruskan environment lengkap ke proses anak, seperti yang dilakukanexec. Sebelum v2.1.299, server stdio, HTTP, dan SSE dalam file tersebut berhenti pada pembaruan pertama semacam itu yang tidak menyebutkan namanya.
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.
[!NOTE]
Dalam sesi yang menerima konektor, server dengan nama yang sama dari
.claude.jsonmilik host atau.mcp.jsonmilik repositori tidak akan terhubung. Untuk menggunakan keduanya, beri server Anda nama yang berbeda.
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.
Menunggu server MCP sebelum giliran pertama
Sesi self-hosted menunggu sebentar server MCP yang masih dalam proses terhubung, pada dua titik terpisah. Server yang terlewat dari suatu penantian akan kehilangan tool-nya saat giliran pertama dimulai, dan tool tersebut akan tersedia kemudian tanpa tindakan apa pun dari Anda. Kedua penantian tersebut adalah:
- Startup sesi: sebelum daftar tool pertama kali diambil, sesi menunggu hingga 5 detik secara default untuk server HTTP atau SSE yang entrinya mengatur
alwaysLoad: true, atau untuk semua server ketika Anda mengaturMCP_CONNECTION_NONBLOCKING=0di environment runner. Jika tidak, server HTTP dan SSE terhubung di latar belakang. Selama sesi menunggu di sini, inisialisasinya menjadi lebih lambat.MCP_CONNECT_TIMEOUT_MSmengubah default 5 detik tersebut. - Giliran pertama: setelah pesan tiba, giliran pertama menunggu hingga 2 detik untuk server stdio yang masih dalam proses terhubung. Selama sesi menunggu di sini, balasan pertama menjadi lebih lambat. Untuk mengubah berapa lama penantian ini berlangsung, atur
CLAUDE_CODE_MCP_STARTUP_WAIT_MSdi environment runner. Variabel ini tidak mengubah server mana yang dicakup oleh penantian tersebut. Memerlukan Claude Code v2.1.274 atau yang lebih baru.
claude mcp add tidak memiliki flag alwaysLoad. Untuk mengatur kunci tersebut, tambahkan server dengan claude mcp add-json sebagai gantinya, yang menerima kunci itu dalam JSON server dan menuliskannya ke .claude.json. Di Dockerfile Anda:
RUN claude mcp add-json core '{"type":"http","url":"https://mcp.example.com/mcp","alwaysLoad":true}' --scope user
Jika tool sebuah server juga tidak muncul pada giliran berikutnya, periksa apakah server tersebut benar-benar mencapai sesi, seperti yang dijelaskan di Server MCP.
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 membuat tugas terjadwal cloud, 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 belum di-commit ATAU commit yang belum di-push, sehingga pekerjaan tidak
# hilang ketika sesi idle dilepaskan dan sehingga tombol "Create PR" di claude.ai/code
# menyala.
#
# Tingkat runner (tidak ada perubahan repo): letakkan file ini di ~/.claude/hooks/
# di host runner dan gabungkan blok pengaturan Stop-hook yang menyertainya ke
# ~/.claude/settings.json — runner menanam keduanya ke dalam setiap sesi.
# Alternatif tingkat repo: commit 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 diandalkan pola ini; 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"
# Pengerasan: skrip ini berjalan di luar sandbox Bash Claude Code, sehingga panggilan
# git-nya melewati beberapa fitur yang dapat memulai program yang disebutkan dalam
# pengaturan git checkout. Panggilan tersebut tidak melewati semuanya, jadi jangan
# hanya mengandalkan sandbox.
GIT_OPTIONAL_LOCKS=0
export GIT_OPTIONAL_LOCKS
git() { command git -c core.fsmonitor= -c core.hooksPath=/dev/null "$@"; }
# Bukan repo git → tidak ada yang perlu didorong.
git -C "$d" rev-parse --git-dir >/dev/null 2>&1 || exit 0
# Tidak ada remote → "push ke remote" tidak dapat dipenuhi; keluar.
[ -z "$(git -C "$d" remote 2>/dev/null)" ] && exit 0
# Perubahan yang belum di-commit (staged, unstaged, atau untracked). Kecualikan
# .claude/ sepenuhnya — pengaturan yang ditanam operator dan status runtime
# yang ditulis CLI (kunci penjadwal, worktree, status routine) tinggal di sana
# dan tidak ada yang merupakan "pekerjaan yang belum di-commit" yang perlu di-push
# model.
s=$(git -C "$d" status --porcelain --ignore-submodules=dirty -- . ':(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 yang belum di-push. Hitung commit 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 yang ada)
# - checkout berbasis clone (origin/* ada)
# - default runner: anak dimulai di branch hasil sesi, yang dibuat runner
# setelah checkout
# - detached HEAD, ketika setup kustom melewati pembuatan branch itu
# Tanpa titik referensi sama sekali (tidak pernah di-fetch), tetap diam daripada
# false-positive pada giliran read-only.
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 dapat dipengaruhi penyerang — git-check-ref-format(1) mengizinkan `"`
# dalam nama ref. `\` dilarang (aturan 10) tetapi tetap di-escape sebagai
# pertahanan berlapis yang murah.
# Escape karakter meta JSON sebelum interpolasi ke payload yang dibuat manual
# sehingga branch 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 meminta Claude untuk melakukan commit dan push sebelum sesi berakhir, dan tetap diam ketika direktori bukan repositori git atau tidak memiliki remote. Untuk sesi dengan beberapa repositori, lihat apa yang ditunjuk oleh $CLAUDE_PROJECT_DIR.
Hook Stop hanya meminta Claude. Agar commit dan push berhasil, siapkan:
- Identitas git: tetapkan
user.namedanuser.emaildi image runner Anda, atau mulai runner dengan--configure-git - Kredensial push: gunakan git yang dikelola Anthropic, atau sediakan kredensial seperti yang dijelaskan di Sertakan konfigurasi git di image Anda
- Untuk sesi yang dilanjutkan: tetapkan
--push-outcome-on-releasepada setiap runner di environment. Sesi yang dilanjutkan kemudian dimulai dari commit yang di-push Claude ke branch sesi. Untuk batasan flag ini, lihat Sesi yang dilanjutkan kehilangan pekerjaan yang belum di-push.
Hook Stop meminta paling banyak sekali per giliran. Jika commit atau push gagal, giliran tetap berakhir. Cari pesan error git di sesi dan perbaiki apa yang disebutkannya.
Pada checkout yang besar, naikkan timeout di pengaturan hook Stop dan mulai ulang runner. git status pada skrip melewati core.fsmonitor, sehingga dapat memakan waktu lebih lama dari 10 detik yang ditetapkan di sana. Hook yang mengalami timeout mengakhiri giliran tanpa prompt.
Submodule dan hook Stop
Hook Stop memeriksa commit mana yang sedang digunakan setiap submodule, bukan file di dalamnya:
- Commit baru di submodule: Claude diminta sampai repositori induk mencatatnya, bahkan jika
.gitmodulesmenetapkanignore = alluntuk submodule tersebut - File yang belum di-commit di submodule: Claude tidak diminta, sehingga pekerjaan itu hanya tersimpan di disk runner
- Commit yang belum di-push di submodule: hook Stop tidak memeriksa apakah commit tersebut sudah di-push, sehingga induk dapat menunjuk ke commit yang tidak dapat di-fetch oleh orang lain
Ganti salinan lama skrip hook Stop
Cari GIT_OPTIONAL_LOCKS di salinan stop-hook-nudge.sh Anda. Jika tidak ada, Anda memiliki salinan lama. Ganti dengan skrip terbaru di mana pun Anda memasangnya, lalu mulai ulang runner. Pertahankan pengaturan hook Stop Anda, kecuali untuk menaikkan timeout pada checkout yang besar.
- Kapan: jika sesi Anda berjalan dengan sandbox Bash aktif, ganti sekarang. Jika tidak, ganti pada build image atau mulai ulang runner berikutnya.
- Apa yang berubah: file yang belum di-commit di submodule tidak lagi meminta Claude. Commit baru di submodule yang ditandai
ignore = alloleh.gitmoduleskini meminta Claude.
[!WARNING]
Hook berjalan di luar sandbox Bash, sehingga panggilan git pada skrip terbaru melewati beberapa fitur git yang dapat memulai program yang disebutkan dalam pengaturan git checkout. Panggilan tersebut tidak melewati semuanya. Jangan hanya mengandalkan sandbox, dan isolasi sesi seperti yang dijelaskan di Perkuat deployment Anda.
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.
Pin mode izin dari wrapper Anda
[!NOTE]
Hanya pin mode auto di lingkungan yang kontainer sesinya berjalan dengan default-deny network egress dan sisa bagian hardening di tempat. Panggilan alat rutin, termasuk permintaan jaringan
Bash, berjalan tanpa manusia dalam loop di set alat pre-disetujui default dan dalam mode auto, jadi batas jaringan adalah apa yang membatasi di mana panggilan itu dapat menjangkau.
Untuk memulai sesi dalam auto mode hingga claude.ai/code atau klien lain menetapkan mode izin untuknya, pin mode tersebut dari skrip wrapper atau hook command Anda. Runner memanggil wrapper dengan flag-nya sendiri sebagai argumen, dan Claude Code menerapkan --permission-mode terakhir pada baris perintah, jadi tambahkan milik Anda setelah "$@":
#!/bin/bash
exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@" --permission-mode auto
Setelah klien menetapkan mode, mode yang Anda tambahkan tidak lagi berlaku, bahkan setelah sesi dimulai ulang di runner. Sebagai contoh, claude.ai/code menetapkan mode bersama pesan pertama sesi ketika dropdown modenya menampilkan Auto, atau ketika seseorang telah memilih mode di sana.
Dalam auto mode, model pengklasifikasi terpisah meninjau tindakan sebelum dijalankan dan memblokir tindakan yang ditolaknya. Aturan ask eksplisit tetap memaksa munculnya permintaan izin. Panggilan yang diblokir oleh aturan deny tetap diblokir di setiap mode.
Setujui sebelumnya tool tertentu
Untuk menyetujui sebelumnya tool tertentu, tambahkan --allowed-tools dengan aturan Anda setelah "$@" di skrip wrapper Anda, misalnya --allowed-tools "Bash(bazel *) Bash(yarn *) mcp__internal__*". Flag daftar seperti --allowed-tools dan --disallowed-tools terakumulasi di seluruh kemunculan alih-alih saling menimpa, jadi aturan Anda berlaku di atas aturan apa pun yang dikirimkan bidang kontrol. Untuk mempersempit, tambahkan --disallowed-tools, yang menolak tool meskipun aturan lain mengizinkannya.
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.
Sesi juga membaca file pengaturan berikut:
- Pengaturan proyek: di luar thread proyek,
.claude/settings.jsonyang di-commit ke repositori diterapkan di atas baseline tingkat pengguna. Dalam sesi dengan beberapa repositori, paling banyak hanya file dari satu repositori yang berlaku. - Pengaturan terkelola: sesi membaca
managed-settings.jsondari jalur sistem standar di image runner Anda. Untuk mengetahui apakah kuncinya berlaku bersama pengaturan terkelola server, lihat bagaimana Claude Code menggabungkan sumber terkelola.
Untuk urutan penerapan sumber-sumber ini, 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; runner yang lebih lama mengabaikan hook yang dipasok. Hook yang dipasok menjalankan python3, jadi pasang python3 di image runner Anda.
- 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, meninggalkansettings.jsonyang ditanam dan skrip Anda sendiri dihooks/<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
--settingsmemasuki konfigurasi hook yang digabungkan biasa, bukan tingkat yang dikelola, jadi pengaturan yang dikelola Anda masih berlaku.disableAllHooksmenonaktifkannya, dan mereka bukan di antara kategori yangallowManagedHooksOnlytetap dimuat.
Ketika seseorang memulai sesinya sendiri, Claude Code mengunduh skills yang diaktifkan untuk akun claude.ai mereka ke direktori konfigurasi sesi tersebut. Eksekusi tugas terjadwal cloud tidak mendapatkan skills pemiliknya, dan sesi yang mengirimkan permintaan model ke Bedrock atau Agent Platform tidak mengunduh skill apa pun. Untuk skill yang dibutuhkan sesi tersebut, tambahkan skill tersebut ke image runner Anda atau, di luar thread proyek, commit skill tersebut ke .claude/skills/ repositori.
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.
Hentikan sesi yang gagal mengambil pengaturan terkelola server
Untuk mencegah sesi yang mengambil pengaturan terkelola server dimulai ketika pengambilan tersebut gagal, atur forceRemoteSettingsRefresh ke true dalam managed-settings.json di image runner Anda. Balasan dari API Anthropic bahwa organisasi Anda tidak memiliki pengaturan terkelola server dihitung sebagai pengambilan yang berhasil.
Sebelum Anda menambahkan kunci tersebut, periksa apakah lingkungan menjalankan sesi channel Claude Tag atau mengirimkan permintaan model ke Bedrock atau Agent Platform:
- Sesi channel Claude Tag: jangan sertakan kunci tersebut pada runner di lingkungan yang dijadikan tujuan channel Claude Tag oleh Owner. API Anthropic menolak permintaan pengaturan terkelola server dari sesi ini, sehingga jika kunci tersebut diatur, sesi tersebut gagal dimulai. Letakkan aturan untuk sesi tersebut di
managed-settings.jsonimage runner, yang tetap dibacanya. Untuk menggunakan kunci tersebut bagi sesi Anda yang lain, jalankan sesi tersebut di lingkungan terpisah. - Bedrock atau Agent Platform: sesi pada penyedia ini tidak mengambil pengaturan terkelola server, sehingga sesi tersebut tetap dimulai terlepas dari apakah Anda mengatur kunci tersebut. Terapkan aturan Anda dari
managed-settings.jsonyang sama sebagai gantinya.
managed-settings.json berikut mengatur kunci tersebut:
{
"forceRemoteSettingsRefresh": true
}
Atur kunci tersebut di file itu meskipun Anda juga mengaturnya di konsol admin claude.ai. Sesi baru tidak memiliki salinan cache dari pengaturan terkelola server Anda, jadi jika Anda mengatur kunci tersebut hanya di konsol, sesi belum memilikinya. Jika pengambilannya kemudian gagal, sesi berjalan tanpa pengaturan tersebut hingga pengambilan berikutnya berhasil.
Untuk memastikan bahwa pengambilan pengaturan yang gagal adalah penyebab sesi tidak dimulai, lihat output terakhir Claude Code di sesi atau di log runner. Output tersebut menyertakan Your organization requires remote managed settings to load, but they could not be loaded.
Pengaturan repositori dalam sesi dengan beberapa repositori
Dalam thread di sebuah proyek, tidak ada .claude/settings.json repositori mana pun yang berlaku sebagai pengaturan proyek dan tidak ada .mcp.json atau skills repositori mana pun yang dimuat, di mana pun thread dimulai. Untuk memberikan skill kepada thread tersebut, tambahkan skill tersebut ke image runner Anda.
Dalam sesi lain dengan beberapa repositori, Claude Code membaca pengaturan proyek dari direktori tempat sesi dimulai, sehingga paling banyak hanya .claude/settings.json dari satu repositori yang berlaku sebagai pengaturan proyek. Hook yang didefinisikan dalam file repositori lain tidak berjalan, aturan deny di dalamnya tidak berlaku, dan env-nya tidak diatur.
--capacity 1, default, dengan checkout bawaan: sesi dimulai di repositori pertama dalam daftar repositorinya..claude/settings.jsonrepositori tersebut berlaku sebagai pengaturan proyek dan.mcp.json-nya dimuat, sedangkan milik repositori lain tidak.--capacitylebih dari satu, atau hookcheckout: sesi dimulai di direktori per sesi yang berisi hasil checkout. Tidak ada.claude/settings.jsonrepositori mana pun yang berlaku sebagai pengaturan proyek, tidak ada.mcp.jsonrepositori mana pun yang dimuat, dan$CLAUDE_PROJECT_DIRdalam perintah hook adalah direktori tersebut, bukan hasil checkout.
CLAUDE.md setiap repositori dimuat di mana pun sesi dimulai. Di luar thread proyek, skills-nya juga dimuat. Runner meneruskan setiap repositori ke Claude Code sebagai direktori tambahan, sehingga Claude Code juga membaca kunci enabledPlugins dan extraKnownMarketplaces dari .claude/settings.json setiap repositori.
Untuk menjalankan hook atau menerapkan aturan izin di setiap sesi, letakkan di ~/.claude/settings.json pada host runner. Runner mengisikan file host ke setiap sesi, di mana pun sesi dimulai. Tulis jalur dalam aturan Read atau Edit sebagai pola absolut // atau relatif terhadap home ~/, karena pola lain berjangkar pada sumber pengaturan atau direktori saat ini.
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
- Reference: setiap flag CLI, variabel lingkungan, dan metrik
- Verify session identity: validasi token sesi dari layanan di luar runner