---
document_type: "plan"
service: "MOMENTRY_CORE"
title: "Birth UUID Implementation Plan - 有意义唯一标识方案"
date: "2026-04-27"
version: "V1.0"
status: "active"
owner: "Warren"
created_by: "OpenCode"
tags:
  - "uuid"
  - "birth_registration"
  - "resource_allocation"
  - "privacy"
  - "mac_binding"
ai_query_hints:
  - "查询 UUID 出生登记实现计划"
  - "Birth UUID 如何生成？"
  - "MAC地址在UUID中的作用是什么？"
  - "如何实现多层次权限管制？"
  - "文件迁移后UUID是否变化？"
related_documents:
  - "src/core/storage/uuid.rs"
  - "src/core/ingestion.rs"
  - "docs_v1.0/OPERATIONS/DOCS_STANDARD.md"
---

# Birth UUID Implementation Plan - 有意义唯一标识方案

| 项目 | 内容 |
|------|------|
| 规划制定人 | OpenCode |
| 制定时间 | 2026-04-27 |
| 规划类型 | 功能实现 |
| 规划状态 | ✅ 规划完成，待实施 |
| 优先级 | High |
| MVP范围 | Phase 1 |

---

## 版本历史

| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-04-27 | 创建规划文档 | OpenCode | glm-5 |

---

## 规划背景

### 问题陈述

当前UUID生成机制存在以下问题：

| 问题 | 当前状态 | 影响 |
|------|---------|------|
| **同名文件冲突** | GOPR0001.mp4在摄影设备中很常见 | UUID重复风险 |
| **文件迁移后变化** | SHA256(path+filename) | 无法追踪原始文件 |
| **无注册来源记录** | 仅路径哈希，无其他元数据 | 无法追溯来源 |
| **隐私信息暴露** | 路径包含用户名，明文可见 | 用户隐私风险 |

### 用户需求

| 需求 | 说明 |
|------|------|
| **唯一性** | 同名文件在不同设备/用户/时间注册，UUID必须不同 |
| **不可变性** | 文件迁移后（热→温→温冷→冷→归档），UUID保持不变 |
| **有意义** | UUID不仅仅是随机ID，应包含实际意义（可追溯） |
| **隐私保护** | MAC/Username等敏感信息不应在UUID中暴露 |
| **资源管制** | MAC用于App绑定保护，Username用于隐私管制 |

---

## 解决方案：Birth UUID（出生登记）

### 核心概念

类似"出生登记"，记录文件首次注册的完整信息：
- **出生时间**: 注册时间戳
- **出生地点**: 注册机器（MAC地址）
- **出生身份**: 注册用户（Username）
- **出生姓名**: 文件名（Filename）

### 关键特性

| 特性 | 说明 |
|------|------|
| **唯一性保证** | MAC + Time + Username + Filename 四重组合 |
| **不可变性** | UUID一旦生成，永久固定（即使文件迁移） |
| **可追溯性** | DB内存储完整birth_registration（仅内部可见） |
| **隐私保护** | 所有元素SHA256哈希（UUID不暴露明文） |
| **资源管制** | MAC用于App绑定，Username用于隐私管制 |

---

## UUID规格定义

### 格式

**纯哈希格式**: `SHA256(mac_address|timestamp|username|filename)[0:32]`

```
示例: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6 (32字符纯哈希)
```

### 输入元素

| 元素 | 来源 | 格式示例 | 作用 | 处理方式 |
|------|------|---------|------|----------|
| **MAC地址** | 注册机器网卡 | `a1:b2:c3:d4:e5:f6` | App绑定 + 资源分配 | 哈希内（不外露）+ DB明文 |
| **注册时间** | 系统时间戳 | `2026-04-27T22:00:00+08:00` | 唯一性保证（时间维度） | 哈希内 + DB明文 |
| **Username** | sftpgo user home | `demo` | 隐私管制（用户维度） | 哈希内 + DB明文 |
| **Filename** | 文件名 | `GOPR0001.mp4` | 文件标识 | 哈希内 + DB明文 |

