Inklusiv.Matrix 0.1.3

dotnet add package Inklusiv.Matrix --version 0.1.3
                    
NuGet\Install-Package Inklusiv.Matrix -Version 0.1.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Inklusiv.Matrix" Version="0.1.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Inklusiv.Matrix" Version="0.1.3" />
                    
Directory.Packages.props
<PackageReference Include="Inklusiv.Matrix" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Inklusiv.Matrix --version 0.1.3
                    
#r "nuget: Inklusiv.Matrix, 0.1.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Inklusiv.Matrix@0.1.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Inklusiv.Matrix&version=0.1.3
                    
Install as a Cake Addin
#tool nuget:?package=Inklusiv.Matrix&version=0.1.3
                    
Install as a Cake Tool

Inklusiv.Matrix

A headless Matrix client for bots, with end-to-end encryption built in. Register it at startup with the bot's account and it takes it from there: login, the sync loop, key management, encrypting what you send, decrypting what arrives. Your code only ever sees plaintext events and calls plain send methods.

Encryption is Element's own engine, matrix-sdk-crypto, embedded as a native library for linux-x64, which is where bots built on this run: inside a Linux container. No sidecar, no proxy, no extra process.

Quick start

builder.Services.AddMatrixBot(o =>
{
    o.HomeServer  = "https://matrix.example.com";
    o.UserId      = "@bot:matrix.example.com";
    o.AccessToken = builder.Configuration["Matrix:AccessToken"];   // or o.Password
    o.DeviceId    = "bot-main";                                    // stable, see below
    o.StorePath   = "/app/data/matrix";                            // persistent volume
});
builder.Services.AddMatrixEventHandler<MyHandler>();
sealed class MyHandler(IMatrixBotClient client) : IMatrixEventHandler
{
    public async Task HandleEventAsync(MatrixRoomEvent e, CancellationToken ct)
    {
        if (e.Type == "m.room.message" && e.MessageType == "m.text")
            await client.SendTextAsync(e.RoomId, $"you said: {e.Body}", ct: ct);
    }
}

That handler runs identically in plain and encrypted rooms. e.WasEncrypted tells you which, e.Shield tells you how much to trust the sender's device.

What the package does for you

Concern Behaviour
Login Pre-issued token validated with whoami, password fallback, silent re-login on 401/403
Sync Long-poll loop as a hosted service; sync token persisted after each fully processed response
Invites Joined automatically (AutoJoinInvites), handlers get HandleJoinedAsync
Receiving m.room.encrypted decrypted before dispatch; events whose key arrives late are retried across the next syncs, then delivered with DecryptionFailed = true
Sending In a room carrying m.room.encryption, every Send* call tracks members, claims Olm sessions, shares the Megolm key and encrypts. Elsewhere it sends as-is
Files SendFileAsync encrypts attachments client-side in encrypted rooms (EncryptedFile v2)
Rooms CreateRoomAsync(new CreateRoomOptions { Encrypted = true }), EnableRoomEncryptionAsync for existing rooms (irreversible)
History GetRoomEventsAsync pages backwards and decrypts what the device has keys for
Escape hatch RawAsync(method, path, body) for anything not wrapped, using the account's session

Rules that keep encryption healthy

  • Stable DeviceId, persistent StorePath. The store is the device's cryptographic identity. Delete it after keys were uploaded and every client will refuse the device.
  • One process per store. The package takes a lock file and refuses to start a second instance. Deployments must stop the old container before starting the new one.
  • Encryption is per room and permanent. Once a room has m.room.encryption it cannot go back.

Configuration

MatrixBotOptions can also be bound from configuration:

builder.Services.AddMatrixBot(builder.Configuration.GetSection("Matrix"));
Option Default Notes
HomeServer URL or bare host (https assumed)
UserId / Username full MXID, or localpart combined with the homeserver host
AccessToken / Password at least one
DeviceId required for encryption
StorePath matrix-store sync token, crypto store, lock
Encryption Auto Off disables the crypto engine entirely
EncryptNewRooms false default for CreateRoomOptions.Encrypted
BootstrapCrossSigning false publish an identity at start-up so the device is not flagged unverified
AutoJoinInvites true
DispatchOwnEvents false deliver the account's own events to handlers
SyncTimeoutMs 30000

Native library and bindings

src/Inklusiv.Matrix/Bindings/*.cs is generated by uniffi-bindgen-cs from matrix-sdk-crypto-ffi at the commit pinned in build/matrix-rust-sdk.commit; the generator tag is pinned in build/uniffi-bindgen-cs.tag. The two uniffi versions must stay on the same line. CI builds the Linux native on Debian 12 (the same glibc as the mcr.microsoft.com/dotnet/aspnet images), regenerates the bindings and fails if they differ from what is committed. The build is cached on the pinned commit, so only a pin bump recompiles.

Nobody needs a Rust toolchain to consume or contribute to the package. The scripts under build/ exist for CI and for the rare pin bump:

docker run --rm -v "$PWD":/repo -w /repo rust:1-bookworm build/build-native.sh   # Linux, Debian 12 ABI
build\build-native.ps1 -GenerateBindings        # Windows, only if you ever need a local DLL for experiments

Other platforms (win-x64, osx-arm64, linux-arm64) are deliberately not shipped.

Publishing to nuget.org (Trusted Publishing, no API key)

The workflow publishes on every v* tag through nuget.org Trusted Publishing: the job's GitHub OIDC token is exchanged for a one-hour API key at push time, so no long-lived secret exists.

One-time setup by the nuget.org account that will own the package:

  1. nuget.org → your username → Trusted Publishing → add a policy:
    • Repository owner: the GitHub owner of this repository (for example inklusiv-team)
    • Repository: this repository's name
    • Workflow file: publish.yml (file name only)
    • Environment: leave empty
  2. In the GitHub repository add the secret NUGET_USER = that nuget.org profile name (not the email).
  3. Push a tag: git tag v0.1.1 && git push origin v0.1.1.

For a private repository the policy starts as temporarily active for 7 days and becomes permanent after the first successful publish. Command-line publishing still works with a classic API key if ever needed; the workflow does not use one.

Samples

samples/EchoBot is a complete bot in one file and doubles as the integration test: give it a throwaway account, put a real client in the room, and watch both directions decrypt.

Cross-signing

Set BootstrapCrossSigning and the account publishes a cross-signing identity the first time it starts, so clients stop marking its messages as coming from an unverified device.

This leans hard on StorePath being persistent. The identity's private half lives only in the crypto store; the homeserver keeps the public half forever. If the store is lost, start-up will not mint a replacement, because that would void every signature made under the old key and reads in every client as the account having been taken over. It logs and runs unverified instead. Restore the store.

Roadmap

  • Server-side key backup, so additional sessions of the same account can read history.
  • Additional native runtimes if a deployment ever needs one (linux-arm64 first).

License

Apache-2.0. Bundles matrix-rust-sdk (Apache-2.0).

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.3 169 9/4/2026
0.1.2 115 9/4/2026
0.1.1 122 9/3/2026