Handle the NIP-67 EOSE tuple shape emitted by newer Parrhesia while keeping compatibility with the previous message format.
Aether
Aether is an external Tribes plugin that adds local social posting and chat to a Tribes node.
It provides:
org.tribe-one.caps.social@1— a local tribe feed backed by Nostr kind1notes.org.tribe-one.caps.chat@1— reusable chat surfaces for public rooms, embeddable context chat, NIP-17 direct messages, and future chat backends.
Features
Local social feed
/aetherrenders 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/chatand/aether/chat/:slugrender standalone chat rooms./aether/chat/embed/:slugrenders 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_nip17backend. - NIP-17 DMs are stored canonically as Parrhesia/Nostr kind
1059giftwrap 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
4DMs 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 TribeOne.TribesPlugin.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:
{
"id": "org.tribe-one.plugins.aether",
"slug": "aether",
"display_name": "Aether",
"version": "0.2.0",
"entry_module": "TribeOne.TribesPlugin.Aether.Plugin",
"host_api": "1",
"otp_app": "tribe_one_aether",
"provides": ["org.tribe-one.caps.social@1", "org.tribe-one.caps.chat@1"],
"requires": ["org.tribe-one.caps.ui@1"],
"assets": {
"global_js": ["aether.js"],
"global_css": ["aether.css"]
},
"migrations": true
}
Runtime contributions from TribeOne.TribesPlugin.Aether.Plugin include:
- nav items for Aether and Chat,
- LiveView pages for
/aetherand/aether/chat, - the
TribeOne.TribesPlugin.Aether.ChatAsh domain, - plugin config schema for chat backend defaults and Marmot UI enablement,
- global JS/CSS assets.
The org.tribe-one.caps.chat@1 provider surface is intentionally small and reusable:
TribeOne.TribesPlugin.Aether.Chat.chat_panel_component/0returns the reusable chat panel component.TribeOne.TribesPlugin.Aether.Chat.recipient_picker_component/0returns the recipient picker.TribeOne.TribesPlugin.Aether.Chat.ensure_context_channel/5creates context-owned chat rooms.TribeOne.TribesPlugin.Aether.Chat.ensure_direct_conversation/4creates a NIP-17 DM projection.TribeOne.TribesPlugin.Aether.Chat.send_message/3,list_conversation_messages/2, andsubscribe_conversation/3dispatch to the selected backend.TribeOne.TribesPlugin.Aether.Chat.embed_path/1,standalone_path/1, andmarmot_path/1expose stable route helpers.
Backend modules implement TribeOne.TribesPlugin.Aether.Chat.Backend:
TribeOne.TribesPlugin.Aether.Chat.Backends.PublicSyncTribeOne.TribesPlugin.Aether.Chat.Backends.NostrNip17TribeOne.TribesPlugin.Aether.Chat.Backends.NostrNip04ReadOnlyTribeOne.TribesPlugin.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:
devenv shell -- plugin validate
devenv shell -- plugin test
devenv shell -- plugin precommit
Outside the devenv shell, use the local wrapper:
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:
cd /path/to/tribes
ln -s /path/to/tribes-plugin-aether plugins/aether
iex --sname dev -S mix phx.server
Then edit this repo normally. The host watches symlinked external plugins and reloads changed Elixir, HEEx, assets, manifests, and migrations.
Assets and hooks
Plugin browser assets are declared in manifest.json and served by the host.
Aether currently uses:
assets/js/aether.jsfor LiveView hooks such as chat auto-scroll,assets/css/aether.cssfor plugin-scoped styles.
External plugin hooks are registered through window.TribesPluginHooks; Phoenix
colocated hooks from external plugin OTP apps are not auto-imported by the host.
Runtime requirements
- A Tribes host checkout/runtime with
org.tribe-one.caps.ui@1. - Parrhesia available through the host for Nostr event storage and streaming.
nakavailable in the runtime path for the current NIP-17/NIP-59/NIP-04 protocol implementation.- Node dependencies under
assets/for browser asset builds.
Release packaging
Guix packaging assembles the plugin artifact from the compiled BEAM output,
priv/, browser assets, and manifest.json. Enable the plugin from the
guix-tribes channel-side node configuration.