Summary
Round out the user-rights trio (alongside #351 self-service password change and #352 self-service account deletion) with a way for an authenticated user to download all their data as a single archive.
This is the export side of the same picture: an end user should be able to walk away with what they own without operator help and without shell access.
Current state
Proposed shape
Endpoint
GET /idp/account/export
Authorization: <existing session / DPoP>
Accept: application/x-tar+gzip
→ 200
Content-Type: application/x-tar+gzip
Content-Disposition: attachment; filename="jss-export-<webid-slug>-<isodate>.tar.gz"
<streamed tar.gz>
- Owner-only — caller's WebID must match the account's WebID.
- Streamed (no temp file, no full pod load into memory) so a multi-GB pod doesn't OOM the server.
- 401 unauthenticated; 403 cross-account.
Archive contents
jss-export/
├── manifest.json # webId, username, email, createdAt, exportedAt, jss version
├── account.json # full account record minus passwordHash
└── pod/ # pod tree, exact filesystem layout including dotfiles
├── .acl
├── profile/card.jsonld
├── public/...
└── ...
manifest.json lets a future "import to a fresh JSS" path reconstruct enough to get a usable pod.
UI
A "Download my data" button on the /idp/account settings page. Single click → triggers the streamed download. No password re-entry needed (this is a read, not a destructive action — auth is sufficient).
Out of scope
- Re-import — separate feature, separate issue when there's demand. Decoupling export from import keeps this issue small.
- Cross-server pod migration (Solid pod portability) — bigger spec problem, separate.
- Periodic / scheduled backups — operator concern, not a per-user right.
- Per-resource selection / partial export — defer until someone asks. The 80% case is "give me everything".
Acceptance
- New endpoint streams a tar.gz of the user's pod data plus an
account.json and manifest.json.
- Authenticated owner gets 200 + the archive.
- Unauthenticated → 401; cross-account attempt → 403.
- Output is a valid tar.gz —
tar -tzf lists all expected files; tar -xzf reconstructs the tree exactly (including dotfiles like .acl, .meta, .quota.json).
manifest.json includes WebID, username, email, account createdAt, server-side exportedAt timestamp, and the JSS version that produced the archive.
passwordHash is not included in account.json (security — bcrypt is per-server, no portability value, and a leaked dump would let an attacker brute-force).
- New
/idp/account UI exposes a "Download my data" button that triggers the export.
- Tests cover happy path, the two auth failure modes, dotfile preservation, and a roundtrip extraction sanity check.
Summary
Round out the user-rights trio (alongside #351 self-service password change and #352 self-service account deletion) with a way for an authenticated user to download all their data as a single archive.
This is the export side of the same picture: an end user should be able to walk away with what they own without operator help and without shell access.
Current state
<root>/<podName>/.... With Single-user mode: pod should be served at server root, not /me/ #348 the pod root may also coincide with the data root for single-user installs.~/.idp/accounts/<id>.json.Proposed shape
Endpoint
Archive contents
manifest.jsonlets a future "import to a fresh JSS" path reconstruct enough to get a usable pod.UI
A "Download my data" button on the
/idp/accountsettings page. Single click → triggers the streamed download. No password re-entry needed (this is a read, not a destructive action — auth is sufficient).Out of scope
Acceptance
account.jsonandmanifest.json.tar -tzflists all expected files;tar -xzfreconstructs the tree exactly (including dotfiles like.acl,.meta,.quota.json).manifest.jsonincludes WebID, username, email, account createdAt, server-side exportedAt timestamp, and the JSS version that produced the archive.passwordHashis not included inaccount.json(security — bcrypt is per-server, no portability value, and a leaked dump would let an attacker brute-force)./idp/accountUI exposes a "Download my data" button that triggers the export.