feat(store): add Area admission and audit outbox [T-010]
This commit is contained in:
@@ -45,7 +45,7 @@ Sense ── 视频流/触发信号 ──> Brain
|
||||
|
||||
主流程:
|
||||
|
||||
1. Bell 持有站点、Area、配额与 `capture_policy`;首期在同一 PostgreSQL 实例内发布版本化只读视图 `bell.site_quota_v1`。T-009 已实现 Bell 源表/视图、最小权限和 Sense PostgreSQL repository;Sense 在新增/启用设备的同一事务内以站点 advisory lock 校验配额并记录所用 `source_version`。未来分库必须发布新版本契约,不能静默改变 v1 语义。
|
||||
1. Bell 持有站点、Area、配额与 `capture_policy`;首期在同一 PostgreSQL 实例内发布 `bell.site_quota_v1` 和 `bell.area_policy_v1` 两个版本化只读视图。T-009/T-010 已实现 Bell 源表/视图、最小权限和 Sense PostgreSQL repository;Sense 按 Area→Site 的固定 advisory-lock 顺序执行策略与配额准入并记录所用版本。未来分库必须发布新版本契约,不能静默改变 v1 语义。
|
||||
2. Sense 维护设备期望态,通过 MediaMTX API 和对账器收敛实际态。
|
||||
3. Brain 消费视频与触发信号,产生符合 v0.1 的事件。
|
||||
4. Bell 做 schema 与代码级断言,生成平台 ULID,保存不可变事件。
|
||||
@@ -63,12 +63,12 @@ Sense ── 视频流/触发信号 ──> Brain
|
||||
4. 平台事件 ULID 由 Bell 生成;Brain 只填 `source_event_id`。
|
||||
5. 一个 PostgreSQL 实例,`sense`/`bell` schema 分离;Brain 无业务 schema。
|
||||
6. 16/128 都不是单机保证;媒体与推理按独立分片横向扩展。
|
||||
7. Bell 拥有 `site.max_video_channels` 并拥有 `bell.site_quota_v1`;Sense 角色只获得视图 `SELECT`,在设备写路径执行准入,不能写 Bell schema 或读取 Bell 源表。
|
||||
7. Bell 拥有 Site/Area、`site.max_video_channels` 与 `capture_policy`,并拥有两个 v1 投影视图;Sense 角色只获得视图 `SELECT`,在设备写路径执行准入,不能写 Bell schema 或读取 Bell 源表。
|
||||
8. 事件片段写入客户侧 MinIO/S3,常态录像留在客户 NVR;元数据/审计、人脸和训练样本使用独立生命周期。
|
||||
9. 投递状态机只依赖 Bell provider 接口,不直接依赖某家短信或语音 SDK;生产前至少两条独立路径并能故障切换。
|
||||
10. 设备领域模型使用 `modality + capabilities`,页面不以摄像头作为唯一根实体;未实现协议适配器明确为 `adapter_not_ready`,不得用模拟遥测伪装交付。
|
||||
11. Tenant/Site/Area/RBAC、配额、`capture_policy` 与全局审计属于 Bell;Sense Control API v1 只管理 Device 期望态与收敛查询,Sense 只读消费版本化投影并在设备写路径执行,投影不可用时只阻断相关新变更,不静默切断已有链路。
|
||||
12. Sense 的设备操作审计先写本地持久化 outbox,再由幂等 relay 异步送入 Bell 全局审计;不得使用“先执行高风险操作、再尽力入队”的顺序。具体字段、签名、重放与留存契约必须由独立 API/契约任务冻结后实现。
|
||||
12. Sense 的设备操作审计先写本地持久化 Outbox,再由幂等 relay 异步送入 Bell 全局审计;不得使用“先执行高风险操作、再尽力入队”的顺序。T-010 已冻结脱敏本地事件并实现原子写入;relay 的 transport、签名、确认、重放窗口与留存仍须独立冻结。
|
||||
|
||||
## 6. 容量架构
|
||||
|
||||
@@ -85,8 +85,8 @@ Sense ── 视频流/触发信号 ──> Brain
|
||||
- 对账器水平触发、幂等、指数退避、限制并发;部分失败不做跨系统回滚,只持续收敛。
|
||||
- 孤儿删除必须有 10% 安全闸和人工可观察指标。
|
||||
- `bell.site_quota_v1` 行缺失、数值越界、版本回退或读取失败只阻止视频设备新增/启用,不中断已有流;降低配额导致超限时不自动停用,后续准入返回稳定错误并产生运维信号。多 Sense 实例使用 PostgreSQL transaction-scoped advisory lock 串行化同 tenant/site 的计数与写入,不能用进程内锁替代。
|
||||
- Area 策略投影读取失败只阻止相关设备新增/启用;已有设备保持原状态并产生运维告警。策略变更与已有成像设备冲突时由 Bell 管理端显式处置。
|
||||
- 高风险设备操作在本地事务内同时写期望态与审计 outbox;异步 relay 可重试、幂等投递到 Bell。T-004 只验证交互归属,不定义或实现接口契约。
|
||||
- `bell.area_policy_v1` 缺失、非法、版本回退或读取失败时,PostgreSQL repository 拒绝相关新增/启用;`non_imaging_only` 允许非成像设备但拒绝具有 `video_capture` 的设备。已有设备保持原状态,策略冲突由 Bell 管理端显式迁移或取消。同库实时视图不以源记录年龄误判 freshness。
|
||||
- 设备创建和期望态受理在本地事务内同时写脱敏 `sense.device_operation_outbox`;Outbox 失败回滚业务写入,相同期望态不增加 generation 但仍审计。异步 relay 尚未实现。
|
||||
- Brain 投递失败落本地队列重试,不阻塞实时推理主链路。
|
||||
- Alert 先落库再投递,进程重启恢复未完成升级链。
|
||||
- 值班排班发布前必须按 Site 时区校验班次空档、重叠、联系人停用和通道验证;排班以新版本和未来生效时间发布,不原地改写历史。交接班是进行中 Alert 的显式责任转移事件,不替代排班版本变更。
|
||||
@@ -94,8 +94,8 @@ Sense ── 视频流/触发信号 ──> Brain
|
||||
|
||||
## 8. 数据与契约
|
||||
|
||||
- Bell 核心实体:Tenant → Site → Area(含 `capture_policy`)以及 Role/Binding/Quota/Audit;Sense 核心实体:Device(含 `modality + capabilities`)→ StreamBinding/Zone,以及带 `source_version`/`synced_at` 的 SiteQuota/AreaPolicyProjection。两个 schema 以稳定逻辑 ID 关联,不跨 schema 写入;配额 v1 的跨 schema 列签名限定为 `tenant_id/site_id/max_video_channels/source_version/source_updated_at`。
|
||||
- Sense Control API v1 使用站点作用域路径、认证上下文 tenant、cursor 分页、幂等键与 ETag;敏感连接引用只写不读。正式签名和兼容规则以 [`contracts/`](contracts/) 为准。T-009 只实现 PostgreSQL migration/repository,不等于 handler、认证或 Bell 管理服务已实现。
|
||||
- Bell 核心实体:Tenant → Site → Area(含 `capture_policy`)以及 Role/Binding/Quota/Audit;Sense 核心实体:Device(含 `modality + capabilities`)→ StreamBinding/Zone,以及只记录已观察版本的 SiteQuota/AreaPolicyProjection。两个 schema 以稳定逻辑 ID 关联,不跨 schema 写入;配额 v1 为五列,Area v1 固定为 `tenant_id/site_id/area_id/capture_policy/source_version/source_updated_at` 六列。
|
||||
- Sense Control API v1 使用站点作用域路径、认证上下文 tenant、cursor 分页、幂等键与 ETag;敏感连接引用只写不读。正式签名和兼容规则以 [`contracts/`](contracts/) 为准。T-009/T-010 已实现 PostgreSQL 准入与审计事务基础,不等于 handler、认证、Bell 管理服务或 Outbox relay 已实现。
|
||||
- 业务实体:Rule → Event → Alert → DeliveryAttempt/Ack;Event 与 Alert 不合并。
|
||||
- Bell 通知域分为三个聚合:Contact/Team 保存身份、成员关系和已验证通道;OnCallSchedule/ScheduleVersion/ShiftException 保存时区、轮换与例外;EscalationPolicy/Step 通过 `person / team / on_call_schedule` 类型化 `target_ref` 引用目标。三者共享逻辑 ID,不复制手机号、班次或轮换字段。
|
||||
- 每个 DeliveryAttempt 创建时解析当时生效的排班版本,并保存实际收件人、通道、`schedule_version` 和解析时间快照;之后联系人或排班修改不得回写既有投递事实。
|
||||
@@ -111,10 +111,10 @@ Sense/cmd + Sense/internal/{device,onvif,mtx,reconcile,probe,trigger,tunnel,auth
|
||||
Brain/{pipeline,models,judge,emit,trigger,contracts}
|
||||
Bell/cmd + Bell/internal/{ingest,event,rule,alert,deliver,feedback,tenant,audit,store}
|
||||
Bell/{web,packs,contracts}
|
||||
deploy/postgres/{001_roles.sql,002_bell.sql,003_sense.sql,004_privileges.sql,tests}
|
||||
deploy/postgres/{001_roles.sql,...,007_privileges_area_audit.sql,tests}
|
||||
```
|
||||
|
||||
当前只有空目录占位;真实脚手架必须由对应任务创建。
|
||||
Sense 脚手架和 PostgreSQL `001`~`007` 已实现;Brain/Bell 应用目录仍是占位,真实服务必须由对应任务创建。
|
||||
|
||||
## 10. 开发顺序
|
||||
|
||||
|
||||
+2
-1
@@ -24,7 +24,8 @@
|
||||
|
||||
- T-008:冻结 Sense Control API v1 与 Bell `site_quota_v1` 只读投影契约;只建立接口和测试门禁,不等于 handler、Bell 表或 migration 已实现。
|
||||
- T-009:使用 PostgreSQL 17.10 建立 `sense`/`bell` schema、Bell 配额源表/只读视图、最小权限和 Sense PostgreSQL repository;SQLite 保留为 M1 本地路径。
|
||||
- 后续任务按 T-008 契约实现认证 tenant 上下文、设备管理 handler、幂等/ETag/cursor 与批量操作;不得把 T-009 数据基础误报为 API 已交付。
|
||||
- T-010:建立 Bell Area/`capture_policy` 只读投影、Sense 成像准入与设备操作审计 Outbox 原子写入;relay 传输协议留给后续任务。
|
||||
- 后续 T-011 按 T-008 契约实现认证 tenant 上下文、设备管理 handler、幂等/ETag/cursor 与批量操作;公共控制 API 只在 PostgreSQL 路径启用,不得把 T-009/T-010 数据基础误报为 API 已交付。
|
||||
- 对账器幂等/退避/并发/10% 安全闸。
|
||||
- WireGuard 边缘隧道与断网恢复。
|
||||
- 16 路批量开通、停用和容量基准。
|
||||
|
||||
+8
-6
@@ -1,6 +1,6 @@
|
||||
# API 与契约
|
||||
|
||||
> Brain → Bell 事件契约 v0.1、Sense Control API v1 与 Bell 站点配额只读投影 v1 已冻结;其他 API 仍在设计阶段。不得把本文的“待定”自行具体化为公共契约。
|
||||
> Brain → Bell 事件契约 v0.1、Sense Control API v1、Bell 配额/Area 只读投影 v1 与 Sense 本地设备审计事件 v1 已冻结;其他 API 仍在设计阶段。不得把本文的“待定”自行具体化为公共契约。
|
||||
|
||||
## 1. 已冻结:Brain → Bell 事件契约
|
||||
|
||||
@@ -22,12 +22,14 @@
|
||||
| 调用方 → 提供方 | 用途 | 当前约束 | 状态 |
|
||||
| --- | --- | --- | --- |
|
||||
| Sense → Bell | 读取站点视频配额 | 同一 PostgreSQL 实例内只读 `bell.site_quota_v1`;默认 16、最大 128;失败时拒绝新增/启用但不影响已有流 | T-008 冻结,T-009 已实现数据路径 |
|
||||
| Sense → Bell | 读取 Area 成像准入 | 只读 `bell.area_policy_v1`;`video_allowed | non_imaging_only`;缺失/非法/回退失败关闭但不影响已有设备 | T-010 已冻结并实现数据路径 |
|
||||
| Sense → Bell | 汇入设备操作审计 | 本地 Outbox 事件已冻结并原子落库;transport、签名、确认与留存未冻结 | T-010 本地基础已实现,relay 待设计 |
|
||||
| Bell → Sense | 请求事件证据/pre-roll 切片 | 幂等、按租户授权、异步结果、不得暴露原始凭据 | 待 M3 设计 |
|
||||
| Bell → Brain | outcome/误报反馈 | 原事件不可变;反馈可重试、去重、审计 | 待 M3 设计 |
|
||||
| Sense → Brain | 流绑定与设备型触发 | 分片可路由,触发入口与流控制解耦 | 待 M2/M3 设计 |
|
||||
| Worker → 控制面 | 注册、心跳、容量 | `max_sources` 来自 profile/压测,不固定为 16 | 待 M3 设计 |
|
||||
|
||||
冻结签名和失败语义见 [`contracts/README.md`](contracts/README.md) 与 [`contracts/site-quota-v1.sql`](contracts/site-quota-v1.sql)。Bell 拥有源数据和视图,Sense 数据库角色只有 `SELECT`;未来分库必须发布新版本,不能在 v1 下把本地视图静默替换为网络调用。
|
||||
冻结签名和失败语义见 [`contracts/README.md`](contracts/README.md)、[`contracts/site-quota-v1.sql`](contracts/site-quota-v1.sql)、[`contracts/area-policy-v1.sql`](contracts/area-policy-v1.sql) 与 [`contracts/sense-device-audit-v1.schema.json`](contracts/sense-device-audit-v1.schema.json)。Bell 拥有投影源数据和视图,Sense 数据库角色只有 `SELECT`;未来分库必须发布新版本,不能在 v1 下把本地视图静默替换为网络调用。
|
||||
|
||||
## 3. 已冻结:Sense Control API v1
|
||||
|
||||
@@ -43,7 +45,7 @@
|
||||
- `endpoint_ref`、`credential_ref`、`profile_token` 只写不读;设备 ID 由服务端生成。普通响应和错误不得包含凭据、完整流 URI、token 或 MediaMTX 内部配置。
|
||||
- v1 不提供删除设备;停用设备保留历史。写入受理只表示期望态已持久化,不能表示实际态已收敛。
|
||||
|
||||
T-008 冻结契约;T-009 已实现 PostgreSQL `bell.sites`/配额视图、最小权限和 Sense repository,但尚未实现 Sense HTTP handler、认证中间件或 Bell 管理服务。
|
||||
T-008 冻结公共控制契约;T-009/T-010 已实现 PostgreSQL Site/Area 源表、两个只读视图、最小权限、Sense 准入 repository 和本地审计 Outbox,但尚未实现 Sense HTTP handler、认证中间件、Bell 管理服务或 Outbox relay。
|
||||
|
||||
## 4. 待冻结的 Bell 公共 API
|
||||
|
||||
@@ -68,7 +70,7 @@ Sense 使用 MediaMTX 官方 OpenAPI 生成客户端并加薄封装。业务代
|
||||
| --- | --- | --- |
|
||||
| ONVIF adapter | `Probe(target)`、`SetSystemDateAndTime(target, time)` | 设备是外部来源;`target` 只含 endpoint ref 与不透明 credential ref。错误稳定映射为认证失败、超时、不可用、响应无效,不记录凭据或完整流地址 |
|
||||
| MediaMTX paths | `CreatePath`、`GetPath`、`EnsurePath`、`DeletePath`、`PathReady` | SQLite 设备台账持有期望态,MediaMTX 只持有运行配置;`EnsurePath` 相同 source 不写、不同 source patch、缺失时 add;当前调和器绝不枚举或删除孤儿 |
|
||||
| Device repository | 设备、期望态、实际态、调和进度 | SQLite 是 M1 默认开发路径;PostgreSQL 是 M2 生产路径并只读 `bell.site_quota_v1`。调和失败次数与下次时间持久化,进程重启不清空退避;配额读取失败时拒绝新增或启用,不关闭已有流 |
|
||||
| Device repository | 设备、期望态、实际态、调和进度 | SQLite 是 M1 默认开发路径;PostgreSQL 是 M2 生产路径并只读 Site/Area 两个 Bell 视图。调和退避持久化;配额/Area 失败时拒绝相关新增或启用,不关闭已有流;高风险写入与脱敏 Outbox 同事务 |
|
||||
|
||||
MediaMTX 薄封装调用同版官方 OpenAPI 的 `/v3/config/paths/get|add|patch|delete/{name}` 与 `/v3/paths/get/{name}`。生成源、版本和 SHA-256 见 `docs/03-tech-stack.md`;业务包不得直接 import 生成包。
|
||||
|
||||
@@ -76,7 +78,7 @@ MediaMTX 薄封装调用同版官方 OpenAPI 的 `/v3/config/paths/get|add|patch
|
||||
|
||||
- 设备类型由 `modality` 表达物理类别,由多值 `capabilities` 表达视频采集、音频、空间规则或遥测能力,避免把“摄像头”固化为唯一设备模型。
|
||||
- 视频配额只统计 `desired_state=enabled` 且具有 `video_capture` capability 的设备;站点默认 16、可配置 1~128。禁用设备和非视频传感器不占视频路数。
|
||||
- SQLite 表使用 `sense_` 前缀;T-009 PostgreSQL 使用 `sense.devices`、`sense.device_capabilities`、`sense.reconcile_state` 和只记录已观察版本的 `sense.site_quota_projection_state`。PostgreSQL 不建立可写 Site 真相副本,站点与配额只来自 Bell 视图;SQLite 的 `sense_sites` 仅是 M1 本地兼容表,不是跨系统公共契约。
|
||||
- SQLite 表使用 `sense_` 前缀且只是 M1 实验室兼容路径;PostgreSQL 使用 `sense.devices`、能力/调和表、两个投影观察表和 `sense.device_operation_outbox`。PostgreSQL 不建立可写 Site/Area 真相副本;后续公共控制 API 只在 PostgreSQL 路径启用,不能把 SQLite 描述为 Area/Outbox 生产等价实现。
|
||||
- 摄像头密码不进入设备普通字段。`credential_ref` 只保存外部密钥引用;ONVIF 返回的 stream URI 只在内存中传给 MediaMTX,不写入设备台账或日志。
|
||||
|
||||
### 5.3 Sense 进程 HTTP 面
|
||||
@@ -90,4 +92,4 @@ T-003 只实现了运维探针:`GET /healthz` 表示进程存活,`GET /ready
|
||||
3. 同步生产者、消费者、契约测试和示例。
|
||||
4. 记录迁移、回滚与版本废弃策略。
|
||||
|
||||
事件 v0.1、Sense Control API v1 或站点配额投影 v1 的破坏性变化必须发布新版本,不能原地修改已被生产者/消费者使用的契约。
|
||||
事件 v0.1、Sense Control API v1、配额/Area 投影 v1 或本地设备审计 v1 的破坏性变化必须发布新版本,不能原地修改已被生产者/消费者使用的契约。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Sense 控制面与站点配额契约 v1
|
||||
# Sense 控制面、准入投影与本地审计契约 v1
|
||||
|
||||
> 冻结日期:2026-08-07。契约版本:`1.0.0`。Sense 是设备期望态的提供方;Bell 是 Tenant、Site、Area、RBAC 与配额的所有者。本文冻结接口,不表示 HTTP handler、Bell 表或 PostgreSQL migration 已实现。
|
||||
> 冻结日期:2026-08-07。契约版本:`1.0.0`。Sense 是设备期望态的提供方;Bell 是 Tenant、Site、Area、RBAC、配额与全局审计的所有者。T-009/T-010 已实现两个只读投影和本地审计事务基础;HTTP handler、Bell 管理服务和 Outbox relay 仍未实现。
|
||||
|
||||
## 契约文件
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
| --- | --- | --- | --- |
|
||||
| [`sense-control-v1.openapi.json`](sense-control-v1.openapi.json) | Sense | Bell 管理面、受控集成方 | 设备查询、创建、修改、启停与批量操作 |
|
||||
| [`site-quota-v1.sql`](site-quota-v1.sql) | Bell | Sense | 单 PostgreSQL 实例内的站点视频配额只读投影 |
|
||||
| [`area-policy-v1.sql`](area-policy-v1.sql) | Bell | Sense | Area 归属与 `capture_policy` 只读投影 |
|
||||
| [`sense-device-audit-v1.schema.json`](sense-device-audit-v1.schema.json) | Sense | 本地 Outbox;未来 Bell relay | 脱敏设备操作审计事实,不包含传输协议 |
|
||||
|
||||
OpenAPI 的 `/api/v1` 路径是公共控制面边界;`/healthz`、`/readyz` 仍是非业务运维探针。v1 不提供设备删除:停用设备使用期望态接口,保留设备、操作和审计历史。Site、Area、配额、RBAC 和审计聚合不由 Sense 提供 CRUD。
|
||||
|
||||
@@ -48,7 +50,13 @@ Bell migration 最终创建 `bell.site_quota_v1`,列顺序和含义固定如
|
||||
|
||||
配额只统计 `desired_state=enabled` 且 capabilities 含 `video_capture` 的设备。创建已启用视频设备或把视频设备启用时,Sense 必须在同一设备写路径读取并记录所用 `source_version`,同时验证 Area 策略投影。降低配额不会自动停用已有设备;若当前占用已超限,后续创建/启用返回 `409 quota_exceeded`。配额行缺失、越界、版本回退或投影不可读时返回 `503 quota_projection_unavailable`,只阻止相关创建/启用,读取、非准入属性修改和停用仍允许,已有流保持运行。
|
||||
|
||||
Area/capture policy 的投影形态不在 T-008 中冻结;Sense v1 仍保留 `area_policy_unavailable` 与 `area_policy_denied` 稳定错误语义,后续契约不得放宽同写路径校验要求。
|
||||
T-010 冻结 `bell.area_policy_v1` 的列顺序为 `tenant_id/site_id/area_id/capture_policy/source_version/source_updated_at`,策略仅允许 `video_allowed | non_imaging_only`。Sense 对所有 PostgreSQL 新建设备验证 Area 归属;具有 `video_capture` 能力的设备在创建(包括 disabled 创建)和启用时检查策略。缺失、非法、版本回退或不可读映射为 `area_policy_unavailable`,`non_imaging_only` 拒绝成像变更并映射为 `area_policy_denied`。同库视图实时读取,不把长期未修改记录的 `source_updated_at` 年龄误判为过期。
|
||||
|
||||
## 本地设备操作审计
|
||||
|
||||
`sense-device-audit-v1.schema.json` 冻结本地审计事实的逻辑 envelope。当前事件只有 `device.created` 和 `device.desired_state.accepted`;主体类型为 `user | service | system`,投影版本与 generation 随事实保存。`data` 只包含 Area、模态、能力和状态变化等脱敏字段,禁止 endpoint、credential、profile token、path、密码、完整流 URI 或 MediaMTX 配置。
|
||||
|
||||
PostgreSQL repository 必须在设备创建/期望态事务内写 `sense.device_operation_outbox`;Outbox 失败回滚业务写入。相同期望态不增加 generation,但仍产生独立审计事实。Schema 不是 Bell relay 协议:传输端点、签名、批量确认、重放窗口和留存由后续任务冻结。
|
||||
|
||||
## 兼容与废弃
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
-- Contract-only reference for the Bell -> Sense Area admission projection.
|
||||
-- The executable migration is deploy/postgres/005_area_policy.sql.
|
||||
|
||||
CREATE OR REPLACE VIEW bell.area_policy_v1 (
|
||||
tenant_id,
|
||||
site_id,
|
||||
area_id,
|
||||
capture_policy,
|
||||
source_version,
|
||||
source_updated_at
|
||||
) AS
|
||||
SELECT
|
||||
area.tenant_id,
|
||||
area.site_id,
|
||||
area.id,
|
||||
area.capture_policy,
|
||||
area.version,
|
||||
area.updated_at
|
||||
FROM bell.areas AS area
|
||||
JOIN bell.sites AS site
|
||||
ON site.tenant_id = area.tenant_id AND site.id = area.site_id
|
||||
WHERE area.deleted_at IS NULL AND site.deleted_at IS NULL;
|
||||
|
||||
COMMENT ON VIEW bell.area_policy_v1 IS
|
||||
'v1 read-only Area capture-policy projection owned by Bell and consumed by Sense';
|
||||
|
||||
ALTER VIEW bell.area_policy_v1 OWNER TO bell_app;
|
||||
REVOKE ALL PRIVILEGES ON TABLE bell.area_policy_v1 FROM PUBLIC;
|
||||
REVOKE ALL PRIVILEGES ON TABLE bell.area_policy_v1 FROM sense_app;
|
||||
GRANT SELECT ON TABLE bell.area_policy_v1 TO sense_app;
|
||||
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://yovision.local/contracts/sense-device-audit-v1.schema.json",
|
||||
"title": "Sense Device Audit Event v1",
|
||||
"description": "Sense 本地持久化的设备操作审计事实;不定义 Bell relay 的传输、签名、确认或留存。",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"event_id",
|
||||
"event_type",
|
||||
"tenant_id",
|
||||
"site_id",
|
||||
"device_id",
|
||||
"actor",
|
||||
"reason",
|
||||
"trace_id",
|
||||
"aggregate_generation",
|
||||
"projection_versions",
|
||||
"data",
|
||||
"occurred_at"
|
||||
],
|
||||
"properties": {
|
||||
"event_id": {
|
||||
"type": "string",
|
||||
"pattern": "^audit_[0-9a-f]{32}$"
|
||||
},
|
||||
"event_type": {
|
||||
"enum": ["device.created", "device.desired_state.accepted"]
|
||||
},
|
||||
"tenant_id": {"$ref": "#/$defs/logicalId"},
|
||||
"site_id": {"$ref": "#/$defs/logicalId"},
|
||||
"device_id": {"$ref": "#/$defs/logicalId"},
|
||||
"actor": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["type", "id"],
|
||||
"properties": {
|
||||
"type": {"enum": ["user", "service", "system"]},
|
||||
"id": {"type": "string", "minLength": 1, "maxLength": 200}
|
||||
}
|
||||
},
|
||||
"reason": {"type": ["string", "null"], "maxLength": 500},
|
||||
"trace_id": {"type": ["string", "null"], "maxLength": 128},
|
||||
"aggregate_generation": {"type": "integer", "minimum": 1},
|
||||
"projection_versions": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["quota_source_version", "area_policy_source_version"],
|
||||
"properties": {
|
||||
"quota_source_version": {"type": ["integer", "null"], "minimum": 1},
|
||||
"area_policy_source_version": {"type": ["integer", "null"], "minimum": 1}
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"oneOf": [
|
||||
{"$ref": "#/$defs/deviceCreatedData"},
|
||||
{"$ref": "#/$defs/desiredStateData"}
|
||||
]
|
||||
},
|
||||
"occurred_at": {"type": "string", "format": "date-time"}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"if": {"properties": {"event_type": {"const": "device.created"}}},
|
||||
"then": {"properties": {"data": {"$ref": "#/$defs/deviceCreatedData"}}}
|
||||
},
|
||||
{
|
||||
"if": {"properties": {"event_type": {"const": "device.desired_state.accepted"}}},
|
||||
"then": {"properties": {"data": {"$ref": "#/$defs/desiredStateData"}}}
|
||||
}
|
||||
],
|
||||
"$defs": {
|
||||
"logicalId": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 128,
|
||||
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
|
||||
},
|
||||
"desiredState": {"enum": ["disabled", "enabled"]},
|
||||
"deviceCreatedData": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["kind", "area_id", "modality", "capabilities", "desired_state"],
|
||||
"properties": {
|
||||
"kind": {"const": "device_created"},
|
||||
"area_id": {"$ref": "#/$defs/logicalId"},
|
||||
"modality": {"enum": ["video", "radar", "contact", "button", "wearable", "other"]},
|
||||
"capabilities": {
|
||||
"type": "array",
|
||||
"maxItems": 16,
|
||||
"uniqueItems": true,
|
||||
"items": {"enum": ["video_capture", "audio_capture", "spatial_rule", "telemetry"]}
|
||||
},
|
||||
"desired_state": {"$ref": "#/$defs/desiredState"}
|
||||
}
|
||||
},
|
||||
"desiredStateData": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["kind", "previous_desired_state", "desired_state", "changed"],
|
||||
"properties": {
|
||||
"kind": {"const": "desired_state_accepted"},
|
||||
"previous_desired_state": {"$ref": "#/$defs/desiredState"},
|
||||
"desired_state": {"$ref": "#/$defs/desiredState"},
|
||||
"changed": {"type": "boolean"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
## 当前阶段
|
||||
|
||||
- 阶段:M0 指定摄像头型号准入已完成;M1 的“一实机 + 四合成源”实验室软件闭环已通过;M2 已完成控制面契约和 PostgreSQL 数据基础,但五条独立真实上游和生产 SLA 尚未验收。
|
||||
- 生产代码:Sense 已包含可构建进程、SQLite/PostgreSQL repository、标准 ONVIF SOAP/WS-Security adapter、凭据引用、MediaMTX 生成客户端、对账、探活和 MediaMTX 重启重建;公共设备管理 API、认证和完整生产部署仍未实现。
|
||||
- 阶段:M0 指定摄像头型号准入已完成;M1 的“一实机 + 四合成源”实验室软件闭环已通过;M2 已完成控制面契约、Site/Area 准入与本地审计事务基础,但五条独立真实上游和生产 SLA 尚未验收。
|
||||
- 生产代码:Sense 已包含可构建进程、SQLite/PostgreSQL repository、Site/Area 准入、设备操作 Outbox、标准 ONVIF SOAP/WS-Security adapter、凭据引用、MediaMTX 生成客户端、对账、探活和 MediaMTX 重启重建;公共设备管理 API、认证、Outbox relay 和完整生产部署仍未实现。
|
||||
- 默认容量:16 路;单站点本阶段上限 128 路,必须横向分片。
|
||||
|
||||
## 仓库现实
|
||||
@@ -13,13 +13,14 @@
|
||||
- `Sense/` 已有 Go module 与 `cmd/sense-api`;`Brain/`、`Bell/` 仍只有目录占位,Bell 的首个数据库源表/视图 migration 位于根目录 `deploy/postgres/`。
|
||||
- Sense 设备模型使用 `modality + capabilities`,SQLite 执行 v1 migration;视频配额默认 16、允许 1~128,17/128/129、新增/启用和“降低配额不关闭已有流”均有测试。
|
||||
- T-009 冻结 PostgreSQL `17.10` 和 `pgx/v5 v5.10.0`,实现 `bell`/`sense` schema、NOLOGIN 权限角色、Bell Site 版本 trigger、`bell.site_quota_v1` 和 Sense PostgreSQL repository;同站点并发准入用事务级 advisory lock,配额缺失/越界/版本回退时失败关闭且不改变已有流。
|
||||
- Windows 隔离测试使用 `D:\pgsql17\bin` 启动随机回环端口临时集群,migration 可重放、权限断言和 8 组真实 PostgreSQL repository 集成场景通过后自动清理;现有 `D:\pgsql17\data` 和 5432 服务未被读取、停止或修改。
|
||||
- T-010 增量实现 `bell.areas`、`bell.area_policy_v1`、Area 版本观察和 `sense.device_operation_outbox`;`non_imaging_only` 拒绝成像设备创建/启用,失败不改变已有设备。设备创建/期望态受理与脱敏 Outbox 同事务,相同期望态不增加 generation 但仍审计。
|
||||
- Windows 隔离测试使用 `D:\pgsql17\bin` 启动随机回环端口临时集群,`001`~`007` migration 可重放、权限断言和 18 个真实 PostgreSQL 测试通过后自动清理;现有 `D:\pgsql17\data` 和 5432 服务未被读取、停止或修改。
|
||||
- MediaMTX 固定为独立二进制 `v1.19.3`,官方 OpenAPI 已按 SHA-256 vendoring,并由固定 `oapi-codegen v2.8.0` 生成客户端;手写薄封装有 create/read/delete、幂等 ensure 与探活假 HTTP 测试。
|
||||
- T-003 对账进度与指数退避持久化,覆盖取消和 SQLite 重启恢复;T-006 增加真实 ONVIF adapter、RTSP router、实验室播种/状态工具、故障代理和五路自动验收。当前仍不枚举/删除孤儿。
|
||||
- T-006 正式使用 1 台准入实机和 4 个独立合成 publisher 连续观察 `1806.6 s` / 180 次采样,四类恢复均通过,最大与最终 `unconverged` 均为 0;详细证据见 `docs/research/sense-5-stream-integration.md`。
|
||||
- `docs/raw/01`~`08` 已记录需求、分析、方案、客户场景、事件比对和三系统职责。
|
||||
- `docs/raw/contracts/event-v0.1.schema.json` 已冻结,并有多份示例与语义说明。
|
||||
- `docs/contracts/sense-control-v1.openapi.json` 已冻结站点作用域的设备查询、创建、修改、启停与最多 128 项批量操作;`site-quota-v1.sql` 已冻结 Bell 所有、Sense 只读的配额视图签名。T-009 已实现数据库 migration/repository;HTTP handler、认证和 Bell 管理服务尚未实现。
|
||||
- `docs/contracts/sense-control-v1.openapi.json` 已冻结站点作用域的设备查询、创建、修改、启停与最多 128 项批量操作;配额/Area 只读视图和本地设备审计事件均已冻结并实现数据库基础。HTTP handler、认证、Bell 管理服务和 Outbox relay 尚未实现。
|
||||
- harness coding 文档、上下文清单、Gitea Issue/PR 模板和治理脚本已接入。
|
||||
- Gitea 已初始化 12 个协作标签;`status/waiting` 用于依赖或外部条件未满足的未领取任务,实时可领取状态必须从 Gitea 查询,不在本文复制。
|
||||
- T-002 已关闭架构影响型问题:首期 S2 民办寄宿学校、客户侧私有部署、ONVIF/RTSP、NVIDIA 主路径、自研 Bell、Web/H5 与客户侧证据存储等边界已批准。
|
||||
@@ -65,12 +66,12 @@ Sense 默认监听 `127.0.0.1:8080`,提供 `/healthz` 与 `/readyz` 运维探
|
||||
- 人脸方向已延后至 M5 的 S4 成人园区候选试点;必要性/PIP 影响评估、单独同意与替代方式、合法底库来源和删除流程未完成,阻塞人脸能力上线。
|
||||
- 短信/语音具体供应商未选;生产前必须选定两条独立投递路径并验证故障切换。
|
||||
- Python/Savant 的精确版本、目标硬件和 Bell 前端栈尚未冻结;Sense M1 的 Go、SQLite driver、MediaMTX、生成器及生成运行时版本已在 T-003 冻结,PostgreSQL/pgx 版本已在 T-009 冻结。
|
||||
- 本机现有 PostgreSQL 5432 实例使用 SCRAM 且当前开发进程没有管理员密码;T-009 不绕过认证,自动验收使用隔离临时集群。向共享/生产实例安装 migration 前仍需管理员私下提供专用数据库、登录角色与备份方案。
|
||||
- 本机现有 PostgreSQL 5432 实例使用 SCRAM 且当前开发进程没有管理员密码;T-009/T-010 不绕过认证,自动验收使用隔离临时集群。向共享/生产实例安装 migration 前仍需管理员私下提供专用数据库、登录角色与备份方案。
|
||||
- 代码知识图谱在无业务代码阶段可能为空;工具不可用时使用 `rg` 处理文档与配置。
|
||||
|
||||
## 下一步
|
||||
|
||||
下一步按 Gitea 流程建立 Sense Control API v1 实现任务:基于 T-009 PostgreSQL repository 实现认证 tenant 上下文、设备 handler、幂等收据、ETag/cursor 和批量操作。客户授权、借用或租赁条件具备后再执行 T-007 五条独立真实上游现场门禁。T-006 的合成结果不解除 T-007,也不形成容量或生产 SLA 承诺。实时领取状态仍以 Gitea 为准。
|
||||
下一步按 Gitea 流程建立 T-011 Sense Control API v1 实现任务:基于 T-009/T-010 PostgreSQL repository 实现认证 tenant 上下文、7 个设备/operation handler、幂等收据、ETag/cursor 和最多 128 项批量操作;公共 API 不在 SQLite 路径启用。客户授权、借用或租赁条件具备后再执行 T-007 五条独立真实上游现场门禁。T-006 的合成结果不解除 T-007,也不形成容量或生产 SLA 承诺。
|
||||
|
||||
## 已知风险
|
||||
|
||||
|
||||
+10
-1
@@ -3,7 +3,7 @@ id: T-010
|
||||
title: 建立 Area 准入投影与设备操作审计 Outbox 基础
|
||||
phase: 2
|
||||
deps: [T-009]
|
||||
status: DOING
|
||||
status: DONE
|
||||
created: 2026-08-07
|
||||
issue: 35
|
||||
context_ref: 0b40cf16c3e72f4670c33a497e7d533d663ba135
|
||||
@@ -82,6 +82,15 @@ T-008 已要求 Sense Control API v1 的设备创建携带 `area_id`,并冻结
|
||||
|
||||
## 执行记录
|
||||
|
||||
### 2026-08-07 完成 Area 准入与本地审计事务基础
|
||||
|
||||
- 新增 `005`~`007` 增量 migration:Bell Area 源表、不可变 tenant/Site/ID 与单调版本 trigger、六列 `bell.area_policy_v1`,以及 Sense Area 观察状态、设备投影版本、脱敏设备操作 Outbox 和最小权限;`001`~`007` 连续执行两遍通过。
|
||||
- 冻结 `video_allowed | non_imaging_only` 和本地设备审计 JSON Schema。PostgreSQL 新建设备必须关联匹配 tenant/Site 的 Area;成像设备在创建(包括 disabled)和启用时检查策略,缺失/非法/回退失败关闭,策略变化不静默修改已有设备。
|
||||
- Repository 按 Area→Site 固定顺序使用 transaction-scoped advisory lock;设备创建/期望态受理与 Outbox 同事务。相同期望态不增加 generation 但生成独立审计事实;Outbox 故障会回滚设备创建或状态变化,payload 不含 endpoint、credential、path、密码、完整 URI 或 MediaMTX 配置。
|
||||
- 新增 9 个 T-010 PostgreSQL 集成场景,总计 18 个 `TestPostgres*`:策略允许/拒绝、缺失/版本回退、并发观察、跨 tenant 隔离、启用重检、既有设备保持、主体上下文/脱敏、创建与期望态两类 Outbox 回滚、过宽 Area 源权限拒绝。`./scripts/test_postgres.ps1 -PgRoot D:\pgsql17` 通过并清理随机回环端口临时集群,现有 `D:\pgsql17\data` 与 5432 listener 未被读取、停止或修改。
|
||||
- `./init.ps1` 通过;`python -m unittest discover -s tests -p "test_*.py"` 共 40 项通过,其中 T-010 静态契约 7 项;上下文/治理校验、`go -C Sense test ./...`、`go -C Sense vet ./...`、`go -C Sense build ./...` 和 `git diff --check` 全部通过。不需要摄像头、UI 或客户现场验收。
|
||||
- SQLite 继续作为 M1 实验室路径,不宣称具备生产 Area/Outbox 一致性;T-011 公共控制 API 必须只在 PostgreSQL 路径启用。Outbox relay 的 transport、签名、确认、重放窗口和留存仍是后续任务边界。
|
||||
|
||||
### 2026-08-07 领取任务
|
||||
|
||||
- dispatcher `ila` 将 Issue #35 分配给 `codex`;`context_ref` 为 `0b40cf16c3e72f4670c33a497e7d533d663ba135`,claim 为 `claims/T-010`,工作分支为 `agent/codex/T-010`。
|
||||
|
||||
Reference in New Issue
Block a user