From 341dcb573f918e979857cf38443ed80a72a4e9cd Mon Sep 17 00:00:00 2001 From: Steffen Beyer Date: Mon, 25 May 2026 17:20:26 +0200 Subject: [PATCH] docs: update Aether README for chat APIs Document the current social and chat feature set, NIP-17/NIP-04 behavior, runtime requirements, and plugin API surface. Remove obsolete template content and bump the plugin version to 0.2.0. --- README.md | 231 +++++++++++++++++++++++++++----------------------- manifest.json | 4 +- mix.exs | 2 +- 3 files changed, 128 insertions(+), 109 deletions(-) diff --git a/README.md b/README.md index fb88e1a..1d95e8e 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,105 @@ # Aether -`aether` is an external Tribes plugin that provides the `social@1` -capability. +Aether is an external Tribes plugin that adds local social posting and chat to a +Tribes node. -## Getting Started +It provides: -Install deps and run the test suite: +- `social@1` — a local tribe feed backed by Nostr kind `1` notes. +- `chat@1` — reusable chat surfaces for public rooms, embeddable context chat, + NIP-17 direct messages, and future chat backends. -```bash -mix deps.get -scripts/plugin test +## Features + +### Local social feed + +- `/aether` renders the local tribe feed. +- Signed-in users can publish Nostr notes with their Tribes identity. +- Feed subscriptions use the host/Parrhesia Nostr event stream. + +### Public and embeddable chat + +- `/aether/chat` and `/aether/chat/:slug` render standalone chat rooms. +- `/aether/chat/embed/:slug` renders an embeddable chat panel for other plugins + such as Sender livestream pages. +- Public chat uses Aether Ash resources as the synced message projection. + +### Direct messages + +- Signed-in users can start a DM with another local tribe user from the chat + recipient picker. +- New direct conversations default to the `:nostr_nip17` backend. +- NIP-17 DMs are stored canonically as Parrhesia/Nostr kind `1059` giftwrap + events and decrypted into local UI message structs at read time. +- Sender and recipient copies are published so both sides can read the thread. +- Legacy NIP-04 kind `4` DMs are supported as a read-only import/decrypt path. + +Current signing model: Aether uses the existing Tribes session-unlocked Nostr +private key. This produces real NIP-17/NIP-59 and NIP-04 protocol artifacts, but +it is still a server-trusting web model, not browser-only/non-custodial E2EE. +The protocol operations are isolated behind `Aether.Chat.Nostr.Nak` so a native +Elixir implementation or NIP-07/NIP-46 signer can replace it later. + +### Marmot scaffold + +A Marmot backend scaffold and UI route exist for future MLS group chat work, but +browser transport, storage, signing, and message rendering are not active yet. +The package pin is kept at `@internet-privacy/marmot-ts@0.5.1`. + +## Plugin contract and API surface + +`manifest.json` declares: + +```json +{ + "name": "aether", + "version": "0.2.0", + "entry_module": "Tribes.Plugins.Aether.Plugin", + "host_api": "1", + "otp_app": "aether", + "provides": ["social@1", "chat@1"], + "requires": ["ui@1"], + "assets": { + "global_js": ["aether.js"], + "global_css": ["aether.css"] + }, + "migrations": true +} ``` -If you are using the shared `tribes` devenv, run the plugin through the local `plugin` helper instead: +Runtime contributions from `Tribes.Plugins.Aether.Plugin` include: + +- nav items for Aether and Chat, +- LiveView pages for `/aether` and `/aether/chat`, +- the `Aether.Chat` Ash domain, +- plugin config schema for chat backend defaults and Marmot UI enablement, +- global JS/CSS assets. + +The `chat@1` provider surface is intentionally small and reusable: + +- `Aether.Chat.chat_panel_component/0` returns the reusable chat panel component. +- `Aether.Chat.recipient_picker_component/0` returns the recipient picker. +- `Aether.Chat.ensure_context_channel/5` creates context-owned chat rooms. +- `Aether.Chat.ensure_direct_conversation/4` creates a NIP-17 DM projection. +- `Aether.Chat.send_message/3`, `list_conversation_messages/2`, and + `subscribe_conversation/3` dispatch to the selected backend. +- `Aether.Chat.embed_path/1`, `standalone_path/1`, and `marmot_path/1` expose + stable route helpers. + +Backend modules implement `Aether.Chat.Backend`: + +- `Aether.Chat.Backends.PublicSync` +- `Aether.Chat.Backends.NostrNip17` +- `Aether.Chat.Backends.NostrNip04ReadOnly` +- `Aether.Chat.Backends.Marmot` + +These are plugin-local today. If other plugins need the same primitives, the +likely host/plugin API candidates are a stable Nostr event publish/query service +and a stable session/external signer interface. + +## Development + +Use the plugin-aware commands from the repo devenv shell: ```bash devenv shell -- plugin validate @@ -20,118 +107,50 @@ devenv shell -- plugin test devenv shell -- plugin precommit ``` -## Development +Outside the devenv shell, use the local wrapper: -For local development alongside a Tribes checkout: +```bash +scripts/plugin validate +scripts/plugin test +scripts/plugin precommit +``` + +Plain `mix test` and `mix precommit` are not the normal entrypoints; this plugin +suite is host-backed and expects the Tribes test environment. + +For local development alongside a Tribes checkout, symlink this repo into the +host plugin directory: ```bash -# Symlink into the host plugins directory once cd /path/to/tribes ln -s /path/to/tribes-plugin-aether plugins/aether - -# Start the Tribes dev server iex --sname dev -S mix phx.server ``` -Then edit `aether` in its own repo. In development, the Tribes host watches -symlinked external plugins and automatically: +Then edit this repo normally. The host watches symlinked external plugins and +reloads changed Elixir, HEEx, assets, manifests, and migrations. -- runs `mix compile` for Elixir, HEEx, manifest, and migration changes -- runs `npm run build --prefix assets` -- reloads the plugin in the running Tribes VM -- triggers a Phoenix browser reload after the rebuild finishes +## Assets and hooks -That means the normal loop is: +Plugin browser assets are declared in `manifest.json` and served by the host. +Aether currently uses: -```bash -cd /path/to/tribes-plugin-aether -mix deps.get +- `assets/js/aether.js` for LiveView hooks such as chat auto-scroll, +- `assets/css/aether.css` for plugin-scoped styles. -# in another terminal -cd /path/to/tribes -iex --sname dev -S mix phx.server -``` +External plugin hooks are registered through `window.TribesPluginHooks`; Phoenix +colocated hooks from external plugin OTP apps are not auto-imported by the host. -Inside the plugin repo's devenv shell, the `plugin` helper forwards to the host devenv automatically: +## Runtime requirements -```bash -plugin validate -plugin test -plugin precommit -``` +- A Tribes host checkout/runtime with `ui@1`. +- Parrhesia available through the host for Nostr event storage and streaming. +- `nak` available in the runtime path for the current NIP-17/NIP-59/NIP-04 + protocol implementation. +- Node dependencies under `assets/` for browser asset builds. -Use `plugin test` / `plugin precommit` for host-backed tests. Plain `mix test` -and `mix precommit` print this guidance; use `mix raw_test` / -`mix raw_precommit` only for deliberate low-level debugging after setting the -same host environment yourself. - -## Project Structure - -``` -aether/ -├── manifest.json -├── mix.exs -├── lib/ -│ ├── aether/ -│ │ ├── application.ex -│ │ └── plugin.ex -│ └── aether_web/ -│ └── live/ -├── assets/ -│ ├── css/aether.css -│ ├── js/aether.js -│ ├── package.json -│ └── package-lock.json -├── priv/ -└── test/ -``` - -## Manifest - -`manifest.json` declares the plugin contract: - -```json -{ - "name": "aether", - "version": "0.1.0", - "entry_module": "Tribes.Plugins.Aether.Plugin", - "host_api": "1", - "otp_app": "aether", - "provides": ["social@1"], - "requires": ["ui@1"], - "enhances_with": [], - "assets": { - "global_js": ["aether.js"], - "global_css": ["aether.css"] - } -} -``` - -See the host plugin docs in [PLUGINS.md](https://github.com/your-org/tribes/blob/master/docs/PLUGINS.md) -for the full manifest and runtime contract. - -## LiveView Hooks - -External plugins register browser hooks from JS bundles listed in -`manifest.json` `assets.global_js`: - -```javascript -window.TribesPluginHooks = window.TribesPluginHooks || {}; -window.TribesPluginHooks.AetherHook = { - mounted() {} -}; -``` - -The host loads plugin JS before connecting LiveSocket and does not auto-import -external plugin `phoenix-colocated/` modules into its `app.js` bundle. - -## Building for Release - -```bash -MIX_ENV=prod mix compile -npm run build --prefix assets -``` +## Release packaging Guix packaging assembles the plugin artifact from the compiled BEAM output, -`priv/`, and `manifest.json`. Enable it from the `guix-tribes` channel-side node -configuration. +`priv/`, browser assets, and `manifest.json`. Enable the plugin from the +`guix-tribes` channel-side node configuration. diff --git a/manifest.json b/manifest.json index 5c4bc68..c97edf9 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "name": "aether", - "version": "0.1.0", - "description": "TODO: Describe what this plugin does", + "version": "0.2.0", + "description": "Local social feed and chat for Tribes, including NIP-17 direct messages.", "entry_module": "Tribes.Plugins.Aether.Plugin", "host_api": "1", "otp_app": "aether", diff --git a/mix.exs b/mix.exs index 4477981..7f0a37c 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Aether.MixProject do def project do [ app: :aether, - version: "0.1.0", + version: "0.2.0", elixir: "~> 1.18", elixirc_paths: elixirc_paths(Mix.env()), start_permanent: Mix.env() == :prod,