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.
This commit is contained in:
2026-05-25 17:20:26 +02:00
parent 446fffcadc
commit 341dcb573f
3 changed files with 128 additions and 109 deletions
+125 -106
View File
@@ -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/<otp_app>` 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.
+2 -2
View File
@@ -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",
+1 -1
View File
@@ -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,