This is the Matrix Client-Server SDK for JavaScript and TypeScript. This SDK can be run in a browser or in Node.js.
The Matrix specification is constantly evolving - while this SDK aims for maximum backwards compatibility, it only guarantees that a feature will be supported for at least 4 spec releases. For example, if a feature the js-sdk supports is removed in v1.4 then the feature is eligible for removal from the SDK when v1.8 is released. This SDK has no guarantee on implementing all features of any particular spec release, currently. This can mean that the SDK will call endpoints from before Matrix 1.1, for example.
Servers may require or use authenticated endpoints for media (images, files, avatars, etc). See the Authenticated Media section for information on how to enable support for this.
Using yarn
instead of npm
is recommended. Please see the Yarn install guide
if you do not have it already.
yarn add matrix-js-sdk
import * as sdk from "matrix-js-sdk";
const client = sdk.createClient({ baseUrl: "https://matrix.org" });
client.publicRooms(function (err, data) {
console.log("Public Rooms: %s", JSON.stringify(data));
});
See below for how to enable end-to-end-encryption, or check the Node.js terminal app for a more complex example.
To start the client:
await client.startClient({ initialSyncLimit: 10 });
You can perform a call to /sync
to get the current state of the client:
client.once(ClientEvent.sync, function (state, prevState, res) {
if (state === "PREPARED") {
console.log("prepared");
} else {
console.log(state);
process.exit(1);
}
});
To send a message:
const content = {
body: "message text",
msgtype: "m.text",
};
client.sendEvent("roomId", "m.room.message", content, "", (err, res) => {
console.log(err);
});
To listen for message events:
client.on(RoomEvent.Timeline, function (event, room, toStartOfTimeline) {
if (event.getType() !== "m.room.message") {
return; // only use messages
}
console.log(event.event.content.body);
});
By default, the matrix-js-sdk
client uses the MemoryStore
to store events as they are received. For example to iterate through the currently stored timeline for a room:
Object.keys(client.store.rooms).forEach((roomId) => {
client.getRoom(roomId).timeline.forEach((t) => {
console.log(t.event);
});
});
Servers supporting MSC3916 (Matrix 1.11) will require clients, like
yours, to include an Authorization
header when /download
ing or /thumbnail
ing media. For NodeJS environments this
may be as easy as the following code snippet, though web browsers may need to use Service Workers
to append the header when using the endpoints in <img />
elements and similar.
const downloadUrl = client.mxcUrlToHttp(
/*mxcUrl=*/ "mxc://example.org/abc123", // the MXC URI to download/thumbnail, typically from an event or profile
/*width=*/ undefined, // part of the thumbnail API. Use as required.
/*height=*/ undefined, // part of the thumbnail API. Use as required.
/*resizeMethod=*/ undefined, // part of the thumbnail API. Use as required.
/*allowDirectLinks=*/ false, // should generally be left `false`.
/*allowRedirects=*/ true, // implied supported with authentication
/*useAuthentication=*/ true, // the flag we're after in this example
);
const img = await fetch(downloadUrl, {
headers: {
Authorization: `Bearer ${client.getAccessToken()}`,
},
});
// Do something with `img`.
In future the js-sdk will only return authentication-required URLs, mandating population of the Authorization
header.
This SDK provides a full object model around the Matrix Client-Server API and emits events for incoming data and state changes. Aside from wrapping the HTTP API, it:
/sync
)RoomMember
information (e.g. display names).Rooms
, RoomState
, RoomMembers
and Users
which can be listened to for things like name changes, new messages, membership
changes, presence changes, and more.matrix-js-sdk
can be used in either Node.js applications (ensure you have the latest LTS version of Node.js installed),
or in browser applications, via a bundler such as Webpack or Vite.
You can also use the sdk with Deno (import npm:matrix-js-sdk
) but its not officialy supported.
The SDK raises notifications to the application using
EventEmitter
s. The MatrixClient
itself
implements EventEmitter
, as do many of the high-level abstractions such as Room
and RoomMember
.
// Listen for low-level MatrixEvents
client.on(ClientEvent.Event, function (event) {
console.log(event.getType());
});
// Listen for typing changes
client.on(RoomMemberEvent.Typing, function (event, member) {
if (member.typing) {
console.log(member.name + " is typing...");
} else {
console.log(member.name + " stopped typing.");
}
});
// start the client to setup the connection to the server
client.startClient();
As well as the primary entry point (matrix-js-sdk
), there are several other entry points which may be useful:
Entry point | Description |
---|---|
matrix-js-sdk |
Primary entry point. High-level functionality, and lots of historical clutter in need of a cleanup. |
matrix-js-sdk/lib/crypto-api |
Cryptography functionality. |
matrix-js-sdk/lib/types |
Low-level types, reflecting data structures defined in the Matrix spec. |
matrix-js-sdk/lib/testing |
Test utilities, which may be useful in test code but should not be used in production code. |
matrix-js-sdk/lib/utils/*.js |
A set of modules exporting standalone functions (and their types). |
This section provides some useful code snippets which demonstrate the core functionality of the SDK. These examples assume the SDK is set up like this:
import * as sdk from "matrix-js-sdk";
const myUserId = "@example:localhost";
const myAccessToken = "QGV4YW1wbGU6bG9jYWxob3N0.qPEvLuYfNBjxikiCjP";
const matrixClient = sdk.createClient({
baseUrl: "http://localhost:8008",
accessToken: myAccessToken,
userId: myUserId,
});
matrixClient.on(RoomEvent.MyMembership, function (room, membership, prevMembership) {
if (membership === KnownMembership.Invite) {
matrixClient.joinRoom(room.roomId).then(function () {
console.log("Auto-joined %s", room.roomId);
});
}
});
matrixClient.startClient();
matrixClient.on(RoomEvent.Timeline, function (event, room, toStartOfTimeline) {
if (toStartOfTimeline) {
return; // don't print paginated results
}
if (event.getType() !== "m.room.message") {
return; // only print messages
}
console.log(
// the room name will update with m.room.name events automatically
"(%s) %s :: %s",
room.name,
event.getSender(),
event.getContent().body,
);
});
matrixClient.startClient();
Output:
(My Room) @megan:localhost :: Hello world
(My Room) @megan:localhost :: how are you?
(My Room) @example:localhost :: I am good
(My Room) @example:localhost :: change the room name
(My New Room) @megan:localhost :: done
matrixClient.on(RoomStateEvent.Members, function (event, state, member) {
const room = matrixClient.getRoom(state.roomId);
if (!room) {
return;
}
const memberList = state.getMembers();
console.log(room.name);
console.log(Array(room.name.length + 1).join("=")); // underline
for (var i = 0; i < memberList.length; i++) {
console.log("(%s) %s", memberList[i].membership, memberList[i].name);
}
});
matrixClient.startClient();
Output:
My Room
=======
(join) @example:localhost
(leave) @alice:localhost
(join) Bob
(invite) @charlie:localhost
A hosted reference can be found at http://matrix-org.github.io/matrix-js-sdk/index.html
This SDK uses Typedoc doc comments. You can manually build and host the API reference from the source files like this:
$ yarn gendoc
$ cd docs
$ python -m http.server 8005
Then visit http://localhost:8005
to see the API docs.
matrix-js-sdk
's end-to-end encryption support is based on the WebAssembly bindings of the Rust matrix-sdk-crypto library.
Do not use matrixClient.initLegacyCrypto()
. This method is deprecated and no longer maintained.
To initialize the end-to-end encryption support in the matrix client:
// Create a new matrix client
const matrixClient = sdk.createClient({
baseUrl: "http://localhost:8008",
accessToken: myAccessToken,
userId: myUserId,
});
// Initialize to enable end-to-end encryption support.
await matrixClient.initRustCrypto();
After calling initRustCrypto
, you can obtain a reference to the CryptoApi
interface, which is the main entry point for end-to-end encryption, by calling MatrixClient.getCrypto
.
WARNING: the cryptography stack is not thread-safe. Having multiple MatrixClient
instances connected to the same Indexed DB will cause data corruption and decryption failures. The application layer is responsible for ensuring that only one MatrixClient
issue is instantiated at a time.
You should normally set up secret storage before using the end-to-end encryption. To do this, call CryptoApi.bootstrapSecretStorage
.
bootstrapSecretStorage
can be called unconditionally: it will only set up the secret storage if it is not already set up (unless you use the setupNewSecretStorage
parameter).
const matrixClient = sdk.createClient({
...,
cryptoCallbacks: {
getSecretStorageKey: async (keys) => {
// This function should prompt the user to enter their secret storage key.
return mySecretStorageKeys;
},
},
});
matrixClient.getCrypto().bootstrapSecretStorage({
// This function will be called if a new secret storage key (aka recovery key) is needed.
// You should prompt the user to save the key somewhere, because they will need it to unlock secret storage in future.
createSecretStorageKey: async () => {
return mySecretStorageKey;
},
});
The example above will create a new secret storage key if secret storage was not previously set up.
The secret storage data will be encrypted using the secret storage key returned in createSecretStorageKey
.
We recommend that you prompt the user to re-enter this key when CryptoCallbacks.getSecretStorageKey
is called (when the secret storage access is needed).
To set up cross-signing to verify devices and other users, call
CryptoApi.bootstrapCrossSigning
:
matrixClient.getCrypto().bootstrapCrossSigning({
authUploadDeviceSigningKeys: async (makeRequest) => {
return makeRequest(authDict);
},
});
The authUploadDeviceSigningKeys
callback is required in order to upload newly-generated public cross-signing keys to the server.
If the user doesn't already have a key backup you should create one:
// Check if we have a key backup.
// If checkKeyBackupAndEnable returns null, there is no key backup.
const hasKeyBackup = (await matrixClient.getCrypto().checkKeyBackupAndEnable()) !== null;
// Create the key backup
await matrixClient.getCrypto().resetKeyBackup();
Once the cross-signing is set up on one of your devices, you can verify another device with two methods:
Use CryptoApi.bootstrapCrossSigning
.
bootstrapCrossSigning
will call the CryptoCallbacks.getSecretStorageKey callback. The device is verified with the private cross-signing keys fetched from the secret storage.
Request an interactive verification against existing devices, by calling CryptoApi.requestOwnUserVerification.
If your application previously used the legacy crypto stack, (i.e, it called MatrixClient.initLegacyCrypto()
), you will
need to migrate existing devices to the Rust crypto stack.
This migration happens automatically when you call initRustCrypto()
instead of initLegacyCrypto()
,
but you need to provide the legacy cryptoStore
and pickleKey
to createClient
:
// You should provide the legacy crypto store and the pickle key to the matrix client in order to migrate the data.
const matrixClient = sdk.createClient({
cryptoStore: myCryptoStore,
pickleKey: myPickleKey,
baseUrl: "http://localhost:8008",
accessToken: myAccessToken,
userId: myUserId,
});
// The migration will be done automatically when you call `initRustCrypto`.
await matrixClient.initRustCrypto();
To follow the migration progress, you can listen to the CryptoEvent.LegacyCryptoStoreMigrationProgress
event:
// When progress === total === -1, the migration is finished.
matrixClient.on(CryptoEvent.LegacyCryptoStoreMigrationProgress, (progress, total) => {
...
});
The Rust crypto stack is not supported in a lot of deprecated methods of MatrixClient
. If you use them, you should migrate to the CryptoApi
. Also, the legacy MatrixClient.crypto
object is not available any more: you should use MatrixClient.getCrypto()
instead.
This section is for people who want to modify the SDK. If you just want to use this SDK, skip this section.
First, you need to pull in the right build tools:
$ yarn install
To build a browser version from scratch when developing:
$ yarn build
To run tests (Jest):
$ yarn test
To run linting:
$ yarn lint