Sandbox và bảo mật
Vì sao cô lập lại quan trọng ở đây
Phần tiêu đề “Vì sao cô lập lại quan trọng ở đây”Một agent đọc tệp, chạy lệnh shell và gọi API bên ngoài thay bạn. Kiểu hỏng hóc bạn cần bận tâm hiếm khi là ác ý — mà là một agent đọc nhầm đường dẫn và viết lại bản checkout đang làm việc của bạn, hoặc đẩy lên một nhánh mà bạn chưa xem xét xong. Vậy nên câu hỏi mà sản phẩm phải trả lời không phải “agent có đáng tin không” mà là “bán kính ảnh hưởng là bao nhiêu khi nó sai”.
Control Center trả lời điều đó bằng nhiều lớp và các lớp này độc lập với nhau một cách có chủ đích. Nếu thiếu một lớp, những lớp khác vẫn đứng vững. Điều đó quan trọng, bởi lớp ngoài cùng — sandbox hệ điều hành — là lớp duy nhất có điều kiện: nó phụ thuộc vào việc máy chủ có cung cấp backend hay không, và trên Windows thì không máy nào có.
Điều gì thực sự ràng buộc một agent
Phần tiêu đề “Điều gì thực sự ràng buộc một agent”Năm thứ ràng buộc một lượt chạy agent trên mọi máy chủ, dù có sandbox hay không. Không thứ nào là ranh giới kernel; tất cả đều là thật. Nơi máy chủ cung cấp backend, sandbox hệ điều hành nằm bên trên chúng và mục “Sandbox bổ sung điều gì và dừng lại ở đâu” bên dưới nói về những gì điều đó thay đổi.
Cây làm việc copy-on-write của riêng nó. Mỗi cuộc hội thoại tự khởi tạo bản
checkout của riêng mình cho từng kho mã đã liên kết, được nhân bản theo cơ chế
copy-on-write thông qua native rift đi kèm thay vì sao chép từng byte một, nên
cô lập đủ rẻ để trở thành mặc định. Kho mã đã đăng ký mà bạn thêm vào không gian
làm việc không phải là thư mục mà agent làm việc trong đó — hai cuộc hội thoại
cùng đụng vào một kho mã không thể thấy các chỉnh sửa của nhau và cả hai đều
không chỉnh sửa bản checkout của bạn.
Độ chi tiết này đáng để nói cho chính xác: cây làm việc là theo từng cuộc hội
thoại, không phải theo từng agent. Hai agent trong cùng một cuộc hội thoại nhận
được những thư mục làm việc overlay riêng biệt nhưng chia sẻ thư mục repos/
của cuộc hội thoại đó — đó chính là mục đích, vì chúng đang cộng tác trên cùng
một thay đổi — trong khi các thư mục agent riêng của chúng vẫn tách biệt.
Copy-on-write là backend duy nhất ở mọi nơi rift được phát hành: một hệ thống
tệp không thể reflink, hoặc một thư mục dữ liệu nằm trên ổ đĩa khác với kho mã,
sẽ làm việc khởi tạo thất bại thay vì quay về phương án khác. Windows là ngoại lệ
— ở đó không có backend copy-on-write, nên git worktree là backend, không phải
một sự suy giảm âm thầm.
Giới hạn đường dẫn trong các công cụ tệp của harness tích hợp. Các công cụ
read, write, edit, apply_patch, search, find và search_files của
harness phân giải mọi đường dẫn dựa trên thư mục làm việc của lượt chạy cộng với
các gốc chia sẻ — thư mục repos/ của cuộc hội thoại, nơi chứa các cây làm việc,
và thư mục tệp đính kèm của space khi nó tồn tại — và từ chối bất cứ thứ gì nằm
ngoài. Đường dẫn tuyệt đối chỉ được chấp nhận nếu chúng vốn đã nằm bên trong. Một
symlink bên trong cây làm việc trỏ ra ngoài cũng bị phân giải và từ chối. Đây là
lý do một agent không thể write vào ~/.ssh ngay cả khi không có sandbox.
Bề mặt công cụ của chế độ. Chế độ của một
cuộc hội thoại quyết định những công cụ nào được hiện thực hóa cho lượt chạy.
Trong các chế độ chỉ đọc (plan, review, orchestrate), các công cụ thay đổi
dữ liệu đơn giản là không có trong sổ đăng ký và chính sách lệnh shell cho những
chế độ đó từ chối thẳng mọi lệnh thay đổi — git commit, git push,
npm install, docker và phần còn lại — thay vì hỏi trước. Một công cụ không
hiện diện thì không thể bị gọi, đó là một bảo đảm mạnh hơn một công cụ hiện diện
nhưng bị kiểm soát.
Rào chắn hành động hợp nhất. Mọi công cụ thay đổi dữ liệu đều khai báo các
lớp hiệu ứng mà nó có thể tạo ra và bộ máy chính sách phân giải từng lớp thành
cho phép, hỏi trước, hoặc từ chối trước khi lệnh gọi chạy. Một quyết định
prompt không có người phê duyệt nào kết nối sẽ phân giải thành bị từ chối —
một câu hỏi không ai trả lời được thì không bao giờ là đồng ý. Xem
Rào chắn để biết mô hình phân giải.
Thông tin xác thực qua broker và một môi trường đã làm sạch. Thông tin xác thực được tạo mới ở mỗi lần khởi chạy và bị thu hồi khi lượt chạy kết thúc, và token GitHub mà một lượt chạy nắm giữ có thể đọc nhưng không thể push — xem “Push đi qua Control Center” bên dưới. ACP, bash của harness
và Claude Code dưới lớp bọc native nhận một môi trường đã bị loại bỏ các vector
chèn của trình liên kết động và trình thông dịch (LD_PRELOAD,
DYLD_INSERT_LIBRARIES, NODE_OPTIONS, GIT_SSH_COMMAND và những thứ tương
tự) trước khi tiến trình khởi động. Claude Code qua NoSandboxAdapter kế thừa
môi trường của máy chủ.
Nơi những ranh giới đó mỏng đi
Phần tiêu đề “Nơi những ranh giới đó mỏng đi”Các công cụ tệp của harness chạy trong tiến trình máy chủ, không phải trong
một tiến trình con, nên không profile sandbox nào có thể ràng buộc chúng kể cả
khi có gắn một cái — chỉ bash đi qua bộ chạy lệnh đã được sandbox. Sự ràng
buộc của chúng là phép kiểm tra đường dẫn đã mô tả ở trên và do đó phép kiểm tra
đó có tính quyết định chứ không phải một lớp phòng thủ thứ hai.
Công cụ bash là chỗ khác cần nói cho chính xác. Các lệnh của nó được đánh giá
dựa trên chính sách lệnh của chế độ rồi được spawn với một môi trường đã làm
sạch và cây làm việc làm thư mục làm việc. Nơi có backend, lần spawn đó được
bọc, nên sandbox chính là thứ ngăn một lệnh shell ghi ra ngoài cây làm việc; nơi
không có — Windows, hoặc Linux thiếu bwrap và socat — chính sách lệnh là thứ
duy nhất nằm giữa lệnh đó và phần còn lại của ổ đĩa. Ở chế độ chat, chính sách
từ chối cứng một danh sách cố định (leo thang đặc quyền, phá hủy hệ thống và ổ
đĩa, thoát khỏi container và namespace) và hỏi trước với các thay đổi trạng thái
từ xa (git push, gh pr create, xuất bản và cài đặt gói). Bất cứ thứ gì không
nằm trong cả hai danh sách đều chạy. Pipeline, chuỗi lệnh và subshell được phân
tích cú pháp và từng lệnh con được đánh giá riêng, với quyết định hạn chế nhất
giành phần thắng.
Claude Code, khi được dùng như một adapter, là trường hợp mà sandbox mang trọng
lượng lớn nhất. Control Center khởi chạy nó với --dangerously-skip-permissions,
bởi một claude -p phi tương tác nếu không thì sẽ chặn mãi mãi ở prompt phê
duyệt của chính nó. Các lệnh gọi read, write và edit của nó diễn ra bên trong tiến trình của chính nó và Control Center không bao giờ thấy chúng. Các lệnh gọi Bash của nó được gửi, trước khi chạy, tới một hook trên máy chủ để phân loại dòng lệnh và hỏi quy tắc tương ứng, còn các công cụ mcp__* do Control Center phục vụ vẫn đi qua cổng như thường lệ. Nơi có backend, những lệnh gọi vô hình đó vẫn diễn ra
bên trong lớp bọc sandbox, nên hệ điều hành là ranh giới bao quanh chúng. Nơi
không có, thì không có gì cả. Mỗi adapter nói rõ điều này trong ma trận thực thi
của nó — xem ma trận trung thực của adapter.
Push đi qua Control Center
Phần tiêu đề “Push đi qua Control Center”Không có cờ quyền hạn theo từng agent. Việc một agent có được tạo commit, push, mở pull request, ghi vào tracker hay truy cập mạng hay không do rào chắn quyết định — cho phép, hỏi trước hoặc từ chối — và cùng các quy tắc đó áp dụng bất kể adapter nào chạy agent.
Push là hành động phải đứng vững ngay cả trước một agent đang tìm cách lách quy
tắc, nên nó được thực thi bằng thứ agent nắm giữ chứ không phải bằng thứ agent
chạy. Khi đã cấu hình GitHub App, một lượt chạy trong worktree GitHub nhận một
token cài đặt giới hạn theo kho mã, có thể đọc nội dung và làm việc với pull
request nhưng không thể push. Cấu hình git của lượt chạy viết lại mọi URL push
GitHub (https://github.com/, git@github.com:, ssh://git@github.com/) về
cổng lượt chạy agent, một endpoint loopback trên cc_server. Cổng đọc xem
lần push cập nhật những ref nào, hỏi quy tắc Push lên remote — nên một quy
tắc giới hạn theo nhánh, như “từ chối push lên main”, cũng được áp dụng — và
chỉ khi đó mới chuyển tiếp lần push tới GitHub bằng một token ghi mà nó tạo ra
cho mục đích này và không bao giờ trao cho agent. Một lần push bị từ chối hiện
ngay trong đầu ra của git:
! [remote rejected] main -> main (Control Center policy: …)Cổng chỉ chuyển tiếp push tới kho mã của chính worktree hoặc tới một kho mã GitHub được liên kết với không gian làm việc. Các lần fetch đi thẳng tới GitHub bằng token đọc.
Các lệnh shell khác gặp một cổng mềm hơn. Công cụ bash của harness và, qua một
hook, công cụ Bash của Claude Code phân loại từng dòng lệnh và hỏi quy tắc mà
nó thuộc về: git commit, merge, rebase, cherry-pick, revert và am là
Tạo một commit; gh pr create là Mở một pull request; gh pr merge,
review, comment và close là Xuất bản review hoặc merge;
gh issue create, edit, comment và close là Ghi vào tracker bên
ngoài; các lệnh cài gói là Cài một gói; curl và wget là Truy cập
mạng. Cơ chế này nhận ra cách viết thông thường của một hành động, và một
agent quyết tâm che giấu nó (eval, một tệp script, một lời gọi API trực tiếp)
có thể làm được. Push không phụ thuộc vào cơ chế này.
Không có khóa API phiếu nào được đặt vào sandbox. Agent ghi phiếu qua các công cụ MCP của Control Center, vốn hỏi quy tắc Ghi vào tracker bên ngoài.
Quy tắc Truy cập mạng được đọc một lần, khi lượt chạy bắt đầu. Mạng của
sandbox chỉ có bật hoặc tắt, nên từ chối sẽ tắt nó, còn cho phép và hỏi trước
giữ nó bật; các công cụ web_fetch và web_search của harness vẫn hỏi ở mỗi
lệnh gọi. Thông tin xác thực bị thu hồi khi lượt chạy kết thúc, cùng với mọi
token ghi mà cổng đã tạo, nên một handle không sống lâu hơn lượt chạy.
Sandbox bổ sung điều gì và dừng lại ở đâu
Phần tiêu đề “Sandbox bổ sung điều gì và dừng lại ở đâu”Enum SandboxBackend có ba giá trị — native (Seatbelt trên macOS, bubblewrap
trên Linux và WSL2), none, và microvm. Việc bọc lệnh của agent chỉ dùng hai
giá trị đầu: microvm là backend VM khép kín cho terminal tương tác và rig,
không phải một bản nâng cấp âm thầm của lớp bọc bash. Hiểu rõ hình dạng native
sẽ giải thích bạn được lợi gì khi có một cái được gắn vào và những gì nó vốn dĩ
sẽ không bao giờ mang lại cho bạn.
cc_server dò máy chủ một lần lúc khởi động và nơi một backend trả lời, nó cấp
cho cả hai đường nối mà đường điều phối có, bởi các transport không dùng chung
một đường. Đường thứ nhất (SandboxPort) bọc CLI Claude Code. Đường thứ hai
(SandboxManager) bọc các adapter ACP và công cụ bash của harness tích hợp.
Chỉ cấp cho một đường sẽ khiến những đường còn lại spawn mà không được bọc, đó là
điều từng xảy ra. Trên một máy chủ không có backend, hoặc dưới --sandbox off,
tất cả chúng spawn không được bọc và máy chủ nói rõ bạn đang ở trường hợp nào
trên dòng khởi động của nó. ACP và bash của harness vẫn làm sạch môi trường;
Claude Code qua NoSandboxAdapter kế thừa môi trường của máy chủ.
Một bề mặt cố tình không tham gia: terminal do máy chủ cung cấp nhận một đối số
backend theo từng phiên và cc_server khởi tạo nó với mặc định là shell của máy
chủ, nên một terminal bạn mở từ ứng dụng sẽ chạy không có sandbox trừ khi máy
khách nêu rõ native. Đó là một shell thật trên máy chủ, không phải một lượt
chạy agent.
Trên Linux, bubblewrap dựng một namespace mount, nên agent thực sự chỉ thấy
những gì đã được gắn vào đó. Trên macOS, sandbox-exec áp dụng một profile
Seatbelt mặc định là dễ dãi — một profile từ chối-hoàn-toàn-theo-mặc-định sẽ
khiến một shell tương tác không dùng được, vì số lượng lệnh gọi dyld, XPC và Mach
cần liệt kê là quá nhiều. Vì vậy profile macOS bắt đầu từ (allow default) rồi
khoét ra các lệnh từ chối: đọc các đường dẫn bí mật, chính sách ghi được đặt lại
thành từ chối cùng những lệnh cho phép tường minh đặt lên trên, chặn unlink trên
mọi đường dẫn bị từ chối và các tổ tiên của nó để một lệnh rename không thể lách
qua một lệnh từ chối ghi, từ chối exec với các tệp nhị phân luôn nguy hiểm và với
bất cứ thứ gì được sao chép vào một thư mục có thể ghi, và mạng bị giới hạn ở
các cổng proxy loopback cùng DNS.
Hệ quả thực tế: trên Linux, tầm nhìn của agent về hệ thống tệp bị thu hẹp; trên macOS, agent vẫn có thể đọc phần lớn máy chủ và các bản checkout đã đăng ký của bạn được bảo vệ bằng những lệnh từ chối ghi và rename tường minh thay vì bằng sự vô hình. Cả hai đều là cô lập ở cấp namespace, không phải một ranh giới bảo mật cấp kernel — chính các backend nói rõ điều đó trong ghi chú khả năng của chúng.
Tập bind-mount là nơi ranh giới giữa các agent được vạch ra. Điều phối overlay
(Claude Code và ACP) gắn tối đa bốn đường dẫn, được phân giải theo từng lượt
chạy: thư mục làm việc overlay của agent ở chế độ đọc-ghi, thư mục cấu hình của
agent ở chế độ chỉ đọc, thư mục repos/ dùng chung của cuộc hội thoại ở chế độ
đọc-ghi khi nó tồn tại, và thư mục tệp đính kèm của space ở chế độ chỉ đọc khi nó
tồn tại. Thư mục của một agent anh em không bao giờ được gắn. Lớp bọc bash của
harness hẹp hơn: nó chỉ bind-mount thư mục làm việc của lượt chạy. Đây là cùng
hình dạng mà phép kiểm tra đường dẫn in-process của harness thực thi bằng phần
mềm, nên cả hai khớp nhau — nhưng chỉ phép kiểm tra đường dẫn mới bao phủ các
công cụ tệp của chính harness, những công cụ không bao giờ chạm tới một namespace
mount cả.
Các bản checkout đã đăng ký của bạn gia nhập nhóm từ chối ghi thêm vào đó, ở mọi
chế độ và trên cả ba đường được bọc (Claude Code, ACP, bash của harness), và
mỗi quy tắc được phát ra theo cả cách viết nguyên văn lẫn cách viết đã phân giải
symlink. Cách viết thứ hai không phải là một chi tiết nhỏ nhặt:
kernel so khớp một quy tắc từ chối với đường dẫn đã phân giải và trên macOS việc
phân giải là trường hợp phổ biến chứ không phải ngoại lệ — /tmp thực ra là
/private/tmp, /var/folders/… thực ra là /private/var/folders/… — nên một
bản checkout đi tới qua một trong hai cách đó sẽ vẫn có thể ghi được trong khi
profile trông như thể đang cấm nó.
Mạng, khi ở trong một sandbox, là một danh sách cho phép theo tên miền cho
từng phiên, được thực thi bởi các proxy loopback in-process. Egress mặc định bị
từ chối: khi mạng tắt, các proxy vẫn khởi động nhưng danh sách cho phép của
chúng rỗng, nên mọi kết nối ra ngoài đều bị từ chối. Khi mạng bật, bộ phân giải
dựng danh sách cho phép từ một đường cơ sở được tuyển chọn — các nhà cung cấp
API LLM, nơi lưu trữ git, các registry gói, danh mục mô hình — cộng với các tên
miền GitHub, rồi đối chiếu máy chủ đích của mọi kết nối với nó. Ký tự đại diện
hoạt động (*.example.com khớp với các tên miền con, không khớp với tên miền
gốc). Một danh sách luôn bị từ chối riêng biệt thắng danh sách cho phép và bao
gồm các endpoint metadata của cloud và các điểm thu thập telemetry. Thứ không
cấu hình được chính là bản thân danh sách: spec mang một trường
egressAllowlist cho các tên miền bổ sung, nhưng không có gì trong đường điều
phối điền vào nó, nên trên thực tế mọi lượt chạy đều nhận đường cơ sở cộng với
GitHub và không hơn.
Các lệnh từ chối, trên macOS, có thể quan sát được: một trình giám sát theo dõi
system log để tìm các lệnh từ chối của Sandbox, lọc nhiễu đã biết, loại trùng các
đợt thử lại và đưa mỗi cái lên thành một SandboxViolation nêu rõ điều gì đã được thử và điều gì đã bị chặn. Trên Linux, đường bọc phân tích stderr để
tìm EPERM, Operation not permitted và Permission denied rồi phát ra cùng
loại sự kiện — không có việc theo dõi system log. Những sự kiện đó đến trên luồng
sự kiện của chính lượt chạy, nên một syscall bị từ chối hiện ra trong bản tường
thuật chứ không chỉ trong một log. Telemetry chỉ tồn tại khi một sandbox native
thực sự đang bọc tiến trình — trên một máy chủ không có backend thì không có gì
để quan sát. Nó cũng không liên quan gì tới rào chắn: một vi phạm đến từ một syscall bị từ chối, không bao giờ từ một lần tra cứu quy tắc.
Xác nhận đến từ rào chắn, không phải từ sandbox
Phần tiêu đề “Xác nhận đến từ rào chắn, không phải từ sandbox”Sandbox không có đường phê duyệt nào. Nó khởi chạy, thực thi, phát sự kiện và kết thúc; không có chỗ nào trong đó để một con người trả lời một câu hỏi. Khi bạn thấy một xác nhận trong một space — phê duyệt lần push này, phê duyệt lần xóa này — thì nó đã được bộ máy rào chắn (hoặc chính sách lệnh của harness) đưa ra trước khi hành động đó chạm tới runtime.
Thứ tự này là có chủ đích. Một phê duyệt diễn ra sau syscall sẽ chỉ là một bản ghi kiểm toán, không phải một cổng chặn. Đó cũng là lý do quy tắc đóng-khi-lỗi nằm trong rào chắn: khi không có người phê duyệt nào kết nối, quyết định được đưa ra trước khi bất cứ thứ gì chạy và nó là một lệnh từ chối.
Chẩn đoán
Phần tiêu đề “Chẩn đoán”Chạy chẩn đoán agent là nơi duy nhất nêu rõ những gì có thể kiểm tra bằng tay.
Khái niệm liên quan
Phần tiêu đề “Khái niệm liên quan”- Rigs và môi trường cách ly: bước nằm trên sandbox — khi công việc cần cả một cỗ máy dùng-một-lần với ranh giới kernel, chứ không phải một tiến trình được bọc
- Rào chắn: lớp chính sách nằm trên sàn hệ điều hành và nơi các phê duyệt thực sự đến từ đó
- Vòng đời điều phối agent: nơi cô lập khớp vào luồng điều phối
- Chế độ: cách một chế độ thu hẹp bề mặt công cụ và chính sách lệnh
- Tham chiếu backend sandbox: các backend và cấu hình của chúng
Hướng dẫn liên quan
Phần tiêu đề “Hướng dẫn liên quan”- Cấu hình chính sách sandbox: các quy tắc hành động, việc thực thi push và thế đứng của hệ thống tệp
- Chạy chẩn đoán agent: phạm vi của các kiểm tra chẩn đoán