### 拼接格式

```
key = "mac_address|timestamp|username|filename"

示例key = "a1:b2:c3:d4:e5:f6|2026-04-27T22:00:00+08:00|demo|GOPR0001.mp4"
```

### 生成逻辑（Rust）

```rust
pub fn compute_birth_uuid(
    mac_address: &str,      // a1:b2:c3:d4:e5:f6
    timestamp: &str,        // 2026-04-27T22:00:00+08:00
    username: &str,         // demo
    filename: &str          // GOPR0001.mp4
) -> String {
    let key = format!("{}|{}|{}|{}", 
        mac_address, 
        timestamp, 
        username, 
        filename
    );
    
    let hash = Sha256::digest(key.as_bytes());
    hex::encode(hash)[0..32].to_string()
}
```

---

## 唯一性保证分析

### 场景矩阵

| 场景 | MAC | Time | User | Filename | UUID是否唯一 |
|------|-----|------|------|----------|-------------|
| 不同设备同名文件 | 不同 | 同 | 同 | 同 | ✅ 唯一 |
| 同设备不同时间注册 | 同 | 不同 | 同 | 同 | ✅ 唯一 |
| 同设备不同用户同名文件 | 同 | 同 | 不同 | 同 | ✅ 唯一 |
| 同设备同用户不同文件 | 同 | 同 | 同 | 不同 | ✅ 唯一 |
| 完全相同的四元素 | 同 | 同 | 同 | 同 | ❌ 相同（预期） |

### 实际场景示例

#### 场景1：摄影设备同名文件（最常见）

```
设备A (MAC: a1:b2:c3):
  GOPR0001.mp4 @ 2026-01-01T10:00:00 → UUID: abc123...

设备B (MAC: d4:e5:f6):
  GOPR0001.mp4 @ 2026-01-01T10:00:00 → UUID: def456...

结果：不同UUID ✅（MAC不同）
```

#### 场景2：同一设备多次注册同名文件

```
设备A (MAC: a1:b2:c3):
  GOPR0001.mp4 @ 2026-01-01T10:00:00 → UUID: abc123...
  GOPR0001.mp4 @ 2026-01-01T14:00:00 → UUID: xyz789...

结果：不同UUID ✅（Time不同）
```

#### 场景3：同一用户不同存储位置

```
MAC: a1:b2:c3, User: demo, Time: 2026-01-01T10:00:00

/Volumes/Hot/demo/GOPR0001.mp4  → UUID: abc123... (注册)
/Volumes/Warm/demo/GOPR0001.mp4 → UUID: abc123... (迁移后，UUID不变)

原因：UUID基于原始注册信息，不随当前路径变化
```

---

## 存储迁移追踪

### 存储层级定义

| 层级 | 路径示例 | 说明 |
|------|---------|------|
| **Hot** | `/Volumes/Hot/demo/` | 热存储（快速访问） |
| **Warm** | `/Volumes/Warm/demo/` | 温存储（中等访问） |
| **Warm-Cold** | `/Volumes/WarmCold/demo/` | 温冷存储 |
| **Cold** | `/Volumes/Cold/demo/` | 冷存储（归档准备） |
| **Archive** | `cloud://archive/demo/` | 云归档 |

### 迁移时间线

```
T0: 注册（Hot存储）
    UUID: abc123...（基于原始注册生成）
    birth_registration: {
        "original_path": "/Volumes/Hot/demo",
        "original_tier": "Hot"
    }
    current_path: /Volumes/Hot/demo/GOPR0001.mp4

T1: 迁移（Warm存储）
    UUID: abc123...（不变！）
    birth_registration: 不变（记录原始）
    current_path: /Volumes/Warm/demo/GOPR0001.mp4
    migration_history: 新增迁移记录

T2: 迁移（Cold存储）
    UUID: abc123...（不变！）
    current_path: /Volumes/Cold/demo/GOPR0001.mp4

T3: 归档
    UUID: abc123...（不变！）
    current_path: cloud://archive/demo/GOPR0001.mp4
```

