# Đấu nối dự án bất kỳ với FlyTrustAgent

FTA dùng HTTPS/JSON làm giao thức chung. Dự án có thể viết bằng Node, Python, PHP, Java, .NET hoặc ngôn ngữ khác, và dùng database riêng. Kết nối SOAP, gRPC, MQTT hoặc hệ thống chỉ có database cần một API adapter do dự án triển khai. FTA hiện chưa có connector native cho các giao thức đó.

## Hai chiều xác thực

| Chiều gọi | Xác thực | Nơi quản lý |
| --- | --- | --- |
| Backend dự án → FTA | Bearer token, scope tenant/project | Console FTA → Token API |
| FTA → API dự án | HMAC, Bearer hoặc API key | Secret phía server FTA + cấu hình `auth` |
| FTA → API công khai | `NONE`, chỉ GET | Project registry |

Token hai chiều là hai credential riêng. Quyền runtime không thay thế quyền tổ chức, người dùng, revision hay gate nghiệp vụ tại dự án.

## 1. Provision kết nối

1. Đăng nhập Gmail tại https://flytrust-agent.io.vn/platform/ bằng quản trị nền tảng.
2. Tạo tenant hoặc chọn tenant phù hợp; thêm người dùng với vai trò cần thiết.
3. Quản trị server thêm API origin vào `FTA_PLATFORM_CONNECTION_ORIGINS` và tên env chứa credential vào `FTA_PLATFORM_CREDENTIAL_ENV_ALLOWLIST`. Biến mới kế thừa `FTA_PLATFORM_SIGNING_ENV_ALLOWLIST` nếu chưa thiết lập. Đây là bước cấu hình server; console không tự thêm origin hoặc lưu secret.
4. Lưu credential thực vào env/secret phía server FTA; không đưa giá trị credential vào JSON, browser hoặc Git. Receiver lưu credential tương ứng của mình.
5. Console → Kết nối API → Đăng ký project. Project ID mới bắt đầu bằng tenant ID + `-`.
6. Khai báo health GET và các capability. Project mới hoặc vừa sửa luôn tắt. Kiểm tra health, rồi bật kết nối. Health 2xx chỉ xác nhận kết nối, chưa chứng minh tác vụ nghiệp vụ.
7. Tạo token OPERATOR, giới hạn project. Token chỉ hiện một lần, hết hạn sau 90 ngày. Reviewer dùng token APPROVER riêng nếu gọi bằng backend.

Ví dụ dưới đây dùng tenant `farmtruth`, project `farmtruth-station`. Đây là cấu hình mẫu; không tự giả định FarmTruth đã có những endpoint này hoặc đã được provision.

```json
{
  "id": "farmtruth-station",
  "name": "FarmTruth Station",
  "baseUrl": "https://farmtruth-station.io.vn",
  "healthPath": "/api/integrations/fta/health",
  "auth": { "type": "HMAC", "credentialEnv": "FTA_FARMTRUTH_SIGNING_SECRET" },
  "capabilities": [
    {
      "name": "station.read", "description": "Đọc trạm trong phạm vi được cấp quyền",
      "method": "GET", "path": "/api/integrations/fta/stations/{stationId}",
      "sideEffect": false, "approval": "NONE", "idempotent": true,
      "request": { "query": ["organizationId"] }, "outputField": "data",
      "inputSchema": { "stationId": { "type": "string", "required": true }, "organizationId": { "type": "string", "required": true } }
    },
    {
      "name": "task.create", "description": "Ghi nhiệm vụ đã được dự án cho phép",
      "method": "POST", "path": "/api/integrations/fta/tasks",
      "sideEffect": true, "approval": "ALWAYS", "idempotent": true,
      "request": { "body": ["organizationId", "stationId", "expectedRevision", "intentId", "task"] },
      "inputSchema": { "organizationId": { "type": "string", "required": true }, "intentId": { "type": "string", "required": true }, "task": { "type": "object", "required": true } }
    }
  ]
}
```

