690528:1524 ADR-030-230 context aware #02
This commit is contained in:
@@ -0,0 +1,314 @@
|
||||
# ADR-030: Context-Aware Prompt Templates for OCR Metadata Extraction
|
||||
|
||||
**Status:** Accepted
|
||||
**Date:** 2026-05-27
|
||||
**Decision Makers:** Development Team, System Architect
|
||||
**Related Documents:**
|
||||
- [ADR-029: Dynamic Prompt Management](./ADR-029-dynamic-prompt-management.md)
|
||||
- [ADR-023A: Unified AI Architecture — Model Revision](./ADR-023A-unified-ai-architecture.md)
|
||||
- [ADR-019: Hybrid Identifier Strategy](./ADR-019-hybrid-identifier-strategy.md)
|
||||
- [ADR-007: Error Handling Strategy](./ADR-007-error-handling-strategy.md)
|
||||
|
||||
---
|
||||
|
||||
## บริบทและปัญหา (Context and Problem Statement)
|
||||
|
||||
ADR-029 แก้ปัญหา hardcoded prompt โดยเก็บ prompt template ในตาราง `ai_prompts` แต่ยังมีข้อจำกัด:
|
||||
|
||||
1. **ไม่มี Context Awareness:** Prompt template ไม่สามารถระบุ master data context (projects, organizations, disciplines, etc.) ที่ AI ต้องใช้ในการ match ข้อมูล
|
||||
2. **Context Size ไม่ถูกควบคุม:** ไม่มีกลไก filter master data ตาม project/contract scope ทำให้ context ใหญ่เกินไป
|
||||
3. **Language Hardcoded:** Prompt template เดิมใช้ภาษาอังกฤษ แต่ LCBP3-DMS เป็นระบบภาษาไทย
|
||||
4. **Output Schema ไม่ครบ:** สกัดเฉพาะ 8 fields แต่ยังขาด fields สำคัญสำหรับ correspondence creation (เช่น recipient organizations, UUID matching)
|
||||
|
||||
---
|
||||
|
||||
## ปัจัยขับเคลื่อนการตัดสินใจ (Decision Drivers)
|
||||
|
||||
- **Context Filtering:** ต้อง filter master data ตาม project/contract scope เพื่อลด context size และป้องกัน cross-project data leak (ADR-023A)
|
||||
- **UUID Matching:** AI ต้องส่งคืน UUID (ไม่ใช่ INT ID) ตาม ADR-019
|
||||
- **Thai Language Support:** Prompt และ output ต้องเป็นภาษาไทย
|
||||
- **Flexible Configuration:** Admin ต้องกำหนด filter criteria และ context config ได้ผ่าน AI Admin Console
|
||||
- **Backward Compatibility:** ต้องรองรับ prompt template เดิมที่ไม่มี context_config
|
||||
|
||||
---
|
||||
|
||||
## ทางเลือกที่ถูกพิจารณา (Considered Options)
|
||||
|
||||
### Option 1: เพิ่มคอลัมน์แยกต่างหาก (project_id, contract_id)
|
||||
- **ข้อดี:** Query ง่าย, type-safe
|
||||
- **ข้อเสีย:** ไม่ flexible, ถ้าอนาคตต้องการ filter criteria อื่นต้อง alter table อีก
|
||||
|
||||
### Option 2: เก็บ filter criteria ใน field_schema (JSON)
|
||||
- **ข้อดี:** ไม่ต้อง alter table
|
||||
- **ข้อเสีย:** ผสม output schema กับ filter config, สับสน, query ซับซ้อน
|
||||
|
||||
### Option 3: เพิ่ม context_config JSON column (ตัวเลือกที่ได้รับเลือก)
|
||||
- **ข้อดี:** Flexible, แยก concern ชัด (field_schema vs context_config), รองรับ config อนาคต
|
||||
- **ข้อเสีย:** ต้อง alter table ครั้งเดียว
|
||||
|
||||
---
|
||||
|
||||
## ผลการตัดสินใจ (Decision Outcome)
|
||||
|
||||
**ทางเลือกที่ได้รับเลือก:** Option 3 — เพิ่ม `context_config` JSON column
|
||||
|
||||
---
|
||||
|
||||
## ข้อตกลงหลัก (Core Decisions — Grilling Session 2026-05-27)
|
||||
|
||||
| # | ประเด็น | การตัดสินใจ |
|
||||
|---|---------|-------------|
|
||||
| 1 | Page Limit | **3 หน้า** (ตาม ADR-023A — classification/tagging rule) |
|
||||
| 2 | Database Query Strategy | **Option A** — Backend ดึง master data แล้วส่งเป็น context ใน prompt (AI ไม่ query DB โดยตรงตาม ADR-023) |
|
||||
| 3 | JSON Output Schema | **11 fields**: projectPublicId, correspondenceTypeCode, disciplineCode, originatorOrganizationPublicId, recipientOrganizationPublicIds, recipientTypes, subject, documentDate, tags, summary, confidence |
|
||||
| 4 | Context Format | **Option A** — List format (array of objects) สำหรับ AI scan ง่าย |
|
||||
| 5 | Filter Strategy | กำหนดใน prompt template โดย filter ด้วย projects และ contracts |
|
||||
| 6 | Filter Storage | **Option C** — เพิ่ม `context_config` JSON column |
|
||||
| 7 | Language | **ภาษาไทย** — Prompt instruction และ summary output |
|
||||
| 8 | UUID Handling | **ADR-019** — AI ส่งคืน UUID string (ไม่ใช่ INT ID) |
|
||||
| 9 | Fallback Strategy | ถ้า AI ไม่พบ match → ส่ง `null` และต้องการ human validation |
|
||||
|
||||
---
|
||||
|
||||
## รายละเอียดเชิงสถาปัตยกรรม (Implementation Details)
|
||||
|
||||
### 1. Schema Changes (ADR-009)
|
||||
|
||||
```sql
|
||||
-- Delta: เพิ่ม context_config สำหรับ filter criteria
|
||||
ALTER TABLE ai_prompts
|
||||
ADD COLUMN context_config JSON NULL
|
||||
COMMENT 'Configuration สำหรับ context ที่ backend ต้องส่งให้ AI (filter, pageSize, language, etc.)';
|
||||
```
|
||||
|
||||
### 2. context_config Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"filter": {
|
||||
"projectId": 123,
|
||||
"contractId": 456
|
||||
},
|
||||
"pageSize": 3,
|
||||
"language": "th",
|
||||
"outputLanguage": "th"
|
||||
}
|
||||
```
|
||||
|
||||
**Field Descriptions:**
|
||||
- `filter.projectId`: INT หรือ null — Filter master data ตาม project scope
|
||||
- `filter.contractId`: INT หรือ null — Filter master data ตาม contract scope
|
||||
- `pageSize`: INT (default: 3) — จำนวนหน้า PDF ที่ OCR สกัด (ตาม ADR-023A)
|
||||
- `language`: string (default: "th") — ภาษา prompt instruction
|
||||
- `outputLanguage`: string (default: "th") — ภาษา output (summary)
|
||||
|
||||
### 3. JSON Output Schema (field_schema)
|
||||
|
||||
```json
|
||||
{
|
||||
"projectPublicId": "string|null",
|
||||
"correspondenceTypeCode": "string|null",
|
||||
"disciplineCode": "string|null",
|
||||
"originatorOrganizationPublicId": "string|null",
|
||||
"recipientOrganizationPublicIds": "string[]|null",
|
||||
"recipientTypes": "string[]|null",
|
||||
"subject": "string|null",
|
||||
"documentDate": "string:YYYY-MM-DD|null",
|
||||
"tags": "string[]|null",
|
||||
"summary": "string|null",
|
||||
"confidence": "float:0-1"
|
||||
}
|
||||
```
|
||||
|
||||
**Mapping to Database:**
|
||||
- `projectPublicId` → `projects.uuid` (ADR-019)
|
||||
- `correspondenceTypeCode` → `correspondence_types.type_code`
|
||||
- `disciplineCode` → `disciplines.discipline_code`
|
||||
- `originatorOrganizationPublicId` → `organizations.uuid` (originator_id)
|
||||
- `recipientOrganizationPublicIds` → `organizations.uuid[]` (correspondence_recipients)
|
||||
- `recipientTypes` → `correspondence_recipients.recipient_type` ("TO", "CC ")
|
||||
- `subject` → `correspondence_revisions.title`
|
||||
- `documentDate` → `correspondence_revisions.document_date`
|
||||
- `tags` → `tags.tag_name[]`
|
||||
- `summary` — AI-generated summary (4-5 ประโยคภาษาไทย)
|
||||
- `confidence` — AI confidence score (0.0-1.0)
|
||||
|
||||
### 4. Context Format (List Format)
|
||||
|
||||
Backend ส่ง master data context ในรูปแบบ:
|
||||
|
||||
```json
|
||||
{
|
||||
"availableProjects": [
|
||||
{"code": "LCBP3", "uuid": "0195...", "name": "โครงการ LCBP3"}
|
||||
],
|
||||
"availableOrganizations": [
|
||||
{"code": "กทท.", "uuid": "0195...", "name": "การทางพิเศษแห่งประเทศไทย"},
|
||||
{"code": "TEAM", "uuid": "0195...", "name": "TEAM Consulting"}
|
||||
],
|
||||
"availableDisciplines": [
|
||||
{"code": "GEN", "name": "General"},
|
||||
{"code": "STR", "name": "Structural"}
|
||||
],
|
||||
"availableCorrespondenceTypes": [
|
||||
{"code": "RFA", "name": "Request for Approval"},
|
||||
{"code": "RFI", "name": "Request for Information"}
|
||||
],
|
||||
"availableTags": [
|
||||
{"name": "Urgent", "color": "red"},
|
||||
{"name": "Review", "color": "blue"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Prompt Template Example (ภาษาไทย)
|
||||
|
||||
```
|
||||
คุณเป็นเอนจิ้นสกัดข้อมูลเอกสารมืออาชีพ
|
||||
วิเคราะห์ข้อความ OCR จากเอกสารโครงการ (3 หน้าแรกเท่านั้น) และสกัดข้อมูลเมตาดาต้า
|
||||
|
||||
ข้อความ OCR:
|
||||
{{ocr_text}}
|
||||
|
||||
ข้อมูลอ้างอิงที่ใช้ได้:
|
||||
{{master_data_context}}
|
||||
|
||||
สกัด fields ต่อไปนี้:
|
||||
1. projectPublicId: UUID ของโครงการ (จาก availableProjects)
|
||||
2. correspondenceTypeCode: รหัสประเภทเอกสาร (เช่น RFA, RFI)
|
||||
3. disciplineCode: รหัสสาขางาน (เช่น GEN, STR)
|
||||
4. originatorOrganizationPublicId: UUID ขององค์กรผู้ส่ง
|
||||
5. recipientOrganizationPublicIds: UUID[] ขององค์กรผู้รับ (หลายองค์กรได้)
|
||||
6. recipientTypes: string[] ("TO", "CC")
|
||||
7. subject: หัวข้อเอกสาร
|
||||
8. documentDate: วันที่เอกสาร (YYYY-MM-DD)
|
||||
9. tags: string[] รายชื่อ tags
|
||||
10. summary: สรุปเอกสาร 4-5 ประโยคภาษาไทย
|
||||
11. confidence: ความมั่นใจ (0.0-1.0)
|
||||
|
||||
ส่งคืนเฉพาะ JSON object ที่ถูกต้อง ไม่รวม markdown code blocks
|
||||
```
|
||||
|
||||
### 6. Backend Implementation
|
||||
|
||||
**AiPromptsService.resolveContext()**
|
||||
```typescript
|
||||
async resolveContext(activePrompt: AiPrompt): Promise<Record<string, unknown>> {
|
||||
const config = activePrompt.contextConfig || {};
|
||||
const filter = config.filter || {};
|
||||
|
||||
const projectId = filter.projectId;
|
||||
const contractId = filter.contractId;
|
||||
|
||||
// Query master data with filter
|
||||
const projects = await this.projectService.findAll({ projectId });
|
||||
const organizations = await this.organizationService.findAll({ projectId, contractId });
|
||||
const disciplines = await this.disciplineService.findAll({ contractId });
|
||||
const correspondenceTypes = await this.correspondenceTypeService.findAll();
|
||||
const tags = await this.tagsService.findAll({ projectId });
|
||||
|
||||
return {
|
||||
availableProjects: projects.map(p => ({ code: p.projectCode, uuid: p.uuid, name: p.projectName })),
|
||||
availableOrganizations: organizations.map(o => ({ code: o.organizationCode, uuid: o.uuid, name: o.organizationName })),
|
||||
availableDisciplines: disciplines.map(d => ({ code: d.disciplineCode, name: d.codeNameTh })),
|
||||
availableCorrespondenceTypes: correspondenceTypes.map(t => ({ code: t.typeCode, name: t.typeName })),
|
||||
availableTags: tags.map(t => ({ name: t.tagName, color: t.colorCode })),
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**AiBatchProcessor.processSandboxExtract()**
|
||||
```typescript
|
||||
const activePrompt = await this.aiPromptsService.getActive('ocr_extraction');
|
||||
const context = await this.aiPromptsService.resolveContext(activePrompt);
|
||||
|
||||
const prompt = activePrompt.template
|
||||
.replace('{{ocr_text}}', ocrText)
|
||||
.replace('{{master_data_context}}', JSON.stringify(context, null, 2));
|
||||
```
|
||||
|
||||
### 7. Frontend Changes (AI Admin Console)
|
||||
|
||||
**Prompt Editor UI:**
|
||||
- เพิ่ม section "Context Configuration"
|
||||
- Dropdown เลือก Project (optional)
|
||||
- Dropdown เลือก Contract (optional)
|
||||
- Input field: Page Size (default: 3)
|
||||
- Select: Language (th/en)
|
||||
|
||||
**n8n Workflow:**
|
||||
- เพิ่ม node "Select Project/Contract" ก่อน "OCR Extraction"
|
||||
- ส่ง `projectId` และ `contractId` ไปยัง DMS API `/ai/ocr-extract`
|
||||
- Backend resolve context จาก context_config หรือ override ด้วย input parameters
|
||||
|
||||
---
|
||||
|
||||
## ผลกระทบ (Consequences)
|
||||
|
||||
### ผลดี
|
||||
- Admin กำหนด filter criteria ได้ runtime ผ่าน AI Admin Console
|
||||
- Context size ถูกควบคุม ตาม project/contract scope
|
||||
- AI ส่งคืน UUID ตาม ADR-019 พร้อมนำเข้า
|
||||
- Prompt และ output เป็นภาษาไทย
|
||||
- Flexible สำหรับ config อนาคต (language, temperature, etc.)
|
||||
|
||||
### ผลเสีย / ข้อระวัง
|
||||
- ต้อง alter table `ai_prompts` เพิ่ม `context_config` column
|
||||
- Backend query master data เพิ่มขึ้นต่อ job (mitigate ด้วย Redis cache)
|
||||
- ต้อง migrate prompt template เดิม (seed data) ให้มี context_config
|
||||
- n8n workflow ต้องอัปเดตให้รองรับ filter criteria
|
||||
|
||||
---
|
||||
|
||||
## Migration Plan
|
||||
|
||||
### Phase 1: Database Schema
|
||||
1. Run delta SQL: `ALTER TABLE ai_prompts ADD COLUMN context_config JSON NULL`
|
||||
2. Update `AiPrompt` entity เพิ่ม `contextConfig` property
|
||||
3. Update DTOs (CreateAiPromptDto, UpdateAiPromptDto)
|
||||
|
||||
### Phase 2: Backend Logic
|
||||
1. Implement `AiPromptsService.resolveContext()`
|
||||
2. Update `AiBatchProcessor` สำหรับส่ง context ไปให้ AI
|
||||
3. Add Redis cache สำหรับ master data context (TTL: 300s)
|
||||
|
||||
### Phase 3: Seed Data
|
||||
1. Update seed data ใน `2026-05-25-create-ai-prompts.sql`
|
||||
2. เพิ่ม context_config สำหรับ version 1 (null = no filter)
|
||||
3. สร้าง version 2 ใหม่ด้วย prompt template ภาษาไทย + context_config example
|
||||
|
||||
### Phase 4: Frontend
|
||||
1. Update AI Admin Console UI ให้มี Context Configuration section
|
||||
2. Update n8n workflow ให้รองรับ filter criteria
|
||||
|
||||
### Phase 5: Testing
|
||||
1. Unit tests สำหรับ context resolution logic
|
||||
2. Integration tests สำหรับ sandbox และ migration pipeline
|
||||
3. Manual testing ด้วย prompt template ใหม่
|
||||
|
||||
---
|
||||
|
||||
## Grilling Session Log
|
||||
|
||||
```
|
||||
2026-05-27 — grilling session ผ่าน Antigravity AI
|
||||
Q1: Page limit → 3 หน้า (ตาม ADR-023A)
|
||||
Q2: Database query strategy → Option A (Backend ดึง master data ส่ง context)
|
||||
Q3: JSON output schema → 11 fields (UUID-based ตาม ADR-019) พร้อมปรับปรุง recipients เป็น Object Array: Array<{ organizationPublicId: string, recipientType: "TO" | "CC" }>
|
||||
Q4: Context format → Option A (List format)
|
||||
Q5: Filter strategy → กำหนดใน prompt template โดย filter ด้วย projects/contracts
|
||||
Q6: Filter storage → Option C (เพิ่ม context_config JSON column)
|
||||
Q7: Tag Suggestion → Option A (ให้ Backend ทำการ Diff ระหว่าง tags ที่ได้มากับ availableTags เพื่อระบุสถานะ isNew: true เอง)
|
||||
Q8: Project Scope Priority → Option C (หาก Template มีการผูกโครงการไว้ใน context_config แต่ request พยายาม override ไปโครงการอื่น ระบบจะทำการ Reject ด้วย ForbiddenException ทันที)
|
||||
Q9: Database Typo Cleanup → Option C (อนุมัติล้าง whitespace typo ของตัวแปร 'CC ' ให้ถูกต้องเป็น 'CC' ทั้งระบบในระดับ Database Schema และอัปเดตไฟล์โครงสร้างหลัก)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related ADRs
|
||||
|
||||
- **ADR-029:** Dynamic Prompt Management (base architecture)
|
||||
- **ADR-023A:** Unified AI Architecture — Model Revision (3-page rule, AI boundary)
|
||||
- **ADR-019:** Hybrid Identifier Strategy (UUID handling)
|
||||
- **ADR-007:** Error Handling Strategy (layered error classification)
|
||||
- **ADR-009:** Database Migration Strategy (direct SQL edits)
|
||||
@@ -0,0 +1,812 @@
|
||||
# ADR-031: NAP-DMS Integration Strategy (Hermes Agent & Telegram Bridge)
|
||||
|
||||
## Status
|
||||
|
||||
- **Status:** Draft
|
||||
- **Date:** 2026-05-28
|
||||
- **Deciders:** NAP-DMS Architecture Team
|
||||
|
||||
### Locked Decisions During Grill
|
||||
|
||||
ข้อตกลงด้านล่างถูกล็อคระหว่าง grill session แล้ว แต่ ADR-031 ยังอยู่ในสถานะ Draft จนกว่า scope และ rollout plan จะ stable:
|
||||
|
||||
* **Hermes role:** Hermes เป็น Developer Operations Agent / Integration Assistant เท่านั้น ไม่ใช่ production DMS service และไม่ใช่ AI inference boundary ของ DMS
|
||||
* **Database access:** Hermes เชื่อม MariaDB ได้เฉพาะ read-only account หรือ read-only replica สำหรับ schema inspection, metadata diagnostics, และ query verification เท่านั้น
|
||||
* **Document storage:** Hermes ห้าม mount, browse, หรืออ่าน permanent document storage ของ production โดยตรง หากต้องตรวจเอกสารจริงต้องผ่าน DMS Backend API
|
||||
* **Telegram scope:** Hermes Telegram ใช้สำหรับ DevOps commands เท่านั้น ไม่ใช่ production DMS command channel
|
||||
* **DMS Telegram:** Telegram command สำหรับ query/mutate เอกสารจริงเป็น future separate ADR/spec และต้อง enforce ADR-009/016/019/023A
|
||||
* **Hermes proxy:** `hermes proxy` เป็น Developer AI Proxy only สำหรับ coding/devops assistance ไม่ใช่ DMS AI runtime หรือ document intelligence path
|
||||
* **Telegram writes:** Telegram write actions ต้องมี explicit confirmation; forbidden actions เช่น push ไป `main`/`master`, production deploy, schema migration execution, direct DB writes, และ storage delete ห้ามทำผ่าน Telegram
|
||||
* **Schema rollout:** ADR-031 rollout ไม่เพิ่มหรือแก้ production DMS schema รวมถึงไม่สร้าง `user_telegram_mapping`
|
||||
|
||||
## Context
|
||||
|
||||
การตั้งค่าระบบ **Hermes Agent** ในโปรเจกต์ **NAP-DMS (LCBP3)** เวอร์ชัน 1.9.0 เพื่อเชื่อมต่อกับโครงสร้างพื้นฐานเดิมและขยายขีดความสามารถผ่าน Telegram Bridge สำหรับการแจ้งเตือนและการสั่งงานผ่านระบบ Command
|
||||
|
||||
Hermes Agent ถูกนิยามเป็น **Developer Operations Agent / Integration Assistant** สำหรับงานพัฒนา, ตรวจสอบ, ประสานเครื่องมือ, และช่วยสั่งงาน DevOps เท่านั้น ไม่ใช่ production DMS service และไม่ใช่ AI inference boundary ของ DMS ตาม ADR-023/ADR-023A
|
||||
|
||||
## Decision
|
||||
|
||||
ใช้ Approach B (Local Development Network) พร้อมควบคุมทรัพยากร (CPU/RAM) และใช้ API Key Authentication สำหรับ CLI Tools
|
||||
|
||||
## Consequences
|
||||
|
||||
### Infrastructure Setup (Approach B - Local Development)
|
||||
|
||||
* **Hermes Agent:** รันบน ASUSTOR NAS ภายใน Docker Network `lcbp3` โดยมีการจำกัดทรัพยากร (Resource Limits) เพื่อป้องกันผลกระทบต่อระบบหลักของ NAS และทำหน้าที่เป็น Developer Operations Agent / Integration Assistant เท่านั้น
|
||||
* **Resource Allocation:**
|
||||
* CPU Limit: 2.0 Cores
|
||||
* RAM Memory: 2GB (Reservation) / 4GB (Limit)
|
||||
|
||||
* **Environment:** เน้นการพัฒนาในรูปแบบ Local Development โดยให้ Agent สื่อสารกับ Gitea และแหล่งข้อมูลตรวจสอบแบบ read-only ภายในเครือข่ายเท่านั้น
|
||||
* **Database Access Boundary:** Hermes สามารถเชื่อมต่อ MariaDB เพื่อการตรวจสอบแบบ read-only ได้เฉพาะผ่าน read-only replica หรือ read-only DB account ที่ฐานข้อมูลบังคับสิทธิ์เอง ใช้สำหรับ schema inspection, metadata diagnostics, และ query verification เท่านั้น ห้ามเขียน production DB ทุกกรณี
|
||||
* **Data Minimization Boundary:** read-only DB account ของ Hermes ต้องเห็นเฉพาะ schema metadata และข้อมูล operational/devops ที่จำเป็น หากต้อง query ตาราง DMS จริงต้องใช้ masked read-only replica หรือ database view ที่ redact PII/document-sensitive fields เช่น เนื้อหาเอกสาร, storage path, token, password hash, และข้อมูลผู้ใช้ละเอียด
|
||||
* **Storage Access Boundary:** Hermes ห้าม mount, browse, หรืออ่าน permanent document storage ของ production โดยตรง หากต้องตรวจสอบเอกสารจริงต้องเรียกผ่าน DMS Backend API ที่ผ่าน RBAC, audit, และ project isolation controls เท่านั้น
|
||||
|
||||
### Security & Access Control Strategy
|
||||
|
||||
* **Authentication:** ใช้ระบบ **API Key** ในการเข้าถึง Hermes Agent จาก CLI Tools (Antigravity/Codex) บน Local Desktop
|
||||
* **Hermes Proxy Boundary:** `hermes proxy` เป็น Developer AI Proxy สำหรับ coding/devops assistance เท่านั้น ไม่ใช่ DMS AI runtime, ไม่ใช่ document intelligence path, และห้ามใช้กับ production document payload, secrets, หรือข้อมูลเอกสารจริง
|
||||
* **Network Exposure Boundary:** Hermes services (`:8080`, `:8766`, `/mcp`, และ internal tool endpoints) ต้อง expose เฉพาะ LAN/VPN ที่จำเป็น ห้ามเปิด public internet โดยตรง หาก Telegram webhook ต้องรับ traffic จาก internet ให้ terminate ผ่าน reverse proxy ที่มี TLS, Telegram secret token verification, IP/rate limit, และ request logging
|
||||
* **Secret Management Boundary:** `HERMES_PROXY_API_KEY`, Telegram bot token, webhook secret, Gitea token, และ read-only DB credential ห้ามอยู่ใน repo/spec/plain `.env` ที่ commit ได้ ต้องเก็บใน ASUSTOR secret store หรือไฟล์ environment นอก repo ที่จำกัด permission และมี rotation plan
|
||||
* **Gitea Token Boundary:** Hermes ต้องใช้ Gitea token แบบ least privilege โดย default เป็น read-only สำหรับ repo/issue/PR status และใช้ write token แยกเฉพาะ action ที่ผ่าน confirmation แล้ว ห้ามใช้ admin token หรือ token ที่ push ไป `main`/`master` ได้
|
||||
* **Operations Log Boundary:** `hermes_operations_log` เป็น log store ของ Hermes เอง เช่น SQLite/Postgres volume ภายใน Hermes stack หรือ structured log file ที่ ship ไป log collector ไม่ใช่ `audit_logs` ของ DMS และต้องมี retention/redaction policy
|
||||
* **Failure Isolation Boundary:** Hermes เป็น optional DevOps assistant เท่านั้น หาก Hermes, Telegram Bridge, MCP, หรือ hermes proxy ล่ม ต้องไม่กระทบ DMS production, Workflow Engine, AI pipeline, หรือ user-facing app
|
||||
* **Validation Boundary:** ข้อมูลทั้งหมดที่ส่งผ่าน CLI, MCP หรือ AI จะต้องอ้างอิงผ่าน `publicId` (UUIDv7) เท่านั้น ห้ามเปิดเผยหรือใช้งาน `INT AUTO_INCREMENT` ภายนอกเด็ดขาด (ADR-019)
|
||||
* **Repository Gate Compliance:** Hermes ต้องเคารพ repo gates เดิมทั้งหมดตาม `AGENTS.md`, lint, tests, CI, และ branch protection; ADR-031 ไม่เป็นเจ้าของ Git Hooks policy และไม่เพิ่ม hook ใหม่
|
||||
* **Telegram Access Check:** Hermes Telegram ต้องใช้ allowlist/admin mapping สำหรับ DevOps commands เท่านั้น หากมี DMS Telegram module ในอนาคต ต้องแยก implementation และตรวจสอบสิทธิ์ผ่าน CASL Guard ของ DMS API เสมอ
|
||||
|
||||
---
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### 1. Infrastructure Configuration
|
||||
|
||||
#### Redis Persistence for BullMQ (Durability)
|
||||
|
||||
เพื่อให้ BullMQ queue ของ Hermes ทนทานต่อการ restart ของ ASUSTOR NAS ต้องกำหนดค่า Redis persistence โดยแยกจาก DMS production Redis เป็นค่า default:
|
||||
|
||||
```yaml
|
||||
# docker-compose.hermes-redis.yml
|
||||
services:
|
||||
hermes-redis:
|
||||
image: redis:7-alpine
|
||||
container_name: hermes_redis_lcbp3
|
||||
networks:
|
||||
- lcbp3_net
|
||||
volumes:
|
||||
- hermes_redis_data:/data
|
||||
- ./hermes.redis.conf:/usr/local/etc/redis/redis.conf
|
||||
command: redis-server /usr/local/etc/redis/redis.conf
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
hermes_redis_data:
|
||||
driver: local
|
||||
```
|
||||
|
||||
```conf
|
||||
# hermes.redis.conf - บันทึกข้อมูลทุก 60 วินาที หากมีการเปลี่ยนแปลงอย่างน้อย 1 key
|
||||
save 60 1
|
||||
save 300 10
|
||||
save 900 10000
|
||||
|
||||
# เปิดใช้งาน AOF (Append-Only File) เพื่อ durability สูงสุด
|
||||
appendonly yes
|
||||
appendfsync everysec
|
||||
|
||||
# ชื่อไฟล์ AOF
|
||||
appendfilename "appendonly.aof"
|
||||
|
||||
# จำกัด memory ใช้งานสูงสุด (ปรับตามสเปก NAS)
|
||||
maxmemory 512mb
|
||||
maxmemory-policy allkeys-lru
|
||||
```
|
||||
|
||||
**ข้อดี:**
|
||||
- หาก ASUSTOR restart ข้อมูลใน Hermes queue จะไม่สูญหาย
|
||||
- AOF บันทึกทุก operation ทำให้สามารถ recovery ได้เกือบ 100%
|
||||
- ไม่ปะปนกับ DMS production BullMQ/lock/cache keys
|
||||
|
||||
**Redis Isolation Policy:**
|
||||
|
||||
* Default คือ Hermes ใช้ Redis instance/volume แยกจาก DMS production Redis
|
||||
* หาก resource จำกัดจนต้องใช้ Redis instance เดียวกับ DMS production ต้องกำหนด `keyPrefix`, DB index แยก, ACL user แยก, monitoring แยก, และ maxmemory policy ที่ไม่ evict DMS keys
|
||||
* ห้ามใช้ `allkeys-lru` บน shared Redis ที่มี DMS locks/cache/queues เพราะอาจ evict key สำคัญของ DMS
|
||||
* Hermes queue names ต้องขึ้นต้นด้วย `hermes-` เช่น `hermes-notification-queue`
|
||||
|
||||
#### Docker Compose (Hermes Agent with Resource Limits)
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
networks:
|
||||
lcbp3_net:
|
||||
name: lcbp3
|
||||
external: true
|
||||
|
||||
services:
|
||||
hermes-agent:
|
||||
image: np-dms.work/hermes/agent:latest
|
||||
container_name: hermes_agent_lcbp3
|
||||
networks:
|
||||
- lcbp3_net
|
||||
environment:
|
||||
- DATABASE_HOST=mariadb_container_name
|
||||
- DATABASE_PORT=3306
|
||||
- GITEA_URL=http://gitea_container_name:3000
|
||||
- HERMES_ENV=local_dev
|
||||
# Non-Swarm Docker Compose fallback. ตรวจสอบว่า ASUSTOR runtime enforce จริงด้วย docker inspect/stats
|
||||
cpus: '2.0'
|
||||
mem_reservation: 2048M
|
||||
mem_limit: 4096M
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2.0'
|
||||
memory: 4096M
|
||||
reservations:
|
||||
memory: 2048M
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
> **Resource Limit Note:** `deploy.resources` อาจถูก ignore ใน Docker Compose แบบ non-Swarm บน ASUSTOR ได้ จึงต้องกำหนด fallback (`cpus`, `mem_reservation`, `mem_limit`) และ verify จาก runtime ด้วย `docker inspect`/`docker stats` ทุกครั้ง
|
||||
|
||||
#### API Key Configuration
|
||||
|
||||
ตัวอย่างบนเครื่องพัฒนา Windows PowerShell:
|
||||
|
||||
```powershell
|
||||
# คอนฟิกค่า API Key ลงในตัวแปรระบบของเครื่องพัฒนา
|
||||
$env:ANTIGRAVITY_API_KEY = "hermes_secure_api_key_v1_9_0"
|
||||
$env:CODEX_API_KEY = "hermes_secure_api_key_v1_9_0"
|
||||
|
||||
# ตัวอย่างการเรียกใช้งาน CLI
|
||||
antigravity-cli sync --target hermes_agent_lcbp3:8080 --key $env:ANTIGRAVITY_API_KEY
|
||||
```
|
||||
|
||||
#### MCP Server Configuration
|
||||
|
||||
ตัวอย่างด้านล่างเป็น template เท่านั้น ต้อง verify package/command ของ MCP server จริงตอน implementation และห้าม commit credential จริงลง config:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"lcbp3-mariadb-mcp": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"@modelcontextprotocol/server-postgres-mariadb",
|
||||
"--host", "${HERMES_MARIADB_READONLY_HOST}",
|
||||
"--port", "${HERMES_MARIADB_READONLY_PORT}",
|
||||
"--db", "${HERMES_MARIADB_READONLY_DATABASE}",
|
||||
"--user", "${HERMES_MARIADB_READONLY_USER}",
|
||||
"--password", "${HERMES_MARIADB_READONLY_PASSWORD}"
|
||||
],
|
||||
"description": "Read-only schema/metadata diagnostics only. Verify MCP package and CLI before rollout."
|
||||
},
|
||||
"lcbp3-gitea-mcp": {
|
||||
"command": "node",
|
||||
"args": ["./scripts/gitea-mcp-bridge.js"],
|
||||
"env": {
|
||||
"GITEA_TOKEN": "${HERMES_GITEA_TOKEN}",
|
||||
"GITEA_BASE_URL": "${HERMES_GITEA_BASE_URL}"
|
||||
},
|
||||
"description": "Least-privilege Gitea token for DevOps diagnostics and approved actions."
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **Security Boundary:** MCP database access สำหรับ Hermes ต้องเป็น read-only account หรือ read-only replica เท่านั้น และใช้เพื่อ developer diagnostics ไม่ใช่ production DMS write path การอ่านไฟล์เอกสาร production ต้องผ่าน DMS Backend API ห้าม mount storage เข้า Hermes container โดยตรง
|
||||
|
||||
> **Data Minimization:** read-only account นี้ต้องถูกจำกัดที่ระดับ DB grant/view/replica ไม่ใช่พึ่ง policy ใน agent เท่านั้น โดย default ให้เห็น schema metadata และ operational diagnostics เท่านั้น หากจำเป็นต้อง query ตาราง DMS จริงให้ใช้ masked views หรือ read-only replica ที่ redact sensitive fields
|
||||
|
||||
> **Implementation Verification:** MCP package name, CLI arguments, remote-server field names, and environment interpolation behavior must be verified against the actual installed MCP client/server before rollout.
|
||||
|
||||
---
|
||||
|
||||
### 2. Telegram Integration Architecture
|
||||
|
||||
#### System Overview
|
||||
|
||||
Telegram Bridge ใน ADR-031 เป็น interface ของ Hermes สำหรับ **DevOps commands เท่านั้น** ไม่ใช่ production DMS command channel และไม่ใช่ช่องทาง query/แก้ไขเอกสารจริง
|
||||
|
||||
* **Inbound Commands (Inbound):** รับคำสั่งผ่าน Webhook ของ Telegram Bot สำหรับ DevOps tasks เช่น CI status, Gitea issue/PR summary, scheduled audit, repository diagnostics, และสถานะ Hermes Agent
|
||||
* **System Commands:** เช่น `/status`, `/ci_status`, `/repo_summary`, `/schedule_audit` จะถูกประมวลผลทันทีใน Hermes command service โดยไม่แตะ production DMS workflow
|
||||
* **Document Queries:** ไม่อยู่ใน scope ของ Hermes Telegram หากต้อง query หรือ mutate เอกสารจริง ต้องทำเป็น DMS Backend Telegram module แยกต่างหาก และต้อง enforce RBAC, audit, project isolation, และ Idempotency-Key ตาม ADR-016/019/023A
|
||||
|
||||
* **Notification Dispatcher (Outbound):** ใช้ **BullMQ** เป็นตัวจัดการคิวการส่งข้อความ
|
||||
* ทุกการส่งงาน (Job) จะต้องระบุ `Transaction ID` (UUIDv7) เพื่อใช้ในการติดตามผล
|
||||
* ทุก Transaction จะถูกบันทึกใน `hermes_operations_log` หรือ log store ของ Hermes เพื่อ trace DevOps operation โดยไม่ปะปนกับ `audit_logs` ของ DMS
|
||||
|
||||
* **Error Handling:** ตาม ADR-007 ระบบต้องทำ Retry 3 ครั้งพร้อม Exponential Backoff หากการเชื่อมต่อ Telegram API ล้มเหลว
|
||||
|
||||
#### Hermes DevOps Telegram Gateway (Pseudocode)
|
||||
|
||||
```typescript
|
||||
// hermes/src/integrations/telegram/hermes-telegram-gateway.ts
|
||||
type HermesTelegramMessage = {
|
||||
message?: {
|
||||
from?: { id?: number; username?: string };
|
||||
text?: string;
|
||||
chat?: { id?: number };
|
||||
};
|
||||
};
|
||||
|
||||
class HermesTelegramGateway {
|
||||
constructor(private readonly commandRouter: HermesDevOpsCommandRouter) {}
|
||||
|
||||
async handleWebhook(payload: HermesTelegramMessage): Promise<HermesCommandResult> {
|
||||
const transactionId = uuidv7();
|
||||
const telegramUserId = payload.message?.from?.id?.toString();
|
||||
const text = payload.message?.text?.trim();
|
||||
|
||||
if (!telegramUserId || !text) {
|
||||
return this.reject(transactionId, 'INVALID_TELEGRAM_PAYLOAD');
|
||||
}
|
||||
|
||||
await hermesOperationsLog.recordInbound({
|
||||
transactionId,
|
||||
telegramUserId,
|
||||
commandText: text,
|
||||
});
|
||||
|
||||
return this.commandRouter.execute({
|
||||
transactionId,
|
||||
telegramUserId,
|
||||
commandText: text,
|
||||
scope: 'DEVOPS_ONLY',
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Hermes Outbound Dispatcher
|
||||
|
||||
```typescript
|
||||
// hermes/src/integrations/telegram/hermes-telegram-dispatcher.ts
|
||||
@Processor('hermes-notification-queue')
|
||||
class HermesTelegramDispatcher extends WorkerHost {
|
||||
@Process('telegram-devops-outbound')
|
||||
async handleOutbound(job: Job<HermesTelegramOutboundJob>): Promise<TelegramSendResult> {
|
||||
const { chatId, message, transactionId } = job.data;
|
||||
const sent = await this.bot.sendMessage(chatId, `${message}\n\n[Ref: ${transactionId}]`);
|
||||
|
||||
await hermesOperationsLog.recordOutbound({ transactionId, chatId });
|
||||
return sent;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **Out of Scope:** Production DMS Telegram commands, document queries, Workflow Engine actions, and AI document interactions are not implemented in Hermes. If the project needs those capabilities, create a separate DMS Backend ADR/spec and enforce DMS Backend API, CASL/RBAC, `Idempotency-Key`, `audit_logs`, and `publicId` rules there.
|
||||
|
||||
#### System Commands
|
||||
|
||||
* `/status`: เช็คสถานะ Agent (Bypass AI)
|
||||
* `/ci_status`: เช็คสถานะ CI ล่าสุดจาก Gitea
|
||||
* `/repo_summary`: สรุป issue/PR หรือ repository diagnostics
|
||||
* `/schedule_audit`: ตั้ง scheduled DevOps audit ผ่าน Hermes
|
||||
* `/help`: แสดงรายการคำสั่งทั้งหมด
|
||||
|
||||
#### Telegram Command Permission Policy
|
||||
|
||||
Hermes Telegram commands ต้องแบ่งสิทธิ์ตามผลกระทบของคำสั่ง:
|
||||
|
||||
| ระดับ | คำสั่งที่อนุญาต | เงื่อนไข |
|
||||
|---|---|---|
|
||||
| **Read-only** | `/status`, `/ci_status`, `/repo_summary`, `/audit_summary` | ทำได้ทันทีสำหรับผู้ใช้ใน allowlist |
|
||||
| **Write with confirmation** | สร้าง branch, เปิด issue/PR, trigger CI, schedule audit | ต้องมี explicit confirmation ใน Telegram และบันทึก `transactionId` ใน `hermes_operations_log` |
|
||||
| **Forbidden from Telegram** | push ไป `main`/`master`, production deploy, schema migration execution, destructive git/file commands, direct DB writes, storage delete | ห้ามทำผ่าน Telegram ทุกกรณี ต้องใช้ workflow ที่มี human review และ approval แยกต่างหาก |
|
||||
|
||||
ตัวอย่างคำสั่งที่ปลอดภัยควรเป็น “เตรียม branch/PR proposal สำหรับ fix/ci-pnpm-cache” ไม่ใช่ “สร้าง branch แล้ว push ให้เลย”
|
||||
|
||||
#### Future DMS Telegram Module (Out of Scope)
|
||||
|
||||
หากต้องการ Telegram command ที่ query หรือ mutate เอกสารจริง ต้องสร้าง ADR/spec แยกสำหรับ DMS Backend Telegram module เท่านั้น และ rollout นั้นต้องผ่าน ADR-009/016/019/023A เต็มรูปแบบ
|
||||
|
||||
ตาราง `user_telegram_mapping` ด้านล่างเป็นตัวอย่าง requirement สำหรับ future module เท่านั้น **ห้าม create table นี้เป็นส่วนหนึ่งของ ADR-031 rollout**
|
||||
|
||||
```sql
|
||||
CREATE TABLE user_telegram_mapping (
|
||||
id BINARY(16) PRIMARY KEY,
|
||||
user_id BINARY(16) NOT NULL,
|
||||
telegram_id VARCHAR(255) NOT NULL UNIQUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX (telegram_id),
|
||||
CONSTRAINT fk_user_telegram FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Security & Audit Requirements
|
||||
|
||||
#### Tier 1 Security Checklist
|
||||
|
||||
1. **Auth Check:** Hermes Telegram ต้องตรวจสอบ allowlist/admin mapping สำหรับ DevOps commands เท่านั้น หากเป็น DMS Backend Telegram module ในอนาคต ต้องตรวจสอบ `telegram_id` กับ `user_id` และสิทธิ์ผ่าน CASL Guard ของ DMS API
|
||||
2. **Hermes Operations Log:** ทุก Transaction ID ต้องถูกบันทึกใน `hermes_operations_log` หรือ log store ของ Hermes ทันทีทั้งตอนได้รับ (Inbound) และส่งออก (Outbound) ไม่ใช้ `audit_logs` ของ DMS เว้นแต่มี DMS Backend module แยกต่างหาก
|
||||
3. **Error Handling:** ตาม `ADR-007` หากการส่งข้อความผ่าน BullMQ ล้มเหลว (เช่น Telegram API ล่ม) ระบบต้องมีการทำ Retry 3 ครั้งโดยใช้ Exponential Backoff
|
||||
|
||||
#### Webhook Security & Verification
|
||||
|
||||
Telegram ส่ง Webhook พร้อม Header `X-Telegram-Bot-Api-Secret-Token` เพื่อ verify ว่า request มาจาก Telegram จริง:
|
||||
|
||||
```typescript
|
||||
// hermes/src/integrations/telegram/hermes-telegram-webhook.ts
|
||||
class HermesTelegramWebhook {
|
||||
async handleWebhook(headers: WebhookHeaders, payload: HermesTelegramMessage) {
|
||||
if (headers['x-telegram-bot-api-secret-token'] !== process.env.HERMES_TELEGRAM_WEBHOOK_SECRET) {
|
||||
throw new Error('INVALID_TELEGRAM_WEBHOOK_SECRET');
|
||||
}
|
||||
return this.hermesTelegramGateway.handleWebhook(payload);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**ข้อควรระวัง:**
|
||||
- หากไม่ verify secret ใครก็ตามที่คาดเดา URL ได้จะสามารถส่งข้อความปลอมในนามระบบได้
|
||||
- ต้องตั้งค่า `secret_token` เมื่อ register webhook กับ Telegram Bot API
|
||||
|
||||
#### Rate Limiting (ป้องกัน Spam)
|
||||
|
||||
ใช้ Redis-based rate limiting เพื่อจำกัดจำนวน request ต่อ `telegram_id` และไม่เก็บ state ใน memory ของ container:
|
||||
|
||||
```typescript
|
||||
const rateLimitKey = `hermes:telegram:${telegramUserId}`;
|
||||
const requestCount = await redis.incr(rateLimitKey);
|
||||
await redis.expire(rateLimitKey, 60);
|
||||
|
||||
if (requestCount > HERMES_TELEGRAM_RATE_LIMIT_MAX) {
|
||||
throw new Error('HERMES_TELEGRAM_RATE_LIMIT_EXCEEDED');
|
||||
}
|
||||
```
|
||||
|
||||
#### Hermes Transaction Status (Tracking)
|
||||
|
||||
ให้ผู้ใช้ติดตามสถานะของ Transaction ID ได้ผ่าน Telegram command `/status <transactionId>` เฉพาะ Hermes DevOps transaction:
|
||||
|
||||
```typescript
|
||||
// hermes/src/commands/status-command.ts
|
||||
async function getHermesTransactionStatus(transactionId: string): Promise<HermesTransactionStatus> {
|
||||
const operation = await hermesOperationsLog.findByTransactionId(transactionId);
|
||||
const job = await hermesNotificationQueue.getJob(transactionId);
|
||||
|
||||
return {
|
||||
transactionId,
|
||||
status: job?.state ?? operation?.status ?? 'unknown',
|
||||
createdAt: operation?.createdAt,
|
||||
completedAt: job?.finishedOn,
|
||||
attempts: job?.attemptsMade,
|
||||
error: job?.failedReason,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**การใช้งาน:**
|
||||
- ผู้ใช้สามารถ query ด้วย `/status <transactionId>` ใน Telegram สำหรับ DevOps/Hermes transaction เท่านั้น
|
||||
- ไม่ expose DMS-style `/api/v1/telegram` endpoint จาก Hermes
|
||||
|
||||
#### Environment Variables
|
||||
|
||||
```env
|
||||
HERMES_TELEGRAM_BOT_TOKEN=your_token_here
|
||||
HERMES_TELEGRAM_WEBHOOK_SECRET=a_very_secret_string_for_security
|
||||
HERMES_TELEGRAM_ALLOWED_USER_IDS=123456789,987654321
|
||||
HERMES_TELEGRAM_RATE_LIMIT_MAX=10
|
||||
HERMES_TELEGRAM_RATE_LIMIT_WINDOW_MS=60000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. Hermes Interface Modes
|
||||
|
||||
Hermes รันได้ 3 mode หลักที่ share config/data เดียวกันใน `~/.hermes`:
|
||||
- **CLI/TUI** — `hermes --tui` interactive session
|
||||
- **Messaging Gateway** — รองรับ 22 platform (Telegram, Discord, Slack, WhatsApp, Signal, LINE, Mattermost, Matrix, Teams, Google Chat ฯลฯ)
|
||||
- **IDE Integration** — เชื่อมกับ Windsurf / Codex ผ่าน MCP
|
||||
|
||||
#### CLI Commands
|
||||
|
||||
ตัวอย่าง command ด้านล่างรันบน ASUSTOR shell หลัง SSH เข้า NAS:
|
||||
|
||||
```sh
|
||||
hermes # interactive TUI session
|
||||
hermes -q "สรุป open issues" # single query แบบ non-interactive
|
||||
hermes --continue # resume session ล่าสุด
|
||||
hermes -z "task" < file.txt # pipe input → capture output (scriptable)
|
||||
hermes -s dms-context -q "..." # preload skill แล้วถาม
|
||||
hermes -w -q "fix issue #42" # isolated git worktree (safe parallel run)
|
||||
```
|
||||
|
||||
#### เหตุผลที่ Telegram เป็น interface หลักบน ASUSTOR
|
||||
|
||||
Hermes รันบน ASUSTOR ตลอด 24/7 การ SSH เข้าไปแค่เพื่อถามคำถามไม่ practical — สามารถ chat จาก **ทุก device** ขณะที่มันทำงานบน NAS ได้เลย
|
||||
|
||||
```
|
||||
[มือถือ / Laptop ที่ไหนก็ได้]
|
||||
↕ Telegram
|
||||
[Hermes บน ASUSTOR Docker]
|
||||
↕ SSH terminal backend
|
||||
[ASUSTOR filesystem / Gitea / MariaDB]
|
||||
```
|
||||
|
||||
#### Updated Stack ภาพรวม
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Laptop / Desktop │
|
||||
│ │
|
||||
│ Windsurf IDE agy (Antigravity CLI) │
|
||||
│ └─ Cascade └─ same engine as desktop │
|
||||
│ └─ MCP (MariaDB, └─ parallel subagents │
|
||||
│ Gitea) └─ /schedule tasks │
|
||||
│ │
|
||||
│ Codex CLI │
|
||||
│ └─ → hermes proxy (Developer AI Proxy only) │
|
||||
└────────────────────┬────────────────────────────────┘
|
||||
│ SSH / Telegram
|
||||
┌────────────────────▼────────────────────────────────┐
|
||||
│ ASUSTOR NAS (ADM + Docker) │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────┐ │
|
||||
│ │ Hermes Agent (Docker container) │ │
|
||||
│ │ │ │
|
||||
│ │ Memory: DMS schema, conventions, ADRs │ │
|
||||
│ │ Skills: dms-context, gitea-watch, rag-ops │ │
|
||||
│ │ Channels: Telegram + hermes proxy │ │
|
||||
│ │ Terminal backend: SSH → Gitea Actions │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Gitea Actions Runners (existing) │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Interface Selection Guide
|
||||
|
||||
| สถานการณ์ | Interface | ทำไม |
|
||||
|---|---|---|
|
||||
| นั่งทำงานที่โต๊ะ coding | Windsurf Cascade | IDE context + MCP |
|
||||
| สั่ง task ซับซ้อน / multi-agent | `agy` CLI หรือ Desktop | parallel subagents |
|
||||
| batch ops / scaffolding | Codex CLI | bash execution |
|
||||
| อยู่นอกบ้าน / มือถือ | Telegram → Hermes | always-on บน ASUSTOR สำหรับ DevOps commands เท่านั้น |
|
||||
| CI watch / scheduled audit | Hermes `/schedule` | 24/7 background |
|
||||
| Codex ต้องการ assistance สำหรับ coding/devops | `hermes proxy` → Codex | Developer AI Proxy เท่านั้น ห้ามใช้กับ production document payload |
|
||||
|
||||
#### 3 วิธีที่คุยกับ Hermes
|
||||
|
||||
**1. Telegram (แนะนำสำหรับ DevOps commands)** — ส่งข้อความจากที่ไหนก็ได้
|
||||
|
||||
ตัวอย่างที่ส่งได้:
|
||||
```
|
||||
"CI run ล่าสุดผ่านไหม?"
|
||||
"สรุป TODO ใน correspondence module ให้หน่อย"
|
||||
"เตรียม branch/PR proposal สำหรับ fix/ci-pnpm-cache"
|
||||
```
|
||||
|
||||
**2. SSH เข้า ASUSTOR → `hermes --tui`** — ใช้เมื่อทำงานหนัก (multi-step coding task)
|
||||
|
||||
```bash
|
||||
ssh admin@<ASUSTOR_IP>
|
||||
hermes --continue # resume session ล่าสุด
|
||||
```
|
||||
|
||||
**3. Codex CLI บน laptop → hermes proxy** — Codex รันบน laptop และใช้ Hermes เป็น Developer AI Proxy สำหรับ coding/devops assistance เท่านั้น
|
||||
|
||||
ตัวอย่างบน Windows PowerShell:
|
||||
|
||||
```powershell
|
||||
$env:OPENAI_BASE_URL = "http://<ASUSTOR_IP>:8766/v1"
|
||||
$env:OPENAI_API_KEY = "<HERMES_PROXY_API_KEY>"
|
||||
|
||||
# ใช้กับ coding/devops task ที่ไม่มี production document payload หรือ secrets
|
||||
codex "scaffold NestJS module for repository diagnostics"
|
||||
```
|
||||
|
||||
> **Secret Handling:** ตัวอย่างค่า key/token ในเอกสารนี้เป็น placeholder เท่านั้น ห้าม commit secret จริงลง repo หรือ spec file
|
||||
|
||||
> **หมายเหตุ Port และ Boundary:** hermes proxy ใช้ port `:8766` — ห้ามใช้ `:8765` ซึ่ง PaddleOCR sidecar ใช้อยู่แล้ว (ADR-023A) และห้ามนำ proxy นี้ไปใช้แทน DMS AI runtime ที่ต้องผ่าน DMS Backend/BullMQ/Ollama boundary ตาม ADR-023A
|
||||
|
||||
---
|
||||
|
||||
### 5. Antigravity CLI (agy) + Hermes MCP Integration
|
||||
|
||||
#### Antigravity CLI (`agy`) คืออะไร
|
||||
|
||||
`agy` เป็น Go binary — single self-contained executable ที่ bidirectional sync กับ desktop app ได้ ใช้ Gemini 3.5 Flash by default รองรับ parallel subagents, slash commands อย่าง `/goal` และ `/schedule`
|
||||
|
||||
```bash
|
||||
agy # TUI interactive
|
||||
agy "scaffold NestJS module for docs" # single task
|
||||
agy /goal "implement RAG pipeline" # set persistent goal
|
||||
agy /schedule "run audit daily 02:00" # background scheduled task
|
||||
```
|
||||
|
||||
#### agy ใช้ Hermes เป็น model backend ตรงๆ ไม่ได้
|
||||
|
||||
`agy` ผูกกับ Google Antigravity backend — ไม่รองรับ custom OpenAI-compatible endpoint โดยตรง
|
||||
|
||||
**วิธีที่ถูกต้อง:** เชื่อม agy กับ Hermes **ผ่าน MCP** — agy ยังใช้ Gemini ในการ orchestrate แต่ดึง DMS memory + tools จาก Hermes มาด้วย
|
||||
|
||||
```
|
||||
agy (Gemini 3.5 Flash)
|
||||
└── MCP: hermes-memory ← ดึง DMS context จาก Hermes
|
||||
└── MCP: hermes-tools ← สั่ง Hermes รัน bash/git บน ASUSTOR
|
||||
└── MCP: mariadb ← DB schema (เดิม)
|
||||
└── MCP: gitea ← Gitea API (เดิม)
|
||||
```
|
||||
|
||||
#### Config Files สำหรับ agy
|
||||
|
||||
**`~/.gemini/config/mcp_config.json`**
|
||||
|
||||
```json
|
||||
{
|
||||
"hermes-memory": {
|
||||
"serverUrl": "http://<ASUSTOR_IP>:8766/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer <HERMES_PROXY_API_KEY>"
|
||||
},
|
||||
"description": "LCBP3 DMS persistent memory and context from Hermes"
|
||||
},
|
||||
"mariadb": {
|
||||
"command": "docker",
|
||||
"args": [
|
||||
"run", "--rm", "-i",
|
||||
"-e", "MARIADB_HOST=<QNAP_IP>",
|
||||
"-e", "MARIADB_PORT=3306",
|
||||
"-e", "MARIADB_USER=dms_user",
|
||||
"-e", "MARIADB_PASSWORD=<password>",
|
||||
"mcp/mariadb"
|
||||
]
|
||||
},
|
||||
"gitea": {
|
||||
"command": "docker",
|
||||
"args": [
|
||||
"run", "--rm", "-i",
|
||||
"-e", "GITEA_URL=http://<QNAP_IP>:3000",
|
||||
"-e", "GITEA_TOKEN=<token>",
|
||||
"mcp/gitea"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **สำคัญ:** field name สำหรับ remote MCP server ใช้ `serverUrl` (ไม่ใช่ `url`) ใน agy — ถ้าผิดตรงนี้ server จะ fail แบบ silent
|
||||
|
||||
**`~/.gemini/antigravity-cli/settings.json`**
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "gemini-3.5-flash-high",
|
||||
"theme": "Default",
|
||||
"autoAccept": false,
|
||||
"contextFiles": [
|
||||
"~/.gemini/antigravity-cli/DMS_CONTEXT.md"
|
||||
],
|
||||
"permissions": {
|
||||
"defaultMode": "suggest",
|
||||
"autoApprove": [
|
||||
"read_file",
|
||||
"list_directory",
|
||||
"mcp_hermes-memory_recall",
|
||||
"mcp_mariadb_query"
|
||||
]
|
||||
},
|
||||
"subagents": {
|
||||
"maxParallel": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**`~/.gemini/antigravity-cli/DMS_CONTEXT.md`**
|
||||
|
||||
```markdown
|
||||
# LCBP3 DMS — agy context
|
||||
|
||||
## Stack
|
||||
- NestJS + Next.js 14, pnpm monorepo, MariaDB + TypeORM
|
||||
- Gitea (QNAP) → CI via ASUSTOR runners
|
||||
- Hermes Agent (ASUSTOR): DMS memory + bash execution
|
||||
|
||||
## When starting any task
|
||||
1. Call mcp_hermes-memory_recall("dms-context") ก่อนเสมอ
|
||||
2. อ่าน schema ผ่าน mcp_mariadb ก่อน scaffold
|
||||
3. งานที่ต้องรัน bash → delegate ให้ Hermes ผ่าน MCP
|
||||
|
||||
## Hard rules (🔴)
|
||||
- ห้าม commit ตรง main/develop
|
||||
- ห้ามรัน migration โดยไม่ได้รับ human approval
|
||||
```
|
||||
|
||||
#### Flow เมื่อใช้ agy + Hermes ร่วมกัน
|
||||
|
||||
```
|
||||
คุณพิมพ์ใน agy:
|
||||
"implement document revision API"
|
||||
│
|
||||
▼
|
||||
agy (Gemini 3.5 Flash)
|
||||
1. เรียก MCP hermes-memory → ดึง DMS context
|
||||
2. เรียก MCP mariadb → อ่าน schema
|
||||
3. วางแผน → spawn 2 subagents parallel:
|
||||
├── subagent A: NestJS service + controller
|
||||
└── subagent B: unit tests
|
||||
4. subagents เรียก MCP hermes-tools
|
||||
→ Hermes รัน git/pnpm บน ASUSTOR
|
||||
5. รายงานผลกลับมาที่ agy
|
||||
```
|
||||
|
||||
| | agy | Hermes |
|
||||
|---|---|---|
|
||||
| Model | Gemini 3.5 Flash (orchestration) | Claude Sonnet (memory/tools) |
|
||||
| Role | สั่งงาน, parallel subagents | จำ context, รัน bash บน ASUSTOR |
|
||||
| เชื่อมกันผ่าน | MCP client | MCP server (`/mcp` endpoint) |
|
||||
|
||||
---
|
||||
|
||||
### 6. Deploy Prerequisites
|
||||
|
||||
#### VLAN Requirements
|
||||
|
||||
ต้องเปิด path นี้ได้ก่อน deploy:
|
||||
```
|
||||
ASUSTOR (VLAN ใดก็ตาม) → QNAP VLAN 10 (MariaDB :3306, Gitea :3000)
|
||||
```
|
||||
หาก inter-VLAN routing ยังติด firewall อยู่ ต้อง allow subnet ของ ASUSTOR เข้า VLAN 10 ด้วย
|
||||
|
||||
#### Network Exposure Requirements
|
||||
|
||||
* Hermes API (`:8080`), Hermes proxy (`:8766`), และ MCP endpoint (`/mcp`) ต้อง bind หรือ firewall ให้อยู่เฉพาะ LAN/VPN ที่กำหนดเท่านั้น
|
||||
* ห้ามเปิด Hermes API/proxy/MCP ตรงสู่ public internet
|
||||
* Telegram webhook เป็น endpoint เดียวที่อาจต้องรับ traffic จาก internet และต้องอยู่หลัง reverse proxy ที่เปิด TLS, verify `X-Telegram-Bot-Api-Secret-Token`, ทำ IP/rate limit, และบันทึก request log
|
||||
* หากใช้ Cloudflare Tunnel, Tailscale Funnel, หรือ reverse proxy ใด ๆ ต้องกำหนด allowlist และ audit config ก่อนเปิดใช้งานจริง
|
||||
|
||||
#### Secret Management Requirements
|
||||
|
||||
* Secret ทั้งหมด (`HERMES_PROXY_API_KEY`, `HERMES_TELEGRAM_BOT_TOKEN`, `HERMES_TELEGRAM_WEBHOOK_SECRET`, Gitea token, read-only DB credential) ต้องเก็บนอก repo
|
||||
* ห้าม commit secret จริงใน `.env`, compose file, MCP config, ADR/spec, README, หรือ screenshot
|
||||
* บน ASUSTOR ให้ใช้ secret store ที่มีอยู่ หรือไฟล์ environment นอก repo ที่จำกัด permission เฉพาะ admin/service account
|
||||
* ต้องมี rotation plan สำหรับ API key/token และ revoke token ทันทีเมื่อเครื่อง client หายหรือ operator ออกจากทีม
|
||||
* Verification ต้องรวม secret scan ก่อน rollout และหลังแก้ config
|
||||
|
||||
#### Gitea Token Requirements
|
||||
|
||||
* Default token ต้องเป็น read-only สำหรับอ่าน repo, issue, PR, CI status และ release metadata
|
||||
* Write action เช่น create branch, create issue, create PR, หรือ trigger CI ต้องใช้ token แยกที่ scope แคบกว่า admin และต้องผ่าน Telegram explicit confirmation ก่อน
|
||||
* ห้ามใช้ admin token กับ Hermes
|
||||
* ห้ามใช้ token ที่สามารถ push ไป `main`/`master` หรือ bypass branch protection ได้
|
||||
* Token ต้องมี expiry/rotation schedule และสามารถ revoke แยกตาม operator/service ได้
|
||||
* ทุก write action ต้องบันทึก `transactionId`, token identity หรือ service identity, target repo, target branch, และผลลัพธ์ใน `hermes_operations_log`
|
||||
|
||||
#### Hermes Operations Log Requirements
|
||||
|
||||
* `hermes_operations_log` ต้องอยู่ใน Hermes-owned storage เท่านั้น เช่น SQLite/Postgres volume ภายใน Hermes stack หรือ structured log file ที่ส่งต่อไป log collector
|
||||
* ห้ามเขียน Hermes DevOps operations ลง `audit_logs` ของ DMS ยกเว้น future DMS Telegram module ที่แยก ADR/spec และผ่าน DMS Backend API
|
||||
* ต้องบันทึกอย่างน้อย `transactionId`, operator identity, command type, target system, status, createdAt, completedAt, และ error classification
|
||||
* ต้อง redact command payload ที่อาจมี secret, token, file path sensitive, หรือ production document content
|
||||
* Retention เริ่มต้น 90 วัน แล้ว archive/delete ตาม policy ของ DevOps logs
|
||||
* Log storage ต้องจำกัดสิทธิ์อ่านเฉพาะ admin/operator ที่จำเป็น
|
||||
|
||||
#### Failure & Degradation Requirements
|
||||
|
||||
* Hermes, Telegram Bridge, MCP, และ hermes proxy ต้องเป็น optional DevOps tooling ไม่ใช่ dependency ของ DMS production runtime
|
||||
* หาก Hermes stack ล่ม ผู้ใช้ DMS ต้องยังใช้งาน frontend/backend, Workflow Engine, notification, และ AI pipeline ตาม ADR-023A ได้ตามปกติ
|
||||
* Degraded mode คือกลับไปใช้ IDE, Gitea UI, CI UI, SSH/manual ops, และ Codex/Windsurf local workflow ตามปกติ
|
||||
* ห้ามให้ production deploy, Workflow Engine transition, AI inference, หรือ document ingestion รอ Hermes availability
|
||||
* Monitoring ต้องแจ้งเตือนเฉพาะ operator/devops team ไม่ alert เป็น production DMS outage เว้นแต่มีผลกระทบจริงกับ DMS service
|
||||
|
||||
#### Monitoring & Alerting Requirements
|
||||
|
||||
* Hermes down, Telegram Bridge down, MCP unavailable, หรือ hermes proxy down ให้แจ้งเป็น DevOps warning ไม่ใช่ production DMS outage
|
||||
* Repeated API key failure, webhook secret failure, Telegram allowlist rejection spike, หรือ rate limit spike ต้องแจ้งเป็น security alert
|
||||
* Failed write-with-confirmation command เช่น create branch/issue/PR/trigger CI ต้องแจ้ง DevOps alert พร้อม `transactionId`
|
||||
* หาก hermes proxy ได้รับ payload ที่คล้าย production document content, secret, token, password, หรือ storage path ต้องถือเป็น security incident และต้อง redact log ทันที
|
||||
* Monitoring ต้องแยก dashboard/status ของ Hermes ออกจาก DMS production service health เพื่อไม่ให้ incident severity ปะปนกัน
|
||||
* Alert ทุกประเภทต้อง link กลับไปยัง `hermes_operations_log` ด้วย `transactionId` เมื่อมี
|
||||
|
||||
#### ASUSTOR-Specific Notes
|
||||
|
||||
ASUSTOR ADM Docker บางรุ่นใช้ path `/share/` แทน `/volume1/` — รัน `df -h` บน ASUSTOR shell หลัง SSH เข้า NAS เพื่อดู shared folder mount แล้วแก้ path ใน `docker-compose.yml` ให้ตรง
|
||||
|
||||
#### ลำดับ Deploy ที่แนะนำ
|
||||
|
||||
```
|
||||
1. สร้าง Telegram bot (@BotFather) → copy token
|
||||
2. สร้าง SSH keypair บน ASUSTOR
|
||||
3. copy files ขึ้น ASUSTOR → แก้ .env (ระวัง HERMES_PROXY_PORT=8766)
|
||||
4. บน ASUSTOR shell: `docker compose up -d` → ทดสอบ Telegram
|
||||
5. ทดสอบ hermes proxy จาก laptop
|
||||
6. บน Windows PowerShell: ตั้ง `$env:OPENAI_BASE_URL = "http://<ASUSTOR_IP>:8766/v1"` → ทดสอบ Codex CLI สำหรับ coding/devops task เท่านั้น
|
||||
7. ส่ง /schedule tasks ผ่าน Telegram
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Roadmap
|
||||
|
||||
### Rollout Stages
|
||||
|
||||
ADR-031 ต้อง rollout แบบเป็น stage เพื่อลดความเสี่ยง และห้ามข้ามไป stage ที่สูงกว่าโดยยังไม่ผ่าน verification gate ของ stage ก่อนหน้า:
|
||||
|
||||
1. **Stage 0 - Documentation Only:** สรุป boundary และ rollout plan ให้ stable ก่อน ยังไม่ deploy, ไม่เปิด network, ไม่เพิ่ม schema
|
||||
2. **Stage 1 - Hermes Container LAN-only:** deploy Hermes container บน ASUSTOR แบบ LAN/VPN-only, ใช้ Redis/log store แยก, ยังไม่เปิด Telegram public webhook
|
||||
3. **Stage 2 - Read-only Diagnostics:** เปิด Gitea read-only และ MariaDB masked/read-only diagnostics ตาม least privilege และ data minimization policy
|
||||
4. **Stage 3 - Telegram Read-only DevOps:** เปิด Telegram DevOps commands เฉพาะ read-only เช่น `/status`, `/ci_status`, `/repo_summary`, `/audit_summary`
|
||||
5. **Stage 4 - Write-with-confirmation DevOps:** เปิด action ที่เขียน Gitea/CI ได้เฉพาะ create branch/issue/PR/trigger CI/schedule audit พร้อม explicit confirmation และ `hermes_operations_log`
|
||||
6. **Stage 5 - Developer AI Proxy:** เปิด `hermes proxy` สำหรับ coding/devops assistance เท่านั้น โดยห้าม production document payload, secrets, และ DMS AI runtime usage
|
||||
|
||||
### Stage Acceptance Gates
|
||||
|
||||
| Stage | Go/No-Go Gate |
|
||||
|---|---|
|
||||
| **Stage 0** | ADR boundary reviewed, locked decisions captured, no schema delta, no deploy files applied |
|
||||
| **Stage 1** | LAN/VPN-only exposure confirmed, ASUSTOR runtime resource limits enforced, Hermes Redis/log store separated from DMS production |
|
||||
| **Stage 2** | read-only DB grant verified, masked/redacted fields verified, Gitea read-only token verified, no direct storage mount |
|
||||
| **Stage 3** | Telegram allowlist verified, webhook secret verified, rate limit verified, `hermes_operations_log` captures inbound/outbound transaction |
|
||||
| **Stage 4** | explicit confirmation flow verified, forbidden action blocklist verified, branch protection and Gitea token scope verified |
|
||||
| **Stage 5** | proxy LAN/VPN-only verified, no secrets/document payload test passed, proxy not wired into DMS AI runtime or document intelligence path |
|
||||
|
||||
### Stage Owner & Approval Matrix
|
||||
|
||||
| Stage | Required Owner/Approval |
|
||||
|---|---|
|
||||
| **Stage 0** | Architecture Team |
|
||||
| **Stage 1** | DevOps/Admin |
|
||||
| **Stage 2** | DBA/Security + DevOps |
|
||||
| **Stage 3** | DevOps/Security |
|
||||
| **Stage 4** | Architecture + Security + Repo Owner |
|
||||
| **Stage 5** | Architecture + Security |
|
||||
|
||||
### Stage Rollback Matrix
|
||||
|
||||
| Stage | Rollback Action |
|
||||
|---|---|
|
||||
| **Stage 1** | Stop/remove Hermes container, keep `hermes_operations_log` for review, verify DMS production remains unaffected |
|
||||
| **Stage 2** | Revoke DB/Gitea read-only credentials, disable MCP servers, confirm no direct storage mount exists |
|
||||
| **Stage 3** | Unregister Telegram webhook, revoke Telegram bot token if needed, stop `HermesTelegramDispatcher` |
|
||||
| **Stage 4** | Revoke Gitea write token, disable write commands, preserve operations log for audit/review |
|
||||
| **Stage 5** | Unset `OPENAI_BASE_URL` on clients, stop hermes proxy, revoke proxy API key |
|
||||
|
||||
### Roadmap Items
|
||||
|
||||
1. **Hermes Telegram Scope:** จำกัด Telegram Bridge ของ Hermes ให้รองรับ DevOps commands เท่านั้น
|
||||
2. **Notification Queue:** ปรับใช้โครงสร้าง `HermesTelegramDispatcher` เพื่อดึงงาน DevOps notification ออกจากคิว `hermes-notification-queue`
|
||||
3. **Command Handling:** สร้าง Telegram Gateway Controller เพื่อรองรับ Webhook และส่งต่อไปยัง DevOps Command Router
|
||||
4. **Logging:** เพิ่ม Hermes operations logging เพื่อบันทึกการทำงานของ Telegram ในทุกจุดที่เปลี่ยนผ่าน Transaction
|
||||
5. **No DMS Schema Rollout:** ADR-031 ไม่เพิ่มหรือแก้ production DMS tables ใด ๆ รวมถึงไม่สร้าง `user_telegram_mapping`
|
||||
|
||||
---
|
||||
|
||||
## Verification Plan
|
||||
|
||||
1. **Resource Limit Test:** บน ASUSTOR shell รัน `docker inspect hermes_agent_lcbp3` และ `docker stats hermes_agent_lcbp3` เพื่อยืนยันว่า runtime enforce RAM ไม่เกิน 4GB และ CPU ไม่เกิน 2 Cores จริง ไม่ใช่แค่มีค่าใน compose file
|
||||
2. **API Key Auth Test:** บน Windows PowerShell รัน `Invoke-WebRequest -Headers @{ "X-API-Key" = "wrong_key" } -Uri "http://<ASUSTOR_IP>:8080/api/v1/sync"` ต้องโดนบล็อกด้วยสิทธิ์ 401 Unauthorized
|
||||
3. **Git Hooks Test:** ลองจงใจพิมพ์ `: any` หรือ `console.log` ลงในไฟล์ `.ts` แล้วกด `git commit` ระบบต้องทำการ `exit 1`
|
||||
4. **Unit Test:** ทดสอบ `handleInboundCommand` ว่าสามารถแยกคำสั่ง `/` ออกจากข้อความปกติได้ถูกต้อง
|
||||
5. **Queue Test:** ตรวจสอบว่า `BullMQ` ดึงงาน `telegram-devops-outbound` จาก `hermes-notification-queue` ไปประมวลผลและตอบกลับ Telegram ได้สำเร็จ
|
||||
6. **Security Test:** ทดสอบว่า Telegram user ที่ไม่อยู่ใน `HERMES_TELEGRAM_ALLOWED_USER_IDS` ไม่สามารถใช้ DevOps commands ได้
|
||||
7. **Schema Safety Test:** ตรวจสอบว่า ADR-031 rollout ไม่มี SQL delta หรือ migration ที่สร้าง `user_telegram_mapping`
|
||||
8. **Secret Scan Test:** ตรวจสอบว่าไม่มี secret จริงใน repo/spec/compose/MCP config และ environment file ที่ใช้ deploy อยู่นอก repo พร้อม permission จำกัด
|
||||
9. **Operations Log Test:** ตรวจสอบว่า Hermes operation ถูกบันทึกใน Hermes-owned log store, payload ถูก redact, และไม่มี write ไปยัง DMS `audit_logs`
|
||||
10. **Failure Isolation Test:** ปิด Hermes container แล้วตรวจว่า DMS frontend/backend, Workflow Engine, AI pipeline, และ user-facing app ยังทำงานตามปกติ
|
||||
11. **Monitoring Test:** จำลอง Hermes down, webhook auth failure, failed write command, และ proxy secret-like payload เพื่อยืนยัน alert severity/channel ถูกต้องและ log ถูก redact
|
||||
|
||||
---
|
||||
|
||||
## Related ADRs
|
||||
|
||||
- ADR-007: Error Handling Strategy
|
||||
- ADR-008: Email Notification Strategy (BullMQ)
|
||||
- ADR-016: Security & Authentication
|
||||
- ADR-019: Hybrid Identifier Strategy (UUIDv7)
|
||||
- ADR-023/ADR-023A: Unified AI Architecture และ AI isolation boundary
|
||||
|
||||
---
|
||||
|
||||
## Change Log
|
||||
|
||||
| Date | Version | Changes |
|
||||
|------|---------|---------|
|
||||
| 2026-05-28 | 1.0.0 | Initial ADR creation - Merged from CONTEXT-ADR-031 and CONTEXT-ADR-031-Added |
|
||||
| 2026-05-28 | 1.1.0 | Added sections 4–6 from CONTEXT-ADR-031-Added-2: Hermes Interface Modes, agy+Hermes MCP Integration, Deploy Prerequisites; fixed port conflict (hermes proxy :8766, not :8765) |
|
||||
@@ -63,6 +63,7 @@ Architecture Decision Records (ADRs) เป็นเอกสารที่บ
|
||||
| [ADR-003](./ADR-003-api-design-strategy.md) | API Design Strategy | ✅ Accepted | 2026-04-04 | Hybrid REST + Action Strategy สำหรับ Resource และ Workflow Operations |
|
||||
| [ADR-007](./ADR-007-error-handling-strategy.md) | Error Handling & Recovery | ✅ Accepted | 2026-04-04 | Layered Error Classification พร้อม User-friendly Messages และ Recovery Actions |
|
||||
| [ADR-008](./ADR-008-email-notification-strategy.md) | Email & Notification Strategy | ✅ Accepted (Pending Review) | 2026-02-24 | BullMQ + Redis Queue สำหรับ Multi-channel Notifications (Email, LINE, In-app) |
|
||||
| [ADR-031](./ADR-031-hermes-agent-telegram-devops-bridge.md) | Hermes Agent & Telegram DevOps Bridge | 📝 Draft | 2026-05-28 | Hermes เป็น optional Developer Operations Agent พร้อม Telegram DevOps commands, read-only diagnostics, และ staged rollout |
|
||||
|
||||
### Observability
|
||||
|
||||
|
||||
Reference in New Issue
Block a user