---

## 数据库设计

### birth_registration JSONB字段

```sql
ALTER TABLE videos ADD COLUMN birth_registration JSONB;

-- 示例数据结构
{
    "uuid": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
    "registration_source": {
        "mac_address": "a1:b2:c3:d4:e5:f6",
        "username": "demo",
        "timestamp": "2026-04-27T22:00:00+08:00",
        "original_path": "./demo",
        "original_filename": "GOPR0001.mp4"
    },
    "permission_control": {
        "mac_binding": {
            "license_key": "demo_license",
            "is_active": true
        },
        "user_privacy": {
            "privacy_level": "private",
            "data_isolation": true
        }
    }
}
```

### mac_allocations表（简化版）

```sql
CREATE TABLE mac_allocations (
    mac_address VARCHAR(17) PRIMARY KEY,
    machine_name VARCHAR(100),
    license_key VARCHAR(64),
    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- 插入当前机器
INSERT INTO mac_allocations VALUES (
    '<actual_mac>',
    'MacBook-Pro',
    'demo_license',
    true
);
```

---

## 多层次权限管制架构

### 当前MVP实现（Phase 1）

| 层级 | 用途 | 实现状态 |
|------|------|---------|
| **MAC层** | App绑定保护 | ✅ Phase 1 实现 |
| **User层** | 隐私管制（数据隔离） | ⚠️ 单user时可跳过 |

### 未来扩展（Phase 2 - 仅文档）

| 层级 | 用途 | 实现状态 |
|------|------|---------|
| **Group层** | 访问权限控制 | 📝 仅文档规划 |
| **Service层** | 处理器权限分配 | 📝 仅文档规划 |
| **Storage层** | 存储位置分配 | 📝 仅文档规划 |

### 权限管制维度说明

| 维度 | 说明 | 示例 |
|------|------|------|
| **MAC** | App绑定保护（类似License） | 不同机器不同权限 |
| **User** | 隐私管制（数据隔离） | 用户A无法访问用户B数据 |
| **Group** | 访问权限控制（谁能access） | admin组可访问所有 |

---

## 实施计划

### Phase 1: MVP实现

| 任务 | 优先级 | 状态 | 说明 |
|------|--------|------|------|
| 更新 uuid.rs | High | Pending | 新增 compute_birth_uuid() |
| 添加 birth_registration | High | Pending | videos表JSONB字段 |
| 创建 mac_allocations 表 | High | Pending | 简化版（MAC+license） |
| 更新 ingestion.rs | High | Pending | 获取MAC并调用新函数 |
| 添加 mac_address crate | High | Pending | Cargo.toml依赖 |
| 单元测试 | High | Pending | 验证UUID生成逻辑 |

### Phase 2: 扩展功能（仅文档）

| 功能 | 状态 | 说明 |
|------|------|------|
| user_privacy表 | 📝 仅文档 | 多用户隐私管制 |
| group_access表 | 📝 仅文档 | 组访问控制 |
| migration_history | 📝 仅文档 | 迁移历史追踪 |
| 多层次权限API | 📝 仅文档 | 完整权限系统 |

---

## 验证计划

### 单元测试

