# 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 `