Wardian docs

ADR-2610071248: export an app as a .wardian file

Status: Accepted Date: 2026-10-07 Drivers: Every app is a self-contained package, and Wardian imports .wardian and .zip files, but nothing exports one. To share an app today a user must find data/apps/<app> and zip it by hand, and there is no way at all to send an app together with the data it holds.

Context#

SPEC.md 7.2 defines a .wardian file as a zip holding one package folder. Packages are complete on their own: components and arrange.js are copied in (wardian add), capabilities and channels are declared, and no shipped app reaches the network. What an app has accumulated lives outside the package, in the data folder: its storage data and layout (state/, ADR-2610071055), its versions (history/, ADR-2610071122), and soon its SQLite tables (db/, ADR-2610071219). Keys, accounts and permission answers live there too, and must never leave with an app.

A double extension such as .wardian.studio was considered and rejected: operating systems read only the last part (.studio), chat and mail turn it into a link because .studio is a top-level domain, and filters distrust double extensions. wardian.studio stays a name for the brand and the website.

Decision#

  1. One file type, .wardian. It stays a zip with one package folder at its top (SPEC.md 7.2). It gets a registered identity: MIME type application/vnd.wardian+zip, and on macOS the uniform type identifier studio.wardian.package. The server sends that type for .wardian downloads.
  2. A manifest. An exported file carries <package>/.wardian/export.json: {format: 1, package, title, exported_at, wardian_version, includes: {app: true, data: bool}, data: {storage: bool, layout: bool, tables: [{name, rows}]}}. Import reads it to show what is inside before anything is installed. A file without it is an app only, as today.
  3. Export the app. The app page gets Download this app (admin, any source), and the command line wardian export <app> [<file>]. The file holds exactly the files wardian check checks — no .git, target/, node_modules/, history or trash — and the export is refused if the package does not pass the check.
  4. Optionally with its data, off by default. A checkbox "Include my data" adds, under <package>/.wardian/data/: the app's storage data (storage.json), its Arrange layout (layout.json), and its SQLite tables (tables.sqlite, a copy made with SQLite's backup interface so it is consistent). Before the download, Wardian lists what will be included, with sizes and row counts, because data such as a Splunk table can hold things not meant to be shared.
  5. Never included: keys and accounts (Anthropic, Bedrock, Splunk, Google), permission answers, history, the trash, and other apps' data or tables.
  6. Import shows what it is. Importing a .wardian file shows its manifest: the app's title, what it may use (capabilities and channels, as in "Sealed"), and, when it holds data, what data. Data is installed only when the user ticks "Also install its data"; it then replaces this Wardian's data for that app, and the previous app and data become a version in History, so it can be undone. Permissions are never imported: the receiver is asked as usual.

Consequences#

Implementation#

Gate: cargo build --release && cargo test --release && hexa analyze . --grade A, with tests that an export passes wardian check, holds no key or permission answer even when every setting is filled, excludes history and build output, and round-trips: export with data from one Wardian, import into a fresh one, and the app opens with the same data and tables, its permissions asked again.

Enforced-By: hexa adr gates (run on demand)#

Gate#

env CARGO_TARGET_DIR=target/verify cargo test --release export

Rerun by hexa adr gates. It builds into target/verify, never into the copy of Wardian a user runs. tests/run-export-e2e.sh checks the same in a browser, between two Wardians.

Notes from the build#

References#

Evidence#

bash -c 'cargo test --release 2>&1 | grep -E "export|test result: ok. [1-9]"; hexa analyze . --grade A 2>&1 | grep -E "Architecture grade|coverage"' at 1882d37 with uncommitted changes on 2026-10-07 18:25 UTC:

test domain::export::tests::exports_what_check_reads ... ok
test domain::export::tests::manifests_round_trip_and_are_checked ... ok
test tests::export_with_data_round_trips_and_never_carries_secrets ... ok
test result: ok. 38 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.47s
  ⬡ Architecture grade: A+ — score 100/100
    coverage 46/46 files in a layer

This page is docs/adrs/ADR-2610071248-export-an-app-as-a-wardian-file.md in the repository. Something wrong or missing? Change that file.