73 lines
4.6 KiB
Markdown
73 lines
4.6 KiB
Markdown
# ADR-0010 — Remote sync / upload targets
|
||
|
||
- **Status:** Accepted
|
||
- **Date:** 2026-06-30
|
||
- **Context source:** User request (2026-06-30)
|
||
|
||
## Context
|
||
|
||
WA stores everything locally. The user wants the **option** to upload meeting artifacts
|
||
(transcript, notes, summary, and optionally the `.wav` recording) to a destination they configure.
|
||
Required (primary) targets: **Nextcloud, ownCloud, Cloudreve, Seafile**. Desired (secondary):
|
||
**OneDrive, Dropbox, Box, Synology NAS**.
|
||
|
||
This must be reconciled with WA's core promise (local-only). The reconciliation: sync is an
|
||
**explicit, user-configured export** — exactly the carve-out the privacy invariant already allows.
|
||
It is **off by default**, never automatic without configuration, and clearly surfaced. The primary
|
||
targets are notably all **self-hostable**, so the default story is "your data, on your server."
|
||
|
||
## Decision
|
||
|
||
### One protocol covers the entire primary set: WebDAV
|
||
Nextcloud, ownCloud, Cloudreve, and Seafile (via SeafDAV) all expose **WebDAV (RFC 4918)**, as does
|
||
**Synology** (WebDAV Server package). So a single `WebDavTarget` implementation serves all four
|
||
primary targets **and** Synology:
|
||
- Upload: `PUT`; create folders: `MKCOL`; existence/listing: `PROPFIND`.
|
||
- Large files (`.wav`): chunked/resumable upload where the server supports it (e.g. Nextcloud
|
||
chunked upload); otherwise single `PUT` with a size guard.
|
||
- Per-server base paths, e.g. Nextcloud/ownCloud `…/remote.php/dav/files/<user>/`,
|
||
Seafile `…/seafdav/`, Cloudreve `…/dav`. Seafile note: SeafDAV is **disabled by default**
|
||
server-side and may need `LOCK` disabled — surfaced in setup help.
|
||
- Auth: URL + username + **app password** (recommended over account password where supported).
|
||
|
||
### Secondary targets use provider APIs + OAuth (one impl each)
|
||
`OneDriveTarget` (Microsoft Graph), `DropboxTarget` (Dropbox API v2), `BoxTarget` (Box API). Each
|
||
uses **OAuth 2.0 Authorization Code + PKCE** with a **loopback redirect** (`http://127.0.0.1:<port>`)
|
||
— desktop-appropriate, no client secret embedded. Resumable/upload-session APIs for large files.
|
||
Synology can alternatively use its FileStation API, but WebDAV is the default path for it.
|
||
|
||
### Common architecture
|
||
- A `SyncTarget` trait (`test`, `ensure_dir`, `put`, `exists`) abstracts all providers; a
|
||
`SyncManager` owns a **durable queue** of upload jobs (per artifact × per target).
|
||
- **What/when to upload is configurable** (FR-SYNC-3): choose artifacts (transcript / notes /
|
||
summary / recording) and trigger (on finalize, manual "Upload now", or retry). Recording is
|
||
uploadable only if it was retained (ADR-0009).
|
||
- **Reliability:** queue with exponential-backoff retry; SHA-256 to skip unchanged files
|
||
(idempotent); resumable upload for large recordings; status per job surfaced via events.
|
||
- **Remote layout:** mirrors local meeting folders under a user-chosen base path.
|
||
- **Credentials:** stored in the **OS credential store** (Windows Credential Manager / DPAPI),
|
||
referenced by target id — **never** in `settings.json` or the DB.
|
||
- **Transport security:** TLS required; plaintext `http://` is refused unless the user explicitly
|
||
allows it for a LAN address, with a warning.
|
||
- **Privacy labeling:** primary/self-hosted targets are labeled "your server"; third-party clouds
|
||
(OneDrive/Dropbox/Box) carry a clear "data leaves your device to a third party" banner, mirroring
|
||
the remote-LLM caveat (ADR-0007). Enabling any sync requires explicit acknowledgment.
|
||
- **Optional client-side encryption** before upload (ties to FR-SEC-3): when the WA vault is
|
||
enabled, artifacts can be encrypted locally so the destination only ever holds ciphertext —
|
||
provider-agnostic, independent of any server-side E2EE.
|
||
|
||
## Consequences
|
||
|
||
- **Positive:** four primary targets + Synology delivered by **one** WebDAV implementation — small,
|
||
testable surface; secondary set is additive and isolated behind the same trait; default-off +
|
||
explicit config keeps the privacy promise intact; self-hostable primaries fit the ethos.
|
||
- **Negative / care:** secondary targets each need an OAuth app registration (client IDs) and
|
||
per-provider quirks (chunk sizes, path APIs) — hence "secondary"; sync introduces real network
|
||
egress, so the privacy egress test (FR-SEC-1) must be widened to *allow only* configured target
|
||
hosts and *fail* on any other; credential handling and TLS enforcement are security-critical.
|
||
- **Phasing:** WebDAV primary set first, then OAuth secondary set (see Phase 9 in `05-roadmap.md`).
|
||
|
||
## Revisit if
|
||
A primary target drops WebDAV, or we need provider-native features (e.g. Nextcloud Talk, Seafile
|
||
library sharing) beyond plain file upload — would add a provider-specific path behind the trait.
|