Sample browser-based Pryv account backup web app — companion to @pryv/account-backup (the CLI + library). Operator-hosted; subjects log in, click Start backup, and download a series of ZIP files containing their portable account dump.
A minimal reference UI for the subject side of a Pryv DSAR (Data Subject Access Request) flow. The webapp does not implement the backup logic itself — it uses the browser-isomorphic resource fetchers exported by @pryv/account-backup (api-resources, events-chunked, audit-as-events, accesses-history, attachments, hf-data, webhooks-export).
Implementers are expected to fork and rebrand. Total runtime size is ~350 LOC of vanilla JS + one CSS file; no framework, no build templating beyond esbuild's bundle step.
Each backup run produces a series of ZIP files (default 100 MB each; configurable in the UI). The ZIPs together contain:
account.json,streams.json,accesses.json,accesses-all.json(deletions + expired),profile_private.json,profile_public.jsonevents-YYYY-MM.json(monthly chunks, initial run) ORevents-incremental-<TS>.json(subsequent runs, only eventsmodified > lastRun)audit_logs.json(fetched via the standard events API on the:_audit:*store streams)app_profiles/profile_app_<accessId>.json(one perapp-type access)accesses-history/<accessId>.json(opt-in via the Advanced section)attachments/<eventId>_<fileName>(opt-in via the Advanced section) — binary streamshf-data/<eventId>.json(one perseries:*event, opt-out via the Advanced section) — HFS data pointswebhooks.json(opt-out via the Advanced section) — aggregated byaccessIdsync-state.json(always written) — portable kv state to drive cross-session incremental on the next visitbackup-index.json(in the last ZIP — cross-ZIP file directory; restore reads this to learn which ZIP carries which file)
Not included by the webapp (use the CLI flavor for these):
- Per-file sha256 integrity manifest (
manifest.json) — the auditor's tamper-evidence story is CLI-only
localStorage carries the operational store during a run (kv + per-category work refs). After a clean run, the orchestrator exports a portable sync-state.json (kv state only) into the final ZIP. The subject keeps it alongside the ZIPs.
On the next visit: the pre-login UI scans localStorage for prior states and displays a status panel — Tool version, Last run at, Events fetched up to, Audit fetched up to, Pending refs (carry-over from an interrupted prior run; re-discovered automatically) + a Reset button per saved state. The subject can also upload their previously downloaded sync-state.json via the Resume from a prior sync-state.json (optional) file picker — useful when localStorage was cleared, a different browser is used, or the run is being performed from a different device. The orchestrator imports the kv state at login; the next run issues a single events?modifiedSince=… round-trip instead of the full chunked refetch.
Full schema: see docs/sync-state.md in the library repo.
Prerequisites: Node 18+.
git clone https://github.com/pryv/pryv-account-backup-webapp.git
cd pryv-account-backup-webapp
npm install
npm run build # produces dist/Serve dist/ from any static HTTPS server on the same origin as the Pryv API (or with CORS configured). For production, copy dist/ to your web server's document root. The webapp is fully static; no backend required.
npm run serve # https://backup.backloop.dev:4443/The dev server is fronted by backloop.dev — any *.backloop.dev subdomain resolves to localhost over HTTPS with a real signed certificate. This is required because the webapp talks to remote HTTPS Pryv APIs (reg.pryv.me, operator-hosted cores); browsers block mixed-content from an http://localhost page, and CORS preflight from a non-HTTPS origin fails. The dev URL prefix is configurable via BACKLOOP_SUBDOMAIN + BACKLOOP_PORT env vars.
The subject's username + password are submitted via the form. Service.login (from pryv lib-js) calls the Pryv API on the same origin, returns a personal token, and the webapp uses that token for every subsequent fetch. Operator hosts the webapp on a domain the subject already trusts (same-origin as the Pryv API endpoint) so the address bar shows the subject's normal Pryv hostname.
The webapp does not handle MFA challenges. If a subject has MFA enabled, point them at the CLI version of the backup tool — the CLI inherits MFA handling from lib-js's Service.login SMS-challenge flow.
Operator security note: the backup bundle includes profile_private.json, which carries profile.mfa.recoveryCodes (10 SMS-bypass tokens) when MFA is enabled. Treat the downloaded files as sensitive — transport over a secure channel; consider rotating MFA recovery codes after the disclosure is complete. This is by-design (the subject is entitled to their full MFA state) but worth flagging in your subject-facing documentation.
The whole UI is in src/:
index.html— four screens (login + state panel / progress / done / error)style.css— minimal styling; rebrand by editing the CSS custom properties at the topsrc/app.js— orchestrator + UI controller (~350 LOC)src/lib/LocalStorageStateStore.js— operational store (kv state + per-category work refs + portable export/import) matchingFolderStateStorein the CLI librarysrc/lib/BrowserBlobZipStorageWriter.js— accumulates files into in-memory ZIPs, downloads via<a download>at the configured threshold
BSD-3-Clause. See LICENSE.