Merge pull request #56 from xmindlab-heicode/feat/agent-endpoint-security-scheme

feat(agent): return A2A endpoint security scheme/profile (#55)
This commit is contained in:
Fasthei
2026-06-10 16:27:34 +08:00
committed by GitHub
3 changed files with 57 additions and 1 deletions
@@ -260,7 +260,12 @@ signature = base64( ed25519_sign( device_priv, sha256(canonical) ) )
"status": "Pending", // Pending | running | failed | stopped …
"runtime_id": "dep-4bb07dc1e376", // AM 侧运行时 id
"created_at": "2026-06-04T09:04:15Z",
"updated_at": "2026-06-04T09:04:15Z"
"updated_at": "2026-06-04T09:04:15Z",
"security": { // ★ #55 A2A 直连安全元数据
"scheme": "http", // http | https
"security_profile": "none", // none | tls | mtls
"secure": false // = security_profile != none
}
}
],
"total": 1
@@ -346,6 +351,8 @@ signature = base64( ed25519_sign( device_priv, sha256(canonical) ) )
> ⚠️ **连之前先确认 agent 就绪**:新建后 `status=Pending`(还在拉起)。等 `GET /api/heicode/agents/{id}/status` 变 `running`、或 `GET {subdomain}/health` 返 200 再连。**2026-06-04 复测:数秒即 `running`,`/health` 200、`/message/send` 带令牌任务 `completed`,直连已通。**
>
> ⚠️ 当前 AM 侧两点(待加固,不影响调通):① **令牌校验尚未真正生效**——无 `X-Agent-Access-Token` 也被放行;客户端仍应规范地每请求都带,等 AM 开启校验即自动生效。② 子域名目前 `http://` 明文,令牌/`api_key` 会明文传输,等 AM 上 HTTPS。
>
> 🔒 **传输安全门(#55)**:agent 对象回传 `security`(`scheme` http/https、`security_profile` none/tls/mtls、`secure` 布尔)。客户端据此在生产强制 HTTPS(`HEICODE_AGENT_REQUIRE_SECURE=1`):`secure=false`(当前明文)→ 拒绝直连并提示。AM 上线 HTTPS/mTLS listener 后 `security_profile` 自动变 tls,客户端无需改包。HM 保守口径:无法确证 TLS 即标 `none`。
- **同步**:`POST {subdomain}/message/send`
- **流式**:`POST {subdomain}/message/stream`(返回 `text/event-stream`)
@@ -0,0 +1,24 @@
package controller
import "testing"
// #55: agent endpoint 安全级别推断 —— 明文/裸主机 = none(客户端生产可拒),显式 https = tls。
func TestAgentEndpointSecurity(t *testing.T) {
cases := []struct {
in string
scheme, prof string
secure bool
}{
{"https://dep-x.agents.example", "https", "tls", true},
{"http://dep-x.agents.example", "http", "none", false},
{"dep-x.taijiagnet.com", "http", "none", false}, // 裸主机:AM 当前明文
{" HTTPS://Dep.Example ", "https", "tls", true},
{"", "http", "none", false},
}
for _, c := range cases {
sc, pr, se := agentEndpointSecurity(c.in)
if sc != c.scheme || pr != c.prof || se != c.secure {
t.Errorf("agentEndpointSecurity(%q) = (%s,%s,%v), want (%s,%s,%v)", c.in, sc, pr, se, c.scheme, c.prof, c.secure)
}
}
}
@@ -111,6 +111,7 @@ func templateAgentResponse(row model.AgentDeployment) gin.H {
if strings.TrimSpace(row.BindingIDsJSON) != "" {
_ = common.UnmarshalJsonStr(row.BindingIDsJSON, &bindingIDs)
}
scheme, profile, secure := agentEndpointSecurity(row.Subdomain)
return gin.H{
"agent_id": row.DeploymentID,
"template_id": row.TemplateID,
@@ -121,6 +122,30 @@ func templateAgentResponse(row model.AgentDeployment) gin.H {
"runtime_id": row.RuntimeDeploymentID,
"created_at": row.CreatedAtText,
"updated_at": row.UpdatedAtText,
// #55: A2A 直连安全元数据。客户端据此在生产强制 HTTPS(HEICODE_AGENT_REQUIRE_SECURE):
// scheme=http/https,security_profile=none/tls,secure=profile!=none。AM 启用 HTTPS/
// mTLS listener 是 AM(azgy)的事;HM 只如实回传当前子域安全级别,明文 http →
// security_profile=none,客户端可拒绝并提示(元数据不含任何 secret_ref)。
"security": gin.H{
"scheme": scheme,
"security_profile": profile,
"secure": secure,
},
}
}
// agentEndpointSecurity 从子域字符串推断 A2A 直连的安全级别(#55)。纯函数,可单测。
// 显式 https:// → (https, tls, true);显式 http:// 或裸主机(AM 当前默认明文) → (http, none, false)。
// 保守口径:无法确证 TLS 即视为 none,宁可让客户端在生产拒绝,也不回传"看似安全"的明文端点。
func agentEndpointSecurity(subdomain string) (scheme, profile string, secure bool) {
s := strings.TrimSpace(strings.ToLower(subdomain))
switch {
case strings.HasPrefix(s, "https://"):
return "https", "tls", true
case strings.HasPrefix(s, "http://"):
return "http", "none", false
default:
return "http", "none", false // 裸主机:AM 当前明文 HTTP,保守标 none
}
}