Harness Academy
Tài liệu gốc 08 15 phútTiếng Việt + English
running_the_code/06_credentials.mdXem bản gốc
Ngôn ngữ bài đọc
Bản dịch tiếng Việt

Bản dịch đầy đủ theo đúng cấu trúc tài liệu; code, liên kết và định danh kỹ thuật được giữ nguyên.

Thông tin xác thực - một bề mặt cấu hình

Settings (config/settings.py) là nguồn sự thật duy nhất cho mọi decode thông tin xác thực đều được giữ. Không có gì khác đọc một. Vì vậy chỉ có một câu hỏi thú vị duy nhất -- làm thế nào một giá trị được vào Settings? — và DECODE_ENV là toàn bộ câu trả lời:

DECODE_ENVChuỗi nguồn (cao nhất đầu tiên)
local (mặc định)xử lý môi trường → .env → mặc định. Kitaru không bao giờ được nhập khẩu.
dev / staging / prodxử lý env → Nhóm môi trường (decode-<env>) → mặc định. .env bị loại bỏ hoàn toàn khỏi chuỗi.

Một bề mặt, hai cơ chế phun, được chọn bởi một biến (ADR-0015). Giá trị đất ở Settings chỉ - không bao giờ os.environ — vậy là một model đã được chọn bash không bao giờ thừa hưởng một.

Câu hỏi thứ hai, nhỏ hơn, được giữ riêng biệt: hộp cát* nhận được giá trị nào trong số đó?* Chính xác là một, chọn tham gia - SANDBOX_GIT_TOKEN (Phần 2). Mọi thứ khác vẫn ở trong quá trình khai thác.

Kitaru chỉ bằng một dòng: Nhóm Môi trường là một bí mật của Kitaru — bí mật duy nhất get_secret đường may trong cơ sở mã (ADR-0015 §6). (MODAL_PROXY_TOKEN_ID / _SECRET không liên quan: Tiêu đề xác thực điểm cuối của Modal - xem 02_modal_endpoints.md)

Phần còn lại là hướng dẫn sử dụng e2e. Mọi trường hợp đều là A/B: cùng một lệnh với một thứ được đảo ngược và một khác nhau có thể quan sát được. Phần 4 là backstop tự động - các yêu cầu tương tự, không có mạng, không PAT, không Kitaru.

Ngắt sạch — Proxy thông tin xác thực đã biến mất (lý do: ADR-0016; hành vi khóa cũ: Phần 2c). Cái thay thế nó là Phần 2 - một token, được chèn trực tiếp, cả hai phần phụ trợ và cảnh báo trung thực rằng mô hình có thể đọc được nó.

Phần 0 - điều kiện tiên quyết

cp .env.example .env                          # set GEMINI_API_KEY
uv run kitaru secrets list                    # Parts 1b+ only: must print a (possibly empty) list, not hang
docker info >/dev/null && echo "docker ok"    # Part 2 only

Phần 1a không cần ** điều đó — tại DECODE_ENV=local (mặc định) decode không bao giờ chạm vào Kitaru hoặc Docker.

Nếu kitaru secrets list thử lại HTTPConnection(host='127.0.0.1', port=8383) … Connection refused, được lưu trữ trạng thái xác thực từ trước đó kitaru login đang trỏ đến một máy chủ không còn chạy nữa. Chọn một trong hai:

uv run kitaru logout    # no server at all — the server-less local database (simplest)
uv run kitaru login     # or bring the local server + web dashboard back up on 127.0.0.1:8383

Trên macOS, máy chủ Kitaru cục bộ cũng có thể ngừng hoạt động giữa chừng khi hủy bỏ an toàn phân nhánh ObjC - OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES bản sửa lỗi nằm trong 03_runtime.md.

Phần 1 - bề mặt cấu hình (DECODE_ENV)

** Đến từ một người lớn tuổi hơn .env (ADR-0015 §4)?** RUNTIME_SECRET_NAME, RUNTIME_SECRET_STORE_CONFIG và RUNTIME_SECRET_STORE_MODEL_KEY bị xóa, và pydantic extra="ignore" có nghĩa là một dòng cũ trong của bạn .env bị âm thầm bỏ qua — quá trình decode bắt đầu và không bao giờ cho bạn biết cái núm đó không làm gì cả. Di chuyển: make sync-secrets ENV=<env> một lần rồi DECODE_ENV=<env> trong mỗi lần chạy. Bí mật cũ cho mỗi thông tin xác thực là trọng lượng chết: uv run kitaru secrets delete <name>.

