From 38260e642fdfe1cc6d2047afdbd41e052f899449 Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Wed, 11 Nov 2026 18:51:23 +0800 Subject: [PATCH 1/2] feat(observation): verify the HK snapshot over internal ingress --- config/hk_snapshot_verification.workflow.yaml | 57 +++++++++++++++++++ docs/read-only-account-observation.zh-CN.md | 2 + 2 files changed, 59 insertions(+) create mode 100644 config/hk_snapshot_verification.workflow.yaml diff --git a/config/hk_snapshot_verification.workflow.yaml b/config/hk_snapshot_verification.workflow.yaml new file mode 100644 index 00000000..1bbf4f77 --- /dev/null +++ b/config/hk_snapshot_verification.workflow.yaml @@ -0,0 +1,57 @@ +# One manual account snapshot verification on Google Cloud Workflows. +# Call logging stays disabled. Only the validated field counts leave the workflow. +main: + steps: + - configuration: + assign: + - origin: ${sys.get_env("HK_SNAPSHOT_ORIGIN")} + - expected_binding: ${sys.get_env("HK_SNAPSHOT_EXPECTED_BINDING")} + - check_configuration: + switch: + - condition: '${not text.match_regex(origin, "^https://[a-z0-9-]+[.]run[.]app$") or not text.match_regex(expected_binding, "^[a-f0-9]{64}$")}' + raise: hk_snapshot_configuration_invalid + - read_snapshot: + try: + call: http.get + args: + url: ${origin + "/account-snapshot"} + timeout: 30 + headers: + Accept: application/json + auth: + type: OIDC + audience: ${origin} + result: response + except: + as: error + steps: + - reject_http_error: + raise: hk_snapshot_request_failed + - select_snapshot: + assign: + - snapshot: ${response.body} + - now: ${sys.now()} + - check_binding_and_fields: + switch: + - condition: '${response.code != 200 or get_type(snapshot) != "map" or map.get(snapshot, "schema_version") != "longbridge_account_snapshot.v1" or map.get(snapshot, "status") != "partial" or map.get(snapshot, "account_scope") != "HK" or map.get(snapshot, "no_order") != true or map.get(snapshot, "live_authority_granted") != false or map.get(snapshot, "cash_complete") != true or map.get(snapshot, "positions_complete") != true}' + raise: hk_snapshot_contract_invalid + - condition: '${map.get(snapshot, ["source_binding", "kind"]) != "deployment_scope_token_version" or map.get(snapshot, ["source_binding", "status"]) != "bound" or map.get(snapshot, ["source_binding", "id"]) != expected_binding}' + raise: hk_snapshot_binding_invalid + - condition: '${get_type(map.get(snapshot, "broker_reported_balances")) != "list" or len(snapshot.broker_reported_balances) == 0 or get_type(map.get(snapshot, "positions")) != "list"}' + raise: hk_snapshot_fields_invalid + - verify_observation_time: + assign: + - started: ${time.parse(snapshot.observed_started_at)} + - finished: ${time.parse(snapshot.observed_finished_at)} + - check_freshness: + switch: + - condition: ${started > finished or finished > now or now - started > 900} + raise: hk_snapshot_time_invalid + - receipt: + return: + account_scope: HK + source_binding_verified: true + read_only_snapshot_verified: true + observed_at: ${snapshot.observed_finished_at} + balance_count: ${len(snapshot.broker_reported_balances)} + position_count: ${len(snapshot.positions)} diff --git a/docs/read-only-account-observation.zh-CN.md b/docs/read-only-account-observation.zh-CN.md index 370e5d85..d4efa802 100644 --- a/docs/read-only-account-observation.zh-CN.md +++ b/docs/read-only-account-observation.zh-CN.md @@ -28,6 +28,8 @@ `independent_get` 适用于可由当前执行器访问的已鉴权 Cloud Run 服务。内部 ingress 返回固定原因 `internal_observer_required`,在调用券商或发送账户事实前停止;不得为采集开放 ingress。内部服务应复用受支持的内部执行入口,另行验证只读采集、私有归档与接收闭环。此文档不表示该内部闭环已经完成。 +HK 内网连通性可用 `config/hk_snapshot_verification.workflow.yaml` 单独验证。它在同一 GCP 项目通过 OIDC 调用固定的 `GET /account-snapshot`,从受保护的部署配置读取服务地址和预期绑定。验证只返回时间、字段数量和绑定结果。Cloud Workflows 的调用日志须关闭,运行身份单独配置,调用者不能传入地址、方法或请求参数。验证入口无定时触发,首次真实读回后再接归档和日报。 + 单次操作最多一次快照 GET 和一次事实 POST,不自动重试未知结果。投影保留券商实际币种、金额、观察时间及来源;账户来源不匹配时拒收,不补零、不改旧数据时间。交易启停、风险预算和通知不由此观察器控制。 本次配置迁移只覆盖独立观察器与该心跳工作流。历史 Scheduler 观察器及其他部署流程尚有旧配置约定,不能据此宣称全仓已完成配置与隐私整改。 From fdc42a3814db2805e1e5664fa9a2f765596c6bc2 Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Wed, 11 Nov 2026 18:55:39 +0800 Subject: [PATCH 2/2] fix(observation): validate the regional URL and fresh HK read --- config/hk_snapshot_verification.workflow.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/config/hk_snapshot_verification.workflow.yaml b/config/hk_snapshot_verification.workflow.yaml index 1bbf4f77..47ae8d68 100644 --- a/config/hk_snapshot_verification.workflow.yaml +++ b/config/hk_snapshot_verification.workflow.yaml @@ -6,9 +6,10 @@ main: assign: - origin: ${sys.get_env("HK_SNAPSHOT_ORIGIN")} - expected_binding: ${sys.get_env("HK_SNAPSHOT_EXPECTED_BINDING")} + - requested_at: ${sys.now()} - check_configuration: switch: - - condition: '${not text.match_regex(origin, "^https://[a-z0-9-]+[.]run[.]app$") or not text.match_regex(expected_binding, "^[a-f0-9]{64}$")}' + - condition: '${not text.match_regex(origin, "^https://[a-z0-9-]+[.][a-z0-9-]+[.]run[.]app$") or not text.match_regex(expected_binding, "^[a-f0-9]{64}$")}' raise: hk_snapshot_configuration_invalid - read_snapshot: try: @@ -45,7 +46,7 @@ main: - finished: ${time.parse(snapshot.observed_finished_at)} - check_freshness: switch: - - condition: ${started > finished or finished > now or now - started > 900} + - condition: ${started < requested_at or started > finished or finished > now or now - started > 900} raise: hk_snapshot_time_invalid - receipt: return: