Artalk.Xmpp is a .NET 10 XMPP client library for connecting to XMPP servers, sending and receiving messages, managing presence and rosters, and using common XMPP extension protocols.
- .NET 10:
Artalk.Xmpp - .NET 10:
Artalk.ExtendedClientsample - .NET 10 Windows:
Artalk.WinClientsample
The core library targets net10.0 and does not require Windows-only packages.
- RFC 6120 TCP XML streams
- RFC 7622-oriented JID parsing, IDN domain normalization, and PRECIS-profile checks
- RFC 7590 TLS recommendations with RFC 6120 STARTTLS and direct TLS support
- XEP-0124 / XEP-0206 XMPP over BOSH
- RFC 7395 XMPP over WebSocket
- RFC 6120 SASL authentication: OAUTHBEARER, CISCO-VTG-TOKEN, SCRAM-SHA3-512-PLUS, SCRAM-SHA3-512, SCRAM-SHA-512-PLUS, SCRAM-SHA-512, SCRAM-SHA-384-PLUS, SCRAM-SHA-384, SCRAM-SHA-256-PLUS, SCRAM-SHA-256, SCRAM-SHA-224-PLUS, SCRAM-SHA-224, SCRAM-SHA-1-PLUS, SCRAM-SHA-1, DIGEST-MD5, PLAIN
- XEP-0388 SASL2 foundation: stream-feature parsing, SASL2 authentication framing, additional-data handling, and no post-success stream restart
- XEP-0386 Bind 2 resource binding over SASL2 with
authorization-identifierparsing - XEP-0440 SASL Channel-Binding Type Capability for SCRAM-PLUS selection
- XEP-0474 SASL SCRAM Downgrade Protection hash verification
- XEP-0480 SASL Upgrade Tasks for SCRAM salted-password hash upgrades over SASL2
- XEP-0515 TLS Channel-Binding Downgrade Protection for SCRAM over TCP TLS
- XEP-0453 DOAP project metadata:
doap.xml - RFC 6120 optional legacy XMPP session establishment
- RFC 6121 instant messaging and presence
- RFC 6121 roster management
- XEP-0045 Multi-User Chat basics: join, leave, groupchat messages, and occupant presence
- XEP-0030 Service Discovery
- XEP-0115 Entity Capabilities
- XEP-0199 XMPP Ping
- XEP-0085 Chat State Notifications
- OMEMO foundation: XEP-0384 device list, bundle, trust/session orchestration, encrypted envelope, and payload crypto helpers
- OMEMO media sharing: XEP-0454
aesgcm://URIs, AES-256-GCM media encryption, strict body parsing, and JPEG thumbnails - XEP-0084 User Avatar, XEP-0107 User Mood, XEP-0118 User Tune, and XEP-0108 User Activity
- XEP-0077 In-Band Registration
- XEP-0049 Private XML Storage
- XEP-0004 Data Forms and XEP-0020 Feature Negotiation for legacy XEP-0095 / XEP-0096 file-transfer flows
- XEP-0065 SOCKS5 Bytestreams and XEP-0047 In-Band Bytestreams
- XEP-0092 Software Version, XEP-0202 Entity Time, XEP-0224 Attention, XEP-0231 Bits of Binary, and XEP-0279 Server IP Check
- XEP-0191 Blocking Command
Install the NuGet package:
dotnet add package Artalk.Xmpp --version 2.20.3Or reference the project directly:
<ProjectReference Include="..\Artalk.Xmpp\Artalk.Xmpp.csproj" />Or package it locally:
dotnet pack .\Artalk.Xmpp\Artalk.Xmpp.csproj -c Releaseusing Artalk.Xmpp.Client;
using Artalk.Xmpp.Im;
var hostname = "example.com";
var username = "myusername";
var password = "mypassword";
using var client = new ArtalkXmppClient(hostname, username, password);
client.Message += (sender, e) => {
Console.WriteLine($"Message from <{e.Jid}>: {e.Message.Body}");
};
client.Connect("my-resource");
client.SendMessage("friend@example.com", "Hello from Artalk.Xmpp", type: MessageType.Chat);Jid normalizes XMPP addresses before comparison and serialization. Domainparts are processed as IDN-aware names, localparts apply RFC 7622-oriented case, width, exclusion, and length checks, and resourceparts are treated as opaque strings with NFC normalization:
var jid = new Jid("USER@XN--BCHER-KVA.Example./Phone");
Console.WriteLine(jid.Node); // user
Console.WriteLine(jid.Domain); // bücher.example
Console.WriteLine(jid.Resource); // Phone
Console.WriteLine(jid); // user@bücher.example/PhoneMost XMPP servers use STARTTLS on port 5222, which remains the default. For servers that expect TLS immediately after TCP connect, use the directTls constructor argument and the server's TLS port, commonly 5223:
using var client = new ArtalkXmppClient(
"xmpp.example.com",
"myusername",
"mypassword",
port: 5223,
directTls: true);
client.Connect("service");For servers that expose XMPP over BOSH, pass the BOSH connection manager URL and the XMPP service domain:
using Artalk.Xmpp.Client;
var boshUrl = new Uri("https://chat.example.com/http-bind");
using var client = new ArtalkXmppClient(
boshUrl,
"example.com",
"myusername",
"mypassword");
client.Connect("bosh-client");
client.SendMessage("room-user@example.com", "Hello over BOSH");For servers that expose RFC 7395 XMPP over WebSocket, pass the WebSocket endpoint URL, the XMPP service domain, and XmppTransportBinding.WebSocket:
using Artalk.Xmpp.Client;
using Artalk.Xmpp.Core;
var webSocketUrl = new Uri("wss://chat.example.com/xmpp-websocket");
using var client = new ArtalkXmppClient(
webSocketUrl,
"example.com",
"myusername",
"mypassword",
XmppTransportBinding.WebSocket);
client.Connect("websocket-client");
client.SendMessage("friend@example.com", "Hello over WebSocket");For servers that advertise SASL OAUTHBEARER, set Username and OAuthBearerToken before connecting:
using Artalk.Xmpp.Client;
using var client = new ArtalkXmppClient("xmpp.example.com");
client.Username = "myusername";
client.OAuthBearerToken = accessToken;
client.Connect("oauth-client");When OAuthBearerToken is set and the server advertises OAUTHBEARER, Artalk.Xmpp prefers it over password-based SASL mechanisms.
For Cisco Unified CM IM and Presence or Cisco Jabber deployments that advertise SASL CISCO-VTG-TOKEN, set Username and CiscoVtgToken before connecting:
using Artalk.Xmpp.Client;
using var client = new ArtalkXmppClient("cisco-im.example.com");
client.Username = "user@example.com";
client.CiscoVtgToken = ciscoToken;
client.Connect("jabber-client");When CiscoVtgToken is set and the server advertises CISCO-VTG-TOKEN, Artalk.Xmpp selects it before password-based SASL mechanisms. The library sends the caller-provided token through Cisco's SASL mechanism; it does not acquire Cisco SSO or edge-authentication tokens.
Artalk.Xmpp includes the XMPP-facing OMEMO layer for XEP-0384: device list and bundle PEP nodes, trust decisions, session orchestration, encrypted message envelopes, payload encryption/authentication, and high-level send/decrypt helpers. It uses the current urn:xmpp:omemo:2 namespace.
using Artalk.Xmpp.Client;
using Artalk.Xmpp.Extensions;
using var client = new ArtalkXmppClient("example.com", "myusername", "mypassword");
client.OmemoDeviceListChanged += (sender, e) => {
Console.WriteLine($"{e.Jid} devices: {string.Join(", ", e.DeviceList.DeviceIds)}");
};
client.Connect("omemo-device");
uint deviceId = 123456;
client.PublishOmemoDeviceList(new uint[] { deviceId });
var bundle = new OmemoBundle(
signedPreKeyId: 1,
signedPreKey: signedPreKeyPublicBytes,
signedPreKeySignature: signedPreKeySignatureBytes,
identityKey: identityKeyBytes,
preKeys: new Dictionary<uint, byte[]> {
{ 1, preKeyPublicBytes }
});
client.PublishOmemoBundle(deviceId, bundle);
OmemoDeviceList contactDevices = client.GetOmemoDeviceList("friend@example.com");
OmemoBundle contactBundle = client.GetOmemoBundle(
"friend@example.com",
contactDevices.DeviceIds[0]);For message encryption, provide an IOmemoSessionCipher implementation that adapts your vetted OMEMO-compatible X3DH and Double Ratchet engine. Artalk.Xmpp handles device discovery, trust policy enforcement, payload encryption, envelope construction, sending, parsing, and payload decryption:
using System.Text;
IOmemoSessionCipher sessionCipher = CreateSignalProtocolAdapter();
var trustStore = new MemoryOmemoTrustStore();
trustStore.SetTrust(
"friend@example.com",
contactDevices.DeviceIds[0],
contactBundle.IdentityKey,
OmemoTrustLevel.Trusted);
byte[] plaintext = Encoding.UTF8.GetBytes("hello");
client.SendOmemoMessage(
"friend@example.com",
plaintext,
localDeviceId: deviceId,
sessionCipher: sessionCipher,
trustStore: trustStore);OmemoTrustPolicy.RequireTrusted is the default. AllowUndecided and TrustOnFirstUse are available when an application explicitly wants that behavior. Artalk.Xmpp does not bundle a GPL Signal Protocol dependency into the MIT package; instead, IOmemoSessionCipher is the adapter boundary for a vetted session implementation and persistent key store.
XEP-0454 uses HTTP File Upload for the encrypted bytes and sends only an aesgcm:// URL inside an OMEMO encrypted message body. OmemoMediaUri creates and parses the strict URI form, encrypts media with AES-256-GCM, and appends the authentication tag to the encrypted file as required by the XEP:
using System.Text;
using Artalk.Xmpp.Extensions;
Uri uploadUrl = new Uri("https://upload.example.com/files/photo.jpg");
byte[] fileBytes = File.ReadAllBytes("photo.jpg");
OmemoMediaUri mediaUri = OmemoMediaUri.Create(uploadUrl);
byte[] encryptedFile = mediaUri.Encrypt(fileBytes);
long encryptedSize = OmemoMediaUri.GetEncryptedLength(fileBytes.LongLength);
// Upload encryptedFile to uploadUrl. Use encryptedSize when requesting an
// HTTP upload slot for the encrypted payload.
string thumbnail = OmemoMediaMessage.CreateJpegThumbnailDataUri(thumbnailJpegBytes);
string body = new OmemoMediaMessage(mediaUri, thumbnail).ToString();
client.SendOmemoMessage(
"friend@example.com",
Encoding.UTF8.GetBytes(body),
localDeviceId: deviceId,
sessionCipher: sessionCipher,
trustStore: trustStore);Receiving applications decrypt the OMEMO message first, parse the media body strictly, download the encrypted HTTPS payload, and then decrypt it with the key and IV from the aesgcm:// URI:
byte[] bodyBytes = client.DecryptOmemoMessage(message, deviceId, sessionCipher);
OmemoMediaMessage media = OmemoMediaMessage.Parse(Encoding.UTF8.GetString(bodyBytes));
byte[] encryptedDownload = Download(media.MediaUri.HttpsUrl);
byte[] originalFile = media.MediaUri.Decrypt(encryptedDownload);Connect retrieves the roster and sends initial presence. Subscribe to StatusChanged before connecting to track online, away, and offline updates from contacts:
using var client = new ArtalkXmppClient("example.com", "myusername", "mypassword");
client.StatusChanged += (sender, e) => {
Console.WriteLine($"{e.Jid}: {e.Status.Availability} {e.Status.Message}");
};
client.Connect("presence-sample");
client.SetStatus(Availability.Online, "Ready");Basic XEP-0045 room participation is available through the high-level client. Join a room with a nickname, receive groupchat messages, track occupant presence, and send room messages:
using Artalk.Xmpp.Client;
using var client = new ArtalkXmppClient("example.com", "myusername", "mypassword");
client.RoomMessage += (sender, e) => {
Console.WriteLine($"[{e.RoomJid}] {e.Nickname}: {e.Message.Body}");
};
client.RoomPresence += (sender, e) => {
Console.WriteLine($"{e.Occupant.Nickname} available: {e.IsAvailable}");
};
client.Connect("muc-sample");
client.JoinRoom("room@conference.example.com", "my-nickname");
client.SendRoomMessage("room@conference.example.com", "Hello room");For long-running clients, use XEP-0199 server pings to detect or prevent idle connection loss:
using var client = new ArtalkXmppClient("example.com", "myusername", "mypassword");
client.KeepAliveInterval = TimeSpan.FromMinutes(5);
client.KeepAliveTimeout = TimeSpan.FromSeconds(15);
client.KeepAliveFailed += (sender, e) => {
Console.WriteLine($"Keepalive failed: {e.Exception.Message}");
};
client.Error += (sender, e) => {
Console.WriteLine(e.Exception);
};
client.Disconnected += (sender, e) => {
Console.WriteLine("Disconnected");
};
client.Connect("long-running-client");You can also ping the connected server on demand with PingServer().
Some servers allow XEP-0077 account registration before authenticating. Create the client without credentials, connect, and fill the server-provided form:
using Artalk.Xmpp.Client;
using Artalk.Xmpp.Extensions.Dataforms;
using var client = new ArtalkXmppClient("example.com");
client.Connect();
client.Register(form => new SubmitForm(
new TextField("username", "newuser"),
new PasswordField("password", "new-password")));STARTTLS now uses the platform certificate validator by default. If a server requires custom certificate validation, pass a RemoteCertificateValidationCallback to the client constructor.
For BOSH, prefer an https:// connection manager URL so the HTTP binding is protected by TLS.
For WebSocket, prefer wss:// endpoint URLs. RFC 7395 uses the xmpp WebSocket subprotocol and sends each stanza as an independent UTF-8 text frame.
OAuth bearer tokens are sent only when the server advertises OAUTHBEARER. Use TLS or HTTPS transport when authenticating with bearer tokens.
Cisco VTG tokens are sent only when the server advertises CISCO-VTG-TOKEN. Use STARTTLS, direct TLS, BOSH over HTTPS, or WebSocket over WSS when authenticating with Cisco token-based mechanisms.
OMEMO support covers current XEP-0384 device list and bundle publication/retrieval, trust policies, trust-store integration, encrypted message envelope parsing/serialization, payload encryption/authentication, and send/decrypt orchestration. Applications must provide a vetted IOmemoSessionCipher adapter and persistent OMEMO key/session store for per-device X3DH and Double Ratchet state.
OMEMO media sharing support covers XEP-0454 aesgcm:// URL creation/parsing, AES-256-GCM encryption/decryption, appended authentication tags, strict message-body parsing, and optional JPEG thumbnails. Do not display aesgcm:// links as browser-openable URLs; the URI fragment contains encryption key material and must stay inside the OMEMO-encrypted message flow. Only HTTPS download/upload URLs are accepted when creating media URIs.
SCRAM -PLUS mechanisms are preferred automatically on encrypted TCP XMPP streams when the server advertises them and a remote certificate is available. Artalk.Xmpp understands XEP-0440 sasl-channel-binding announcements and uses SCRAM-PLUS only when tls-server-end-point is mutually supported, while preserving compatibility with servers that have not implemented XEP-0440 yet. RFC 9266 defines tls-exporter as the TLS 1.3 channel-binding type, but .NET SslStream does not currently expose TLS exporter keying material; the related runtime API requests are tracked in dotnet/runtime#73118 and dotnet/runtime#112529. Until that API exists, Artalk.Xmpp intentionally does not advertise or select tls-exporter. The .NET API also does not expose the TLS Finished messages needed for tls-unique.
When a SCRAM server-first-message includes the XEP-0474 h attribute, Artalk.Xmpp verifies it against the hash of the SASL mechanism list and XEP-0440 channel-binding type list that were actually advertised for the active SASL profile. A mismatch fails authentication before the client proof is sent, protecting SCRAM and SCRAM-PLUS negotiation from active downgrade tampering.
When a SCRAM server-first-message includes the XEP-0515 t attribute on a TCP TLS stream, Artalk.Xmpp compares it with the negotiated SslStream TLS version encoded as four lowercase hexadecimal characters such as 0303 for TLS 1.2 and 0304 for TLS 1.3. A mismatch fails authentication before the client proof is sent. BOSH and WebSocket transports do not expose the negotiated TLS version through the current .NET transport APIs, so XEP-0515 verification is only available for TCP TLS and STARTTLS streams.
When an encrypted stream advertises XEP-0388 SASL2, Artalk.Xmpp uses the SASL2 <authentication/> profile, sends initial responses inside <initial-response/>, verifies SCRAM server signatures from <additional-data/>, and waits for the authenticated <stream:features/> without restarting the stream.
When the same SASL2 profile advertises XEP-0386 Bind 2, Artalk.Xmpp includes an inline <bind xmlns='urn:xmpp:bind:0'/> request in <authenticate/>, uses the Connect(resource) value as the optional Bind 2 <tag/>, and reads the final full JID from <authorization-identifier/>. If Bind 2 is not advertised, the client falls back to the legacy RFC 6120 resource bind IQ.
If the SASL2 feature advertises XEP-0480 SCRAM upgrade tasks and password authentication is available, Artalk.Xmpp requests supported UPGR-SCRAM-* tasks, handles the SASL2 <continue/> flow, derives the requested salted password from the server-provided salt and iteration count, and returns it inside <task-data><hash/></task-data>. Upgrade tasks that are not SCRAM hash upgrades remain unsupported and fail with a clear authentication error.
When a server advertises legacy XMPP session establishment, Artalk.Xmpp completes it. Modern servers that omit the legacy session feature are no longer rejected during sign-in.
Automatic UPnP port mapping is disabled in the .NET 10 cross-platform build because the previous implementation depended on a Windows COM component. File transfer still supports SOCKS5, configured proxies, in-band bytestreams, and STUN-based address discovery.
The avatar extension no longer exposes System.Drawing.Image. Avatar events now provide raw bytes and MIME type through AvatarChangedEventArgs.AvatarData and AvatarChangedEventArgs.MimeType, which keeps the library usable in services, containers, and non-Windows environments.
dotnet build .\Artalk.XmppSoloution.sln
dotnet test .\Artalk.XmppSoloution.slnArtalk.Xmpp publishes XEP-0453-compatible DOAP metadata in doap.xml. This RDF/XML file describes the project, repository, NuGet package, and implemented XMPP RFC/XEP support for software listings and automated XMPP ecosystem indexes.
| RFC | Area | Support |
|---|---|---|
| RFC 6120 | XMPP Core | partial |
| RFC 6121 | Instant Messaging and Presence | partial |
| RFC 7395 | XMPP over WebSocket | complete |
| RFC 7590 | TLS in XMPP | partial |
| RFC 7622 | XMPP Address Format | partial |
The DOAP metadata includes the current XEP document version, the first Artalk.Xmpp 2.x package version that exposed the support, implementation status, and notes for each XEP.
| XEP | Spec Version | Since | Status | Notes |
|---|---|---|---|---|
| XEP-0004 | 2.13.2 | 2.0.0 | partial | Data Forms are used by the registration and feature-negotiation helpers. |
| XEP-0020 | 1.6 | 2.0.0 | partial | Deprecated feature-negotiation helpers remain for legacy stream-initiation and file-transfer flows. |
| XEP-0030 | 2.5.0 | 2.0.0 | partial | Service discovery query helpers are available. |
| XEP-0045 | 1.35.5 | 2.2.0 | partial | Basic room join, leave, groupchat message, and occupant presence support. |
| XEP-0047 | 2.0.1 | 2.0.0 | partial | In-band bytestream support is available for file-transfer flows. |
| XEP-0049 | 1.2 | 2.0.0 | complete | Private XML storage get/set helpers are available. |
| XEP-0065 | 1.8.2 | 2.0.0 | partial | SOCKS5 bytestream and STUN-assisted address discovery helpers are available. |
| XEP-0077 | 2.4 | 2.0.0 | partial | In-band registration form flow is supported. |
| XEP-0084 | 1.1.4 | 2.0.0 | partial | User avatar publication and change events expose raw avatar data and MIME type. |
| XEP-0085 | 2.1 | 2.0.0 | complete | Chat state notification helpers are available. |
| XEP-0092 | 1.1 | 2.0.0 | complete | Software version replies are available. |
| XEP-0095 | 1.2 | 2.0.0 | partial | Deprecated stream initiation support remains for legacy file-transfer flows. |
| XEP-0096 | 1.3.1 | 2.0.0 | partial | Deprecated SI file-transfer support remains for legacy interoperability. |
| XEP-0107 | 1.2.2 | 2.0.0 | partial | User mood publication/parsing helpers are available. |
| XEP-0108 | 1.3 | 2.0.0 | partial | User activity publication/parsing helpers are available. |
| XEP-0115 | 1.6.0 | 2.0.0 | partial | Entity capabilities helpers are available. |
| XEP-0118 | 1.3.0 | 2.0.0 | partial | User tune publication/parsing helpers are available. |
| XEP-0124 | 1.11.2 | 2.5.0 | complete | BOSH session open and restart support are available. |
| XEP-0163 | 1.2.2 | 2.0.0 | partial | PEP is used by the avatar and OMEMO helpers. |
| XEP-0191 | 1.3 | 2.0.0 | partial | Simplified blocking command support is available. |
| XEP-0199 | 2.0.1 | 2.3.0 | complete | Server ping and keepalive flows are available. |
| XEP-0202 | 2.0 | 2.0.0 | complete | Entity time replies are available. |
| XEP-0206 | 1.4 | 2.5.0 | complete | XMPP over BOSH transport is available. |
| XEP-0224 | 1.0 | 2.0.0 | complete | Attention messages are available. |
| XEP-0231 | 1.1 | 2.0.0 | partial | Bits of Binary helpers are available. |
| XEP-0279 | 0.2 | 2.0.0 | complete | Server IP check support is available. |
| XEP-0384 | 0.9.1 | 2.8.0 | partial | XMPP-facing OMEMO device, bundle, envelope, payload, trust, and session orchestration helpers are available; applications provide the session cipher implementation. |
| XEP-0386 | 1.1.0 | 2.20.0 | partial | Bind 2 discovery, SASL2 inline bind request, optional tag, and authorization-identifier parsing are supported; linked inline session features remain future work. |
| XEP-0388 | 1.0.4 | 2.15.0 | partial | SASL2 authentication framing and SCRAM additional-data verification are supported. |
| XEP-0440 | 1.0.0 | 2.14.0 | complete | SASL channel-binding type discovery is supported. |
| XEP-0453 | 0.1.2 | 2.19.1 | complete | The project publishes RDF/XML DOAP metadata. |
| XEP-0454 | 0.1.0 | 2.13.0 | partial | OMEMO media URI, AES-GCM media encryption, encrypted length, and message-body helpers are available. |
| XEP-0474 | 0.5.0 | 2.17.0 | complete | SCRAM downgrade-protection hash verification is supported. |
| XEP-0480 | 0.2.0 | 2.16.0 | partial | SCRAM salted-password hash upgrade tasks are supported. |
| XEP-0515 | 0.1.0 | 2.18.0 | complete | TLS version downgrade-protection verification is supported for TCP TLS streams. |
This project is released under the MIT License.
Artalk.Xmpp is maintained as an open-source .NET/XMPP library. If this project saves you time, helps a production integration, or gives you a cleaner starting point for XMPP work, a donation helps keep the library maintained, tested, documented, and published on NuGet.
You can also donate directly through this PayPal payment link: Donate to Artalk.Xmpp.