DECODE_ENV quyết định ở đâu Settings nhận các giá trị của nó và không có gì khác — không phải thư mục phiên, không phải đường dẫn nhật ký, không MEMORY.md. Đây là biến bootstrap, vì vậy nó được đọc ngoài băng tần (của bạn .env tập tin, được phủ bởi process env) trước khi chuỗi được xây dựng.

1a. TẮT — local, và bất biến đi kèm với nó

Xác nhận quyền sở hữu: ở env mặc định, decode hoàn toàn không nhập kitaru. Đó là một lớp lót để kiểm tra, và tương tự one-liner là mặt B của A/B:

uv run python -c "
import sys, decode.cli
print('kitaru imported:', any(m.split('.')[0] == 'kitaru' for m in sys.modules))
from decode.config.settings import settings
print('DECODE_ENV =', settings.decode_env, '| opik project =', settings.opik_project_name)"
# → kitaru imported: False
# → DECODE_ENV = local | opik project = decode-local

DECODE_ENV=staging uv run python -c "
import sys, decode.cli
print('kitaru imported:', any(m.split('.')[0] == 'kitaru' for m in sys.modules))
from decode.config.settings import settings
print('DECODE_ENV =', settings.decode_env, '| opik project =', settings.opik_project_name)"
# → kitaru imported: True
# → DECODE_ENV = staging | opik project = decode-staging

Đang làm việc: False tại local, True ở một môi trường xa xôi. Điều nhập khẩu thứ hai đó là chi phí của môi trường - một Ngăn xếp ZenML và liên lạc mạng trước lời nhắc đầu tiên - và đó chính xác là lý do local là mặc định. Lưu ý tác dụng phụ miễn phí: dự án Opik tuân theo môi trường (decode-local / decode-staging), vậy dấu vết tự sắp xếp. Đặt OPIK_PROJECT_NAME một cách rõ ràng và giá trị của bạn luôn chiến thắng.

local đọc .env và không có gì để phản ánh, vì vậy tập lệnh đồng bộ từ chối hoàn toàn:

make sync-secrets ENV=local
# → Error: `local` reads your .env directly — there is nothing to sync. Pick dev, staging or prod.

1b. BẬT - gương .env vào Nhóm Môi trường

Tên nhóm có nguồn gốc ** (decode-<env>); không có nút ghi đè nên "DECODE_ENV=staging chỉ vào nhóm sản phẩm" không thể trình bày được. Một lệnh viết nó:

make sync-secrets ENV=staging       # → uv run python scripts/sync_secrets.py --env staging
Mirroring .env → decode-staging (key names only; values are never printed).
decode-staging does not exist yet — it will be created.
Skipped (not a Settings field): MODAL_TOKEN_ID
  + GEMINI_API_KEY
  + OPENROUTER_API_KEY
This REPLACES the entire contents of decode-staging with these 2 key(s) — `kitaru secrets set` overwrites the whole key set.
Proceed? [y/N]:

Mỗi dòng đầu ra đó là một quyết định thiết kế:

  • Chỉ tên khóa, không bao giờ có giá trị — trong phần khác biệt, phần xác nhận, thậm chí là lỗi kitaru (tiêu chuẩn của nó là được biên tập lại trước khi in).
  • THAY THẾ — kitaru secrets set ghi đè bộ khóa toàn bộ, do đó, nhóm chính xác là một gương tập tin của bạn; một khóa bạn xóa khỏi .env sẽ biến mất trong lần đồng bộ tiếp theo.
  • Phím Bỏ qua thì không Settings các trường (MODAL_TOKEN_ID, …) - đọc từ os.environ, cái xô không bao giờ có thể cho chúng ăn.
  • Một chiều — .env → Kitaru, không bao giờ quay lại: đổ thùng sản phẩm vào cây làm việc của nhà phát triển là thất bại thiết kế này tồn tại để ngăn chặn. --yes bỏ qua lời nhắc (CI).

Xác nhận nó đã hạ cánh (chỉ tên - --show-values tồn tại và bạn không cần nó):

uv run kitaru secrets list                    # → decode-staging: … (private)
uv run kitaru secrets show decode-staging     # metadata + key names

1c. BẬT - chạy ngược lại với thùng, không có chìa khóa trong môi trường của bạn

