Parley

Parley is een chatnetwerk zonder centraal punt. Elke persoon (of team) draait een kleine instantie voor hun eigen domein. Instanties vinden elkaar via DNS en 'well-known' identiteitsdocumenten, wisselen ondertekende berichten uit via HTTPS en presenteren het gehele gefedereerde netwerk aan gewone IRC-clients zoals irssi, WeeChat of Textual, zonder dat daar plugins voor nodig zijn.

Identiteiten zien eruit als e-mailadressen: alice@foo.com draait op foo.com, bob@bar.com op bar.com. Als Bob /msg alice@foo.com hi typt, werkt dit direct, zelfs als de twee instanties nog nooit van elkaar hebben gehoord.

Status: Werkend proof of concept. Het demonstreert het ontwerp van begin tot eind en draait een echte instantie, maar is nog niet volledig gehard. Zie de sectie 'Beperkingen'.

Recente technische update: Verbetering van vermeldingen (Mentions)

Er is een wijziging doorgevoerd waarbij een melding (push notification) alleen wordt verzonden naar de persoon die de verzender daadwerkelijk bedoelde.

Oorzaak

Voorheen telde namesAccount een naam als een vermelding overal waar de nick alleen stond, aangezien een dubbele punt een woord beëindigt. Bijvoorbeeld, als er een bob op foo.com en een bob op bar.com in één kamer was:

  • bob:bar.com: lunch? getypt op foo.com activeerde beide Bobs, niet alleen die van bar.com.
  • Een kale bob: lunch? vanaf bar.com activeerde ook de Bob op foo.com.

Omdat berichtteksten via de federatie worden verzonden zoals ze zijn getypt, bereikte dezelfde regel elke instantie met een Bob in de kamer, waardoor iedereen een melding kreeg.

Wijziging

  • Een naam wordt nu gelezen zoals deze aan de verzender werd getoond:
  • bob:bar.com (net als het adres bob@bar.com) is altijd Bob op bar.com, ongeacht wie het schreef.
  • Een kale bob is de Bob op de eigen instantie van de verzender; dit duidt dus alleen op een account hier als de verzender lokaal is.
  • bar binnen alice:bar.com of alice@bar.com duidt op niemand.
  • Alleen het domein van deze instantie of dat van een peer kwalificeert een naam. Dit dekt elke nick die een client getoond kan hebben, en zorgt ervoor dat bob:D en bob:10 nog steeds naar Bob verwijzen.

Een specifiek gevolg is dat een regel van een andere instantie die iemand hier alleen met een kale nick noemt, niet langer een melding verzendt. Vanuit de verzender gezien werd er iemand anders benoemd, en hun client had hen de gekwalificeerde nick aangeboden.

---

Functionaliteiten

Werkt met bestaande clients

Parley werkt met de IRC-clients die je al hebt. Twee instanties (foo.com en bar.com) kunnen direct federeren wanneer een gebruiker een query opent met /msg alice@foo.com. Gebruikers zien elkaar vervolgens als bob on bar.com en alice on foo.com. Beide kunnen deelnemen aan #lobby, een globaal kanaal dat over hun instanties wordt gerepliceerd.