Chỉ khai báo `idempotent: true` khi receiver thực sự chống trùng bền vững. API ADMIN `PUT /v1/projects/{id}` cần thêm `adapter: "HTTP_JSON"`; quản trị qua console dùng session, CSRF và các allowlist. Cập nhật cần `expectedRevision` khớp revision hiện tại và không còn run chưa kết thúc.

Thay đổi xác thực outbound bằng một trong các cấu hình:

```json
{ "type": "BEARER", "credentialEnv": "FTA_PROJECT_ACCESS_TOKEN" }
{ "type": "API_KEY", "credentialEnv": "FTA_PROJECT_API_KEY", "headerName": "X-API-Key" }
{ "type": "NONE" }
```

`authSecretEnv` vẫn được hỗ trợ cho các kết nối HMAC cũ; không khai báo cùng `auth`. Không cho phép tùy ý header Cookie/Host/Authorization hoặc header runtime trong API_KEY.

## 2. Quy tắc request và output

- Path là đường dẫn tuyệt đối trên origin đã đăng ký. `{resourceId}` lấy giá trị từ input, kiểm tra identifier rồi URL encode. Không nhận URL tùy ý từ operator.
- `request.query` chọn các field input scalar đưa vào query. Không hỗ trợ array/object trong query hiện tại.
- `request.body` chọn các field input đưa vào JSON body. Path/query field không được khai báo lặp trong body.
- Nếu không khai báo mapping, GET đưa input còn lại vào query; các method khác đưa input còn lại vào JSON body. Field đã dùng cho path/query tự bỏ khỏi body.
- `outputField: "data"` lấy trường đó làm output. Expectation và input binding của bước sau tham chiếu output đã chọn. Thiếu trường sẽ làm bước thất bại.
- Method hỗ trợ: GET, POST, PUT, PATCH, DELETE. Response thành công phải là JSON hoặc body rỗng; redirect bị chặn. Request/response tối đa 1 MiB; timeout 100–60.000 ms.
- Các credential outbound không xuất hiện trong output DRY_RUN. Chỉ đưa dữ liệu nghiệp vụ cần thiết vào input: input/output được lưu trong runtime và hiển thị cho người có quyền tenant.

## 3. Backend dự án gọi FTA

```bash
curl --fail --silent --show-error \
  https://api.flytrust-agent.io.vn/v1/integrations/contract \
  -H "Authorization: Bearer $FTA_OPERATOR_TOKEN"
```

SDK Node 22+:

```typescript
import { RuntimeClient } from '@flytrust/agent-runtime/client';
const fta = new RuntimeClient('https://api.flytrust-agent.io.vn',
  process.env.FTA_OPERATOR_TOKEN!, { apiPath: '/v1' });
await fta.integrationContract();
const projects = await fta.projects();
// Lưu mô hình đầy đủ theo OpenAPI/Workbench và ghi modelId/revision tại dự án.
const plan = await fta.createPlan({
  name: 'Đọc trạng thái trạm', modelId: savedModelId, modelRevision: savedRevision,
  mode: 'LIVE', steps: [{ id: 'read', name: 'Đọc trạm',
    projectId: 'farmtruth-station', capability: 'station.read',
    input: { stationId, organizationId }, dependsOn: [],
    expectation: { field: 'id', equals: stationId } }]
});
if (plan.status !== 'READY') throw new Error(plan.validationErrors.join(' '));
const run = await fta.startRun(plan.id, stableBusinessIntentId);
// Persist intentId, planId, run.id ở database dự án; poll fta.run(run.id).
```

`savedModelId`, `savedRevision`, `stationId`, `organizationId` và `stableBusinessIntentId` được backend dự án cung cấp sau khi kiểm tra quyền. Bắt đầu bằng DRY_RUN để xem request trước khi LIVE. Mutation đợi APPROVER duyệt từng bước; quyết định luôn có lý do. Không đưa token FTA vào frontend. SDK vẫn mặc định `/api/runtime/v1` để tương thích tích hợp cũ.