env -u GEMINI_API_KEY DECODE_ENV=staging uv run decode run "say hi in exactly three words"
env -u GEMINI_API_KEY DECODE_ENV=staging uv run decode                    # the TUI, identically

Đang làm việc: nó trả lời. Không có khóa nhà cung cấp nào trong quá trình env, .env không có trong chuỗi và không có gì viết cho os.environ - toàn bộ bề mặt được hydrat hóa thành Settings từ decode-staging tại Singleton xây dựng, do đó TUI và dòng chảy headless hoạt động giống hệt nhau (quá trình hydrat hóa nằm trong phạm vi quy trình, không phải là chuyển đổi chỉ không có đầu).

1d. Tiêu cực - bốn cách này phải thất bại (và thắng)

LệnhLàm việc trông giống như
Thiếu nhóm (hoặc máy chủ cục bộ Kitaru ngừng hoạt động): DECODE_ENV=prod uv run decode run "hi"MỘT dòng stderr thân thiện, exit 1, no traceback — và nó đặt tên cho bản sửa lỗi, không phải khóa bị thiếu: Decode: DECODE_ENV=prod nhưng không thể tải nhóm môi trường 'decode-prod' (nó bị thiếu hoặc máy chủ cục bộ Kitaru không hoạt động) — chạy make sync-secrets ENV=prod (xem Running_the_code/06_credentials.md).
Tương tự, trong TUI: DECODE_ENV=prod uv run decodeDòng giống, lối ra 1 - REPL được bảo vệ trước khi bắt đầu. Cả hai bề mặt hoặc nó không phải là bề mặt cấu hình.
Không chèn lấp: xóa GEMINI_API_KEY từ xô (make sync-secrets ENV=staging sau khi loại bỏ nó khỏi .env), đặt nó trở lại .env, sau đó env -u GEMINI_API_KEY DECODE_ENV=staging uv run decode run "hi"Decode: set GEMINI_API_KEY in your environment or .env to start (see .env.example). — nó bị lỗi lớn tiếng mặc dù chìa khóa vẫn ở ngay trong đó .env. Tập tin đó không có trong chuỗi ở một env từ xa. Đây chính là mục đích của việc có môi trường: máy tính xách tay của nhà phát triển không được che giấu khoảng trống cung cấp.
Quy trình thắng env: GEMINI_API_KEY=<a-real-key> DECODE_ENV=staging uv run decode run "hi"Nó trả lời bằng cách sử dụng phím của bạn — luôn được ưu tiên process env > (.env | bucket) > defaults. Tiện dụng để ghi đè một lần; cũng là lối thoát khi chìa khóa xô đã cũ.

Phần 2 - token git hộp cát (SANDBOX_GIT_TOKEN)

Một núm, một cơ chế, cả hai phụ trợ (ADR-0016 §2). Đặt nó và token nhập env của Worker dưới dạng GITHUB_TOKEN:

giá trị được đưa vào như thế nàonó mua gì
dockera không có giá trị -e GITHUB_TOKEN trên docker run; giá trị dựa trên docker **env của khách hàng **, vì vậy nó không bao giờ nằm trong argv hiển thị trên máy chủ (không ps, không có lỗi hiển thị nào có thể đọc được)model có thể git push / gh pr create từ bên trong /workspace
modala modal.Secret trên hộp cátgiống nhau
nonen/a — none là máy chủ lưu trữ, với thông tin xác thực xung quanh của riêng bạnkhông có

Cả hai phần phụ trợ sau đó xâu chuỗi giống nhau git credential-helper (x-access-token:$GITHUB_TOKEN), vậy là git Xác thực vận chuyển HTTPS; gh đọc GITHUB_TOKEN nguyên bản. Không đặt hoặc để trống → no env var, no người trợ giúp thông tin xác thực và docker run argv byte giống hệt với trường hợp không có token. Hộp cát không chứa được thông tin xác thực cả.

⚠️ Quy trình đóng hộp cát CÓ THỂ đọc $GITHUB_TOKEN. Đó là chi phí cố ý của việc xóa Thông tin xác thực Proxy, được nêu rõ ràng (ADR-0016, Hậu quả). Một lời nhắc được tiêm agent có thể echo $GITHUB_TOKEN. Biện pháp giảm thiểu là chính sách, không phải mã: đưa nó chi tiết, có phạm vi repo, có thể thu hồi PAT — không bao giờ là PAT có phạm vi rộng hoặc toàn tổ chức — và thu hồi nó khi bạn xong. Bạn muốn tài sản mạnh hơn? Rời đi SANDBOX_GIT_TOKEN bỏ đặt và nhận lại sự hỗ trợ từ phía máy chủ (2a).

