Ably.PubSub.Core
2.0.0
Prefix Reserved
dotnet add package Ably.PubSub.Core --version 2.0.0
NuGet\Install-Package Ably.PubSub.Core -Version 2.0.0
<PackageReference Include="Ably.PubSub.Core" Version="2.0.0" />
<PackageVersion Include="Ably.PubSub.Core" Version="2.0.0" />
<PackageReference Include="Ably.PubSub.Core" />
paket add Ably.PubSub.Core --version 2.0.0
#r "nuget: Ably.PubSub.Core, 2.0.0"
#:package Ably.PubSub.Core@2.0.0
#addin nuget:?package=Ably.PubSub.Core&version=2.0.0
#tool nuget:?package=Ably.PubSub.Core&version=2.0.0

Ably Pub/Sub .NET SDK
The .NET SDK for Ably Pub/Sub: publish and subscribe to channels, and use message history and presence, from servers and from applications on end-user devices.
Overview
The SDK is published as two packages, one for each side of the connection. You choose the package by where your code runs, and create your client through that package's entry point.
| Package | Use it for | Entry point | Platforms |
|---|---|---|---|
Ably.PubSub.Server |
Servers and other trusted backend environments | PubSubServer.CreateRealtimeClient, PubSubServer.CreateHttpClient |
.NET, .NET Framework, Mono |
Ably.PubSub.Device |
Applications on end-user devices | PubSubDevice.CreateClient |
.NET, .NET Framework, Mono, .NET for Android and iOS, MAUI, Unity |
Choosing a package
| Your code runs in | Package | Create clients with |
|---|---|---|
| An ASP.NET Core app, a worker service, an Azure host, a console job or any other backend you operate | Ably.PubSub.Server |
PubSubServer.CreateRealtimeClient(...) or PubSubServer.CreateHttpClient(...) |
| A desktop app (WPF, WinForms), a MAUI or .NET for Android/iOS app, a Unity game, or any other client the end user holds | Ably.PubSub.Device |
PubSubDevice.CreateClient(...) |
Choose by where the code runs, not by which Ably features you use: both packages expose the whole
Ably.PubSub API. The device package has a single entry point; for HTTP-only calls on a device,
use the client's HttpClient property, as shown in the device quick start below.
Requirements
| Platform | Requirement |
|---|---|
| .NET | .NET 6.0 or later. Both packages ship net6.0 and net7.0 builds; later .NET versions use the net7.0 build. |
| .NET Framework | 4.6.2 or later, or Mono. |
| .NET for Android, .NET for iOS, MAUI | With Ably.PubSub.Device. See "MAUI and assembly trimming" under Platform notes. |
| Xamarin and other .NET Standard 2.0 runtimes | Through the netstandard2.0 build of either package. |
| Unity | 2019.x or later, with the API Compatibility Level set to .NET Standard 2.0. See "Unity" under Platform notes. |
Installation
Use exactly one of Ably.PubSub.Server or Ably.PubSub.Device in an app. Never reference
Ably.PubSub.Core directly: it is the shared implementation, and both packages bring in the
matching version for you.
Server
dotnet add package Ably.PubSub.Server
Or add a package reference to your project file:
<PackageReference Include="Ably.PubSub.Server" Version="2.0.0" />
Device
dotnet add package Ably.PubSub.Device
Or add a package reference to your project file:
<PackageReference Include="Ably.PubSub.Device" Version="2.0.0" />
Unity
Unity projects install the .unitypackage attached to each
GitHub release instead of a NuGet
package. See "Unity" under Platform notes.
Migrating from 1.x
Version 2 replaces the ably.io package with these two packages and moves the root namespace from
IO.Ably to Ably.PubSub. Remove ably.io, add the package for your side, change each
using IO.Ably...; to using Ably.PubSub...; (IO.Ably.Rest becomes Ably.PubSub.Http), and
replace new AblyRealtime(...) and new AblyRest(...) with the entry points above. They return
PubSubRealtimeClient and PubSubHttpClient.
dotnet remove package ably.io
dotnet add package Ably.PubSub.Server # or Ably.PubSub.Device
Other changes that can affect a running app:
Endpoint replaces Environment, RestHost and RealtimeHost,
publish methods return a PublishResult,
releasing a channel that isn't detached throws,
and device push-receive is not available.
See UPDATING.md for the full list.
Quick start
Server
Use an API key on a server. The following program connects, subscribes to a channel, publishes a message to it and waits for the message to arrive.
using System;
using System.Threading.Tasks;
using Ably.PubSub;
using Ably.PubSub.Realtime;
using Ably.PubSub.Server;
PubSubRealtimeClient realtime = PubSubServer.CreateRealtimeClient("xxxxxx.yyyyyy:zzzzzzzz");
realtime.Connection.On(ConnectionEvent.Connected, _ => Console.WriteLine("Connected to Ably"));
// Subscribing attaches the channel. Publishing while the client is still
// connecting queues the message until the connection is established.
IRealtimeChannel channel = realtime.Channels.Get("test-channel");
var received = new TaskCompletionSource<Message>();
channel.Subscribe(msg => received.TrySetResult(msg));
Result<PublishResult> result = await channel.PublishAsync("test-event", "hello world");
if (result.IsFailure)
{
Console.WriteLine($"Publish failed: {result.Error}");
return;
}
Message message = await received.Task;
Console.WriteLine($"Received: {message.Data}");
realtime.Close();
To publish and read history over HTTP without a realtime connection, create an HTTP client
instead. Publishing returns a PublishResult with the serial of each message:
using System;
using Ably.PubSub;
using Ably.PubSub.Http;
using Ably.PubSub.Server;
PubSubHttpClient http = PubSubServer.CreateHttpClient("xxxxxx.yyyyyy:zzzzzzzz");
IHttpChannel channel = http.Channels.Get("test-channel");
PublishResult published = await channel.PublishAsync("test-event", "hello world");
Console.WriteLine($"Published, serials: {string.Join(", ", published.Serials)}");
PaginatedResult<Message> history = await channel.HistoryAsync();
foreach (Message message in history.Items)
{
Console.WriteLine($"{message.Name}: {message.Data}");
}
Your server also issues the tokens that devices authenticate with. Return a token request from
the endpoint your devices use as their AuthUrl:
using System;
using Ably.PubSub;
using Ably.PubSub.Server;
PubSubHttpClient http = PubSubServer.CreateHttpClient("xxxxxx.yyyyyy:zzzzzzzz");
// Authenticate the caller first, then issue a token request for that user only,
// granting only the operations the user needs.
var capability = new Capability("{\"test-channel\":[\"publish\",\"subscribe\",\"history\"]}");
string tokenRequest = await http.Auth.CreateTokenRequestAsync(new TokenParams
{
ClientId = "me", // the authenticated user's ID
Capability = capability, // without it, the token gets all of the API key's capabilities
});
// Return this JSON unchanged, with the content type application/json. A text/plain
// response is treated as a token string.
Console.WriteLine(tokenRequest);
In an ASP.NET Core minimal API, for example, return it with
Results.Content(tokenRequest, "application/json"). To work with it as a TokenRequest object, use
JsonHelper.Deserialize<TokenRequest>(tokenRequest). See
token authentication for how devices use it.
Important: clients from
PubSubServerdeclare themselves as servers. Authenticate them with an API key. Token authentication works only with a token that carries the signedx-ably-clientType=serverclaim (a JWT); with any other token, Ably rejects the connection or request with error 40167. Clients fromPubSubDeviceare not affected. See Server clients and token authentication.
Device
Authenticate a device with tokens issued by your own server, through AuthUrl or AuthCallback.
Never embed an API key in an app that runs on an end-user device.
using System;
using System.Threading.Tasks;
using Ably.PubSub;
using Ably.PubSub.Device;
using Ably.PubSub.Realtime;
PubSubRealtimeClient realtime = PubSubDevice.CreateClient(options =>
{
options.AuthUrl = new Uri("https://your-server.example.com/ably-auth");
options.ClientId = "me";
});
// The client connects automatically. Subscribing attaches the channel.
IRealtimeChannel channel = realtime.Channels.Get("test-channel");
var received = new TaskCompletionSource<Message>();
channel.Subscribe(msg => received.TrySetResult(msg));
Result<PublishResult> result = await channel.PublishAsync("test-event", "hello world");
if (result.IsFailure)
{
Console.WriteLine($"Publish failed: {result.Error}");
return;
}
Message message = await received.Task;
Console.WriteLine($"Received: {message.Data}");
realtime.Close();
To get tokens from your own code instead of a URL, set AuthCallback. It can return a token
string, a TokenDetails, or a token request, including the serialized request from
Auth.CreateTokenRequestAsync:
PubSubRealtimeClient realtime = PubSubDevice.CreateClient(options =>
{
options.AuthCallback = async tokenParams =>
{
// Ask your server for a token request, token details or token string.
return await RequestTokenFromYourServerAsync(tokenParams); // your function
};
options.ClientId = "me";
});
The device package has no separate HTTP entry point. For HTTP calls only, create the client
without connecting and use its HttpClient:
using System;
using Ably.PubSub;
using Ably.PubSub.Device;
PubSubRealtimeClient client = PubSubDevice.CreateClient(options =>
{
options.AuthUrl = new Uri("https://your-server.example.com/ably-auth");
options.AutoConnect = false; // HTTP calls only; call client.Connect() to go realtime
});
PaginatedResult<Message> history = await client.HttpClient.Channels.Get("test-channel").HistoryAsync();
foreach (Message message in history.Items)
{
Console.WriteLine($"{message.Name}: {message.Data}");
}
Common configuration
Every entry point accepts an API key, a ClientOptions object, or an Action<ClientOptions>
that configures one.
Endpoint
Endpoint selects the Ably host for both HTTP requests and the realtime connection. It replaces
the 1.x Environment, RestHost and RealtimeHost options.
PubSubRealtimeClient realtime = PubSubServer.CreateRealtimeClient(options =>
{
options.Key = "xxxxxx.yyyyyy:zzzzzzzz";
options.Endpoint = "nonprod:sandbox";
});
Never put a port in Endpoint: localhost:8080 is not a hostname and does not resolve. Set the
port with TlsPort instead, or with Port when TLS is off (Tls = false, which needs token
authentication). See
Choose the host with Endpoint
for how each value resolves.
Proxy
The SDK uses your runtime's default proxy settings, for both HTTP requests and the realtime
connection. On .NET, these are
HttpClient.DefaultProxy,
which comes from the HTTPS_PROXY and HTTP_PROXY environment variables or from the system proxy
settings. On .NET Framework, they come from the system proxy settings and your app's configuration.
To set a proxy in code, pass your own HttpClient. It applies to HTTP requests only; the
realtime connection still uses the default proxy settings.
using System.Net;
using System.Net.Http;
using Ably.PubSub;
using Ably.PubSub.Server;
var handler = new HttpClientHandler
{
Proxy = new WebProxy("http://proxy.example.com:8080"),
UseProxy = true,
};
PubSubHttpClient http = PubSubServer.CreateHttpClient(options =>
{
options.Key = "xxxxxx.yyyyyy:zzzzzzzz";
options.HttpClient = new HttpClient(handler);
});
If the proxy needs authentication, set WebProxy.Credentials.
Logging
LogLevel sets what the SDK logs: LogLevel.Debug (the most verbose), Warning (the default),
Error or None. The SDK writes log lines to the ILoggerSink you set as LogHandler; without
one, log lines aren't written anywhere. This works the same way as in 1.x.
using System;
using Ably.PubSub;
using Ably.PubSub.Server;
PubSubRealtimeClient realtime = PubSubServer.CreateRealtimeClient(options =>
{
options.Key = "xxxxxx.yyyyyy:zzzzzzzz";
options.LogLevel = LogLevel.Debug;
options.LogHandler = new ConsoleLogSink();
});
class ConsoleLogSink : ILoggerSink
{
public void LogEvent(LogLevel level, string message)
{
Console.WriteLine($"[{level}] {message}");
}
}
All clients in a process share one logger. Each client you create applies its LogLevel, and its
LogHandler if set, to that shared logger, so give every client the same logging options.
Platform notes
Unity
Download ably.pubsub.<version>.unitypackage from the
latest GitHub release and import it
with Assets > Import Package > Custom Package. The package bundles Ably.PubSub.Device, so
create clients with PubSubDevice.CreateClient(...). Set ClientOptions.CustomContext to run
callbacks on the main thread. WebGL builds are not supported. See the
Unity README.
MAUI and assembly trimming
The SDK uses reflection. In a MAUI or trimmed app, keep the Ably assembly from being trimmed by
adding this to your .csproj:
<ItemGroup>
<TrimmerRootAssembly Include="Ably.PubSub.Core" />
</ItemGroup>
.NET Framework
Both packages run on .NET Framework 4.6.2 or later. For high-throughput use on .NET Framework, for example messages larger than 50 KB, set the WebSocket send and receive buffers to the 64 KB maximum:
using Ably.PubSub;
using Ably.PubSub.Server;
using Ably.PubSub.Transport;
var maxBufferSize = 64 * 1024;
var options = new ClientOptions("xxxxxx.yyyyyy:zzzzzzzz")
{
TransportFactory = new MsWebSocketTransport.TransportFactory(new MsWebSocketOptions
{
SendBufferInBytes = maxBufferSize,
ReceiveBufferInBytes = maxBufferSize,
}),
};
PubSubRealtimeClient realtime = PubSubServer.CreateRealtimeClient(options);
In a device app, set the same TransportFactory on the ClientOptions you pass to
PubSubDevice.CreateClient.
Known limitations
- Device push-receive (activating a device for push notifications on Android or iOS) is not
available in 2.0. The API stays public:
Push.Activate()andPush.Deactivate()throw anAblyException, and the client'sDeviceand a channel'sPusharenull. To request the feature, comment on https://github.com/ably/ably-pubsub-dotnet/issues/1360. See Push notifications. - Push administration (
Push.Admin) is available on clients from bothAbly.PubSub.DeviceandAbly.PubSub.Server. It is intended for server-side use and needs thepush-admincapability, so use it from a backend built onAbly.PubSub.Server. - Unity WebGL builds are not supported (https://github.com/ably/ably-pubsub-dotnet/issues/1211).
Further reading
- Getting started with Pub/Sub using .NET
- Ably Pub/Sub documentation and examples
- Token authentication
- UPDATING.md: migrating from 1.x
- CHANGELOG.md: release notes; all Ably releases are listed on changelog.ably.com
- Unity README: using the SDK in Unity
Contributing
Read CONTRIBUTING.md
before contributing; it also describes the release process. The build uses Cake: run
./build.sh --target=Build.NetStandard (or build.cmd on Windows) to build and lint before you
push.
Support, feedback and troubleshooting
For help, visit Ably's support page. Report bugs and compatibility problems on GitHub Issues.
License
Licensed under the Apache License, Version 2.0. See LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net46 is compatible. net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.6
- Newtonsoft.Json (>= 13.0.1)
- System.Threading.Channels (>= 4.6.0)
-
.NETStandard 2.0
- Newtonsoft.Json (>= 13.0.1)
- System.Threading.Channels (>= 4.6.0)
-
net6.0
- Newtonsoft.Json (>= 13.0.1)
- System.Threading.Channels (>= 4.6.0)
-
net7.0
- Newtonsoft.Json (>= 13.0.1)
- System.Threading.Channels (>= 4.6.0)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Ably.PubSub.Core:
| Package | Downloads |
|---|---|
|
Ably.PubSub.Device
The supported entry point for Ably Pub/Sub in an end-user device application - a mobile or desktop app, a Unity game, a set-top box or any other client the end user holds. Create clients through PubSubDevice.CreateClient(...) in the Ably.PubSub.Device namespace; it accepts an API key, an Ably token, a ClientOptions or an Action<ClientOptions>, and returns the ordinary PubSubRealtimeClient so the whole of the Ably.PubSub API remains available, including device-side connectionless operations such as message history, presence reads and token requests. Creating a client any other way - including directly from the Ably.PubSub.Core implementation package this depends on - does not classify it as device-side, which Ably's platform behaviour and billing depend on. Install Ably.PubSub.Server instead in a server-side application. See https://www.ably.com for more info. |
|
|
Ably.PubSub.Server
The supported entry point for Ably Pub/Sub in a server-side application - an ASP.NET or Azure host, a worker, a console app or any other backend the end user does not hold. Create clients through PubSubServer.CreateRealtimeClient(...) and PubSubServer.CreateHttpClient(...) in the Ably.PubSub.Server namespace; each accepts an API key, a ClientOptions or an Action<ClientOptions>, and returns the ordinary PubSubRealtimeClient or PubSubHttpClient so the whole of the Ably.PubSub API remains available. Server clients authenticate with an API key, or with a JWT that carries the signed server claim; Ably rejects any other token with error 40167. Creating a client any other way - including directly from the Ably.PubSub.Core implementation package this depends on - does not classify it as server-side, which Ably's platform behaviour and billing depend on. Install Ably.PubSub.Device instead in an end-user device application. See https://www.ably.com for more info. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.0 | 65 | 11/3/2026 |