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ục | Lựa chọn | Phiên bản | Lý do |
|---|---|---|---|
| Ngôn ngữ | Go | 1.27 | Stdlib có uuid.NewV7, encoding/json/v2, testing/synctest đã GA, và goroutine-leak profile. |
| HTTP | Thư viện chuẩn net/http | Go 1.27 | http.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 transport | Server-Sent Events | stdlib | Client 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êng | v1.74.0 | SDK 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 interface | v3.x | Phủ OpenAI, Ollama, OpenRouter, vLLM qua base URL. Adapter tự tạo block start/stop. |
| Database | PostgreSQL qua pgx | pgx v5.11.0 | Đã loại SQLite hai dialect vì dễ phình scope. |
| Query code | sqlc | v1.31.1 | Sinh 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 đó. |
| Migrations | goose dạng thư viện, embed.FS | v3.29.0 | Migration SQL nhúng sẵn, một binary. |
| Tenancy | tenant_id NOT NULL trên mọi bảng, scoped store dựng từ context | n/a | Fail-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ế. |
| Identity | hango sở hữu tenant, user, role, API key | n/a | Khô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 model | Một dòng runs bền vững, được scheduler lane worker thực thi, SSE đăng ký theo dõi | n/a | Số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. |
| Messages | Dòng chỉ thêm vào (append-only), số seq, block jsonb | n/a | Delta theo từng turn, địa chỉ theo từng message cho compaction và subagent. |
| Observability | OpenTelemetry + log/slog | otel v1.46.0 | Hằng số GenAI semconv của riêng hango. Nội dung LLM tắt theo mặc định. |
| Rate limiting | golang.org/x/time/rate theo API key | latest | Trong tiến trình, có ghi rõ giới hạn chỉ một replica. |
| Testing | testing, testing/synctest, testcontainers-go cho Postgres | v0.44.0 | Database thật trong integration test, không mock store. |
| Lint | golangci-lint v2 | v2.13.2 | errcheck, govet, staticcheck, gosec, bodyclose, sqlclosecheck. |
| Container | Dockerfile nhiều stage, distroless static nonroot | n/a | CGO_ENABLED=0, compose cùng Postgres. |
| Web UI (làm sau) | Svelte 5 | n/a | Cho 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,
otelhttpvà 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/httpthuầ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.