```rust
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_birth_uuid_generation() {
        let uuid = compute_birth_uuid(
            "a1:b2:c3:d4:e5:f6",
            "2026-04-27T22:00:00+08:00",
            "demo",
            "video.mp4"
        );
        assert_eq!(uuid.len(), 32);
    }

    #[test]
    fn test_different_mac() {
        let uuid1 = compute_birth_uuid(
            "a1:b2:c3", "2026-01-01", "demo", "video.mp4"
        );
        let uuid2 = compute_birth_uuid(
            "d4:e5:f6", "2026-01-01", "demo", "video.mp4"
        );
        assert_ne!(uuid1, uuid2);  // MAC不同
    }

    #[test]
    fn test_different_time() {
        let uuid1 = compute_birth_uuid(
            "a1:b2:c3", "2026-01-01T10:00:00", "demo", "video.mp4"
        );
        let uuid2 = compute_birth_uuid(
            "a1:b2:c3", "2026-01-01T14:00:00", "demo", "video.mp4"
        );
        assert_ne!(uuid1, uuid2);  // Time不同
    }

    #[test]
    fn test_different_user() {
        let uuid1 = compute_birth_uuid(
            "a1:b2:c3", "2026-01-01", "demo", "video.mp4"
        );
        let uuid2 = compute_birth_uuid(
            "a1:b2:c3", "2026-01-01", "warren", "video.mp4"
        );
        assert_ne!(uuid1, uuid2);  // User不同
    }
}
```

---

## 向后兼容

### UUID类型识别

| UUID类型 | 长度 | birth_registration | 生成方式 |
|---------|------|-------------------|---------|
| **旧UUID** | 16字符 | 无字段 | SHA256[path+filename](0:16) |
| **新UUID** | 32字符 | 有字段 | SHA256[mac+time+user+filename](0:32) |

### 兼容策略

```rust
pub fn is_birth_uuid(uuid: &str) -> bool {
    uuid.len() == 32 && !uuid.contains('_')  // 纯哈希32字符
}

// 处理时自动识别
pub fn get_uuid_type(uuid: &str) -> UuidType {
    if is_birth_uuid(uuid) {
        UuidType::Birth
    } else {
        UuidType::Legacy
    }
}
```

---

## API影响

### 外部API（不变）

| API | 影响 |
|-----|------|
| `/api/v1/videos/:uuid` | ✅ UUID参数传递不变 |
| `/api/v1/videos?uuid=xxx` | ✅ 查询参数不变 |
| Python scripts `--uuid` | ✅ 参数传递不变 |

### 内部API（新增 - 可选）

```rust
// 管理员查询birth_registration（仅内部）
GET /api/admin/videos/:uuid/birth-info

Response:
{
    "uuid": "a1b2c3d4...",
    "registration_source": {
        "mac_address": "a1:b2:c3...",
        "username": "demo",
        "timestamp": "2026-04-27...",
        "original_filename": "GOPR0001.mp4"
    }
}
```

---

## 隐私保护级别

| 保护项 | 保护方式 | 保护级别 | 外部可见 |
|---------|----------|----------|----------|
| **UUID** | SHA256哈希 | ✅ 最高 | ❌ 不可解码 |
| **MAC地址** | 哈希内 + DB明文 | ✅ 高 | ❌ 仅内部 |
| **Username** | 哈希内 + DB明文 | ✅ 高 | ❌ 仅内部 |
| **注册时间** | 哈希内 + DB明文 | ✅ 高 | ❌ 仅内部 |
| **外部API** | 无暴露API | ✅ 最高 | ❌ 外部无法查询 |

---

## 执行状态

| 状态 | 说明 |
|------|------|
| 规划完成 | ✅ 规划文档已存档 |
| 待实施 | ⏸ Phase 1 待执行 |
| Phase 2 | 📝 仅文档规划 |

---

## 参考文档

| 文档 | 说明 |
|------|------|
| `src/core/storage/uuid.rs` | 当前UUID生成逻辑 |
| `src/core/ingestion.rs` | 文件注册流程 |
| `docs_v1.0/OPERATIONS/DOCS_STANDARD.md` | 文档规范 |
| `AGENTS.md` | 项目总览 |

---

**注意**: Phase 2 功能（group_access、多层次权限API等）仅在本文档中规划，暂不实施。待多用户场景出现后再实现。
