Files
webmanager-mvp/project_docs/REMOTE_CLIENT_SHARES_V1_DESIGN.md
T

718 lines
16 KiB
Markdown

# Remote Client Shares V1.1 Design
## Status
Dit document beschrijft de V1.1-doelscope voor Remote Client Shares.
Per huidige repositorystatus valt de beschreven V1.1 read-mostly scope onder afgeronde implementatie van:
- client registry
- browse via `/Clients`
- file info
- tekstpreview
- eenvoudige image preview
- download
De expliciet niet in V1.1 opgenomen onderdelen hieronder blijven buiten scope en vormen in dit document geen aparte vervolgfase.
## Doel
Een gebruiker van WebManager moet naast de bestaande server-side storage-roots ook een beperkte set lokale mappen van zijn eigen client-Mac kunnen benaderen, zonder de hele homefolder bloot te geven.
Voorbeelden van toegestane client-shares:
- `Downloads`
- `Movies`
- `Pictures`
De oplossing moet:
- simpel blijven
- veilig blijven
- de bestaande storage-workflow niet breken
- WebManager niet laten vastlopen als een remote helper-agent offline is
---
## Kernbeslissingen voor V1.1
Deze beslissingen liggen in V1.1 vast.
- Remote client shares worden niet opgenomen in `root_aliases`.
- `/Clients` wordt een aparte virtuele bron naast `/Volumes`.
- Remote paden lopen niet door de bestaande lokale filesystem-resolutie.
- `client_id` is intern de enige leidende identiteit.
- `display_name` is alleen voor UI-weergave.
- De agent werkt alleen met `share key + relatief pad`.
- Alle agent-calls vereisen authenticatie, niet alleen registratie.
- Offline agents mogen alleen hun eigen subtree beïnvloeden, nooit de rest van de app.
- V1 blijft read-mostly: registry, browse, info, preview, download.
---
## Waarom niet als gewone root alias
De huidige backend gaat uit van server-side whitelisted filesystem roots.
Dat model werkt voor:
- `/Volumes/...`
- gemounte server storage
- container-side toegankelijke paden
Dat model werkt niet goed voor:
- de lokale schijf van de browsergebruiker
- een remote Mac die buiten de server draait
- clients die offline kunnen zijn
- clients die dynamische IP-adressen hebben
Daarom mogen remote client shares niet in hetzelfde model worden gestopt als `root_aliases`.
---
## Scope V1.1
### In scope
- beperkte client-shares: `Downloads`, `Movies`, `Pictures`
- lokale helper-agent op macOS
- agent registratie in WebManager
- heartbeat/status tracking
- virtuele `Clients` bron in de WebUI
- browse van remote shares
- bestand-info
- tekstpreview
- image preview waar triviaal
- download van bestanden
- nette offline-afhandeling
### Expliciet niet in V1.1
- hele homefolder
- willekeurige custom paths buiten de toegestane sharelijst
- shell/subprocess execution
- rename
- mkdir
- delete
- upload naar remote share
- bookmarks voor `/Clients/...`
- startup paths voor `/Clients/...`
- cross-source copy of move
- complete taakrunner-integratie zoals server copy/move tasks
- automatische LAN discovery
- multi-user auth met OS user mapping
Status:
- deze lijst blijft expliciet uitgesloten van V1.1
- dit document definieert hiervoor geen Phase 4 of andere vervolgfase
---
## Gewenste gebruikerservaring
In de WebUI komt naast server-storage een extra virtuele bron:
- `/Volumes`
- `/Clients`
Onder `/Clients` ziet de gebruiker geregistreerde clients, bijvoorbeeld:
- `MacBook Pro van Jan`
- `iMac Woonkamer`
Onder een client ziet de gebruiker alleen de toegestane shares:
- `Downloads`
- `Movies`
- `Pictures`
Voor de gebruiker kan dat eruitzien als:
- `/Clients/MacBook-Pro-van-Jan/Downloads`
- `/Clients/MacBook-Pro-van-Jan/Movies`
- `/Clients/MacBook-Pro-van-Jan/Pictures`
Maar intern mag routing niet op `display_name` leunen.
Intern moet WebManager werken met een stabiele client-identiteit en een mappinglaag:
- `client_id` voor routing en opslag
- `display_name` voor weergave
- optioneel een afgeleide slug voor browse-url-presentatie
---
## Architectuuroverzicht
Er zijn drie componenten.
### 1. WebManager backend
Verantwoordelijk voor:
- registry van bekende remote clients
- status- en heartbeat-tracking
- virtuele browse-root `Clients`
- proxying van requests naar agents
- timeouts en foutafhandeling
- scheiding tussen local-source en remote-source afhandeling
### 2. WebUI frontend
Verantwoordelijk voor:
- tonen van `Clients` als extra bron
- navigeren binnen client/share paden
- offline status tonen
- requests afvuren naar gewone WebManager backend-routes
### 3. Remote helper-agent op macOS
Verantwoordelijk voor:
- toegang tot vaste lokale shares
- strikte padvalidatie binnen die shares
- simpele browse/info/read/download endpoints
- zichzelf registreren bij WebManager
- heartbeat sturen
- auth afdwingen op alle agent-endpoints
---
## Bereikbaarheidsmodel
Dit is de eerste harde productbeslissing.
### V1.1-keuze
V1.1 gaat uit van een omgeving waarin WebManager de agent rechtstreeks kan bereiken.
Dat betekent praktisch:
- dezelfde LAN
- of een expliciet configureerbaar agent-endpoint
- of een deployment waar server en client netwerkmatig direct verbonden zijn
### Waarom deze keuze
Dit is het simpelste model dat functioneel klopt zonder reverse tunnels, websockets als transportlaag, of extra relay-infrastructuur.
### Wat V1.1 niet probeert op te lossen
Deze versie garandeert niet dat een agent achter willekeurige NAT/firewall altijd bereikbaar is.
Dus:
- self-registration blijft het discoverymodel
- direct bereikbare agent-endpoint blijft het V1-transportmodel
- reverse-connect of tunnelmodellen zijn uitgesteld
### Fallback
Een handmatige endpoint override blijft toegestaan als operationele fallback, bijvoorbeeld:
- `http://192.168.1.25:8765`
Maar dat is geen hoofdmodel en geen productbelofte.
---
## Hoe de remote agent bekend wordt in WebManager
### Gekozen model: agent registreert zichzelf
De agent meldt zichzelf actief aan bij WebManager. Niet andersom.
Dat betekent:
- geen handmatig client-IP nodig als hoofdmodel
- geen server-naar-client discovery nodig
- geen afhankelijkheid van LAN-broadcasting
- geen probleem als het client-IP wisselt, zolang het geregistreerde endpoint actueel is
### Registratiestroom
Bij starten van de agent:
1. de agent leest lokale config
2. de agent bepaalt:
- `client_id`
- `display_name`
- `shares`
- `endpoint`
3. de agent registreert zich bij WebManager
4. WebManager slaat client-record op of werkt het bij
5. de agent stuurt periodieke heartbeats
### Benodigde velden bij registratie
Voorstel:
```json
{
"client_id": "f4b2c8f8-2b1b-4d89-9ed2-8d6d7b1f3abc",
"display_name": "MacBook Pro van Jan",
"platform": "macos",
"agent_version": "1.1.0",
"endpoint": "http://192.168.1.25:8765",
"shares": [
{ "key": "downloads", "label": "Downloads" },
{ "key": "movies", "label": "Movies" },
{ "key": "pictures", "label": "Pictures" }
]
}
```
### Backend bewaart per client
- `client_id`
- `display_name`
- `platform`
- `agent_version`
- `endpoint`
- `shares`
- `last_seen`
- `status`
- `last_error`
- `reachable_at`
- eventueel `registration_token_id`
### Heartbeat
De agent stuurt elke 15-30 seconden een heartbeat.
Bijvoorbeeld:
```json
{
"client_id": "f4b2c8f8-2b1b-4d89-9ed2-8d6d7b1f3abc",
"agent_version": "1.1.0"
}
```
### Statusmodel
Deze velden moeten logisch gescheiden blijven:
- `last_seen`
Laatste succesvolle heartbeat van de agent.
- `status`
Afgeleide UI-status, bijvoorbeeld `online` of `offline`.
- `last_error`
Laatste connect- of browsefout richting agent.
- `reachable_at`
Laatste moment waarop een directe agent-call echt succesvol was.
Belangrijk:
- een heartbeat bepaalt niet automatisch dat elke browse-call werkt
- een enkele browse-timeout mag niet blind `last_seen` overschrijven
- status mag niet gaan flappen op basis van één los incident
### Aanbevolen statusregels
- `online` als `last_seen` recent is
- `offline` als heartbeat-timeout overschreden is
- extra foutdetails via `last_error`
- optioneel UI-label zoals `online with recent errors` later, maar niet nodig in V1.1
---
## Authenticatie en beveiliging
### Backend registratie-auth
Registratie vereist een bearer token.
Bijvoorbeeld:
- `Authorization: Bearer <registration-token>`
### Agent endpoint-auth
Alle agent-calls vereisen authenticatie. Niet alleen registratie.
Dus ook:
- `/health`
- `/api/list`
- `/api/info`
- `/api/read`
- `/api/download`
moeten beschermd zijn.
### V1.1 minimum
Voor V1.1 volstaat een eenvoudige gedeelde agent-token, bijvoorbeeld:
- WebManager bewaart een secret per client of per installatie
- backend stuurt dat token mee op elke agent-call
- agent weigert requests zonder geldig token
Voorbeeld:
- `Authorization: Bearer <agent-access-token>`
### Niet doen in V1.1
- open agent-HTTP API zonder auth
- browse/download endpoints publiek bereikbaar maken op het LAN
---
## Virtueel padmodel
Remote client shares krijgen een aparte namespace.
Voorstel voor de gebruikersweergave:
- `/Clients`
- `/Clients/<client-display>`
- `/Clients/<client-display>/<share-label>`
- `/Clients/<client-display>/<share-label>/subdir/file.ext`
Intern moet de backend dit mappen naar:
- `client_id`
- `share_key`
- relatief share-pad
Belangrijk:
- dit zijn logische WebManager-paden
- het zijn geen echte lokale backend filesystem-paden
- ze mogen niet door de bestaande lokale `PathGuard` resolved worden
### Consequentie voor de codebasis
`/Clients/...` moet vroeg in routing worden onderschept door een aparte browse- of source-facade.
Dus:
- niet de lokale `PathGuard` uitbreiden tot remote sources
- niet overal `if remote` in bestaande lokale services strooien
- wel een duidelijke scheiding tussen local source en remote source
---
## Share-validatie in de agent
De agent werkt niet met vrije absolute paden.
De agent heeft een vaste share-map, bijvoorbeeld:
```json
{
"downloads": "/Users/jan/Downloads",
"movies": "/Users/jan/Movies",
"pictures": "/Users/jan/Pictures"
}
```
Een request bevat dan:
- `share = downloads`
- `path = Some/Subdir/file.txt`
Niet:
- `/Users/jan/...`
### Validatieregels
- onbekende `share` weigeren
- `..` weigeren
- pad resolven binnen de gekozen share-root
- symlink escape blokkeren
- alleen toegestane bestandshandelingen toestaan
---
## Read, preview en download limieten
V1.1 moet resource-grenzen expliciet vastleggen.
### Tekstpreview
- maximum grootte voor tekstpreview vastleggen
- voorstel: zelfde orde als huidige server-side preview/edit-limieten, of kleiner
- grote tekstbestanden niet volledig in memory laden voor preview
### Binary versus text
- agent moet tekstpreview alleen teruggeven voor ondersteunde teksttypes
- binaire content mag niet per ongeluk als tekst in JSON-responses worden gepusht
### Download
- downloads moeten gestreamd worden
- geen volledige bestand-buffering in memory
### Image preview
- alleen triviale image preview in V1.1
- geen zware thumbnail-pipeline in deze fase
---
## Offline gedrag
Dit is een harde eis.
WebManager mag niet vastlopen als de agent niet draait.
### Backendregels
- alle agent-calls krijgen korte timeouts, bijvoorbeeld 1-3 seconden
- connect- of timeoutfouten worden vertaald naar nette app-fouten
- offline agent blokkeert nooit globale pagina-initialisatie
- browse- en file-fouten blijven lokaal tot betreffende request
### Frontendregels
- `/Clients` mag laden, ook als sommige clients offline zijn
- offline clients mogen zichtbaar blijven in de lijst
- browsen in offline subtree toont foutmelding
- andere panes blijven bruikbaar
- geen endless spinner
---
## API-ontwerp
## 1. Backend registry endpoints
### `POST /api/clients/register`
Registreert of update een remote agent.
### `POST /api/clients/heartbeat`
Werkt `last_seen` bij.
### `GET /api/clients`
Geeft bekende clients terug met:
- `client_id`
- `display_name`
- `status`
- `last_seen`
- `last_error`
- `shares`
---
## 2. Backend browse facade voor UI
De frontend blijft praten met gewone WebManager-routes.
### `GET /api/browse?path=/Clients`
Geeft alle bekende clients terug als directories.
### `GET /api/browse?path=/Clients/<client>/`
Geeft shares van die client terug als directories.
### `GET /api/browse?path=/Clients/<client>/<share>/...`
Backend vertaalt dit naar een agent-call.
Belangrijk:
- browse facade bepaalt eerst of pad onder `/Clients` valt
- alleen niet-remote paden mogen daarna naar bestaande lokale browse-paths
---
## 3. Agent endpoints
Eenvoudig houden. Geen shell.
### `GET /health`
Gezondheidscheck met auth.
### `GET /api/list?share=downloads&path=subdir`
Directory-inhoud binnen een share.
### `GET /api/info?share=downloads&path=file.txt`
Metadata.
### `GET /api/read?share=downloads&path=file.txt`
Tekstpreview.
### `GET /api/download?share=downloads&path=file.txt`
Gestreamde download.
---
## Haalbaarheid
## Goed haalbaar in V1.1
- client registry
- heartbeat online/offline
- virtuele `Clients` root
- browse
- file info
- tekstpreview
- eenvoudige image preview
- gestreamde download
## Bewust uitgesteld
- rename
- mkdir
- delete
- upload
- bookmarks/startup paths
- cross-source copy
- cross-source move
- unified history
- task-runner integratie
---
## Veranderingen per gebied
## Backend
Nieuwe onderdelen:
- client registry repository
- client registry service
- routes voor register/heartbeat/list
- browse/source facade voor `Clients/...`
- agent HTTP client met harde timeouts en auth
Bestaande onderdelen die waarschijnlijk geraakt worden:
- [routes_browse.py](/workspace/webmanager-mvp/webui/backend/app/api/routes_browse.py)
Om `/Clients` vroeg te routeren.
- [dependencies.py](/workspace/webmanager-mvp/webui/backend/app/dependencies.py)
Voor nieuwe registry- en agent-services.
- [app/main.py](/workspace/webmanager-mvp/webui/backend/app/main.py)
Voor nieuwe routers.
Liever niet verbreden:
- [path_guard.py](/workspace/webmanager-mvp/webui/backend/app/security/path_guard.py)
Deze hoort lokaal filesystemgericht te blijven.
- [file_ops_service.py](/workspace/webmanager-mvp/webui/backend/app/services/file_ops_service.py)
Deze service is nu server-filesystemgericht en moet niet vervuild raken met remote transportlogica.
## Frontend
Waarschijnlijk aanpassen:
- [app.js](/workspace/webmanager-mvp/webui/html/app.js)
Voor:
- extra virtuele root
- render van clients en shares
- offline status
- source-aware browse/view/download/info flows
- [index.html](/workspace/webmanager-mvp/webui/html/index.html)
Alleen als extra statuslabels of clientindicatoren nodig zijn
## Remote agent
Te baseren op:
- [finder_commander/app/main.py](/workspace/webmanager-mvp/finder_commander/app/main.py)
- [finder_commander/run-local.sh](/workspace/webmanager-mvp/finder_commander/run-local.sh)
- [finder_commander/requirements.txt](/workspace/webmanager-mvp/finder_commander/requirements.txt)
Maar vereenvoudigd:
- geen shell command endpoint
- geen hele home-root
- alleen `share key + relatief pad`
- registratie en heartbeat toevoegen
- auth afdwingen op alle endpoints
---
## Minimale agent-config
Voorstel lokaal configbestand:
```json
{
"webmanager_base_url": "https://webmanager.example.com",
"registration_token": "registration-secret",
"agent_access_token": "agent-secret",
"client_id": "f4b2c8f8-2b1b-4d89-9ed2-8d6d7b1f3abc",
"display_name": "MacBook Pro van Jan",
"shares": {
"downloads": "/Users/jan/Downloads",
"movies": "/Users/jan/Movies",
"pictures": "/Users/jan/Pictures"
},
"listen_host": "0.0.0.0",
"listen_port": 8765,
"public_endpoint": "http://192.168.1.25:8765"
}
```
Opmerking:
- `public_endpoint` is het endpoint dat WebManager gebruikt
- `listen_host` en `public_endpoint` hoeven niet identiek te zijn
---
## Open keuzes die bewust zijn uitgesteld
Deze keuzes zijn echt later werk, niet meer V1.1:
- reverse-connect of tunnelmodel
- cross-source copy
- cross-source move
- bookmarks/startup paths voor `/Clients/...`
- write-acties op remote shares
- sterkere pairing of key rotation
---
## Beslisadvies
Aanbevolen implementatievolgorde voor V1.1:
1. agent registry + heartbeat
2. virtuele `Clients` root in browse
3. online/offline status met gescheiden statusvelden
4. browse/info/preview/download voor remote shares
Niet in V1.1:
5. write-acties
6. bookmarks/startup paths
7. cross-source flows
---
## Samenvatting
De juiste V1.1-richting is:
- geen hele homefolder
- wel beperkte shares zoals `Downloads`, `Movies`, `Pictures`
- remote helper-agent op macOS
- agent registreert zichzelf bij WebManager
- WebManager bewaart `client_id`-geleide registry en status
- `/Clients` wordt een aparte virtuele bron
- remote paden blijven buiten lokale filesystem services
- alle agent-calls vereisen auth
- offline agents mogen nooit de rest van WebManager verstoren
Dit model is haalbaar, beperkt in scope, en houdt de bestaande lokale storage-architectuur schoon.