diff --git a/app-stacks/README.md b/app-stacks/README.md new file mode 100644 index 0000000..64c6efc --- /dev/null +++ b/app-stacks/README.md @@ -0,0 +1,263 @@ +# Zapstore App Stacks — Privacy Review + +A systematic privacy and security review of apps in the [Zapstore](https://zapstore.dev) catalog. Each app is cloned from its source repository, scanned for tracking SDKs, permissions, and privacy practices, then assigned a verdict. Approved apps are organized into curated stacks for publication to the Zapstore relay. + +## Project Structure + +``` +. +├── README.md # This file +├── .gitignore +├── plans/ # Planning documents and methodology +│ ├── app-review-methodology.md +│ ├── proposed-categories.md +│ └── review-todo.md +├── scripts/ # Python scripts for review and publishing +│ ├── review_apps.py # Main review script (SSH-based) +│ ├── generate_stacks.py # Map apps to categories, generate stack events +│ ├── publish_stack.py # Sign and publish stacks to relay +│ └── ... +├── data/ # Review results and app data (JSON) +│ ├── review_results_final.json # All 280 apps with verdicts +│ ├── apps_data.json # Zapstore catalog data +│ └── ... +├── reports/ # Human-readable reports (Markdown) +│ ├── review_report_by_category.md # Full category-by-category report +│ ├── final_report.md # Top 2 apps per category +│ └── ... +└── stacks/ # Generated Nostr stack events (kind 30267) + ├── all_stacks.json # All 20 stacks in one file + └── ... +``` + +## Review Results + +| Verdict | Count | Meaning | +|---------|-------|---------| +| ✅ **APPROVED** | **175** | No tracking SDKs, minimal/justified permissions, open source | +| ⚠️ **FLAGGED** | **36** | Contains tracking SDKs or excessive permissions | +| ⏭️ **SKIPPED** | **41** | Could not clone repository (no mirror, no GitHub URL) | +| ❓ **UNCLEAR** | **28** | Needs human investigation (app not in catalog, auth required) | + +## 20 Curated App Stacks + +These stacks are ready to publish to the Zapstore relay as kind-30267 events. + +### 1. Bitcoin & Lightning Wallets +Self-custodial Bitcoin and Lightning wallet apps. +- ZEUS, Nunchuk, Cake Wallet, Alby Go, Blitz Wallet, BlueWallet, Electrum, Oubli, MercaSats, Lightning Reaction + +### 2. Nostr Clients +Nostr-native social and communication clients. +- Amethyst, Amber, Wisp, Dark Wisp, White Noise, Nospeak, Nostrord, Shosho, TravelTelly, Nmail, Divine, YakiHonne, Nests, Nostria, Zapstore Alpha, PearCal, PearCircle, Ditto, Flotilla + +### 3. Secure Messaging +End-to-end encrypted messaging and email apps. +- SimpleX, Conversations, Delta Chat, aTalk, Element, Element X, Thunderbird, FairEmail, Tuta, Quiet, SpamBlocker, Deku SMS + +### 4. VPN & Privacy Tools +VPN clients, firewalls, and network privacy tools. +- Orbot, Mullvad VPN, Tailscale, AmneziaVPN, Proton VPN, Rethink, PCAPdroid, WG Tunnel, ByeDPI, NeoStumbler, SD Maid, InviZible Pro, Private DNS Quick Setting + +### 5. Password Managers & Auth +Password managers, 2FA authenticators, and identity tools. +- Bitwarden, KeePassDX, Aegis, Authnkey, Ente Auth, Keep, AliasVault, PassVault, LibreFind + +### 6. Maps & Navigation +Offline maps, navigation, and location tools. +- Organic Maps, OsmAnd~, StreetComplete, CoMaps, OwnTracks, OSMTracker + +### 7. Media Players & Streaming +Video and music players, streaming clients. +- VLC, NewPipe, PipePipe, Auxio, Musify, Metrolist, FreeTube, Zaptrax, Zappix, mpvEx, KurobaEx + +### 8. Productivity & Notes +Note-taking, task management, and productivity tools. +- Flux, Notesnook, Quillpad, Saber, DAVx⁵, Super Productivity, Trilium Notes, SilentNotes, Manent, Meiso, Florid, Plektos, Urn, Screen Time, Grit, Numo, timeto.me, Converter NOW + +### 9. File Management & Cloud Sync +Cloud storage, file sync, and file management tools. +- Nextcloud, Syncthing-Fork, Seafile, File transfer, primitive ftpd, Paperless Mobile, GitSync, wormhole, SFTP Documents Provider, OSS Document Scanner + +### 10. Browsers +Privacy-focused web browsers. +- DuckDuckGo, Cromite + +### 11. Social Media +Federated and alternative social media clients. +- Mastodon, Nekogram, Infinity+, NewsBlur, Claw, Polymarket Viewer + +### 12. Finance & Budgeting +Personal finance, expense tracking, and budgeting. +- Flow, Pennywise AI Tracker, Dev Stocks Widget, Seeker, PearGuard, Mostro, Shopt + +### 13. Health & Fitness +Health tracking, diet, and fitness apps. +- Daily Dozen, Screen Time, Astronia + +### 14. Education & Reference +Learning, dictionary, and reference tools. +- freeCodeCamp, freeDictionary, Ciyue, Keyman, NeverTooManyBooks, Calibre Web Companion, Repertoire, ListenBrainz, CPU Info, microMathematics Plus, Mental Math + +### 15. Photography & Image Tools +Camera, photo editing, scanning, and gallery apps. +- Image Toolbox, FairScan, PhotoPrism, PicGuard, Gallery apps, YTDLnis, ElCaju, Espy, PDF Wallet + +### 16. Utilities & Tools +System utilities, converters, and general-purpose tools. +- Binary Eye, CPU Info, Converter NOW, Catima, BT Remote, Lawnicons, Peristyle, BinEd, MMRL, Canta, Amarok, ServerBox, wX, Mako, Rush, HeliBoard, DeskClock, Redomi, Scrobble, Feeder, SMS2Email, Inure, Unciv + +### 17. Games +Open source games across genres. +- Shattered Pixel Dungeon, Wesnoth, Feudal Tactics, Burger Party, ChipDefense, Roboyard, Breakout 71, Damas Clash, Unciv + +### 18. Communication (Non-Nostr) +Voice/video calls, remote desktop, and messaging. +- Telegram, RustDesk, Sideband, Meshtastic + +### 19. Development Tools +Code editors, Git clients, and developer tools. +- Acode, GitSync, BinEd, freeCodeCamp, GitHub Store, Kai 9000 + +### 20. Calendar & Scheduling +Calendar apps, scheduling, and time management. +- Calendar by Form*, PearCal, timeto.me, Sidestep + +## Review Methodology + +Each app is reviewed using a structured process: + +1. **Clone** from local Gitea mirror (or GitHub fallback) +2. **Scan AndroidManifest.xml** for requested permissions +3. **Check build.gradle\*** for actual dependency declarations (distinguishing `implementation` vs `playImplementation` vs `compileOnly`) +4. **Verify API calls** — search for actual SDK method invocations (not just dependency names) +5. **Check manifest metadata** that disables analytics +6. **Check build flavors** (Google Play vs F-Droid) +7. **Determine verdict**: ✅ APPROVED or ⚠️ FLAGGED + +See [`plans/app-review-methodology.md`](plans/app-review-methodology.md) for full details. + +## Publishing + +Stack events (kind 30267) are published to `wss://relay.zapstore.dev` using the [`publish_stack.py`](scripts/publish_stack.py) script, which signs via `qrexec` (Qubes OS `nostr_signer`). + +## Nostr Kinds Reference + +The following Nostr event kinds are relevant to this project: + +| Kind | Name | Usage | Status | +|------|------|-------|--------| +| **32267** | Software Application | The app listing event published by developers. Identified by `d` tag (Android package name) and publisher pubkey. Referenced in stacks via `a` tags as `32267::`. | Core data source | +| **30267** | App curation set | Curated collections of apps (stacks). We publish privacy-approved stacks as kind 30267 events with `a` tags referencing each approved app. | Used for output | +| **30078** | App-specific data | Generic parameterized replaceable event. Already used by Zapstore for device state, bookmarks, etc. Candidate for storing per-app review results from LLM agents. | Planned for reviews | +| **3063** | Software Asset | APK metadata including hash, size, version code, platform. Used to track current version info for reviewed apps. | Used for version tracking | +| **30063** | Release artifact set | Group of artifacts for a software release. Links to kind 3063 assets and kind 32267 app events. | Reference | +| **1986** | Relay reviews | Existing review kind, but scoped to Nostr relays rather than software apps. | Not applicable | + +See [`references/registry-of-kinds/schema.yaml`](references/registry-of-kinds/schema.yaml) for the full kinds registry and [`references/nips/51.md`](references/nips/51.md) for list/set definitions. + +## Kind 30078 Event Data Model + +We use **kind 30078** (App-specific data) events for two purposes: app definitions and app reviews. The frontend queries the Nostr relay directly for these events on page load, rather than reading a static file. + +### App Definition Events + +Each app in the catalog is defined by a kind 30078 event with `#t: app-definition`. These are published when an app is added via the PHP backend. + +```jsonc +{ + "kind": 30078, + "content": "{\"name\":\"Amethyst\",\"identifier\":\"com.vitorpamplona.amethyst\",\"repository\":\"https://github.com/vitorpamplona/amethyst\",\"description\":\"...\"}", + "tags": [ + ["d", "app-com.vitorpamplona.amethyst"], + ["t", "app-definition"], + ["t", "nostr-clients"], // ← category tag + ["name", "Amethyst"], + ["repository", "https://github.com/vitorpamplona/amethyst"], + ["gitea", "com.vitorpamplona.amethyst"], + ["url", "https://cdn.zapstore.dev/..."], + ["f", "android-arm64-v8a"], + ["published_at", "1729302793"] + ] +} +``` + +### App Review Events + +Each model's review of an app is stored as a kind 30078 event with `#t: app-review`. The review is pinned to a specific version via SHA256 hash. + +```jsonc +{ + "kind": 30078, + "content": "{\"verdict\":\"APPROVED\",\"level\":1,\"summary\":\"...\",\"findings\":{...}}", + "tags": [ + // Unique ID: model + app identifier + version + ["d", "review-deepseek/deepseek-v4-flash-com.vitorpamplona.amethyst-v1.2.3"], + + // Reference to the app definition event + ["a", "30078::app-com.vitorpamplona.amethyst", "wss://relay.zapstore.dev"], + + // The SPECIFIC asset/APK that was reviewed (kind 3063 event) + ["e", "", "wss://relay.zapstore.dev"], + + // Version info for querying + ["version", "1.2.3"], + ["version_code", "1234"], + + // SHA256 hash of the reviewed APK — cryptographic pin + ["x", "a1b2c3d4e5f6..."], + + // Review metadata + ["model", "deepseek/deepseek-v4-flash"], + ["model_name", "DeepSeek Flash 4"], + ["verdict", "APPROVED"], + ["level", "1"], + ["p", ""], + ["t", "app-review"] + ] +} +``` + +### Tag Reference + +| Tag | Event Type | Purpose | +|-----|-----------|---------| +| `d` | Both | Unique identifier (`app-{identifier}` or `review-{model}-{identifier}-v{version}`) | +| `t` | Both | `app-definition` or `app-review` — also carries category for app definitions | +| `name` | App definition | Human-readable app name | +| `repository` | App definition | Source code URL | +| `gitea` | App definition | Gitea mirror identifier | +| `url` | App definition | APK download URL | +| `f` | App definition | Platform (e.g. `android-arm64-v8a`) | +| `a` → kind 30078 | App review | Reference to the app definition event | +| `e` → kind 3063 | App review | The specific APK asset that was reviewed | +| `x` | App review | SHA256 hash — cryptographic pin of the reviewed binary | +| `version` / `version_code` | App review | Version info | +| `model` | App review | LLM model string (e.g. `deepseek/deepseek-v4-flash`) | +| `verdict` | App review | Review verdict | +| `level` | App review | Privacy level (1-4) | +| `p` | App review | Agent pubkey who performed the review | + +### Page Load Flow + +``` +Page opens + → Queries relay for kind 30078, #t: app-definition + → Groups apps by #t category tags → builds categories + → Queries relay for kind 30078, #t: app-review + → Matches reviews to apps via a tags + → Renders categories + apps + per-model review status +``` + +### Version Pinning Flow + +``` +App v1.2.3 defined (kind 30078, #t: app-definition) + └── APK asset (kind 3063, SHA256: abc...) + └── Review (kind 30078, #t: app-review, pinned via x tag) + +App v1.2.4 published (kind 3063, SHA256: def...) + └── NEW review needed — old review's SHA256 doesn't match +``` + +If a bad actor modifies the app in v1.2.4, the review for v1.2.3 (with its SHA256 `abc...`) does not carry over. A new review must be performed on the new binary. diff --git a/app-stacks/plan.md b/app-stacks/plan.md new file mode 100644 index 0000000..5dc7df8 --- /dev/null +++ b/app-stacks/plan.md @@ -0,0 +1,149 @@ +# App Stacks — Forward Plan + +## Current State + +[`www/app-stacks.html`](../www/app-stacks.html) has: +- A publish form that takes a Gitea URL + category and publishes a kind 30078 app-definition event +- A subscription that displays received app-definition events grouped by category + +## New Understanding + +- **Categories are actually app stacks** — kind 30267 events (App curation set) +- An **app stack** is a curated collection of apps, e.g. "Nostr Clients", "Bitcoin & Lightning Wallets" +- An **app definition** (kind 30078) belongs to an app stack, not a free-text category +- We need two separate publish flows: + 1. **Create an App Stack** — publishes a kind 30267 event + 2. **Publish App Definition** — selects an existing app stack to belong to + +## Nostr Event Types + +### App Stack (kind 30267) +```json +{ + "kind": 30267, + "content": "{\"name\":\"Nostr Clients\",\"description\":\"Nostr-native social and communication clients.\"}", + "tags": [ + ["d", "nostr-clients"], + ["t", "app-stack"], + ["name", "Nostr Clients"], + ["description", "Nostr-native social and communication clients."], + // References to apps in this stack: + ["a", "30078::app-com.vitorpamplona.amethyst", "wss://relay.zapstore.dev"], + ["a", "30078::app-com.example.other", "wss://relay.zapstore.dev"] + ] +} +``` + +### App Definition (kind 30078) — updated +```json +{ + "kind": 30078, + "content": "{\"name\":\"Amethyst\",\"identifier\":\"com.vitorpamplona.amethyst\",\"repository\":\"...\",\"description\":\"\"}", + "tags": [ + ["d", "app-com.vitorpamplona.amethyst"], + ["t", "app-definition"], + ["t", "nostr-clients"], // ← references the app stack's d tag + ["name", "Amethyst"], + ["repository", "https://github.com/vitorpamplona/amethyst"], + ["gitea", "com.vitorpamplona.amethyst"] + ] +} +``` + +## Implementation Phases + +### Phase 1: App Stack CRUD + +**Goal:** Create, list, and display app stacks (kind 30267 events). + +**Changes to [`www/app-stacks.html`](../www/app-stacks.html) (JS only):** + +1. **New subscription** — subscribe to kind 30267 with `#t: app-stack`: + ```javascript + subscribe({ kinds: [30267], '#t': ['app-stack'], limit: 200 }, ...); + ``` + +2. **New function: `parseAppStack(evt)`** — parse kind 30267 events into `{ dTag, name, description, appRefs: [], eventId }` + +3. **New function: `renderStacks()`** — display app stacks in a section above or alongside app definitions + +4. **New function: `publishAppStack(name, description)`** — create and publish a kind 30267 event: + ```javascript + { + kind: 30267, + content: JSON.stringify({ name, description }), + tags: [ + ['d', name.toLowerCase().replace(/\s+/g, '-')], + ['t', 'app-stack'], + ['name', name], + ['description', description], + ], + created_at: Math.floor(Date.now() / 1000), + } + ``` + +5. **New function: `showCreateStackForm()`** — form with name and description fields + +6. **Update `renderApps()`** — add a "Create App Stack" button/area + +### Phase 2: Link App Definitions to Stacks + +**Goal:** App definitions select an app stack instead of typing a free-text category. + +**Changes to [`www/app-stacks.html`](../www/app-stacks.html) (JS only):** + +1. **Update publish form** — replace the free-text category input with a `