# Remote Client Shares V1.1 Design ## 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 --- ## 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 ` ### 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 ` ### 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/` - `/Clients//` - `/Clients///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//` Geeft shares van die client terug als directories. ### `GET /api/browse?path=/Clients///...` 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.