# file_uuid 設計理念與規格

> Version: 1.0 | Date: 2026-04-30
> Architecture: Birth Identity Model (戶籍制度模型)

---

## 1. 核心概念

系統將每個媒體檔案視為一個「自然人」，擁有一個**終身不變的身份證字號** (`file_uuid`)。

| 戶籍概念 | 系統對應 | 說明 |
| :--- | :--- | :--- |
| **身分證字號** | `file_uuid` | 檔案的終身唯一標識，出生後永不變更 |
| **出生登記** | 首次 `register` | 檔案首次被系統納管，觸發分析處理 (ASR, Face, etc.) |
| **戶籍地** | `file_path` | 檔案當前存放位置，可隨搬家而變更 |
| **主管單位** | `MAC Address` | 核發身份的伺服器/機器，確保跨機器的管轄獨立 |
| **居住證申請時間** | `registration_time` | 檔案在該管轄單位登記的時間戳記 |

---

## 2. file_uuid 生成公式

```text
file_uuid = SHA256( MAC_Address | Birthday | Canonical_Path | Filename )[0:32]
```

### 設計原則

| 原則 | 說明 |
| :--- | :--- |
| **唯一性** | 同一台機器上，相同路徑與檔名只會產生一個 UUID |
| **穩定性** | **生日 (Birthday)** 是身份錨點。如果檔案在原地重新註冊，系統會找回原始生日，確保 UUID 不變 |
| **管轄獨立** | 不同機器的 MAC 不同，確保跨伺服器身份獨立 |
| **路徑綁定** | **Canonical Path** 參與計算。檔案移動到新路徑會產生新 UUID（視為新環境下的註冊） |
| **隱私保護** | 所有元素經 Hash 處理，無法反推出原始資訊 |

### 關鍵元素

| 元素 | 說明 |
| :--- | :--- |
| `Birthday` | 首次註冊的時間戳記。系統會透過檔名查詢資料庫，找回原始生日，確保身份連續 |
| `Canonical Path` | 檔案的絕對路徑。確保位置的唯一性 |
| `Filename` | 檔案名稱 |

---

## 3. 生命週期

### 3.1 出生 (Birth / 首次納管)

當檔案首次被系統發現並執行 `register` 時：

```
1. 取得本机 MAC Address
2. 讀取 Filename
3. 查詢資料庫：是否有同檔名 (Filename) 的紀錄？
   ├─ 有紀錄 → 取出其 registration_time 作為「生日 (Birthday)」
   └─ 無紀錄 → 使用 NOW() 作為「生日 (Birthday)」
4. 計算 file_uuid = SHA256(MAC | Birthday | Canonical_Path | Filename)[0:32]
5. 檢查 DB 是否已存在該 UUID
   ├─ 已存在 → 拒絕重複登記 (已有出生紀錄)
   └─ 不存在 → 建立新生紀錄
6. 記錄 registration_time (居住證申請時間)
```

**出生後**：`file_uuid` 即成為該檔案的終身身份，不可更改。

### 3.2 搬家 (Move / 路徑變更)

當檔案從 `/data/demo/` 移動到 `/archive/2024/` 時：

```
1. 檔案路徑變更 (Canonical Path 改變)
2. 系統以新 Path 計算 UUID → 產生新 UUID
3. 查詢 DB → 找不到該 UUID (視為新身份)
4. 但若檔名相同，會查詢到舊的「生日 (Birthday)」
5. 執行動作：
   ├─ 建立新紀錄 (新 UUID，新路徑)
   ├─ 使用原始的 Birthday (保持血緣關係)
   └─ 可選擇是否繼承舊紀錄的分析結果
```

**關鍵邏輯**：
- 路徑改變 = 新環境 = 新 UUID
- 但透過 **Birthday 查詢機制**，系統知道這是同一個「人」搬到了新家

### 3.3 跨機器遷移 (Cross-Machine)

當檔案從 Server-A 複製到 Server-B 時：

```
Server-A (MAC: aa:bb:cc:dd:ee:ff):
  file_uuid = SHA256("aa:bb:cc:dd:ee:ff|Birthday|/path|video.mp4") → "abc123..."

Server-B (MAC: 11:22:33:44:55:66):
  file_uuid = SHA256("11:22:33:44:55:66|Birthday|/path|video.mp4") → "def456..."
```

- **結果**：兩台伺服器各自擁有獨立管轄權
- **意義**：各管各的戶口，互不干擾

