Tự host HarnessRouter: một API cho nhiều agent
Chạy Codex, Claude Code và Hermes qua một API tự host. Xem lệnh Docker, bind loopback, tài khoản mặc định cần đổi và cách truy cập qua TLS.
HarnessRouter loại bỏ những gì
Bạn tự host HarnessRouter Community Edition để đặt một API phía trước nhiều agent harness trên server do bạn sở hữu. Agent harness là chương trình dòng lệnh điều khiển model trong một vòng lặp: chương trình duy trì session, chỉnh sửa file, chạy command và stream tiến độ về nơi đã yêu cầu thực hiện công việc. Codex, Claude Code và Hermes đều làm nhiệm vụ đó. Mỗi công cụ có cách cài đặt riêng, định dạng credential riêng và cách hiểu riêng về session. HarnessRouter chạy tất cả trong một container và cung cấp một HTTP endpoint, một lần đăng nhập cùng một secret store duy nhất ở phía trước.
Đó là toàn bộ ý tưởng. Chi phí của cách này cần được nói rõ. Bạn thêm một container, một lần đăng nhập, một volume và một quy trình nâng cấp vào server để biến nhiều thành phần thành một. Nếu hiện tại bạn chỉ chạy đúng một harness, cách này kém hơn việc cài trực tiếp harness đó. Phần cuối sẽ phân tích đánh đổi này, vì vậy hãy đọc trước khi deploy.
Toàn bộ nội dung dưới đây đã được kiểm tra với image tag 0.5.5, được pull vào ngày 19 August 2026. Project phát hành tag mới hầu hết các ngày, vì vậy hãy kiểm tra tag bạn thực sự đang chạy thay vì tin rằng trang này vẫn chính xác sau một tháng. Vào ngày 12 October 2026, trang này được kiểm tra lại theo tài liệu hiện tại của project. Khi đó, latest trỏ đến 0.32.9, và nội dung cũng nêu rõ những điểm mà các release hiện tại hoạt động khác. Các command lấy từ repository của project tại github.com/HarnessRouter/harnessrouter. Chúng nằm trong README tại 0.5.5; chi tiết thiết lập hiện nằm trong docs/self-hosting-guide.md của cùng repository.
Unified Harness Protocol thực sự là gì
HarnessRouter triển khai Unified Harness Protocol (UHP), được công bố tại unifiedharnessprotocol.org. UHP mô tả cách một sản phẩm khởi chạy tác vụ trên một harness, theo dõi tác vụ trong khi tác vụ chạy, quản lý session và file, cũng như báo cáo lỗi. Đặc tả được đánh version theo ngày. Version đang được dùng vào ngày 19 August 2026 có ngày 2026-08-11. Tính đến ngày 12 October 2026, website liệt kê 2026-10-04 là version hiện tại và vẫn gọi đây là một draft standard, “đủ ổn định để xây dựng trên đó và được version hóa để có thể thay đổi an toàn”.
Hãy đọc kỹ cụm từ “open standard” trong trường hợp này. Cùng một công ty viết đặc tả, reference implementation và conformance suite dùng để xác định thành phần nào đáp ứng chuẩn. Quy tắc quản trị của dự án cũng quy định rằng không thay đổi nào được đưa vào nếu cả ba thành phần không được cập nhật cùng lúc. Tính đến ngày 12 October 2026, website của protocol mô tả suite này có 95 kiểm tra có thể chạy. Trong khi đó, lần chạy tham chiếu được ghi trong self-hosting guide, có ngày 4 September 2026, đã vượt qua 64 kiểm tra. Vì vậy, chính số lượng kiểm tra vẫn đang thay đổi. Đây là điều bình thường đối với một protocol còn mới như vậy. Giấy phép Apache-2.0 cho phép bạn fork bất kỳ phần nào của protocol. Tuy nhiên, điều đó cũng có nghĩa UHP chưa phải là một standard đa vendor. Hãy xem UHP là một protocol đang hình thành: hữu ích, còn thay đổi, và code của bạn nên có khả năng ngừng sử dụng nó mà không cần viết lại.
Những thứ cần chuẩn bị trước khi bắt đầu
Docker và khoảng 4 GB dung lượng đĩa trống. Bạn cũng cần API key của một model provider mà bạn đã trả phí. Việc pull image cần khoảng 700 MB, phần dung lượng còn lại dành cho các agent CLI và workspace mà chúng ghi dữ liệu vào. Image không kèm model hoặc trial key, nên task sẽ fail cho đến khi bạn kết nối một provider. Bản thân HarnessRouter được cấp phép theo Apache-2.0. Các agent CLI không thuộc license đó, nên chúng được fetch ở lần khởi động đầu tiên thay vì được đóng gói trong image.
Tự host HarnessRouter bằng một lệnh docker run
docker pull harnessrouter/harnessrouter
docker run -d --name harnessrouter \
-p 127.0.0.1:3000:3000 \
-v harnessrouter:/data \
harnessrouter/harnessrouterSau đó theo dõi container khởi động. Lần khởi động đầu tiên chậm, và log sẽ cho biết lý do.
docker logs -f harnessrouterKhi hoạt động, bạn sẽ thấy các dòng tương tự như sau:
installing Claude Code (Anthropic's terms apply)…
installing Codex (Apache-2.0)…
installing Hermes (check its upstream license before use)…Chờ ready on :3000. Hướng dẫn hiện tại cho biết console cần thêm khoảng nửa phút sau dòng đó thì http://localhost:3000 mới phản hồi. Các CLI đã cài được lưu trong volume, nên những lần khởi động sau thường nhanh hơn nhiều. Tuy nhiên, backend bạn thêm sau đó, hoặc backend trước đó cài thất bại, vẫn có thể kích hoạt quá trình cài đặt.
Có 2 điểm cần lưu ý từ lượt tải xuống này, và cả 2 đều quan trọng trên VPS. Thứ nhất, lần boot đầu tiên cần quyền truy cập mạng outbound. Image không tự chứa mọi thành phần cần thiết. Vì vậy, máy nằm sau egress filter hoặc không có route ra ngoài sẽ không thể tải các agent CLI. Trên 0.5.5 được kiểm thử ở đây, điều đó biểu hiện bằng việc lần khởi động đầu tiên không bao giờ in ra ready on :3000. Đây là thời điểm khó phát hiện lỗi, vì docker pull đã hoạt động trước đó. Các release hiện tại xem việc cài đặt thất bại là lỗi không nghiêm trọng và ghi rõ trong log. Hãy kiểm tra những backend bị bỏ qua trong dòng backends available:. Thứ hai, bạn đang cài phần mềm bên thứ ba theo điều khoản của bên thứ ba. Claude Code được cung cấp theo điều khoản của Anthropic, còn Hermes theo điều khoản do upstream của nó quy định. Hãy kiểm tra cả 2 trước khi sử dụng cho mục đích thương mại.
-v harnessrouter:/data tạo một Docker volume có tên. Mọi dữ liệu cần giữ lại đều nằm trong /data: database SQLite, file đã lưu, secret store và workspace của agent. Xóa volume đó đồng nghĩa với xóa instance, bao gồm provider key và toàn bộ transcript. Hãy backup khi container đã dừng, vì sao chép database SQLite trong lúc database đang được ghi có thể tạo ra file không mở được. Quy tắc dừng rồi mới sao chép cũng áp dụng cho mọi container có state trên máy, nhưng chi tiết sẽ khác nhau tùy service, vì PhotoPrism và Immich đều cần lệnh backup riêng.
docker stop harnessrouter
docker run --rm -v harnessrouter:/data -v "$PWD":/backup alpine \
tar czf /backup/harnessrouter-data.tgz -C / data
docker start harnessrouterBiến thể compose và dòng bạn phải thay đổi
Repository có một file compose. File này publish "3000:3000", nghĩa là service bind trên mọi interface của host. Hãy thay đổi dòng đó trước khi khởi động trên server public.
services:
harnessrouter:
image: harnessrouter/harnessrouter:0.5.5
ports:
- "127.0.0.1:3000:3000"
env_file:
- .env
volumes:
- harnessrouter-data:/data
restart: unless-stopped
volumes:
harnessrouter-data:Có 2 điểm khác upstream: địa chỉ bind và version tag được pin thay cho latest. Việc pin version rất quan trọng vì đã có 16 version tag được phát hành từ ngày 9 đến ngày 18 tháng 8 năm 2026, và tốc độ phát hành vẫn chưa chậm lại: riêng ngày 12 tháng 10 năm 2026 đã có 5 tag pre-release 0.33.0 được push lên Docker Hub. Agent runtime thay đổi ngoài dự kiến sẽ khó debug. Hãy pin bản release hiện tại thay vì sao chép 0.5.5 từ trang này; vào ngày 12 tháng 10 năm 2026, giá trị đó là 0.32.9. Sau đó sao chép file environment, khóa permission của file và khởi động.
cp .env.example .env
chmod 600 .env
docker compose up -d
docker compose logs -f.env lưu provider key dưới dạng plain text, vì vậy mode 600 là mức tối thiểu. Nếu subcommand docker compose chưa quen thuộc, cheat sheet lệnh Docker Compose trình bày các lệnh thường dùng hằng ngày.
Vì sao port được publish trên 127.0.0.1 thay vì 0.0.0.0
-p 3000:3000 publish port trên mọi interface mà host có. -p 127.0.0.1:3000:3000 chỉ publish port trên loopback, nghĩa là chỉ có thể truy cập từ chính VPS. Container luôn lắng nghe trên port 3000 ở bên trong, nên bạn chỉ cần thay phần bên trái. Kiểm tra kết quả:
docker port harnessrouter
sudo ss -ltnp | grep 3000ss hiển thị 127.0.0.1:3000 là đúng. 0.0.0.0:3000 có nghĩa là console đang nằm trên Internet công cộng. Trong trường hợp này, điều đó nguy hiểm hơn hầu hết các app tự host khác, vì console tạo harness, đọc mọi transcript, chạy agent và cấp cho các agent đó một shell cùng filesystem thật trong workspace của chúng. Console cũng lưu provider key mà bạn đã kết nối. Bất kỳ ai truy cập được console khi chưa có bảo vệ đều có thể đọc công việc của bạn, chạy command và tiêu tốn key của bạn.
Host firewall không thể bảo vệ bạn khỏi việc này. Docker publish port bằng cách tự ghi rule vào bảng nat của kernel. Các rule này được xử lý trước chain mà ufw quản lý, nên port đã publish vẫn có thể truy cập ngay cả khi sudo ufw status liệt kê port đó là bị deny. Hãy kiểm tra từ một máy khác, không phải từ VPS; nếu không, bạn sẽ không kiểm tra được gì. Đây cũng là bài học giống như chạy dsh headless trên port 3080: bind service vào loopback, rồi chủ động quyết định cách bạn truy cập service đó.
Đổi thông tin đăng nhập mặc định trước khi làm bất kỳ việc gì khác
Đăng nhập tại http://localhost:3000 bằng username harnessrouter và password harnessrouter. Các thông tin này được ghi trong README vì chúng chỉ là giá trị giữ chỗ, không phải thông tin bí mật. Container sẽ cảnh báo mỗi lần khởi động cho đến khi bạn đổi chúng:
using the DEFAULT password. Set HR_AUTH_PASSWORD, or change it from the profile page, before exposing this instance.Đổi thông tin này trên trang Profile hoặc đặt khi khởi động để triển khai bằng script. HR_AUTH_USER và HR_AUTH_PASSWORD ghi đè các giá trị mặc định.
docker run -d --name harnessrouter \
-p 127.0.0.1:3000:3000 \
-v harnessrouter:/data \
-e HR_AUTH_USER='you' \
-e HR_AUTH_PASSWORD='the-password-you-chose' \
harnessrouter/harnessrouterKhông có email đặt lại password vì hệ thống không có account và mail server. Nếu bạn quên password, hãy xóa file xác thực trong volume rồi khởi động lại. Sau đó đăng nhập lại bằng thông tin mặc định.
docker stop harnessrouter
docker run --rm -v harnessrouter:/data alpine rm -f /data/selfhost-auth.json
docker start harnessrouterHR_AUTH_DISABLED=1 tắt hoàn toàn bước yêu cầu đăng nhập. Tài liệu self-hosting gọi tùy chọn này là “chỉ hợp lý trên một máy mà không ai khác có thể truy cập”. VPS có địa chỉ IP public không phải là máy như vậy. Vì thế, hãy để bật bước yêu cầu đăng nhập trừ khi bạn chạy ứng dụng trên laptop.
Kiểm tra version của bạn vì các bản cũ không có lớp kiểm soát truy cập
Đây là phần cần đặc biệt lưu ý. Các version 0.1.x và 0.2.0 được phát hành mà hoàn toàn không có lớp xác thực: bất kỳ ai truy cập được port 3000 đều đã vào được console. 0.3.0 là release đầu tiên có màn hình đăng nhập. Các tag cũ đó vẫn được publish và vẫn có thể pull tính đến ngày 12 October 2026. Vì vậy, một tag cũ được pin hoặc một file compose do đồng nghiệp sao chép có thể khiến console không có kiểm soát truy cập chạy trên public port ngay hôm nay.
Tính đến ngày 19 August 2026, tag mới nhất được publish là 0.5.5, có ngày phát hành 18 August 2026. Vào ngày 12 October 2026, latest trỏ đến 0.32.9, được push vào ngày 11 October 2026; các release candidate 0.33.0 cũng đã được publish cùng thời điểm. Kiểm tra version đang dùng, sau đó so sánh với danh sách tag trên Docker Hub:
docker image ls harnessrouter/harnessrouterMọi version thấp hơn 0.3.0 phải được thay thế ngay, không được để lên lịch sau. Các version bằng hoặc cao hơn version đó vẫn cần đổi password, vì với người đang quét port 3000, password mặc định và không có password chẳng khác gì nhau. Không xem các version trên trang này là version hiện tại. Chúng chỉ đúng tại các thời điểm đã nêu, và project này phát hành version rất nhanh.
Kết nối provider
Không có gì chạy cho đến khi kết nối một model provider. Thêm một provider từ trang Integrations trong console, hoặc truyền provider đó cho docker run qua environment. Giá trị này là JSON, vì vậy hãy đặt nó trong dấu nháy khi chạy lệnh trong shell:
-e HR_SECRET_GLOBAL_HARNESS_CONN_ANTHROPIC='{"name":"anthropic","provider":"anthropic","api_key":"sk-ant-…"}'.env.example khai báo một biến connection cho từng nhóm provider: HR_SECRET_GLOBAL_HARNESS_CONN_ANTHROPIC cho backend claude-code, HR_SECRET_GLOBAL_HARNESS_CONN_OPENAI cho backend codex, HR_SECRET_GLOBAL_HARNESS_CONN_GOOGLE cho backend gemini và HR_SECRET_GLOBAL_HARNESS_CONN_CUSTOM cho mọi endpoint tương thích với OpenAI. Đây là nơi dùng aggregator hoặc inference server của bạn. Các biến HR_SECRET_GLOBAL_HARNESS_POLICY_CLAUDE, HR_SECRET_GLOBAL_HARNESS_POLICY_CODEX và HR_SECRET_GLOBAL_HARNESS_POLICY_HERMES tương ứng cho biết mỗi backend mặc định dùng connection nào. Mỗi backend cũng có một biến policy riêng. HR_SECRET_KEY là một thành phần khác. .env.example vẫn mô tả rằng biến này chỉ cần để kết nối database với agent. Tuy nhiên, guide hiện tại cũng yêu cầu bạn đặt biến này trước khi kết nối một đích HarnessRouter Cloud, sau đó giữ nguyên key này qua các lần restart và upgrade.
HR_BACKENDS chọn các backend sẽ được cài đặt và load, như trong HR_BACKENDS=claude,codex,hermes. Nếu không đặt biến này, các release hiện tại sẽ cài một danh sách mặc định dài hơn, khoảng hai chục backend, từ claude, codex và hermes đến opencode, gemini, goose cùng nhiều backend khác. Rút gọn danh sách giúp tiết kiệm dung lượng disk và thời gian khởi động lần đầu. Guide self-hosting hiện tại nói rằng hãy "bỏ backend khỏi HR_BACKENDS nếu bạn không dùng", và ví dụ tối giản của guide là HR_BACKENDS=opencode, hoàn toàn không cài Hermes. Backend cài đặt thất bại không làm hệ thống dừng hoàn toàn: các backend khác vẫn hoạt động, còn task dùng backend chưa cài sẽ fail ở lượt đầu tiên; log của container sẽ ghi rõ installation nào thất bại. Các release cũ xử lý trường hợp này kém linh hoạt hơn. README của 0.5.5 được kiểm tra ở đây có một known issue: mọi giá trị loại bỏ hermes đều khiến container thoát ngay với status 1 mà không có error message. Vì vậy, nếu dùng tag cũ như vậy, hãy để HR_BACKENDS không được đặt hoặc upgrade. Nếu Hermes là harness duy nhất bạn cần, chạy agent Hermes trên VPS riêng sẽ là deployment gọn hơn.
Gọi API mà không cần console
Console là tùy chọn. Cả hai đều dùng cùng một API và API này sử dụng contract theo kiểu Responses. Trước tiên, hãy đăng nhập để nhận session cookie:
curl -c hr.cookies http://localhost:3000/api/selfhost/login \
-H 'content-type: application/json' \
-d '{"username":"harnessrouter","password":"your-password"}'Sau đó gửi một task. Chỉ định harness trong metadata.harness_id và một model mà provider đã kết nối thực sự cung cấp:
curl -s -b hr.cookies http://localhost:3000/api/harness/v1/responses \
-H 'content-type: application/json' \
-d '{"input":"Reply with exactly this and nothing else: it works.",
"metadata":{"harness_id":"codex"},
"model":"gpt-5.4-mini",
"stream":false}'Một JSON object chứa output block và số lượng token cho biết harness đã chạy. Đổi harness_id từ codex sang claude-code sẽ gửi cùng request đến một harness khác. Việc thay đổi harness này chính là mục đích của phần mềm. Lưu ý rằng id là claude-code, dù tên backend trong HR_BACKENDS và policy variable là claude. Custom connection ở trên là cách trỏ harness đến một endpoint tương thích với OpenAI mà bạn đã tự host, giống như cách cấu hình một harness DeepSeek tự host trên VPS.
Truy cập từ laptop mà không public port
Có 2 cách, và không cách nào mở trực tiếp một port trên 0.0.0.0.
SSH tunnel là lựa chọn đơn giản nhất và không cần cài thêm gì trên server. Nó chuyển tiếp một port local trên máy của bạn đến loopback trên VPS.
ssh -N -L 3000:127.0.0.1:3000 you@your-vpsGiữ tunnel đó đang chạy rồi mở http://localhost:3000 trong trình duyệt. Nếu SSH in ra bind: Address already in use, một tiến trình trên laptop của bạn đã chiếm port 3000. Khi đó, chọn một port local khác bằng -L 3100:127.0.0.1:3000 rồi truy cập port 3100.
Reverse proxy có TLS termination phù hợp khi người khác cũng cần truy cập. Proxy giữ chứng chỉ TLS (transport layer security) và chuyển tiếp đến loopback. Hướng dẫn self-hosting có một cấu hình Caddy như sau:
console.example.com {
encode zstd gzip
reverse_proxy 127.0.0.1:3000 {
flush_interval -1 # agent turns stream for minutes; never buffer them
}
}flush_interval -1 là dòng nhiều người bỏ sót. Agent tạo stream token trong nhiều phút. Proxy buffer response sẽ giữ các token đó cho đến khi turn kết thúc, nên console trông như bị treo rồi in toàn bộ nội dung cùng lúc. Trong Nginx, thiết lập tương đương là proxy_buffering off; bên trong location block. Dù chọn cách nào, hãy trỏ DNS name đến proxy và giữ container trên loopback. So sánh Nginx, Caddy và Traefik khi dùng làm reverse proxy giải thích lựa chọn phù hợp với máy chủ của bạn.
Chạy bằng user riêng, không chạy bằng root
Docker daemon chạy bằng root. Việc là thành viên của group docker tương đương với có quyền root, vì thành viên trong group này có thể khởi động container và mount filesystem của host. Vì vậy, việc “thêm team vào docker group” đồng nghĩa với việc cấp quyền root trên máy đang lưu provider key của bạn.
Cách đơn giản là tạo một service account sở hữu compose file và .env. Không đặt các file này trong home directory dùng chung.
sudo adduser --disabled-password --gecos "" harness
sudo install -d -o harness -g harness -m 750 /srv/harnessrouterKhông cố xử lý bằng cách thêm --user vào docker run hoặc thêm user: vào compose file. Hướng dẫn hiện tại yêu cầu container phải khởi động bằng root. Từ 0.8.2, container sẽ từ chối khởi động theo cách khác và ghi rõ điều này trong một dòng của log.
Cách chặt chẽ hơn là dùng rootless Docker, trong đó chính daemon chạy bằng user không có đặc quyền đó. Cách này cần package uidmap cho newuidmap và newgidmap, cùng ít nhất 65536 subordinate UID trong /etc/subuid và /etc/subgid cho user này. uidmap có trong Ubuntu archive, nhưng docker-ce-rootless-extras thì không. Package này được phát hành từ apt repository riêng của Docker tại download.docker.com, được thêm khi cài Docker engine. Nếu bạn chưa cài engine từ repository đó, grep -rl download.docker.com /etc/apt/sources.list.d/ sẽ không in ra gì và lệnh cài đặt bên dưới sẽ không tìm thấy package.
sudo apt install -y uidmap docker-ce-rootless-extras
sudo loginctl enable-linger harness
sudo -iu harness
dockerd-rootless-setuptool.sh install
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
systemctl --user enable --now dockerloginctl enable-linger là bắt buộc trong trường hợp này. Nếu không có, systemd instance của user sẽ dừng khi session cuối cùng đóng, khiến container dừng khi bạn logout. Xác nhận kết quả bằng docker info. Lệnh này liệt kê rootless trong Security Options. Rootless mode không thể bind các port dưới 1024 nếu không cấu hình thêm. Điều này không ảnh hưởng ở đây vì port 3000 lớn hơn giới hạn đó. Cách thiết lập account được trình bày trong tạo user có quyền tối thiểu trên VPS.
Điều gì bị lỗi và bạn sẽ thấy gì
Container thoát sau một giây và log trống. docker ps -a hiển thị Exited (1). Trên 0.5.5 và các tag cũ khác, đó là vấn đề HR_BACKENDS ở trên: giá trị của bạn thiếu hermes. Thêm lại giá trị đó hoặc nâng cấp. Các bản release hiện tại không lỗi theo cách này, nên nếu đang dùng tag mới, hãy đọc dòng cuối của docker logs: từ 0.8.2, container được khởi động bằng --user sẽ thoát và hiển thị một dòng cho biết container phải được khởi động với root.
Lần khởi động đầu tiên không bao giờ hoàn tất. Log dừng sau một dòng installing và ready on :3000 không bao giờ xuất hiện. Máy chủ không thể truy cập network để tải các agent CLI vì chúng không có trong image. Trên các bản release hiện tại, triệu chứng nhẹ hơn: log liệt kê các lần cài đặt thất bại và chúng không xuất hiện trong dòng backends available:. Dù gặp trường hợp nào, hãy sửa route outbound hoặc proxy settings rồi khởi động lại.
Console tải được nhưng mọi task đều thất bại. Chưa có provider nào được kết nối. Image không kèm model và cũng không có free tier, vì vậy instance mới có thể cho phép bạn đăng nhập nhưng vẫn không chạy được gì.
Console bị treo giữa câu trả lời khi chạy sau proxy. Output chỉ xuất hiện thành một khối khi turn kết thúc. Đây là do response buffering. Đặt flush_interval -1 trong Caddy hoặc proxy_buffering off; trong Nginx.
Bạn không thể truy cập từ laptop dù tunnel đang hoạt động. Chạy docker port harnessrouter trên server. Nếu không hiển thị gì, container không publish cổng nào vì được khởi động mà không có -p.
Có đáng chạy không?
Đáng chạy nếu bạn thực sự dùng nhiều hơn một harness và muốn có một endpoint cùng một kho credential thay vì ba endpoint và ba kho credential. Cũng đáng chạy nếu bạn đang xây dựng sản phẩm trên đó và muốn harness là một giá trị cấu hình thay vì phải viết lại code. Đó là lợi ích UHP mang lại, với lưu ý ở trên về việc protocol này còn mới.
Không đáng chạy nếu bạn chỉ dùng một harness. Cài CLI đó trực tiếp trên server sẽ có ít thành phần hơn, và không có bước đăng nhập nằm giữa bạn và CLI. Đây cũng không phải mô hình phù hợp nếu bạn muốn nhiều agent phối hợp trong một task, thay vì một API đứng trước nhiều harness; đó là một công cụ khác: xem một harness multi-agent như Omnigent để biết mô hình này. Dù chọn cách nào, các quy tắc triển khai vẫn không đổi. Bind vào loopback, đổi password, dùng tag được ghim ở 0.3.0 trở lên và chạy bằng user riêng.
FAQ
Có an toàn khi public HarnessRouter trên cổng 3000 không?
Không. Console có thể tạo harness, đọc toàn bộ transcript, chạy agent với quyền truy cập shell và filesystem, đồng thời giữ provider key bạn đã kết nối. Vì vậy, một cổng mở sẽ để lộ toàn bộ các chức năng này. Hãy publish trên loopback bằng -p 127.0.0.1:3000:3000, rồi truy cập qua SSH tunnel hoặc reverse proxy có TLS termination. Chỉ dùng host firewall là chưa đủ: Docker tự ghi rule vào bảng kernel nat, nên cổng đã publish vẫn nhận kết nối từ Internet ngay cả khi ufw hiển thị cổng đó là bị chặn. Xác minh bằng sudo ss -ltnp | grep 3000; kết quả phải in ra 127.0.0.1:3000.
Phiên bản HarnessRouter nào đã thêm login gate?
0.3.0. Các phiên bản 0.1.x và 0.2.0 được phát hành mà hoàn toàn không có authentication. Tính đến 12 October 2026, cả hai vẫn được publish và vẫn có thể pull, nên người chạy các phiên bản này đang phụ thuộc vào việc không ai tìm thấy cổng đó. Tính đến 19 August 2026, tag mới nhất là 0.5.5, có ngày 18 August 2026; vào 12 October 2026, latest trỏ đến 0.32.9. Chạy docker image ls harnessrouter/harnessrouter để xem bạn đang dùng phiên bản nào. Hãy đối chiếu với danh sách tag trên Docker Hub, không phải với trang này, và đổi password mặc định ngay cả khi đang dùng phiên bản hiện tại.
Vì sao container thoát ngay sau khi tôi đặt HR_BACKENDS?
Với release hiện tại thì việc này không xảy ra. Hướng dẫn self-hosting yêu cầu bỏ qua mọi backend bạn không dùng, và việc cài đặt một backend thất bại không phải lỗi fatal. Đây là issue đã biết trong các release cũ, bao gồm 0.5.5, là phiên bản được dùng để test trang này lần đầu. Khi đó, mọi giá trị bỏ hermes đều khiến container thoát với status 1 mà không có error message. Nếu thấy Exited (1) trong docker ps -a trên một tag cũ như vậy, hãy thêm lại hermes, như trong HR_BACKENDS=claude,codex,hermes, hoặc upgrade. Với tag gần đây, hãy đọc docker logs: từ 0.8.2, container khởi động bằng --user sẽ thoát và ghi một dòng cho biết container phải khởi động với root.
HarnessRouter có cần truy cập Internet ở lần khởi động đầu tiên không?
Có. Các agent CLI được tải xuống ở lần khởi động đầu tiên thay vì được đóng gói trong image, vì mỗi CLI có license riêng. Trên 0.5.5, một máy không có route outbound đã in các dòng installing rồi không bao giờ đạt đến ready on :3000; các release hiện tại ghi rõ tên những lần cài đặt thất bại trong log. Các CLI đã cài được cache trên volume, nên những lần khởi động sau thường nhanh hơn và không cần network ngoài model provider bạn đã kết nối, trừ khi bạn bật backend mới hoặc trước đó có backend cài đặt thất bại.
Tôi quên password của console. Làm cách nào để đăng nhập lại?
Không có email reset vì không có account system và cũng không có mail server. Hãy stop container, xóa /data/selfhost-auth.json khỏi volume, start lại container, sau đó đăng nhập bằng credential mặc định và đặt password mới trên trang Profile. Khi container và volume đều có tên harnessrouter, thứ tự lệnh là docker stop harnessrouter, rồi docker run --rm -v harnessrouter:/data alpine rm -f /data/selfhost-auth.json, rồi docker start harnessrouter.