Wat Parley doet

  • Standaard IRC aan de voorzijde: Verbind met elke IRC-client. Log in met PASS of SASL PLAIN via je wachtwoord of een IRC-token. IRCv3-functies zoals server-time, message-tags, echo-message, multi-prefix en setname worden aangeboden. TAGMSG geeft client-only tags (zoals typ-indicatoren) door over de federatie en tussen lokale clients.
  • Scrollback die je volgt: De geschiedenis volgt het account, niet de client. CHATHISTORY bladert door kanalen en privéberichten. Een join speelt af wat je nog niet gezien hebt, en draft/read-marker onthoudt tot waar je hebt gelezen. Het markeren van een kanaal als 'gelezen' op je telefoon wist de melding dus ook op je desktop.
  • Beheerbare accounts: Accounts staan in de datadirectory van de instantie, niet in een configuratiebestand. Ze kunnen worden aangemaakt via parleyctl, de admin-pagina, de HTTP API, of via single sign-on (OpenID Connect of identity headers van een reverse proxy).
  • Ontdekking via DNS + WKID: parley.tcp.<domein> SRV-records wijzen naar de instantie. https://<host>/.well-known/parley/instance.json publiceert de ed25519 publieke sleutel en inbox; /.well-known/parley/<user>.json bevestigt dat een gebruiker bestaat.
  • Ondertekende HTTP-federatie: Elk event is een JSON-document dat via POST naar de /inbox van de peer wordt verzonden, met een losse ed25519-handtekening in de headers. Ontvangers verifiëren dit tegen de sleutel die ze zelf hebben ontdekt.
  • Automatisch peering: Berichten naar een nieuw domein zorgen ervoor dat instanties automatisch linken. Gelinkte instanties wisselen bekende peers uit (gossip), waardoor een mesh-netwerk ontstaat zonder handmatige configuratie.
  • Twee soorten kanalen:
  • #dev is globaal en wordt gerepliceerd over elke gelinkte instantie met leden. Niemand is eigenaar, dus er is geen topic en geen operator.
  • &notes is lokaal: het verlaat de instantie nooit, is onzichtbaar voor peers en is de enige plek waar een topic bestaat.
  • Moderatie zonder kanaaleigendom: Omdat niemand een globaal kanaal bezit, werkt /ban als een blokkeerlijst op basis van maskers: /ban quark@example.com voor één persoon, of /ban !@example.com voor een hele instantie.
  • IRC-identiteitsmapping: De nick van een gebruiker is het lokale deel en de instantie is de host (bijv. alice!alice@foo.com). /whois alice@foo.com raadpleegt het WKID-document van haar instantie.
  • Doorzoekbare geschiedenis: Elke instantie bewaart kanaalgeschiedenis in SQLite met een full-text index (parleyctl search) en serveert dit via /channels/<name>/feed. Bij een herstart na downtime haalt een peer de gemiste berichten op.

Snelstart (Twee instanties op één machine, zonder DNS)

git clone https://git.mills.io/prologic/parley.git && cd parley
make build

# Instantie van Alice voor foo.com
./parleyd -domain foo.com -irc 127.0.0.1:6667 -http 127.0.0.1:8080 \
-endpoint http://127.0.0.1:8080 -data ./data/foo -admin-token foo-admin \
-insecure -resolve bar.com=http://127.0.0.1:8081

# Instantie van Bob voor bar.com
./parleyd -domain bar.com -irc 127.0.0.1:6668 -http 127.0.0.1:8081 \
-endpoint http://127.0.0.1:8081 -data ./data/bar -admin-token bar-admin \
-insecure -resolve foo.com=http://127.0.0.1:8080

# Maak accounts aan
./parleyctl -endpoint http://127.0.0.1:8080 -token foo-admin accounts create alice -password secret
./parleyctl -endpoint http://127.0.0.1:8081 -token bar-admin accounts create bob -password secret

Verbind vervolgens met twee terminals:

irssi -c 127.0.0.1 -p 6667 -n alice -w secret
irssi -c 127.0.0.1 -p 6668 -n bob -w secret

Voorbeelden van commando's:

  • /join #dev (beide gebruikers)
  • /msg #dev hello (Bob ziet: <alice> hello, host foo.com)
  • /msg alice@foo.com psst (direct bericht over instanties heen)
  • /join &notes (lokaal, nooit gefedereerd)
  • /whois alice@foo.com

Let op: -insecure en -resolve zijn alleen voor lokale ontwikkeling.

Een eigen instantie draaien

De container image is prologic/parley. Een instantie voor example.com, bereikbaar via chat.example.com, vereist:

  1. Opslag: Een plek voor /data op persistente opslag.

``bash docker run -d --name parley -v parley-data:/data \ -p 6667:6667 -p 8443:8443 \ -e PARLEYADMINTOKEN="s3cret" \ prologic/parley -domain example.com -endpoint https://chat.example.com ``

  1. Accounts: Maak accounts aan via parleyctl of de admin-pagina.
  2. HTTPS: Een reverse proxy die TLS afhandelt voor poort 8443 op chat.example.com.
  3. SRV Record: Zodat andere instanties je kunnen vinden:

parley.tcp.example.com. IN SRV 0 0 443 chat.example.com.

  1. IRC over TLS: Omdat de IRC-listener van parleyd plaintext is, moet TLS aan de voorkant worden afgehandeld (bijv. via Caddy's layer4 module).

Controle van de instantie

Gebruik parleyctl check om te controleren of een instantie correct is geconfigureerd:

$ parleyctl check alice@example.com

Dit controleert DNS, well-known documenten, het endpoint, de inbox, het account en de IRC-TLS poort.

Architectuur

De flow ziet er als volgt uit: irssi $\xrightarrow{\text{IRC}}$ parleyd (foo.com) $\xleftrightarrow{\text{signed HTTPS}}$ parleyd (bar.com) $\xrightarrow{\text{IRC}}$ irssi

  1. Bob's client verzendt PRIVMSG alice@foo.com :hi.
  2. bar.com zoekt parley.tcp.foo.com op, haalt het instantiedocument op en cached de sleutel.
  3. bar.com ondertekent het event en POST dit naar de inbox van foo.com.
  4. foo.com ontdekt bar.com op dezelfde manier om de handtekening te verifiëren en levert het bericht af bij Alice.

Configuratie

Er is geen configuratiebestand. Configuratie is verdeeld over vlaggen (process-level) en instellingen (database).

Vlaggen (Flags / Env Variables)

Deze zijn nodig voordat de database wordt geopend.

VlagEnv VariabeleBetekenisStandaard
-domainPARLEY_DOMAINIdentiteitsdomein van de instantieVerplicht
-dataPARLEYDATADIRLocatie voor sleutels, databases en status./data
-endpointPARLEY_ENDPOINTGeadverteerde basis-URLhttps://<domain>
-ircPARLEYIRCLISTENIRC listener (plaintext):6667
-httpPARLEYHTTPLISTENHTTP(S) listener voor UI, discovery, inbox:8443
-irc-host/portPARLEYIRCHOST/PORTWaar clients IRC over TLS bereikenhost, 6697
-admin-tokenPARLEYADMINTOKENBearer token voor admin API / parleyctluit

Instellingen (Settings)

Deze staan in de database en kunnen worden gewijzigd via de admin-pagina of parleyctl settings.

InstellingBetekenisStandaard
motdMessage of the daybuilt-in
history_replayAantal berichten dat wordt afgespeeld bij JOIN50
history_retentionHoe lang geschiedenis wordt bewaard8760h
message_rateBerichten per seconde per account1
message_burstHoeveel berichten tegelijk per account20
max_connsGelijktijdige IRC-verbindingen naar instantie1024
federation.policyOpen, of allowlist via uitnodigingopen
auth.localToon gebruikersnaam/wachtwoord login formuliertrue

Geavanceerde netwerkinstellingen

Echte client-adressen achter een TCP-proxy

Wanneer TLS voor de IRC-listener wordt afgehandeld, lijken alle clients te komen van de proxy. Om dit op te lossen moet de proxy in irc_proxies worden geplaatst en een PROXY protocol header (v1 of v2) verzenden.

  • Waarschuwing: Wijzig zowel de lijst als de header-instellingen tegelijk; als één van de twee ontbreekt, worden verbindingen gedropt.
  • Web-listener: Gebruik http_proxies om X-Forwarded-For te vertrouwen voor rate-limiting op de web-interface.

Beheer en Onderhoud

Back-up van de instantie-sleutel

Het bestand <data_dir>/identity.key kan niet worden geregenereerd. Verlies van deze sleutel betekent dat peers de instantie weigeren.

  • Bekijk publieke sleutel: parleyctl key show
  • Exporteer geheime sleutel: parleyctl key export -y > instance.key
  • Importeer sleutel: parleyctl key import < instance.key

Upgraden

De database past zichzelf aan bij de eerste start. Belangrijke wijzigingen per versie:

  • v0.6.0: Introductie van messagerate en messageburst om spam te beperken.
  • v0.5.0: Scheidingsteken voor remote gebruikers gewijzigd van / naar : (bijv. alice:foo.com).

Projectstructuur

  • cmd/parleyd: De daemon.
  • cmd/parleyctl: CLI client voor de HTTP API.
  • internal/: Kernlogica (accounts, auth, discovery, protocol, store, server).
  • internal/web: HTML templates (HTMX + DaisyUI + Tailwind).
  • demo/: CoreDNS + Docker Compose demo.
  • docs/: Documentatie over het protocol, API en authenticatie.

Beperkingen en Roadmap

Bekende beperkingen

  • Alleen sleutels op instantie-niveau (geen end-to-end encryptie per gebruiker).
  • Geen kanaal-modes of kanaal-operators (besluit: globale kanalen hebben geen eigenaar).
  • Nicks zijn gebonden aan accounts; /nick is niet beschikbaar.
  • Globale kanalen zijn 'eventually consistent' en hebben geen topic-autoriteit.
  • Er is bewust geen web-chatclient; Parley is een server voor bestaande IRC-clients.

Roadmap ideeën

  • Per-user sleutels via WKID voor versleutelde DM's.
  • Meer IRCv3 ondersteuning (zoals message redaction en user metadata).

Licentie: MIT.