---

## 4. 資料庫欄位定義

### videos 表

| 欄位 | 類型 | 說明 | 範例 |
| :--- | :--- | :--- | :--- |
| `file_uuid` | VARCHAR(32) | **身分證字號** (不可變) | `384b0ff44aaaa1f1...` |
| `file_path` | TEXT | **戶籍地址** (可變) | `/data/demo/video.mp4` |
| `file_name` | VARCHAR(255) | 原始檔名 | `video.mp4` |
| `registration_time` | TIMESTAMPTZ | **居住證申請時間** | `2026-04-30T02:00:00+08` |
| `birth_registration` | JSONB | 出生登記詳情 | 見下方結構 |

### birth_registration JSONB 結構

```json
{
  "registration_source": {
    "mac_address": "ba:f5:ee:bc:45:78",
    "original_path": "/Users/accusys/momentry/var/sftpgo/data/demo",
    "original_filename": "Old_Time_Movie_Show_-_Charade_1963.HD.mov",
    "timestamp": "2026-04-29T02:25:14+08:00"
  }
}
```

---

## 5. 代碼實作

### 5.1 UUID 計算 (`src/core/storage/uuid.rs`)

```rust
pub fn compute_birth_uuid(
    mac_address: &str,
    birthday: &str, 
    path: &str,
    filename: &str,
) -> String {
    let key = format!("{}|{}|{}|{}", mac_address, birthday, path, filename);
    let hash = Sha256::digest(key.as_bytes());
    hex::encode(hash)[0..32].to_string()
}
```

### 5.2 註冊流程 (`src/api/server.rs`)

```rust
// 1. 取得 MAC、路徑與檔名
let mac_address = get_mac_address();
let canonical_path = path.canonicalize()...;
let filename = path.file_name()...;

// 2. 查詢生日 (Identity Anchor)
// 以檔名查詢 DB，若有紀錄則使用原始生日，否則使用 NOW()
let birthday = db.find_birthday_by_filename(&filename).await.unwrap_or(now());

// 3. 計算穩定身份
let file_uuid = compute_birth_uuid(&mac_address, &birthday, &canonical_path, &filename);

// 4. 檢查是否已出生
if let Some(existing) = db.get_video_by_uuid(&file_uuid).await? {
    if existing.registration_time.is_some() {
        return Ok(already_exists_response);
    }
}

// 5. 新生登記 + 觸發分析
db.register_video(&record).await?;
```

---

## 6. 情境對照表

| 情境 | file_uuid | file_path | Birthday | 觸發分析? | 說明 |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **首次註冊** | 新生成 | 記錄當前路徑 | NOW() | ✅ 是 | 出生登記，全面納管 |
| **同一檔案再次註冊** | 相同 | 不變 | 原始 | ❌ 否 | 已有戶籍，拒絕重複 |
| **檔案移動到同機另一目錄** | **不同** | 新路徑 | 原始 | ✅ 是 | 新位置視為新環境 |
| **檔案複製到另一台伺服器** | 不同 | 記錄新路徑 | ✅ 是 | 新管轄區，獨立登記 |
| **檔名變更** | 不同 | 記錄新路徑 | ✅ 是 | 視為不同身份 |
| **檔案刪除後重新加入** | 相同 | 記錄新路徑 | ⚠️ 視情況 | 若 DB 紀錄仍存在，可恢復關聯 |

---

## 7. 設計優勢

1. **身份錨點**：透過 Birthday 機制，即使路徑改變，系統仍能識別檔案的歷史血緣
2. **路徑綁定**：UUID 包含 Canonical Path，確保每個位置的檔案都有獨立身份，避免混淆
3. **管轄清晰**：MAC Address 確保每台伺服器的數據獨立
4. **可追溯性**：`birth_registration` 記錄原始出處與 Birthday，便於審計
5. **防止重複**：系統以 UUID 為準，同一位置同一檔案絕不會重複登記

---

## 8. 相關文件

| 文件 | 說明 |
| :--- | :--- |
| `src/core/storage/uuid.rs` | UUID 生成實作 |
| `src/api/server.rs` | 註冊端點與流程 |
| `src/core/ingestion.rs` | Watcher 自動 ingestion 邏輯 |
| `docs_v1.0/UUID_LENGTH_ISSUE.md` | 舊版 UUID 長度問題分析 |
| `docs_v1.0/UUID_CLEANUP_PLAN.md` | 歷史數據清理方案 |