## 4. Database và receiver của dự án

FTA lưu tenant, cấu hình kết nối, mô hình, kế hoạch, run, approval và audit. Dự án giữ database nghiệp vụ và tự quản lý migration. Không cần dùng chung engine/schema hoặc mở port database cho FTA.

```text
DB dự án → outbox → backend dự án → FTA plan/run
FTA → API receiver → transaction: receipt + thay đổi nghiệp vụ → DB dự án
DB dự án / poll run → outcome observation
```

Receiver kiểm tra credential, tenant/organization, intentId, revision và gate nghiệp vụ. Với HMAC, dùng raw body nguyên gốc và `verifyProjectSignature` trong SDK, cửa sổ timestamp ±5 phút. Chữ ký bao gồm METHOD, pathname + query, timestamp, idempotency key, run ID, step ID, SHA256(raw body), nối LF không LF cuối. Giữ raw body trước JSON parser; xem receiver HMAC mẫu trong `examples/`.

Chống trùng cần unique constraint theo identity project/organization + idempotency key. Trong cùng transaction: đối chiếu fingerprint request; cùng key/cùng payload trả receipt đã commit; khác payload trả 409; commit thay đổi nghiệp vụ cùng receipt, rồi mới trả `accepted: true`. Nếu ghi ra dịch vụ khác, dùng outbox thay vì hứa transaction xuyên dịch vụ. Không dùng cache hoặc Map RAM làm chứng cứ chống trùng.

Khi timeout hoặc kết quả mutation không rõ, FTA chuyển `RECOVERY_REQUIRED`. Reviewer đối chiếu receipt bên dự án trước khi xác nhận đã thực hiện/chưa thực hiện; không tự gửi lại với key mới.

## 5. Prompt giao cho agent của dự án

> Triển khai tích hợp HTTPS/JSON giữa backend dự án này và FlyTrustAgent tại https://api.flytrust-agent.io.vn/v1. Trước tiên kiểm tra source, database, auth và các gate nghiệp vụ hiện có. Giữ dữ liệu nghiệp vụ trong database dự án. Dùng token OPERATOR theo tenant/project, lưu trong secret backend; dùng APPROVER riêng khi cần. Đọc OpenAPI và GET /integrations/contract có xác thực. Đề xuất capability thật từ service hiện có, không tự giả định endpoint hoặc credential đã có. Triển khai receiver HTTPS/JSON với health GET, xác thực HMAC/Bearer/API key theo cấu hình được provision, cưỡng chế organization/intent/revision và quyền nghiệp vụ. Tạo migration bổ sung receipt/inbox/outbox nếu source chưa có; receipt và mutation phải commit nguyên tử, cùng key khác payload trả 409. Lưu intentId/planId/runId, submit với Idempotency-Key ổn định, poll trạng thái và hiển thị approval/error/recovery đúng thực tế. Không nhận credential hoặc URL tùy ý từ frontend. Kiểm thử auth, tenant isolation, chống trùng, stale revision, DRY_RUN không gọi mutation, LIVE qua approval và timeout/reconciliation. Hoàn thành code, migration review và staging evidence trước khi triển khai production theo quyền đã được người dùng cấp. Không khai báo kết nối thành công chỉ vì health HTTP 200.

## Giới hạn hiện tại

FTA đang dùng durable JSON và lease một tiến trình, tối đa 4 run READY/RUNNING cùng lúc. Tích hợp đa dự án không đồng nghĩa đã hỗ trợ nhiều replica hoặc tải lớn; mở rộng cần database transaction/queue và migration riêng. Không có OAuth refresh outbound hoặc connector native cho mọi giao thức ở phiên bản này.
