Chuyển đến nội dung
Tech Stack

Tech Stack

Các quyết định được chốt ngày 2026-09-22 sau khi nghiên cứu (plans/reports/researcher-260922-0104-*.md). Phiên bản được kiểm tra với proxy.golang.org vào ngày đó.

Các quyết định

Hạng mụcLựa chọnPhiên bảnLý do
Ngôn ngữGo1.27Stdlib có uuid.NewV7, encoding/json/v2, testing/synctest đã GA, và goroutine-leak profile.
HTTPThư viện chuẩn net/httpGo 1.27http.ServeMux khớp được method và path parameter (Go 1.22+), và r.Pattern cho biết route để đặt tên span và http.route, nên otelhttp chạy được mà không cần adapter cho framework. SSE flush và hủy khi client ngắt kết nối đều hoạt động nguyên vẹn. Nhóm route, xử lý lỗi tập trung và JSON binding là một router nhỏ trong internal/server. goclaw cũng làm như vậy.
Streaming transportServer-Sent EventsstdlibClient POST, server stream. Không cần hai chiều. Đã loại WebSocket.
LLM provider (đầu tiên)anthropic-sdk-go sau Provider interface riêngv1.74.0SDK lo việc giải mã union và gộp stream. Việc học tập trung vào loop. Interface là điểm nối để thêm provider khác.
LLM provider (thứ hai)openai-go sau cùng interfacev3.xPhủ OpenAI, Ollama, OpenRouter, vLLM qua base URL. Adapter tự tạo block start/stop.
DatabasePostgreSQL qua pgxpgx v5.11.0Đã loại SQLite hai dialect vì dễ phình scope.
Query codesqlcv1.31.1Sinh code pgx có kiểu từ internal/store/pg/queries/*.sql và các migration, từ giai đoạn 2.1. Chạy bằng go run ở phiên bản được ghim, nên không nằm trong go.mod hay binary. Một query mà sqlc không diễn đạt được có thể giữ là pgx viết tay kèm comment nêu rõ giới hạn đó.
Migrationsgoose dạng thư viện, embed.FSv3.29.0Migration SQL nhúng sẵn, một binary.
Tenancytenant_id NOT NULL trên mọi bảng, scoped store dựng từ contextn/aFail-closed. Mỗi bảng có test đọc chéo tenant. RLS với SET LOCAL làm lớp bảo vệ phụ về sau, không bao giờ thay thế.
Identityhango sở hữu tenant, user, role, API keyn/aKhông có mật khẩu hay giao diện đăng nhập. Có admin key khởi tạo. API key thuộc về một user.
Run modelMột dòng runs bền vững, được scheduler lane worker thực thi, SSE đăng ký theo dõin/aSống sót khi client ngắt kết nối, cho phép cancel, resume, HITL và graceful drain. Chỉ một replica ở v1.
MessagesDòng chỉ thêm vào (append-only), số seq, block jsonbn/aDelta theo từng turn, địa chỉ theo từng message cho compaction và subagent.
ObservabilityOpenTelemetry + log/slogotel v1.46.0Hằng số GenAI semconv của riêng hango. Nội dung LLM tắt theo mặc định.
Rate limitinggolang.org/x/time/rate theo API keylatestTrong tiến trình, có ghi rõ giới hạn chỉ một replica.
Testingtesting, testing/synctest, testcontainers-go cho Postgresv0.44.0Database thật trong integration test, không mock store.
Lintgolangci-lint v2v2.13.2errcheck, govet, staticcheck, gosec, bodyclose, sqlclosecheck.
ContainerDockerfile nhiều stage, distroless static nonrootn/aCGO_ENABLED=0, compose cùng Postgres.
Web UI (làm sau)Svelte 5n/aCho web chat và trang admin sau các giai đoạn của v1. Cả hai dùng cùng HTTP API và SSE stream như mọi client khác.

Những thứ bị loại rõ ràng

  • Các agent framework (eino, adk-go, genkit, langchaingo): chính harness là thứ cần học.
  • Echo, Gin, chi, Fiber, gorilla: router của thư viện chuẩn đủ cho các route của hango, và kiểu context của framework sẽ lan vào mọi handler và middleware. Echo v5 cũng không có OpenTelemetry middleware còn được bảo trì. fasthttp của Fiber làm hỏng SSE flush, otelhttp và việc hủy. chi là phương án dự phòng nếu router viết tay lớn đến mức một người không còn nắm hết trong đầu, vì handler của nó vẫn là net/http thuần.
  • ORM (GORM, ent, bun): chúng che mất SQL, mà SQL là nơi tenant scoping nằm. sqlc giữ SQL trong các file và chỉ sinh phần Go xung quanh, nên nó được chọn ở giai đoạn 2.1. Giai đoạn 2 đã loại nó, rồi đảo lại quyết định đó để học sqlc khi store mới có năm file query.
  • WebSocket, Redis, message queue: không cần ở quy mô một replica.
  • Thư mục pkg/: chưa công khai gì cho đến khi có người dùng bên ngoài.