Coding agent gửi telemetry gì ra ngoài? Cách audit
Coding agent có 4 luồng traffic: model inference, analytics, crash report và integration. Audit ngay trên máy để biết dữ liệu đi đâu và chặn phần bạn không chấp nhận.
Telemetry của coding agent thực sự bao gồm những gì
Telemetry của coding agent là 4 luồng chia sẻ dữ liệu khác nhau nhưng thường được gọi bằng cùng một tên. Mỗi luồng có cơ chế kiểm soát riêng. Model inference gửi prompt và code của bạn đến bên cung cấp model, và không có setting nào tắt được việc này. Product analytics và crash report được gửi đến vendor, đôi khi còn được gửi đến một công ty logging do vendor thuê. Việc dữ liệu có được giữ lại để training hay không là vấn đề hợp đồng, không phải vấn đề network. Luồng thứ tư thường bị bỏ qua: mỗi integration bạn thêm vào có thể mở connection đến một host mà bạn chưa từng chọn.
Danh sách default hiện tại của từng vendor là phần lỗi thời nhanh nhất trong chủ đề này. Một release có thể thay đổi default, và một feature mới có thể thêm destination mà không có switch hiện tại nào kiểm soát được. Vì vậy, kỹ năng có giá trị lâu dài là thực hiện một cuộc audit có thể lặp lại với bất kỳ agent nào: đọc tài liệu của vendor, kiểm tra config nào thực sự được áp dụng trên máy này, monitor process ngay trên máy, rồi chọn các biện pháp kiểm soát mà bạn chấp nhận chi phí. Mọi command bên dưới đều được chạy trên máy của bạn và kiểm tra traffic của chính bạn.
Bốn nhóm và lý do chúng cần các biện pháp kiểm soát khác nhau
Lưu lượng suy luận của model là không thể tránh khỏi. Agent gửi prompt của bạn, các file mà nó đã đọc, output của những command nó đã chạy và phần văn bản do chính nó tạo ra đến một model endpoint. Đó là cách product hoạt động. Quyết định thực sự duy nhất là bên nào nhận dữ liệu: một API do bên khác vận hành, hay một model do bạn tự chạy. Tài khoản cloud của công ty (Bedrock, Vertex, Foundry) chỉ thay đổi bên nhận, không loại bỏ luồng dữ liệu. Không nội dung nào trong phần còn lại của bài viết này làm giảm lưu lượng suy luận, vì vậy hãy tách nó khỏi ba nhóm còn lại khi đánh giá.
Analytics của product và báo cáo crash là một luồng khác đến các host khác. Bộ đếm mức sử dụng, số liệu độ trễ, các lần tra cứu feature flag và stack trace thường được gửi đến những hostname không liên quan gì đến model API, thường là đến một hệ thống theo dõi lỗi của bên thứ ba. Vendor thường gọi chúng là "metrics" và "error reports", đồng thời thường cung cấp một biến môi trường cho mỗi nhóm. Lưu lượng rất nhỏ, nên việc đếm byte sẽ không bao giờ tìm ra chúng. Bạn cần tìm hostname, không phải băng thông.
Retention và training là chính sách, không phải packet. Vendor lưu prompt của bạn hay không, lưu trong bao lâu và có dùng chúng để train model trong tương lai hay không đều được quy định trong các điều khoản đi kèm plan của bạn. Consumer plan và commercial plan thường khác nhau, còn thỏa thuận zero-retention thường là một thỏa thuận riêng. Bạn không thể xác minh những điều này bằng tcpdump, vì packet trông giống hệt nhau trong cả hai trường hợp. Hãy đọc điều khoản. Nếu vấn đề này quan trọng với công ty của bạn, hãy yêu cầu xác nhận bằng văn bản.
Integration âm thầm thêm một hop. Một server MCP (model context protocol), một plugin marketplace, một lần kiểm tra auto-update, một công cụ web search hoặc một safety check resolve URL trước khi fetch: mỗi thành phần đều tạo một request đến một host không phải model endpoint. Đây là nơi thường phát sinh bất ngờ, vì một harness có thể định tuyến công việc mà bạn tưởng là chạy local qua service của chính nó, và một release có thể bắt đầu làm việc đó mà không thay đổi một dòng nào trong config của bạn. Hãy xem mọi tool bạn thêm vào là một destination mới cho đến khi bạn theo dõi được nó trên wire.
Bước 1: tài liệu của nhà cung cấp nói gì?
Mở tài liệu tham chiếu cài đặt và trang sử dụng dữ liệu dành cho agent của bạn. Đọc các tài liệu này cùng một danh sách từ khóa: metrics, analytics, error reporting, crash, feedback, survey, update check, safety check, marketplace. Mỗi từ khóa thường tương ứng với một switch riêng. Ghi lại chính xác tên các biến, vì bước 2 sẽ dùng grep để tìm chúng.
Một từ khóa có thể khiến bạn hiểu sai. Trong một số agent, “telemetry” trong tài liệu có nghĩa là export OpenTelemetry mà bạn cấu hình để gửi metrics đến collector do bạn chạy. Đây là điều ngược lại với việc dữ liệu được gửi đến nhà cung cấp. Claude Code là một trong các trường hợp này: đặt CLAUDE_CODE_ENABLE_TELEMETRY=1 sẽ bắt đầu export đến endpoint mà bạn chỉ định trong OTEL_EXPORTER_OTLP_ENDPOINT. Thiết lập này không liên quan đến analytics riêng của nhà cung cấp, vốn có opt-out khác. Hãy xác định hướng luồng dữ liệu trước khi đặt bất kỳ thiết lập nào.
Hãy dự kiến sẽ có một master switch, nhưng cũng cần dự kiến các phạm vi không được bao phủ. Tính đến tháng 8 năm 2026, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC của Claude Code sẽ tắt metrics, báo cáo lỗi, lệnh feedback và khảo sát session cùng lúc. Tuy nhiên, chính tài liệu đó nói rằng thiết lập này không bao phủ WebFetch domain safety check. Tính năng này gửi hostname bạn sắp fetch đến vendor API và có setting riêng. Đây không phải vấn đề chỉ xảy ra với một sản phẩm. Đây là đặc điểm chung của vấn đề: master switch chỉ bao phủ các danh mục đã tồn tại khi nó được viết.
Opt-out cũng có thể khiến bạn mất một số chức năng. Tài liệu tương tự ghi rõ rằng tắt telemetry cũng tắt việc đánh giá feature flag mà một số tính năng phụ thuộc vào. Vì vậy, một switch được bật để bảo vệ quyền riêng tư có thể tắt một tính năng bạn đang dùng, nhưng không có thông báo lỗi nào cho biết hai việc này liên quan với nhau. Hãy đọc câu ngay cạnh flag, không chỉ đọc tên flag.
Bước 2: thực tế file cấu hình nào đã được áp dụng?
Một setting bạn đã ghi không có nghĩa là setting đó đã được áp dụng. Agent hợp nhất cấu hình từ nhiều file, trong đó có thể có một file nằm trong repository bạn vừa clone từ người khác. Hãy bắt đầu bằng environment của chính shell bạn đang dùng.
env | grep -Ei 'telemetry|otel|analytics|error_report|do_not_track|proxy'Sau đó in ra mọi file settings mà tool đọc, theo đúng thứ tự trong tài liệu. Với Claude Code, tính đến tháng 8 năm 2026, đó là file của user, hai file của project và một thư mục policy được quản lý trên Linux.
for f in ~/.claude/settings.json .claude/settings.json .claude/settings.local.json; do
echo "== $f"; [ -f "$f" ] && cat "$f"
done
ls -l /etc/claude-code/ 2>/dev/nullMột file của project đi kèm git clone là cấu hình do người khác viết, và file đó có thể bật lại setting mà file của user đã tắt. Nếu agent có command status liệt kê các nguồn đã được load, đó là cách nhanh nhất để biết sự thật: Claude Code in các nguồn settings đã load trong /status.
Cách kiểm tra chắc chắn nhất là đọc process đang chạy thay vì đọc bất kỳ file nào. Trước tiên, hãy cấp cho agent một user account Linux riêng. Việc này làm cho mọi command trong bài viết ngắn hơn. Sau đó đọc environment mà process được khởi động cùng.
pgrep -u agent -a node
sudo tr '\0' '\n' < /proc/$(pgrep -u agent -n node)/environ | grep -Ei 'telemetry|proxy|otel'/proc/<pid>/environ hiển thị các biến mà process có tại thời điểm exec. Vì vậy, nó phát hiện trường hợp lệnh export .bashrc của bạn không đến được service do systemd khởi động. Nếu biến bạn đã đặt không xuất hiện ở đây, biến đó chưa bao giờ có hiệu lực, bất kể dotfiles của bạn ghi gì.
Bước 3: agent kết nối đến những host nào?
Bắt đầu bằng các socket đang mở, lọc theo account mà agent chạy dưới đó.
sudo ss -tnpe state established-e thêm trường uid: vào mỗi dòng. Nhờ đó, bạn có thể tách các kết nối của agent khỏi kết nối của browser mà không cần đọc tên process. Ghi lại các địa chỉ từ xa, rồi xác định các hostname tương ứng. Nguồn hostname rõ ràng nhất là quá trình TLS (transport layer security) handshake, vì mỗi kết nối mới đều bắt đầu bằng một ClientHello chứa trường SNI (server name indication). Đây là hostname mà client yêu cầu.
sudo apt install -y tshark
sudo tshark -i any -f 'tcp port 443' -Y 'tls.handshake.type == 1' \
-T fields -e ip.dst -e tls.handshake.extensions_server_nameBạn nhận được một dòng cho mỗi kết nối mới. Đây chính là danh sách cần kiểm kê: model API, update server, analytics host, error tracker và mọi đích đến do integration thêm vào. Cột hostname trống nghĩa là client đã dùng ECH (encrypted client hello), nên hostname không xuất hiện trên network traffic. Khi đó, hãy dùng destination IP address, reverse lookup hoặc proxy ở bước 4.
Góc nhìn DNS (domain name system) là một cách đối chiếu hữu ích. Nó hiển thị các hostname mà agent đã tra cứu, kể cả với những kết nối không hoàn tất.
sudo tcpdump -ni any -l 'udp port 53'Mỗi dòng query kết thúc bằng record type và hostname theo dạng A? host.example.net. (39). Capture trên any thay vì interface bên ngoài, vì khi dùng systemd-resolved, ứng dụng kết nối đến một local stub listener trên 127.0.0.53 và chỉ stub mới kết nối ra bên ngoài. Nếu không thấy DNS traffic nào trong khi agent vẫn rõ ràng đang hoạt động, runtime đó đang tự thực hiện DNS over HTTPS. Khi đó, chỉ bước 4 mới cung cấp được hostname.
Hãy capture trong lúc agent thực hiện công việc thực tế. Bắt đầu một session, yêu cầu agent đọc một file, yêu cầu agent chạy một command, rồi để agent gặp lỗi ở một thao tác. Traffic chỉ xuất hiện một lần khi startup hoặc chỉ xuất hiện khi có exception sẽ không bao giờ xuất hiện trong capture lúc idle. Capture lúc idle là cách phổ biến nhất khiến một cuộc audit đi đến kết luận sai nhưng có vẻ hợp lý.
Bước 4: các request chứa gì?
Hostname cho biết kết nối đến ai. Để biết nội dung là gì, hãy đặt một proxy do bạn kiểm soát trước agent và chỉ cho runtime đó tin cậy CA của proxy. mitmproxy là công cụ thường dùng. Dự án khuyến nghị dùng binary độc lập từ mitmproxy.org và có tài liệu hướng dẫn uv tool install mitmproxy theo cách cài package Python.
mitmdump -w /tmp/agent-flows.mitmLần chạy đầu tiên ghi một CA vào ~/.mitmproxy/, trong đó mitmproxy-ca-cert.pem là file chứng chỉ riêng. Trong shell mà bạn sẽ dùng để chạy agent, hãy trỏ client đến proxy và đến chứng chỉ đó.
export HTTP_PROXY=http://127.0.0.1:8080
export HTTPS_PROXY=http://127.0.0.1:8080
export NODE_EXTRA_CA_CERTS="$HOME/.mitmproxy/mitmproxy-ca-cert.pem"
export REQUESTS_CA_BUNDLE="$HOME/.mitmproxy/mitmproxy-ca-cert.pem"
export SSL_CERT_FILE="$HOME/.mitmproxy/mitmproxy-ca-cert.pem"Nhiều CLI của agent là chương trình Node. Node đọc NODE_EXTRA_CA_CERTS khi process khởi động, vì vậy hãy export biến này trước khi chạy agent, không phải sau đó trong một terminal khác. Client Python đọc REQUESTS_CA_BUNDLE hoặc SSL_CERT_FILE, còn binary Go dùng standard library sẽ đọc SSL_CERT_FILE trên Linux. Hãy xác nhận đường dẫn hoạt động bằng curl trước khi quy lỗi cho agent.
curl -sS -o /dev/null -w '%{http_code}\n' https://example.comProxy hoạt động sẽ in 200 và request sẽ xuất hiện trong output của mitmdump. CA không được tin cậy sẽ gây ra curl: (60) SSL certificate problem: self-signed certificate in certificate chain; lỗi tương đương từ agent Node là lỗi chứa mã SELF_SIGNED_CERT_IN_CHAIN. Sau đó, hãy đọc các flow đã lưu bằng console viewer. Bạn có thể mở từng request để đọc header và body.
mitmproxy -r /tmp/agent-flows.mitmCó 4 kết quả cần phân biệt. Bạn thấy các request; khi đó hãy đọc chúng và tự quyết định. Agent từ chối khởi động với lỗi chứng chỉ; đây là vấn đề tin cậy trong runtime đó, không phải bằng chứng về vendor. Bạn chỉ thấy model API; điều này có nghĩa là các nhóm khác đang tắt, hoặc chúng chỉ chạy khi có một event mà bạn chưa kích hoạt. Hoặc bạn không thấy gì trong khi agent vẫn hoạt động rõ ràng; điều này có nghĩa là client bỏ qua các biến môi trường proxy hoặc pin chứng chỉ, và không thể tin bất kỳ cài đặt ứng dụng nào để biết sự thật. Kết quả cuối cùng này quan trọng nhất. Nó đưa bạn quay lại bước 3, vì packet capture vẫn sẽ thấy một kết nối dù ứng dụng không thể che giấu nó.
Các biện pháp kiểm soát, từ yếu nhất đến mạnh nhất
Cài đặt opt-out. Đây là cách rẻ nhất và yếu nhất, vì nó phụ thuộc vào việc vendor tuân thủ các cài đặt đó và chỉ bao phủ một nhóm đã tồn tại từ trước. Hãy đặt chúng ở nơi vẫn được giữ sau khi reboot và mở terminal mới, chẳng hạn trong file cài đặt của user hoặc shell profile. Thêm DO_NOT_TRACK=1 ngay tại đó: đây là quy ước được nhiều công cụ dòng lệnh tôn trọng, trong đó có một số agent, và không tốn chi phí. Sau lần update tiếp theo, hãy chạy lại bước 3, vì đó là lúc phạm vi bao phủ thay đổi.
Hạn chế egress. Ở đây bạn không còn yêu cầu nữa mà bắt đầu thực thi chính sách. Chạy agent bằng user riêng, sau đó chỉ cho user đó truy cập loopback và DNS, rồi drop toàn bộ phần còn lại. Cách này thêm một table riêng nên không ảnh hưởng đến các firewall rule hiện có.
table inet agentegress {
chain output {
type filter hook output priority filter; policy accept;
meta skuid "agent" ip daddr 127.0.0.0/8 accept
meta skuid "agent" udp dport 53 accept
meta skuid "agent" counter log prefix "agent-egress-drop " drop
}
}Áp dụng bằng sudo nft -f /etc/nftables.d/agent.nft, theo dõi counter bằng sudo nft list table inet agentegress và xem các gói bị drop bằng sudo journalctl -k -g agent-egress-drop. Counter drop tăng cùng một hostname bạn không dự kiến là mục tiêu chính của việc này. Có hai giới hạn cần lưu ý. meta skuid khớp với user sở hữu socket, nên chỉ có hiệu lực khi account đó không thể chuyển thành user khác: nếu agent có quyền sudo không cần password thì rule này chỉ còn mang tính gợi ý. Ngoài ra, để UDP 53 mở với mọi server vẫn chừa một kênh có thể đưa dữ liệu ra ngoài qua tên truy vấn, nên hãy đóng kênh này nếu threat model của bạn yêu cầu, bằng cách trỏ resolver của agent đến một host do bạn quản lý. Hostname allowlist nên đặt trong proxy thay vì nftables, vì API endpoint thường nằm sau content delivery network và địa chỉ IP của chúng có thể thay đổi mà bạn không kiểm soát. Cái giá của biện pháp này là lỗi phát sinh và công việc bảo trì: package install, git qua SSH và cơ chế tự kiểm tra update của agent đều sẽ fail cho đến khi bạn cho phép chúng; từ đó danh sách này là phần bạn phải tự duy trì. Nếu bạn triển khai trên server thay vì laptop, layout account và firewall tương tự là nền tảng cho chạy Claude Code an toàn trên VPS.
Một máy dùng một lần. Cấp cho agent một máy ảo (VM) không chứa credential nào quan trọng với bạn và destroy máy sau khi hoàn tất task. Cách này không giảm lượng dữ liệu agent gửi đi; nó giảm những gì agent có quyền gửi đi, đây thường mới là rủi ro bạn thực sự quan tâm. Hãy kết hợp với các egress rule ở trên, vì một VM mới có Internet unrestricted vẫn có thể truy cập mọi host trong phạm vi capture của bạn. Phương pháp này và state phải rebuild sau mỗi lần dùng được trình bày trong chạy coding agent trong VM dùng một lần, còn vấn đề sizing được trình bày trong chạy coding agent trên VPS.
Tự host model. Đây là biện pháp duy nhất loại bỏ luồng inference, vì prompt không bao giờ rời khỏi phần cứng của bạn. Chi phí là có thật: bạn không thể tự host closed model, nên phải chọn open weights và chấp nhận khoảng cách về capability trong các task khó, đồng thời cần phần cứng để serve model. Phần trade-off được phân tích trong bạn có thể tự host Claude hay không, còn khác biệt về capability giữa các agent chính được trình bày trong Claude Code, Cursor, Codex và Copilot khác nhau như thế nào.
Không biện pháp nào trong 4 biện pháp trên thay đổi những gì agent được phép đọc trên disk, và inference traffic mang theo mọi dữ liệu mà agent đã đọc. Nếu có file .env trong working directory, file đó sẽ được gửi đến model ngay khi agent grep tên một biến. Việc giữ những dữ liệu đó ngoài phạm vi truy cập là một công việc riêng, được trình bày trong giữ secret ngoài context của AI agent.
Cần kiểm tra gì sau mỗi lần cập nhật
- So sánh các trang cài đặt và sử dụng dữ liệu của nhà cung cấp với bản ghi lần trước, để tìm các tùy chọn mới và các service mới có tên cụ thể.
- Đọc lại environment của tiến trình từ
/proc/<pid>/environđể xác nhận các tùy chọn từ chối của bạn vẫn được áp dụng cho tiến trình đang chạy. - In lại các file cài đặt của project, vì một
git pullcó thể nạp một file cấu hình mà đồng nghiệp đã thay đổi. - Chạy lệnh capture SNI trong suốt một phiên làm việc thực tế và so sánh danh sách hostname với danh sách lần trước.
- Kiểm tra bộ đếm số gói bị firewall drop, vì một đích mới thường xuất hiện ở đó trước khi bạn nhận thấy ở nơi khác.
Việc này mất khoảng mười phút và là phần duy nhất của quy trình không bị lỗi thời. Một giá trị mặc định bạn đã xác minh vào tháng 8 năm 2026 chỉ là thông tin đúng cho tháng 8 năm 2026. Bản capture phản ánh đúng trạng thái của hôm nay.
FAQ
Tôi có thể ngăn coding agent gửi code của mình đến model không?
Không. Mọi setting tuyên bố làm được việc đó thực ra đang mô tả một cơ chế khác. Việc gửi prompt, các file agent đã đọc và output của những command agent đã chạy đến model endpoint là cách inference hoạt động. Vì vậy, biến số duy nhất là ai nhận dữ liệu đó. Bạn có thể thay đổi bên nhận bằng cách trỏ agent đến tài khoản cloud của công ty hoặc đến model do bạn tự host. Bạn cũng có thể giảm lượng dữ liệu được gửi bằng cách giới hạn những gì agent được phép đọc. Tắt analytics và error reporting hoàn toàn không ảnh hưởng đến luồng này.
Làm cách nào để xem coding agent kết nối đến những host nào?
Chạy agent bằng một Linux user riêng, sau đó capture TLS ClientHello của mọi connection mới trong khi bạn sử dụng agent: sudo tshark -i any -f 'tcp port 443' -Y 'tls.handshake.type == 1' -T fields -e ip.dst -e tls.handshake.extensions_server_name. Mỗi connection sẽ có một dòng, gồm destination address và hostname được yêu cầu. Đối chiếu các hostname này với sudo tcpdump -ni any 'udp port 53', đồng thời capture trên any vì local resolver stub trên 127.0.0.53 sẽ xử lý query trước. Hãy capture khi agent đang thực hiện công việc thực tế. Các ping lúc startup và crash report sẽ không xuất hiện trong capture khi agent đang idle.
Proxy của tôi không hiển thị traffic khi agent hoạt động. Đã xảy ra lỗi gì?
Hoặc client bỏ qua HTTP_PROXY và HTTPS_PROXY, hoặc client pin certificate rồi từ chối CA của bạn. Trước tiên hãy kiểm tra đường đi bằng curl: nếu curl truy cập Internet thông qua proxy nhưng agent không xuất hiện trong flow list, agent không sử dụng proxy environment variable. Một số runtime yêu cầu cung cấp CA theo cách cụ thể. Đặc biệt, Node chỉ đọc NODE_EXTRA_CA_CERTS khi process khởi động, nên export biến này sau khi launch agent sẽ không có tác dụng. Khi proxy không thể thấy traffic, hãy chuyển sang packet capture vì không có setting nào của application có thể bypass cách này.
Tắt telemetry có ngăn code của tôi được dùng để training không?
Không. Analytics và crash reporting là một flow khác với inference. Vì vậy, việc tắt chúng chỉ loại bỏ usage counter và stack trace, còn mọi prompt vẫn được gửi đến model như trước. Việc các prompt đó có được lưu giữ hay không, và có được dùng để train model trong tương lai hay không, phụ thuộc vào điều khoản của plan bạn đang dùng. Consumer plan và commercial plan thường có điều khoản khác nhau. Đây là vấn đề cần đọc trong hợp đồng, không phải vấn đề cần capture packet. Hãy kiểm tra trang data usage của plan bạn đang dùng. Khi cần, hãy thỏa thuận commercial hoặc zero-retention trước khi bắt đầu session đầu tiên.