Memories
Memories 的 Android/iOS 用戶端。Flutter 使用者介面透過 crates/ffi/memories.h 呼叫共用的 Rust 服務;不會另外實作儲存或密碼學協定。
選擇模式
首次啟動時(包括從尚無模式偏好設定的版本升級後首次啟動),請選擇要在哪裡工作:
| 模式 | 資料位置 | 所需條件 |
|---|---|---|
| Standalone(獨立模式) | 此手機上的加密資料庫;可離線使用 | 建立本機資料庫或解鎖現有資料庫 |
| Center | 自行架設伺服器的託管儲存區 | 伺服器來源位址,例如 https://center.example.com,以及成員權杖 |
| SaaS | https://memories.ivjn.us 的託管儲存區 |
該服務的成員權杖;URL 固定 |
應用程式的 Center 模式指啟用託管儲存的伺服器(memories-center --data DIR)。它與僅提供票證授權、為本機裝置發送簽署票證的 Center 不同。Center 與 SaaS 使用相同的託管用戶端,差異在於端點選擇與儲存的連線設定檔。兩者都不需要先建立或解鎖本機資料庫。
首次連線
- 選擇模式。應用程式會記住選擇,供下次啟動使用。
- 在 Standalone 模式中,建立或解鎖資料庫。在 Center 模式中,輸入伺服器的 HTTPS 來源位址,不要加上 API 路徑;SaaS 則保留固定服務位址。
- 在任一託管模式中,輸入服務營運者提供的成員權杖,並選擇 連線。成員的角色與範圍決定可執行的操作;處理內容時請使用成員權杖,而非營運者管理權杖。
- 連線後,開啟 託管知識,即可瀏覽、搜尋、檢視、編輯或刪除知識。也能審核請求或開啟 已配對裝置。伺服器位址、目前保管庫與授予範圍會持續顯示。
託管內容以營運者持有的密碼進行靜態加密,但營運者仍可讀取內容。應用程式會顯示 伺服器可讀取。裝置對裝置的封存秘密具有獨立的端對端保護邊界。
切換、中斷連線與重新連線
登入前或登入後,都可使用主工具列中的 切換模式。對話框會標示目前模式並說明影響;選擇其他模式即可切換,或選擇 取消 保留工作階段。請先完成或離開已開啟的編輯器;離開未儲存的草稿時會要求確認。
| 操作 | 結果 |
|---|---|
| 切換模式 | 鎖定本機資料庫、停止分享、關閉目前託管連線並清除可見內容;儲存新模式 |
| 返回 Standalone | 重新開啟處於鎖定狀態的本機資料庫畫面;現有資料保留 |
| 再次進入 Center 或 SaaS | 若有可用設定,恢復該模式儲存的連線 |
| 中斷連線 | 移除目前設定檔儲存的權杖並關閉工作階段;URL 與其他模式的設定檔保留 |
| 將應用程式移至背景 | 鎖定本機存取、清除託管工作階段並關閉敏感頁面與草稿 |
| 從背景返回 | 在託管頁面使用 重新連線至已儲存的工作階段,或輸入權杖再次連線 |
切換模式不會上傳、複製、合併或刪除資料庫資料。中斷連線不會撤銷伺服器上的權杖。Center 與 SaaS 將各自的 URL/權杖組合存放在不同的平台安全儲存項目中。升級時,舊託管連線僅會匯入對應模式;請參閱設定與移轉。
可用功能
| 工作流程 | Standalone | Center / SaaS |
|---|---|---|
| 建立、備份與還原本機資料庫 | 可用 | 使用託管服務;無須本機設定 |
| 瀏覽、搜尋、檢視、編輯與刪除知識 | 解鎖後可用 | 託管知識;操作受權杖範圍限制 |
| 建立知識卡片 | 本機全頁編輯器 | 託管全頁編輯器,提供託管 API 接受的欄位 |
| 審核託管授權請求 | 切換至 Center 或 SaaS | 待處理/歷史紀錄分頁、核准/拒絕確認與重新整理 |
| 裝置配對 | 本機裝置/成員核准流程 | 託管配對、封存秘密與裝置控制,受權限限制 |
| 中繼分享與雲端連線 | 可透過本機資料庫控制項使用 | 託管頁面不提供 |
建立託管內容時不包含標籤、來源與關聯;編輯現有一般卡片則支援這些欄位,並保留其他中繼資料。一般記憶使用文字編輯器。結構化個人資料、受保護卡片、類型範本與 E2E 卡片在此通用編輯器中僅供檢視。兩種編輯器都會在儲存失敗時保留草稿;草稿不會在應用程式鎖定或移至背景後保留。請參閱內容編輯與授權。
連線疑難排解
應用程式在儲存連線前會透過 /v1/session 驗證權杖,即使是僅具寫入權限的帳號也一樣。請同步升級自行架設的伺服器與應用程式。唯讀帳號可瀏覽,但不會顯示修改控制項;僅具寫入權限的帳號可建立內容,而無須載入資料庫或類型清單。伺服器會再次授權每項操作。搜尋不包含私人內容、憑證與檔案;清除查詢即可返回知識清單。過期/已撤銷的工作階段、權限不足及伺服器無法使用,都會顯示在地化錯誤訊息。遇到 401 必須中斷連線,再以有效權杖連線。
- 無法連線:檢查完整 HTTPS 來源位址、網路、憑證與權杖。自行架設的 Center 必須啟用託管儲存;僅提供票證的服務無法提供此畫面。手機上的
localhost指手機本身。 - 權限遭拒:向服務營運者確認權杖的保管庫、角色、範圍與到期時間。選擇其他模式不會授予存取權。
- 沒有待處理請求:重新整理,並檢查請求者/保管庫/核准者的路由。歷史紀錄分頁不是完整的伺服器稽核記錄。
- 移至背景後工作階段已關閉:請明確重新連線。未儲存的草稿已隨鎖定關閉,無法還原。
- 原生服務無法啟動:使用包含相符 Rust 程式庫的建置版本。託管模式也使用 FFI,因此需要原生程式庫。
開發
先從儲存庫根目錄使用 scripts/build-android.ps1 或 scripts/build-ios.sh 建置原生程式庫。工具鏈需求與簽署說明請參閱根目錄的 README。
使用與 CI 相同的 Flutter 3.44.2。在此目錄執行:
flutter pub get
flutter gen-l10n
dart format --output=none --set-exit-if-changed lib test integration_test
flutter analyze
flutter test
flutter test integration_test/native_smoke_test.dart -d DEVICE_ID
flutter run -d DEVICE_ID
Widget 測試使用假後端,測試鎖定畫面的生命週期行為、模式選擇/切換、託管工作階段隔離、卡片、在地化與秘密參照流程。剪貼簿測試模擬 Flutter 的平台通道;不會驗證內容是否送達作業系統剪貼簿。整合測試會載入真正的原生程式庫,並在裝置上建立可拋棄資料庫。一般執行使用平台的應用程式支援目錄,以及受 Keychain/Keystore 保護的本機因子。開發時請使用測試資料;目前信任邊界請參閱 SECURITY.md。
文件秘密與剪貼簿
在 Standalone 模式中,開啟記憶/卡片選單並選擇 保護文件中的秘密,將目前內文中的精確值替換為具名參照。確認歷史紀錄警告,輸入參照名稱與隱藏秘密,並在提示時授權。Rust 服務會在同一筆交易中儲存憑證並替換內文。
在文件、受保護卡片或憑證上選擇 複製秘密,即可在確認與授權後複製目前值。文件會先顯示參照選擇器。OTP 卡片複製的是種子;其獨立 OTP 操作會產生一次性代碼。不會顯示值,移至背景或鎖定後才收到的回應會遭捨棄。完成的剪貼簿複製可能在鎖定後仍然保留。
一般編輯會保留參照對應。舊文件版本與既有備份會保留原先內容。參照規則、更新、相容性與剪貼簿限制請參閱 wiki 秘密指南。新增這些操作時,請一併重新建置原生程式庫與 Flutter 應用程式。
在地化
使用應用程式列中的語言圖示,可選擇英文、簡體中文、日文、德文、法文或系統預設語言。偏好設定與加密資料庫分開儲存,鎖定時也可使用。切換語言會保留目前資料庫工作階段。
翻譯位於 lib/l10n/app_*.arb;flutter gen-l10n 會產生已忽略的 lib/l10n/generated/ 目錄。Material 與 Cupertino 控制項使用 Flutter 的在地化委派。新增訊息或語言時,請遵循在地化指南。
分享工作階段(中繼 RPC 主機)
在 Standalone 模式中,資料庫已解鎖且應用程式位於前景時,更多選單提供 分享工作階段。
它會使用高熵權杖加入 wss://…/v1/rpc/{room}/a,讓桌面 CLI 能以插槽 b 連接(MEMORIES_DAEMON_RELAY / MEMORIES_DAEMON_ROOM / MEMORIES_DAEMON_TOKEN)。移至背景或鎖定會停止工作階段。MVP 的行動端不會監聽 HTTP/WS;原生 FFI 拒絕清單仍然適用。請參閱遠端 daemon。
生物辨識解鎖與敏感操作
在已解鎖的 Standalone 資料庫中,使用更多選單 → 使用生物辨識/裝置鎖解鎖(預設關閉;開啟時需要一次系統檢查)。lib/biometric.dart 封裝 local_auth(Android 上使用 BiometricPrompt,並可退回裝置 PIN/圖形/密碼;iOS 上使用 Face ID / Touch ID,Info.plist 中設定 NSFaceIDUsageDescription)。MainActivity 依 local_auth 要求繼承 FlutterFragmentActivity。
- 快速解鎖的資料庫密碼以
AndroidOptions.biometric(resetOnError: false)儲存:使用設定了setUserAuthenticationRequired(true)與setInvalidatedByBiometricEnrollment(true)的 Android Keystore AES 金鑰。iOS 的 Keychain 項目使用KeychainAccessibility.passcode。 - 受驗證閘門保護的操作:解鎖資料庫、複製/顯示憑證明文、使用受保護卡片/OTP、核准配對與 Hosted Center 請求、封存與重新封存。
- 生物辨識註冊變更(
KeyPermanentlyInvalidatedException)時,應用程式會告知使用者,以裝置憑證重新驗證,只捨棄過期的快速解鎖副本,並在一次密碼解鎖後重新建立金鑰。絕不會清除資料庫。 - 沒有生物辨識/裝置憑證,或開關關閉時:使用既有密碼流程。
- 測試模擬
DeviceAuth/UnlockSecretStore(test/biometric_test.dart)。
內容編輯器
lib/content_editor.dart 由新增記憶、憑證與 Hosted Center 項目共用:檔案匯入(以檔名作為標題)、適合 .env 類內容的等寬字體模式(自動偵測或手動切換),以及以內容為優先、由檔名或第一個非空白行推斷的名稱,只有儲存時仍為空白才會提示輸入。手寫(筆/橡皮擦/復原/清除,ML Kit Digital Ink)與 OCR(相機/照片/檔案,ML Kit 文字辨識)都在裝置上執行;請參閱手寫與 OCR。Widget 測試注入 ScanServices 假實作。
記憶與知識卡片會持續開啟,直到儲存成功。寫入失敗會保留草稿供重試;帶著變更離開時會要求確認。草稿僅保留在記憶體中,移至背景或閒置鎖定仍會關閉草稿。檔案匯入在取代既有文字前會詢問。記憶在儲存前會顯示私人/一般設定,附件重試會略過本次編輯中已儲存的檔案。
知識卡片使用全頁編輯器,具備持續顯示的儲存操作、自訂欄位、標籤、來源與關聯。編輯內容 會開啟共用文字編輯器,以進行檔案匯入、手寫與 OCR。託管建立僅顯示既有行動 API 接受的中繼資料。
行動端授權
Center 與 SaaS 頁面將待處理請求與歷史紀錄分開。確認核准或拒絕前,請檢查請求者、保管庫、存取範圍、持續時間與完整理由。核准仍使用既有裝置驗證閘門,請求者必須另外領取授權。發生錯誤時會保留請求供重試;下拉或使用重新整理按鈕即可重新整理。
Center 請求 API 不會傳回完整決策歷史紀錄。歷史紀錄分頁結合目前連線期間做出的決策,以及 Center 傳回的已核准請求。離開頁面時會清除這些本機紀錄。失敗的新連線會關閉,不會取代儲存的設定檔。
本機成員管理只顯示所呈現角色允許的操作。確認畫面會指明成員與舊/新存取權。Rust 服務會重新檢查目前原則;使用者介面篩選不會授予任何權限。一般角色操作無法變更擁有者成員資格,只有擁有者能看到擁有權移轉。
品牌
標誌原始檔:assets/brand/logo.svg(另有 logo-mono.svg 與自適應的 logo-foreground.svg)。圖示與啟動畫面影像使用 rsvg-convert 算繪;標誌變更時請以相同尺寸重新執行。