token đến từ đâu? Bề mặt một cấu hình: .env tại local, cái xô ở điều khiển từ xa env — sandbox_git_token() đọc đã ngậm nước Settings, không cần tra cứu lần thứ hai. Phản chiếu nó giống như bất kỳ cái nào khác chìa khóa và DECODE_ENV=staging SANDBOX_MODE=docker decode run --repo … hoạt động với token vắng mặt trong vỏ hoàn toàn.

Nơi lệnh gọi công cụ thực sự chạy. Chỉ bash (và các công cụ tìm kiếm/tập tin) chạy bên trong Worker. web_fetch chạy phía máy chủ - đơn giản httpx gọi trong quá trình decode (tools/web.py) — vì vậy nó nhìn thấy mạng của máy chủ và không thấy môi trường nào của hộp cát. Yêu cầu model "NHẬN URL này" và nó sẽ vươn tới web_fetch, không bao giờ chạm vào Công nhân hoặc GITHUB_TOKEN. Nếu bạn muốn tập luyện hộp cát, nói "sử dụng công cụ bash" trong lời nhắc và kiểm tra nhật ký chạy running tool: bash.

2a. TẮT - mặc định: không có thông tin xác thực trong hộp cát và nhánh vẫn quay trở lại

rm -rf .decode/sandbox        # ← REQUIRED before any --repo run; see the box below

env -u SANDBOX_GIT_TOKEN SANDBOX_MODE=docker \
  uv run decode run --repo https://github.com/<you>/<repo> \
  "create NOTES.md with one line and commit it"

Đang hoạt động: mô hình cam kết bên trong /workspace, và khi hoàn thành tay lại đẩy một decode/<session-id> nhánh tới repository của bạn — với thông tin xác thực git máy chủ xung quanh của bạn, bởi vì mọi lệnh git chuyển giao đều là một host quy trình con chống lại .decode/sandbox (sandbox/handback.py, ADR-0012 §8). Chứng minh Công nhân không có thông tin xác thực trong quá trình chạy từ một terminal thứ hai:

WORKER=$(docker ps -q --filter ancestor=ghcr.io/astral-sh/uv:python3.12-bookworm-slim)
docker exec $WORKER env | grep -i token     # EMPTY — no GITHUB_TOKEN, nothing
docker exec $WORKER git config --global --get credential.helper   # EMPTY — no helper either

Đây là chế độ mặc định an toàn và nó đã gửi lại tác phẩm của bạn. Bạn chỉ cần Phần 2b cho câu hỏi lớn hơn: để chính mô hình đẩy và mở PR.

rm -rf .decode/sandbox trước bất kỳ --repo chạy - không tùy chọn

--repo chỉ sao chép vào một Vùng làm việc trống (04_sandboxing.md); một nơi đông dân cư .decode/sandbox được tái sử dụng và --repo lặng lẽ phớt lờ — không origin, và lực đẩy kết thúc với một bối rối 'origin' does not appear to be a git repository. Xác nhận trước khi bạn đổ lỗi cho token: git -C .decode/sandbox remote -v — một bản sao có một origin, một cây cào còn sót lại không có.

2b. BẬT - mô hình đẩy nhánh và tự mở PR

rm -rf .decode/sandbox        # ← 2a left a populated Workspace behind

SANDBOX_GIT_TOKEN=<fine-grained-PAT> SANDBOX_MODE=docker \
  uv run decode run --repo https://github.com/<you>/<repo> \
  "create NOTES.md with one line, commit it, push the branch, then open a PR against main"

Đang hoạt động: nhánh được đẩy và PR mở ra — từ bên trong hộp cát, ngoài token được chèn. Cả hai git và gh được cài đặt trong Worker (docker cài đặt chúng mỗi phiên; modal đưa chúng vào một lớp hình ảnh được lưu trong bộ nhớ cache), vì vậy git push xác thực thông qua người trợ giúp thông tin xác thực và gh pr create tắt GITHUB_TOKEN. Xác minh việc tiêm trong quá trình chạy:

WORKER=$(docker ps -q --filter ancestor=ghcr.io/astral-sh/uv:python3.12-bookworm-slim)
docker exec $WORKER env | grep GITHUB_TOKEN                       # → GITHUB_TOKEN=<your PAT>  ← YES, it prints
docker exec $WORKER git config --global --get credential.helper   # → the x-access-token helper
ps aux | grep -F "<your-PAT>"                                     # EMPTY — never in a host argv

Dòng đầu tiên in PAT của bạn là kết quả mong đợi và đó là toàn bộ chi phí trung thực của thiết kế này: Công nhân giữ token, vì vậy mọi thứ chạy trong Công nhân đều có thể đọc được. Dòng thứ ba là một thuộc tính đường dẫn docker vẫn mua bạn - giá trị phụ thuộc vào env của docker client, vì vậy nó không nằm trong docker runargv của và không thể được loại bỏ khỏi bảng tiến trình máy chủ.

SANDBOX_MODE=modal là câu chuyện tương tự với một modal.Secretvà cảnh báo tương tự cũng được áp dụng (token nằm trong env của hộp cát từ xa).

2c. Tiêu cực - bằng mọi cách nó phải tránh xa

LệnhLàm việc trông giống như
SANDBOX_GIT_TOKEN= SANDBOX_MODE=docker uv run decode run "hi" (rõ ràng trống)không có gì được thêm vào — kiểm soát giá trị, không hiện diện: không GITHUB_TOKEN, không có trình trợ giúp thông tin xác thực và một argv giống hệt với trường hợp chưa đặt
SANDBOX_GIT_TOKEN=<PAT> uv run decode run "hi" (chế độ none)không được — none là chủ nhà; không có Worker env nào để đưa vào
Một cái cũ SANDBOX_CREDENTIAL_PROXY_ENABLED=true / SANDBOX_PROXY_IMAGE=… trong của bạn .envbị bỏ qua trong im lặng — cả hai khóa đều bị xóa (ADR-0016 §1); không có vùng chứa proxy nào tồn tại để bắt đầu. docker ps cho thấy Người lao động ở một mình.
Trình nền Docker đã dừng lại, SANDBOX_MODE=dockermột dòng stderr thân thiện, thoát khác 0, không có luồng nào được xây dựng

Phần 3 - dọn dẹp

rm -rf .decode/sandbox                                # the Workspace Part 2 left behind
uv run kitaru secrets delete decode-staging           # the bucket, if you ran Part 1b
docker ps -a --filter ancestor=ghcr.io/astral-sh/uv:python3.12-bookworm-slim   # empty — the Worker is reaped

Và thu hồi PAT mà bạn đã trao sandbox ở 2b. Nó có phạm vi và có thể thu hồi được - đó chính là vấn đề.

Phần 4 - backstop tự động

Mọi thứ ở trên được bao phủ mà không cần PAT và không có mạng:

# Environment Bucket — the chain per DECODE_ENV, the no-backfill property, the captured failure.
uv run pytest tests/unit/decode/config/test_env_bucket.py \
              tests/unit/decode/config/test_settings.py \
              tests/unit/decode/config/test_env_example_drift.py -v

# The sync script — full-surface replace, key-names-only output, one-way, the local refusal.
uv run pytest tests/unit/scripts/test_sync_secrets.py -v

# Token injection — the value-less -e passthrough, the shared credential helper, the empty-token
# byte-identical argv. Both backends, no docker, no modal.
uv run pytest tests/unit/decode/sandbox/test_docker_backend.py \
              tests/unit/decode/sandbox/test_modal_backend.py \
              tests/unit/decode/sandbox/test_workspace.py -v

# The capstone's one-mechanism claims: both backends share ONE helper + ONE gate; unset → no machinery.
uv run pytest tests/integration/test_sandbox_capstone.py -k token -v

test_sandbox_capstone.py khẳng định các tuyên bố thủ công của Phần 2: một hằng số trợ giúp, một cổng token, bí mật trong no argv; chưa đặt → không có máy móc xác thực; và, khi một docker daemon hoặc thông tin xác thực Modal tồn tại, tiêm trực tiếp bằng token giả (không có lệnh gọi GitHub). test_env_example_drift.py là tại sao .env.example không thể nói dối: nó KEY= dòng và Settings các trường phải khớp trong cả hai chỉ dẫn — điều này cũng đảm bảo rằng các khóa proxy đã ngừng hoạt động thực sự đã biến mất.

Mục lục nguồn