Supporting-docs now in matrix.org repo.

pull/977/head
Richard van der Hoff 7 years ago
parent 17af66105d
commit e7772af5c3

@ -1,9 +1,7 @@
This repository contains the documentation for Matrix.
This repository contains the Matrix specification.
Primarily, that means the Matrix protocol specifcation, but this repo also
comtains a lot of supporting documents, including some introductions to Matrix,
and, notably, a list of projects using Matrix which is visible on the
`matrix.org website <https://matrix.org/docs/projects/try-matrix-now.html>`_.
Note that the Matrix Project lists, which were previously kept in this
repository, are now in https://github.com/matrix-org/matrix.org.
Structure of this repository
============================
@ -24,8 +22,6 @@ Structure of this repository
- ``scripts``: scripts to generate formatted versions of the
documentation, typically HTML.
- ``specification``: the specification split up into sections.
- ``supporting-docs``: additional documents which explain design
decisions, examples, use cases, etc.
- ``templating``: the templates and templating system used to
generate the spec.

@ -12,9 +12,6 @@ mkdir -p assets
# and the swagger
./scripts/dump-swagger.py -o assets/spec/client_server/unstable.json
# also need the supporting-docs, which become the jekyll posts.
cp -rT supporting-docs assets/jekyll-posts
# create a tarball of the assets. Exclude the spec index for now, as
# we want to leave it pointing at the release versions of the specs.
# (XXX: how to maintain this?)

@ -1,129 +0,0 @@
Application Services
====================
This file contains examples of some application service
IRC Bridge
----------
Pre-conditions:
- Server admin stores the AS token "T_a" on the homeserver.
- Homeserver has a token "T_h".
- Homeserver has the domain "hsdomain.com"
1. Application service registration
::
AS -> HS: Registers itself with the homeserver
POST /register
{
url: "https://someapp.com/matrix",
as_token: "T_a",
namespaces: {
users: [
{
"exclusive": true,
"regex": "@irc\.freenode\.net/.*"
}
],
aliases: [
{
"exclusive": true,
"regex": "#irc\.freenode\.net/.*"
}
]
}
}
Returns 200 OK:
{
hs_token: "T_h"
}
2. IRC user "Bob" says "hello?" on "#matrix" at timestamp 1421416883133:
::
- AS stores message as potential scrollback.
- Nothing happens as no Matrix users are in the room.
3. Matrix user "@alice:hsdomain.com" wants to join "#matrix":
::
User -> HS: Request to join "#irc.freenode.net/#matrix:hsdomain.com"
HS -> AS: Room Query "#irc.freenode.net/#matrix:hsdomain.com"
GET /rooms/%23irc.freenode.net%2F%23matrix%3Ahsdomain.com?access_token=T_h
[Starts blocking]
AS -> HS: Creates room. Gets room ID "!aasaasasa:hsdomain.com".
AS -> HS: Sets room name to "#matrix".
AS -> HS: Sends message as ""@irc.freenode.net/Bob:hsdomain.com"
PUT /rooms/%21aasaasasa%3Ahsdomain.com/send/m.room.message
?access_token=T_a
&user_id=%40irc.freenode.net%2FBob%3Ahsdomain.com
&ts=1421416883133
{
body: "hello?"
msgtype: "m.text"
}
HS -> AS: User Query "@irc.freenode.net/Bob:hsdomain.com"
GET /users/%40irc.freenode.net%2FBob%3Ahsdomain.com?access_token=T_h
[Starts blocking]
AS -> HS: Creates user using CS API extension.
POST /register?access_token=T_a
{
type: "m.login.application_service",
user: "irc.freenode.net/Bob"
}
AS -> HS: Set user display name to "Bob".
[Finishes blocking]
[Finished blocking]
- HS sends room information back to client.
4. @alice:hsdomain.com says "hi!" in this room:
::
User -> HS: Send message "hi!" in room !aasaasasa:hsdomain.com
- HS sends message.
- HS sees the room ID is in the AS namespace and pushes it to the AS.
HS -> AS: Push event
PUT /transactions/1?access_token=T_h
{
events: [
{
content: {
body: "hi!",
msgtype: "m.text"
},
origin_server_ts: <generated by hs>,
user_id: "@alice:hsdomain.com",
room_id: "!aasaasasa:hsdomain.com",
type: "m.room.message"
}
]
}
- AS passes this through to IRC.
5. IRC user "Bob" says "what's up?" on "#matrix" at timestamp 1421418084816:
::
IRC -> AS: "what's up?"
AS -> HS: Send message via CS API extension
PUT /rooms/%21aasaasasa%3Ahsdomain.com/send/m.room.message
?access_token=T_a
&user_id=%40irc.freenode.net%2FBob%3Ahsdomain.com
&ts=1421418084816
{
body: "what's up?"
msgtype: "m.text"
}
- HS modifies the user_id and origin_server_ts on the event and sends it.

@ -1,141 +0,0 @@
---
layout: post
title: Getting involved
categories: guides
---
# How can I get involved?
Matrix is an ecosystem consisting of several apps written by lots of people. We at Matrix.org have written one server and a few clients, and people in the community have also written several clients, servers, and Application Services. We are collecting [a list of all known Matrix-apps](https://matrix.org/blog/try-matrix-now/).
|
You have a few options when it comes to getting involved: if you just want to use Matrix, you can [register an account on a public server using a public webclient](#reg). If you have a virtual private server (VPS) or similar, you might want to [run a server and/or client yourself](#run). If you want to look under the hood, you can [checkout the code and modify it - or write your own client or server](#checkout). Or you can write an [Application Service](#as), for example a bridge to an existing ecosystem.
|
We very much welcome [contributions](https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.rst) to any of our projects, which you can find in our [github space](https://github.com/matrix-org/).
|
<a class="anchor" id="reg"></a>
## Access Matrix via a public webclient/server
The easiest thing to do if you want to just have a play, is to use the [Riot.im
webclient](https://riot.im). You can use it as a guest, or register for an
account. For details of other clients, see
[https://matrix.org/blog/try-matrix-now](https://matrix.org/blog/try-matrix-now).
<a class="anchor" id="run"></a>
## Run a server and/or client yourself
You can clone our open source projects and run clients and servers yourself. Here is how:
### Running your own client:
You can run your own Matrix client; there are [numerous clients
available](https://matrix.org/blog/try-matrix-now/). You can easily [run your
own copy](https://github.com/vector-im/vector-web#getting-started) of the
Riot.im web client.
### Running your own homeserver:
One of the core features of Matrix is that anyone can run a homeserver and join the federated network on equal terms (there is no hierarchy). If you want to set up your own homeserver, please see the relevant docs of the server you want to run. If you want to run Matrix.org's reference homeserver, please consult the [readme of the Synapse project](https://github.com/matrix-org/synapse/blob/master/README.rst).
|
Note that Synapse comes with a bundled Matrix.org webclient - but you can tell it to use your [development checkout snapshot instead](https://github.com/matrix-org/matrix-angular-sdk#matrix-angular-sdk) (or to not run a webclient at all via the "web_client: false" config option).
|
<a class="anchor" id="checkout"></a>
## Checkout our code - or write your own
As described above, you can clone our code and [run a server and/or client yourself](#run). Infact, all the code that we at Matrix.org write is available from [our github](http://github.com/matrix-org) - and other servers and clients may also be open sourced - see [our list of all known Matrix-apps](https://matrix.org/blog/try-matrix-now/).
|
You can also implement your own client or server - after all, Matrix is at its core "just" a specification of a protocol.
|
### Write your own client:
The [client-server API
spec](http://matrix.org/docs/spec/client_server/latest.html) describes what API
calls are available to clients, and there is a [HOWTO
guide](http://matrix.org/docs/guides/client-server.html) which provides an
introduction to using the API along with some common operations. A quick
step-by-step guide would include:
|
1. Get a user either by registering your user in an existing client or running the [new-user script](https://github.com/matrix-org/synapse/blob/master/scripts/register_new_matrix_user) if you are running your own Synapse homeserver.
2. Assuming the homeserver you are using allows logins by password, log in via the login API:
```
curl -XPOST -d '{"type":"m.login.password", "user":"example", "password":"wordpass"}' "http://localhost:8008/_matrix/client/api/v1/login"
```
3. If successful, this returns the following, including an `access_token`:
{
"access_token": "QGV4YW1wbGU6bG9jYWxob3N0.vRDLTgxefmKWQEtgGd",
"home_server": "localhost",
"user_id": "@example:localhost"
}
4. This ``access_token`` will be used for authentication for the rest of your API calls. Potentially the next step you want is to make a call to the sync API and get the last few events from each room your user is in:
```
curl -XGET "http://localhost:8008/_matrix/client/r0/sync?access_token=YOUR_ACCESS_TOKEN"
```
5. The above will return something like this:
{
"next_batch": "s72595_4483_1934",
"rooms": {
"join": {
"!726s6s6q:example.com": {
"state": {
"events": [
...
]
},
"timeline": {
"events": [
...
]
}
},
...
}
}
}
You can then use the "next_batch" token to start listening for new events like so:
```
curl -XGET "http://localhost:8008/_matrix/client/r0/sync?access_token=YOUR_ACCESS_TOKEN&since=s72595_4483_1934"
```
6. This request will block waiting for an incoming event, timing out after several seconds if there is no event. This ensures that you only get new events. Now you have the initial room states, and a stream of events - a good client should be able to process all these events and present them to the user. And potentially you might want to add functionality to generate events as well (such as messages from the user, for example)!
### Write your own server:
We are still working on the server-server spec, so the best thing to do if you are interested in writing a server, is to come talk to us in [#matrix:matrix.org](https://matrix.to/#/#matrix:matrix.org).
If you are interested in how federation works, please see the [Server-Server API spec](http://matrix.org/docs/spec/server_server/unstable.html).
|
<a class="anchor" id="as"></a>
## Write an Application Service:
Information about Application services and how they can be used can be found in the [Application services](./application_services.html) document! (This is based on Kegan's excellent blog posts, but now lives here so it can be kept up-to-date!)

@ -1,691 +0,0 @@
---
layout: post
title: FAQ
date: 2015-08-19 16:58:07
categories: guides
---
<link href="/docs/css/faq.css" type="text/css" rel="stylesheet" />
# FAQ
{:.no_toc}
Categories
----------
{:.no_toc}
[General](#general)
[Quick Start](#quick-start)
[Standard](#standard)
[Servers](#servers)
[Clients](#clients)
|
FAQ Content
-----------
{:.no_toc}
* TOC
{:toc .toc}
### General
##### What is Matrix?
Matrix is an open standard for interoperable, decentralised,
real-time communication over IP. It can be used to power Instant
Messaging, VoIP/WebRTC signalling, Internet of Things communication - or anywhere
you need a standard HTTP API for publishing and subscribing to
data whilst tracking the conversation history.
|
Matrix defines the standard, and provides open source reference implementations
of Matrix-compatible Servers, Clients, Client SDKs and Application Services
to help you create new communication solutions or extend the capabilities
and reach of existing ones.
##### What is Matrix's Mission?
Matrix's initial goal is to fix the problem of fragmented IP communications:
letting users message and call each other without having to care what app
the other user is on - making it as easy as sending an email.
|
The longer term goal is for Matrix to act as a generic HTTP messaging and data
synchronisation system for the whole web - allowing people, services and devices
to easily communicate with each other, empowering users to own and control their
data and select the services and vendors they want to use.
##### What does Matrix provide?
Matrix provides:
- [Open Standard](/docs/spec) HTTP APIs for transferring JSON messages (e.g. instant messages, WebRTC signalling), including:
- [Client\<-\>Server API](/docs/spec#client-server-api-v1) - defines how Matrix compatible clients communicate with Matrix homeservers.
- [Server\<-\>Server API](/docs/spec#federation-api) - defines how Matrix homeservers exchange messages and synchronise history with each other.
- [Application Service API](/docs/spec/#application-service-api) - defines how to extend the functionality of Matrix with 'integrations' and bridge to other networks.
- [Modules](/docs/spec/#modules) - specifies features that must be implemented by particular classes of clients.
- Open source reference implementations of:
- Clients (Web (React), iOS, Android)
- Client SDKs (Javascript, Web (React), iOS, Android)
- Homeservers (Synapse)
- Application Services (bridges to IRC, Slack, Skype, Lync and more...)
- The actual ecosystem and community of everyone running Matrix servers and services
- Loads of 3rd party contributions of clients, SDKs, servers and services.
You can find the full list of Matrix enabled projects at [https://matrix.org/blog/try-matrix-now](https://matrix.org/blog/try-matrix-now).
##### What does this mean for users?
The aim is to provide an analogous ecosystem to email - one where you
can communicate with pretty much anyone, without caring what app or
server they are using, using whichever app & server you chose to use,
and use a neutral identity system like an e-mail address or phone
number to discover people to talk to.
##### What kind of company is Matrix.org?
Matrix.org is an open initiative which acts as a neutral and independent custodian
of the Matrix standard. As of Sept 2017 we are finally in the process of incorporating
it as a dedicated non-profit entity (most likely a limited by guarantee UK private company
called the Matrix.org Foundation).
##### Who is funding Matrix.org?
Matrix.org is currently (Sept 2017) funded by the community, through a
combination of community support (via
[Patreon](https://patreon.com/matrixdotorg),
[Liberapay](https://liberapay.com/matrixdotorg), Bitcoin and Ethereum),
corporate sponsorship, and grant funding. Current Elliptic-level supporters on
Patreon and corporate sponsors can be found on our [supporters
page](https://matrix.org/blog/supporters). If you would like to support the core
Matrix team as a member of the community, please visit our
[Patreon](https://patreon.com/matrixdotorg) or
[Liberapay](https://liberapay.com/matrixdotorg) pages, or you can send us
Bitcoin at 1LxowEgsquZ3UPZ68wHf8v2MDZw82dVmAE or Ethereum at ETH
0xA5f9a4f9E024F6D727f7afdA9257e22329A97485. If you would like to sponsor the
team as a corporation, or are interested in paying for prioritised or custom
development, please [get in touch](mailto:matthew@matrix.org).
For the first three years of Matrix's development (2014-2017), most of the core
contributors worked for [Amdocs](https://www.amdocs.com), who paid for them to
work fulltime on Matrix. In July 2017, Amdocs considered the project to be
sufficiently successful that it could now self-support and so stopped funding.
The majority of the core team is now employed by New Vector, an independent company
set up to hire the team and support Matrix's development. Other contributors
are funded by their own employers or donate their own time to the project.
##### Who is building Matrix?
The core team is ~12 people with extensive experience in building custom
VoIP and Messaging apps for mobile network operators. Most of us work for New Vector,
but there are an increasing number of contributors from other companies and
folks all over the internet.
##### Why are you called Matrix?
We are called Matrix because we provide a structure in which all
communication can be matrixed together.
|
No, it's nothing to do with the film (although you could go and build virtual
worlds on top of Matrix if you wanted :)
##### Why have you released this as open source?
We believe that any open standard defining interoperable communication
needs to be justified, demonstrated and validated with transparent open
source implementations. For Matrix to achieve its mission of making all
communications services interoperable we believe it needs to be truly
open, giving people access to take all the code we produce and to use
and build on top of it.
##### What do you mean by open?
Matrix is an open standard, meaning that we have freely published the
details for how to communicate interoperably using the Matrix set of
HTTP APIs. We encourage anyone and everyone to use the APIs and build
their own projects which implement them and so benefit from
interoperability with the rest of the Matrix ecosystem. We also
ensure the standard is not encumbered by any known patent licensing
requirements.
|
Matrix is also open source, meaning that we have released the source
code of the reference servers, clients and services to the public domain
under the [Apache Licence v2](http://www.apache.org/licenses/LICENSE-2.0.html), to
encourage anyone and everyone to run their own servers and clients, and
enhance them and contribute their enhancements as they see fit.
##### What does federated mean?
Federation allows separate deployments of a communication service to
communicate with each other - for instance a mail server run by Google
federates with a mail server run by Microsoft when you send email from
@gmail.com to @hotmail.com.
|
Federation is different to interoperability, as interoperable clients
may simply be running on the same deployment - whereas in federation the
deployments themselves are exchanging data in a compatible manner.
|
Matrix provides open federation - meaning that anyone on the internet
can join into the Matrix ecosystem by deploying their own server.
##### How is this like e-mail?
When email was first set up in the early 80s, companies like Compuserve
and AT&T and Sprint set up isolated email communities which only allowed
you to exchange mail with users on the same system.  If you got your
email from one service and your friend from another, then you couldn't
message each other.  This is basically the situation we're in today with
VoIP and IM.
##### Why has no-one done this before?
There have been several attempts before including SIP, XMPP and RCS.
 All of these have had some level of success, but many different
technological/usability/economic factors have ended up limiting their
success. Unfortunately, we've not ended up in a world where everyone
has a SIP URI or Jabber ID on their business card, or a phone that
actually uses RCS.
##### What is the difference between Matrix and IRC?
We love IRC.  In fact, as of today the core Matrix team still uses it as
our primary communication tool. Between us we've written IRCds, IRC bots
and admined dreamforge, UnrealIRCd, epona, ircservices and several
others. That said, it has some limitations that Matrix seeks to improve
on:
- Text only
- No history
- No multiple-device support
- No presence support
- Fragmented identity model
- No open federation
- No standard APIs, just a rather limited TCP line protocol
- Non-standardised federation protocol
- No built-in end-to-end encryption
- Disruptive net-splits
- Non-extensible
[IRCv3](http://ircv3.net) exists and is addressing some of these issues;
this is great news and we wish them well. It's almost a contradiction
in terms to get competitive between openly interoperable communication
projects - we look forward to increasing the richness of Matrix\<-\>IRC
bridges as the project progresses.
##### What is the difference between Matrix and XMPP?
The Matrix team used XMPP (Openfire, ejabberd, spectrum, asmack,
XMPPFramework) for IM before starting to experiment with open HTTP APIs
as an alternative in around 2012. The main issues with XMPP that
drove us in this direction **as of 2012** were:
- Not particularly web-friendly - you can't easily speak XMPP from a
web browser. *N.B. Nowadays you have options like XMPP-FTW and
Stanza.io that help loads with letting browsers talk XMPP*
- Single logical server per MUC is a single point of control and
availability. *MUCs can be distributed over multiple physical
servers, but they still sit behind a single logical JID and domain.
FMUC improves this with a similar approach to Matrix, but as of Oct
2015 there are no open source implementations. The MIX XMPP extension
also aims to address this limitation*.
- History synchronisation is very much a second class citizen feature
- Bridging to other protocols and defragmenting existing communication
apps and networks is very much a second class citizen feature
- Stanzas aren't framed or reliably delivered without extensions. *See
[wiki.xmpp.org](http://wiki.xmpp.org/web/Myths#Myth_Four:_XMPP_is_unreliable_without_a_bunch_of_extensions.)
for an XMPP take on this*
- Multiple device support is limited. *Carbons and MAM aim to resolve this*
- Baseline feature set is so minimal that fragmentation of features
between clients and servers is common, especially as interoperability
profiles for features have fallen behind (as of July 2015)
- No strong identity system (i.e. no standard E2E PKI, unless you
count X.509 certs, which [are
questionable](http://www.thoughtcrime.org/blog/ssl-and-the-future-of-authenticity/))
- Not particularly well designed for mobile use cases: push;
bandwidth-efficient transports. *Since the time of writing a [Push
XEP has appeared](http://xmpp.org/extensions/xep-0357.html), and
[wiki.xmpp.org](http://wiki.xmpp.org/web/Myths#Myth_Three:_It.27s_too_bandwidth-inefficient_for_mobile.)
states that XMPP is usable over a 9600bps + 30s latency link.*
This said, the whole area of XMPP vs Matrix is quite subjective.
Rather than fighting over which open interoperable communication
standard works the best, we should just collaborate and bridge everything
together. The more federation and interoperability the better.
|
We think of Matrix and XMPP as being quite different; at its core
Matrix can be thought of as an eventually consistent global JSON db with
an HTTP API and pubsub semantics - whilst XMPP can be thought of as a
message passing protocol. You can use them both to build chat systems;
you can use them both to build pubsub systems; each comes with different
tradeoffs. Matrix has a deliberately extensive 'kitchen sink' baseline
of functionality; XMPP has a deliberately minimal baseline set of
functionality. If XMPP does what you need it to do, then we're genuinely
happy for you :) Meanwhile, rather than competing, an XMPP Bridge like
[Skaverat's xmpptrix beta](https://github.com/SkaveRat/xmpptrix) or
[jfred's matrix-xmpp-bridge](https://github.com/jfrederickson/matrix-xmpp-bridge)
or Matrix.org's own [purple-matrix](https://github.com/matrix-org/purple-matrix/)
has potential to let both environments coexist and make the most of each
other's benefits.
##### What is the difference between Matrix and PSYC?
PSYC is a open federated messaging protocol loosely inspired by IRC.  In
version 1 it was a standalone protocol, and in version 2 it is being
reutilised as a messaging layer on top of GNUnet.  We honestly don't
know that much about it, beyond trying to use psycd as an XMPP\<-\>IRC
bridge in 2010. Matrix differentiates primarily by providing simple HTTP
APIs rather than the more exotic compact line protocol in PSYC v1 or the
comprehensive GNUnet stack in v2, and Matrix focuses more on decentralised
conversation history rather than just decentralised chat servers.
On the other hand, Matrix doesn't provide the metadata protection
guarantees that GNUnet/PSYC aims for.
|
See [http://about.psyc.eu/Matrix](http://about.psyc.eu/Matrix) for
PSYC's views on Matrix.
##### What is the difference between Matrix and Tox?
Tox.chat looks to be a very cool clone of Skype - a fully decentralised
peer-to-peer network.  Matrix is deliberately not a 'pure' peer-to-peer
system; instead each user has a well-defined homeserver which stores
his data and that he can depend upon.  Matrix provides HTTP APIs;
Tox.chat provides C APIs. As of October 2015 Tox doesn't seem to have an
answer yet for decentralised conversation history storage.
##### How does Matrix compare with something like Trillian or Pidgin?
Trillian and Pidgin and similar aggregating IM clients merge all your IM
activity into a single app.  However, your history and
identity is still fragmented across the networks.  People can't find you
easily, and your history is fragmented (other than on the device
where the client runs).   And rather than being able to chose the right
app for the job when communicating with people, you are pushed towards
relying on a specific aggregation app.
Matrix lets you get the best of both worlds by linking to all the
different networks (XMPP, AIM, ICQ, Lync, Skype etc) on the serverside,
using bridges which can be run by anyone. Matrix then provides a simple
standard HTTP API to access any of these networks, and lets you choose
whichever client you prefer (either as a 'native' Matrix client or using
a non-Matrix client from one of the networks which has been bridged in).
##### What Matrix compliant apps are there?
Quite a few, ranging from the glossy mass-market to the geeky command-line. There's even an emacs macro. Check out [https://matrix.org/blog/try-matrix-now](https://matrix.org/blog/try-matrix-now) for the current
list of Matrix enabled projects.
##### What bridges to other networks are available?
The number of 'bridges' which integrate existing communication networks into
Matrix are growing on a daily basis - both written by the Matrix core team
and contributed by the wider community. The full list can be seen at
[https://matrix.org/blog/try-matrix-now](https://matrix.org/blog/try-matrix-now), but the core ones as of Oct 2015 include:
* [matrix-appservice-irc](https://github.com/matrix-org/matrix-appservice-irc) - an increasingly comprehensive Matrix\<-\>IRC bridge
* [matrix-appservice-verto](https://github.com/matrix-org/matrix-appservice-verto) - links from Matrix to FreeSWITCH via the Verto protocol
* [matrix-appservice-slack](https://github.com/matrix-org/matrix-appservice-slack) - a basic bridge to Slack
* [node-purple](https://github.com/matrix-org/node-purple) - lets you access any of the 20+ protocols supported by
[libpurple](https://developer.pidgin.im/wiki/WhatIsLibpurple), including
Skype, Lync, XMPP, etc)
* [matrix-appservice-bridge](https://github.com/matrix-org/matrix-appservice-bridge) - a general NodeJS framework for writing bridges
Writing new bridges is incredibly fun and easy - see the [matrix-appservice-bridge HOWTO](https://github.com/matrix-org/matrix-appservice-bridge/blob/master/HOWTO.md)
for an example of how to write a fully functional Slack bridge in less than 100 lines of code!
##### Why do you think existing apps will ever join this officially?
We firmly believe it is what is right for the consumer. As people begin
to use interoperable communications tools, service providers will see the
benefit and compete on quality of service, security and features rather
than relying on locking people into their walled garden. We believe as
soon as users see the availability and benefits of interoperable
services they will demand it.
##### Why aren't you doing this through the IETF? or W3C? or 3GPP?
We do recognise the advantages of working with existing standards
bodies. We have been focused on writing code and getting it out, and the standard has been evolving rapidly since initial release in September 2014.
Once the standard has matured sufficiently it may well be appropriate to work with an official
standard body to maintain it going forwards.
### Quick Start
##### How do I get an account and get started?
The quickest way is to pick a client from [https://matrix.org/blog/try-matrix-now](https://matrix.org/blog/try-matrix-now) and sign up.
Please note that you can point clients to access any homeserver - you don't have to use matrix.org,
although as of day 1, matrix.org is the only communal homeserver
available.
##### What can I actually do with this?
A typical client provides a simple chatroom interface to Matrix -
letting the user interact with users and rooms anywhere within the
Matrix federation.  Text and image messages are supported, and basic
voice-only VoIP calling via WebRTC is supported in one-to-one rooms.
(As of October 2015, experimental multi-way calling is also available
on Riot.im).
##### How do I connect my homeserver to the public Matrix network?
See
[http://github.com/matrix-org/synapse](http://github.com/matrix-org/synapse)
for details
##### How do I Matrix-enable my existing app?
If your app doesn't have any communication capability already, you'll want
to use one of the Matrix client SDKs to add it in. These come in different
levels of sophistication - ranging from a simple HTTP API wrapper (like matrix-js-sdk, matrix-ios-sdk or matrix-android-sdk)
through to reusable UI components (like matrix-react-sdk and matrix-ios-kit). Pick
the one for your platform, or a 3rd party one if none of the above work for you,
and get plugging it in. You'll probably also want to read the [Client-Server API
HOWTO](http://matrix.org/docs/howtos/client-server.html) too.
If you already have communication infrastructure set up (XMPP, custom HTTP, or whatever),
then you'll want to run a bridge to expose it to the wider Matrix ecosystem.
See [matrix-appservice-bridge HOWTO](https://github.com/matrix-org/matrix-appservice-bridge/blob/master/HOWTO.md) for a
guide of how to write bridges using the matrix-appservice-bridge framework, or co-opt one
from the list at [https://matrix.org/blog/try-matrix-now](https://matrix.org/blog/try-matrix-now).
[Application Service API](/docs/spec/#application-service-api) gives the details of the API
that bridges have to implement.
##### How can I write a client on Matrix?
See the [Client-Server API
HOWTO](http://matrix.org/docs/howtos/client-server.html) and the [API
docs](/docs/api) and [the Spec](/docs/spec) for all the details you need
to write a client.
##### How can I help out with this?
Come say hi on [\#matrix:matrix.org](https://matrix.to/#/#matrix:matrix.org)! Install synapse and tell us how you get on. Critique the spec.  Write
clients. Write bridges! Run bridges! Nose around in [Jira](https://matrix.org/jira) and
send us some pull requests on github to fix some bugs or add some features! You could even
try to write a homeserver (but be warned, Matrix's architecture makes homeservers orders of
magnitude harder than clients or bridges.)
See [CONTRIBUTING.rst](http://github.com/matrix-org/synapse/tree/master/CONTRIBUTING.rst) for
full details on how to contribute to the project. All are welcome!
##### Where can I get support?
[\#matrix:matrix.org](https://matrix.to/#/#matrix:matrix.org) aka \#matrix on irc.freenode.is your best bet.
##### How do I register custom matrix event types?
We're not yet managing a registry of custom matrix event types.  If you
have any particularly good ones you want to tell the world about, please
let us know on #matrix-dev:matrix.org.
##### How mature is this?
We started working on Matrix in July 2014, and opened it to the
public in September 2014. We got all the core features in place in December 2014
and entered beta, and since then have been iterating away on the beta refining the
architecture and APIs, fixing bugs and scalability, and adding new features, clients,
bridges etc.
As of October 2015 (synapse 0.10) it's good for serious experimentation and
non-production services and can absolutely be used in the real world. However, we're
still in beta and we'll want to freeze the spec and implement clustering and other
nice features before we really declare it ready for production.
### Standard
##### What is a client?
Users in Matrix use one or more clients to communicate. This could be any combination of a web client, a command line client, a mobile client - or embedded clients built into existing apps. It could even be a piece of hardware (e.g. a drone) that is Matrix enabled.
##### Can I use Matrix without installing a Matrix client?
Sure. An ever increasing number of protocols are being bridged into Matrix, so if you use something like IRC on Freenode you may well be indirectly benefiting from Matrix, as others may be connected into the IRC channel via Matrix.
##### What is a home server?
A user's clients connect to a single homeserver, which stores the communication history and account information for that user, and shares data with the wider Matrix ecosystem by synchronising communication history with other homeservers.
##### What is a MXID?
Matrix user IDs (MXID) are unique user IDs. They are in the format ```@username:homeserver.tld``` (this format is used to avoid confusing them with email addresses). They are intended to be fairly hidden (although right now they are not) - instead you will find and identify other users via 3PIDs.
##### What is a 3PID?
Third-party IDs (3PIDs) are IDs from other systems or contexts, such as email addresses, social network accounts and phone numbers.
##### What is an identity server?
Users in Matrix are identified internally via their matrix user ID (MXID). However, existing 3rd party ID (3PID) namespaces such as email addresses or phone numbers should be used publically to identify Matrix users, at least for invitation purposes. A Matrix "Identity" describes both the user ID and any other existing IDs from third party namespaces linked to their account.
|
Matrix users can link third-party IDs (3PIDs) to their user ID. Linking 3PIDs creates a mapping from a 3PID to a user ID. This mapping can then be used by Matrix users in order to discover the MXIDs of their contacts.
|
In order to ensure that the mapping from 3PID to user ID is genuine, a globally federated cluster of trusted "Identity Servers" (IS) are used to verify the 3PID and persist and replicate the mappings.
Usage of an IS is not required in order for a client application to be part of the Matrix ecosystem. However, without one clients will not be able to look up user IDs using 3PIDs.
|
The precise architecture of identity servers is currently in flux and subject to change as we work to fully decentralise them.
##### Where do my conversations get stored?
Each homeserver stores the communication history and account information for all of its clients, and shares data with the wider Matrix ecosystem by synchronising communication history with other homeservers and their clients. Clients typically communicate with each other by emitting events in the context of a virtual room. Room data is replicated across all of the homeservers *whose users are participating in a given room*.
##### What are redactions?
Since events are extensible it is possible for malicious users and/or servers to add keys that are, for example offensive or illegal. Since some events cannot be simply deleted (e.g. membership events) we instead 'redact' events, essentially stripping the event of all keys that are not required by the protocol. Redacting an event cannot be undone, allowing server owners to also delete the offending content from the databases.
##### How do you do VoIP calls on Matrix?
Voice (and video) over Matrix uses the WebRTC 1.0 standard to transfer call media (i.e. the actual voice and video traffic). Matrix is used to signal the establishment and termination of the call by sending call events, like any other event.
##### Are VoIP calls encrypted?
WebRTC encrypts the media that's being sent. The signalling events that set up (and end) the call are encrypted if the room they were sent in has enabled encryption.
##### Do I need a TURN server?
VoIP calls should work if both parties are on public networks. However, in practice one (or both) devices are often behind NAT, and so having a [TURN](https://en.wikipedia.org/wiki/Traversal_Using_Relays_around_NAT) server is important to help set up the call.
See [this guide](https://github.com/matrix-org/synapse/blob/master/docs/turn-howto.rst) for setting up a TURN server with Synapse.
##### Can I log into other homeservers with my username and password?
Currently, no. We are looking at options for decentralising or migrating user accounts between multiple servers, and might add this feature at a later stage.
##### Why Apache Licence?
The Apache Licence is a permissive licence. We want the Matrix protocol itself to be free and open, but people are free to create both free and commercial apps and services that uses the protocol. In our opinion, any Matrix-service only enhances the Matrix ecosystem.
##### Can I write a Matrix homeserver?
Yes. Matrix is just a spec, so implementations of the spec are very welcome! It should be noted that as of October 2015, changes are still being made to the spec, so if you want to write a Matrix homeserver, it is strongly recommended that you chat to the Matrix.org devs in [\#matrix:matrix.org](https://matrix.to/#/#matrix:matrix.org) first! You can also read about the [Federation API here](https://matrix.org/docs/spec/server_server/unstable.html).
##### How secure is this?
Server-server traffic is mandatorily TLS from the outset. Server-client traffic mandates transport layer encryption other than for tinkering. Servers maintain a public/private key pair, and sign the integrity of all messages in the context of the historical conversation, preventing tampering. Server keys are distributed using a [Perspectives](https://perspectives-project.org/)-style system.
End-to-end encryption is now available in the various [Riot.im](https://Riot.im) builds! This allows you to encrypt both 1:1 and group chats to protect user data stored on servers, using the [Olm](https://matrix.org/git/olm) cryptographic ratchet implementation. Read more on the [blog post](https://matrix.org/blog/2016/11/21/matrixs-olm-end-to-end-encryption-security-assessment-released-and-implemented-cross-platform-on-riot-at-last/) that announced the feature!
Privacy of metadata is not currently protected from server administrators - a malicious homeserver administrator can see who is talking to who and when, but not what is being said (once E2E encryption is enabled). See [this presentation from Jardin Entropique](http://matrix.org/~matthew/2015-06-26%20Matrix%20Jardin%20Entropique.pdf) for a more comprehensive discussion of privacy in Matrix.
##### What is Perspectives?
Rather than relying on Certificate Authorities (CAs) as in traditional SSL, a [Perspectives](https://perspectives-project.org/)-style system uses a more decentralized model for verifying keys. Perspectives uses notary servers to verify that the same key is seen across the network, making a man-in-the-middle attack much harder since an attacker must insert itself into multiple places. For federation in Matrix, each Home Server acts as a notary. When one Home Server connects to another Home Server that uses a key that it doesn't recognize, it contacts other Home Servers to ensure that they all see the same key from that Home Server.
##### Why HTTP? Doesn't HTTP suck? Why don't you use websockets/CoAP/HTTP2/etc?
HTTP is indeed not the most efficient transport, but it is ubiquitous, very well understood and has numerous implementations on almost every platform and language. It also has a simple upgrade path to HTTP/2, which is relatively bandwidth and round-trip efficient.
It has thus been chosen as the mandatory baseline of the exchange, but it is still entirely possible to use more fancy protocols for communication between clients and server (see for example this [websocket transport draft](https://github.com/matrix-org/matrix-doc/blob/master/drafts/websockets.rst)), and it's also possible in the future that negotiation of more efficient protocols will be added for the federation between servers, with HTTP+JSON remaining as the compability baseline.
### Servers
##### What is Synapse?
Synapse is a reference "homeserver" implementation of Matrix from the core development team at matrix.org, written in Python 2/Twisted. It is intended to showcase the concept of Matrix and let folks see the spec in the context of a codebase and let you run your own homeserver and generally help bootstrap the ecosystem.
##### How do I join the global Matrix federation?
You can download and run one of the available Matrix servers - please see [this guide](http://matrix.org/docs/guides/getting_involved.html#run) for details!
##### What ports do I have to open up to join the global Matrix federation?
We recommend servers use port 8448 for server\<-\>server HTTPS traffic. Look at ["Setting up Federation"](https://github.com/matrix-org/synapse#setting-up-federation) in the Synapse readme file for details.
Client\<-\>Server traffic can talk directly to Synapse via port 8448, but as by default Synapse creates a self-signed TLS certificate this can cause problems for clients which can't easily trust self-signed certificates (e.g. most web browsers). Instead, you can proxy access to Synapse's HTTP listener on port 8008 via an existing HTTPS proxy with a valid certificate (e.g. an nginx listening on port 443), or you can point Synapse at a valid X.509 signed TLS certificate. In future, Synapse will probably use letsencrypt to autogenerate valid certificates rather than self-signed ones during installation, simplifying this process enormously.
You can also put Synapse entirely behind an existing TLS load balancer and not expose port 8448 at all. In this situation, Synapse will need to be configured to share the same *public* TLS certificate as the load balancer (as Synapse uses the public certificate for identity in other areas too, and it has to match the certificate that other servers see when they connect).
##### How do I run my own homeserver?
Follow the instructions for the homeserver you want to run. If you want to run Synapse, the reference homeserver from Matrix.org, follow [these instructions](https://github.com/matrix-org/synapse#synapse-installation).
##### Can I run my own identity server?
Yes - the reference implementation is
[sydent](https://github.com/matrix-org/sydent) and you can run your own ID server cluster that tracks 3rd party to Matrix ID mappings. This won't be very useful right now, though, and we don't recommend it.
If you want your server to participate in the global replicated Matrix ID
service then please get in touch with us. Meanwhile, we are looking at
ways of decentralising the 'official' Matrix identity service so that
identity servers are 100% decentralised and can openly federate with
each other. **N.B. that you can use Matrix without ever using the
identity service - it exists only to map 3rd party IDs (e.g. email
addresses) to matrix IDs to aid user discovery**.
##### What are Synapse's platform requirements?
Synapse will use as much RAM as you give it in order to cache conversations in RAM to avoid hitting the database. For small deployments (<50 active users) around 512MB of RAM is probably okay. You can configure the amount of RAM used by synapse with the event_cache_size config parameter - the more events in the cache, the more RAM required. Synapse itself requires relatively little diskspace other than for logging (which as of October 2015 is quite verbose for debugging purposes), but as it caches the content of all the file attachments (images, videos etc) viewed by its users, you may need to size storage appropriately. Synapse is currently effectively single threaded, and will never use more than 1 core.
|
For better performance, one should back Synapse with a Postgres database rather than the default SQLite - see [https://github.com/matrix-org/synapse/tree/master/README.rst#using-postgresql](https://github.com/matrix-org/synapse/tree/master/README.rst#using-postgresql) for details.
##### Why is Synapse in Python/Twisted?
This is because both provide a mature and well known event-driven async IO framework for writing serverside code. Whilst this has been okay for our initial experimentation and proof of concept, it's likely that future homeserver work will be written in a more strongly typed language (e.g. Go).
##### Why aren't you using an ORM layer like SqlAlchemy in Synapse?
Synapse is *very* database dependent (as of Oct 2015; this is improving in the near future however), and we like having the flexibility to sculpt our own queries.
##### Will Synapse share my chat data with other servers in the federation?
Data is only shared between servers of participating users of a room. If all users in a room are on your server, no data is shared with other servers.
##### Why can't I rename my homeserver?
Currently, the homeserver name is assumed never to change. This means that if you rename your server, other servers will think it's a different server.
Perhaps in the future we will add an API for changing the homeserver name, but for now this is not supported.
### Clients
##### Where can I find a mobile app?
Riot is available for Android and iOS.
The iOS version can be downloaded from the [Apple store](https://itunes.apple.com/us/app/vector.im/id1083446067).
The Android version can be downloaded from the [Google Play store](https://play.google.com/store/apps/details?id=im.vector.alpha) or [F-Droid](https://f-droid.org/repository/browse/?fdid=im.vector.alpha). If you are not sure which one to choose, install Riot from the [Google Play store](https://play.google.com/store/apps/details?id=im.vector.alpha).
For the Android app, you can also install the latest development version
built by [Jenkins](http://matrix.org/jenkins/job/VectorAndroidDevelop). Use it at your own risk and only if you know what you are doing.
##### I installed Riot via F-Droid, why is it draining my battery?
The F-Droid release of Riot does not use [Google Cloud Messaging](https://developers.google.com/cloud-messaging/). This allows users that do not have or want Google Services installed to use Riot.
The drawback is that Riot has to pull for new messages, which can drain your battery. To counter this, you can change the delay between polls in the settings. Higher delay means better battery life (but may delay receiving messages). You can also disable the background sync entirely (which means that you won't get any notifications at all).
If you don't mind using Google Services, you might be better off installing the [Google Play store](https://play.google.com/store/apps/details?id=im.vector.alpha) version.
##### Where can I find a web app?
You can use [Riot.im](https://Riot.im) - a glossy web client written on top of [matrix-react-sdk](https://github.com/matrix-org/matrix-react-sdk).
You can also run Vector, the code that Riot.im uses, on your own server. It's a static web application, just download the [last release](https://github.com/vector-im/vector-web/) and unpack it.
##### Where can I find a desktop client?
You can use the desktop build of [Riot.im](https://riot.im/desktop.html).
There are also other desktop clients - check the list of clients on [matrix.org](http://matrix.org/docs/projects/try-matrix-now.html#clients).
##### Why can't end-to-end encryption be turned off?
When encryption is enabled in a room, a flag is set in the room state, so that
all clients know to encrypt any messages they send. The room state stores
information about the room like the topic, the avatar, and the membership list.
Imagine if encryption could be turned off the same way as it is turned
on. Anyone with admin rights in the room could clear the flag and then messages
would start being transmitted unencrypted. It would be very easy for a user to
miss the change in configuration, and accidentally send a sensitive message
without encryption.
Worse yet, anyone with sysadmin access to a server could also clear the flag
(remember that the main reason for using e2e encryption is that we don't trust
the sysadmins), and could then easily read any sensitive content which was
sent.
The solution we have taken for now is to make clients ignore any requests to
disable encryption. We might experiment with ways to improve this in the future
- for instance, by alerting the user next time they try to send a message in
the room if encryption has been disabled.
##### Why isn't end-to-end encryption enabled by default?
We are deliberately keeping E2E opt-in during the beta as there is a small risk of undecryptable messages, and we dont want to lull folks into a false sense of security. As soon as we are out of beta, we will turn E2E on for any room with private history by default. Another consideration is to give other clients a chance to catch up with E2E support before it's used by default.
|
### QUESTIONS TO BE ANSWERED!
This FAQ is a constant work in progress - patches and pull requests are *very* welcome to help us improve it. Some of the frequent questions where we need to write an answer include:
* How do I change the TLS key of my server?
* How do I maintain my synapse's DB (e.g. prune old conversations)?
* How do I maintain my synapse's content repository (e.g. prune old content)?
* Why is the spec so big, especially relative to the XMPP baseline spec?
* How do I contribute to the spec?
* What is the privacy policy on Matrix.org?
* How precisely does E2E work?
* How does Matrix actually work architecturally?
* What IOT use cases are there for Matrix?
* Why is are the Matrix reference implementations written in so many different languages?
* How does push work?
* What's on the roadmap?
* How can I use Matrix to talk on Freenode or other IRC networks?
* Where can I learn more about Matrix? (link to PDFs of other presentations etc)
* Why is synapse so resource intensive immediately after federating for the first time?
* \[your question goes here...\]
|
Any other questions? Please contact us in
[\#matrix:matrix.org](https://matrix.to/#/#matrix:matrix.org).

@ -1,146 +0,0 @@
---
layout: post
title: Application services
categories: guides
---
# Application services
Application services are distinct modules which which sit alongside a homeserver providing arbitrary extensible functionality decoupled from the homeserver implementation. Just like the rest of Matrix, they communicate via HTTP using JSON. Application services function in a very similar way to traditional clients, but they are given much more power than a normal client. They can reserve entire namespaces of room aliases and user IDs for their own purposes. They can silently monitor events in rooms, or any events directed at any user ID. This power allows application services to have extremely useful abilities which overall enhance the end user experience.
|
One of the main use cases for application services is protocol bridges. Our Matrix server on Matrix.org links in to various IRC channels and networks. This functionality was initially implemented as a simple bot which resided as a user on the Matrix rooms we wanted to link to freenode channels (#matrix, #matrix-dev, #openwebrtc and #vuc etc). There was nothing special about this bot; it is just treated as a client. However, as we started to rely on it more and more though, we realised that there were things that were impossible for simple client-side bots to do by themselves - for example, the bot could not reserve the virtual user IDs it wanted to create, and could not lazily bridge arbitrary IRC rooms on-the-fly - and this spurred the development of Application Services.
|
### Some of the features of the IRC application service we have since implemented include:
- Specific channel-to-matrix room bridging : This is what the original IRC bot did. You can specify specific channels and specific room IDs, and messages will be bridged.
- Dynamic channel-to-matrix room bridging : This allows Matrix users to join any channel on an IRC network, rather than being forced to use one of the specific channels configured.
- Two-way PM support : IRC users can PM the virtual "M-" users and private Matrix rooms will be created. Likewise, Matrix users can invite the virtual "@irc_Nick:domain" user IDs to a room and a PM to the IRC nick will be made.
- IRC nick changing support: Matrix users are no longer forced to use "M-" nicks and can change them by sending "!nick" messages directly to the bridge.
- Ident support: This allows usernames to be authenticated for virtual IRC clients, which means IRC bans can be targeted at the Matrix user rather than the entire application service.
|
### The use of the Application Services API means:
- The bot can reserve user IDs. This prevents humans from registering for @irc_... user IDs which would then clash with the operation of the bot.
- The bot can reserve room aliases. This prevents humans from register for #irc_... aliases which would then clash with the operation of the bot.
- The bot can trivially manage hundreds of users. Events are pushed to the application service directly. If you tried to do this as a client-side bot, you would need one event stream connection per virtual user.
- The bot can lazily create rooms on demand. This means Matrix users can join non-existent room aliases and have the application service quickly track an IRC channel and create a room with that alias, allowing the join request to succeed.
|
### Implementation details:
- Written in Node.js, designed to be run using <code>forever</code>.
- Built on the generic <a href="http://github.com/matrix-org/matrix-appservice-node">matrix-appservice-node</a> framework.
- Supports sending metrics in statsd format.
- Uses matrix-appservice-node to provide a standardised interface when writing application services, rather than an explicit web framework (though under the hood matrix-appservice-node is using Express).
|
At present, the IRC application service is in beta, and is being run on #matrix and #matrix-dev. If you want to give it a go, <a title="Matrix-IRC Application Service" href="https://github.com/matrix-org/matrix-appservice-irc">check it out on Github</a>!
|
# What Application services can do for you
Application services have enormous potential for creating new and exciting ways to transform and enhance the core Matrix protocol. For example, you could aggregate information from multiple rooms into a summary room, or create throwaway virtual user accounts to proxy messages for a fixed user ID on-the-fly. As you may expect, all of this power assumes a high degree of trust between application services and homeservers. Only homeserver admins can allow an application service to link up with their homeserver, and the application service is in no way federated to other homeservers. You can think of application services as additional logic on the homeserver itself, without messing around with the book-keeping that homeservers have to do. This makes adding useful functionality very easy.
|
### Example
The application service (AS) API itself uses webhooks to communicate from the homeserver to the AS:
- Room Alias Query API : The homeserver hits a URL on your application server to see if a room alias exists.
- User Query API : The homeserver hits a URL on your application server to see if a user ID exists.
- Push API : The homeserver hits a URL on your application server to notify you of new events for your users and rooms.
A very basic application service may want to log all messages in rooms which have an alias starting with "#logged_" (side note: logging won't work if these rooms are using end-to-end encryption).
Here's an example of a very basic application service using Python (with Flask and Requests) which logs room activity:
# app_service.py:
import json, requests  # we will use this later
from flask import Flask, jsonify, request
app = Flask(__name__)
@app.route("/transactions/&lt;transaction&gt;", methods=["PUT"])
def on_receive_events(transaction):
events = request.get_json()["events"]
for event in events:
print "User: %s Room: %s" % (event["user_id"], event["room_id"])
print "Event Type: %s" % event["type"]
print "Content: %s" % event["content"]
return jsonify({})
if __name__ == "__main__":
app.run()
Set your new application service running on port 5000 with:
python app_service.py
The homeserver needs to know that the application service exists before it will send requests to it. This is done via a registration YAML file which is specified in Synapse's main config file e.g. <code>homeserver.yaml</code>. The server admin needs to add the application service registration configuration file as an entry to this file.
# homeserver.yaml
app_service_config_files:
- "/path/to/appservice/registration.yaml"
NB: Note the "-" at the start; this indicates a list element. The registration file <code>registration.yaml</code> should look like:
# registration.yaml
# An ID which is unique across all application services on your homeserver. This should never be changed once set.
id: "something-good"
# this is the base URL of the application service
url: "http://localhost:5000"
# This is the token that the AS should use as its access_token when using the Client-Server API
# This can be anything you want.
as_token: wfghWEGh3wgWHEf3478sHFWE
# This is the token that the HS will use when sending requests to the AS.
# This can be anything you want.
hs_token: ugw8243igya57aaABGFfgeyu
# this is the local part of the desired user ID for this AS (in this case @logging:localhost)
sender_localpart: logging
namespaces:
users: []
rooms: []
aliases:
- exclusive: false
regex: "#logged_.*"
**You will need to restart the homeserver after editing the config file before it will take effect.**
|
To test everything is working correctly, go ahead and explicitly create a room with the alias "#logged_test:localhost" and send a message into the room: the HS will relay the message to the AS by PUTing to /transactions/&lt;tid&gt; and you should see your AS print the event on the terminal. This will monitor any room which has an alias prefix of "#logged_", but it won't lazily create room aliases if they don't already exist. This means it will only log messages in the room you created before: #logged_test:localhost. Try joining the room "#logged_test2:localhost" without creating it, and it will fail. Let's fix that and add in lazy room creation:
@app.route("/rooms/&lt;alias&gt;")
def query_alias(alias):
alias_localpart = alias.split(":")[0][1:]
requests.post(
# NB: "TOKEN" is the as_token referred to in registration.yaml
"http://localhost:8008/_matrix/client/api/v1/createRoom?access_token=TOKEN",
json.dumps({
"room_alias_name": alias_localpart
}),
headers={"Content-Type":"application/json"}
)
return jsonify({})
This makes the application service lazily create a room with the requested alias whenever the HS queries the AS for the existence of that alias (when users try to join that room), allowing any room with the alias prefix #logged_ to be sent to the AS. Now try joining the room "#logged_test2:localhost" and it will work as you'd expect.  You can see that if this were a real bridge, the AS would have checked for the existence of #logged_test2 in the remote network, and then lazily-created it in Matrix as required.
|
Application services are powerful components which extend the functionality of homeservers, but they are limited. They can only ever function in a "passive" way. For example, you cannot implement an application service which censors swear words in rooms, because there is no way to prevent the event from being sent. Aside from the fact that censoring will not work when using end-to-end encryption, all federated homeservers would also need to reject the event in order to stop developing an inconsistent event graph. To "actively" monitor events, another component called a "Policy Server" is required, which is beyond the scope of this post.  Also, Application Services can result in a performance bottleneck, as all events on the homeserver must be ordered and sent to the registered application services.  If you are bridging huge amounts of traffic, you may be better off having your bridge directly talk the Server-Server federation API rather than the simpler Application Service API.
I hope this demonstrates how easy it is to create an application service, along with a few ideas of the kinds of things you can do with them. Obvious uses include build protocol bridges, search engines, invisible bots, etc. For more information on the AS HTTP API, check out the new <a href="http://matrix.org/docs/spec/#application-service-api">Application Service API</a> section in the spec, or the raw drafts and spec in <a href="https://github.com/matrix-org/matrix-doc/" target="_blank">https://github.com/matrix-org/matrix-doc/</a>.

@ -1,18 +0,0 @@
---
layout: default
categories: guides
---
<div class="home">
<h1>Guides</h1>
<p>Here is a collection of guides that might help you get involved with Matrix.</p>
<p>First, there is the <a href="./getting_involved.html" title="Getting Involved">Getting Involved</a> guide, which explains various ways of getting started with Matrix, and the <a href="./faq.html" title="FAQ">FAQ</a>, which tries to answer all your questions relating to Matrix.</p>
<p>The <a href="/docs/guides/client-server.html" title="Client-Server API">Client-Server API</a> guide explains in detail how to use the CS API, which is useful if you want to write a client (or modify an existing one) - or if you're just interested in how it works "under the hood".</p>
<p>If you were using the old v1 CS API, there is also the <a href="/docs/guides/client-server-migrating-from-v1.html">v1 migration guide</a> which justs lists the changes from v1 to r0.</p>
<p><a href="./lets-encrypt.html">Let's Encrypt Matrix</a> explains how to use Let's Encrypt's certificates with your Synapse installation. This guide was written by William A Stevens.</p>
<p>The <a href="./application_services.html" title="Application services">Application services</a> guide introduces and explains Application services, and what they can be used for.</p>
<p><a href="./types-of-bridging.html">Types of Bridging</a> should be read by all bridge developers to ensure everyone has the same mental map of terminology when implementing bridges.</p>
<p>The <a href="./e2e_implementation.html">End-to-end Encryption Implementation Guide</a> is intended for client developers who wish to add support for end-to-end encryption.</p>
</div>

@ -1,85 +0,0 @@
---
layout: post
version: v1.0
title: Code of Conduct
categories: guides
---
<link href="/docs/css/faq.css" type="text/css" rel="stylesheet" />
# Matrix Code of Conduct
This code of conduct outlines our expectations for participants within the Matrix community, as well as steps for reporting unacceptable behaviour. We are committed to providing a welcoming and inspiring community for all, and expect our code of conduct to be honoured. Anyone who violates this code of conduct may be banned from the community.
This applies to conversation in the #matrix* rooms (#matrix:matrix.org, #matrix-dev:matrix.org, #matrix-spam:matrix.org) and commits and comments relating to any project in the [matrix-org](https://github.com/matrix-org) github space.
Our open source community strives to:
* **Be friendly and patient.**
* **Be welcoming**: We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability.
* **Be considerate**: Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that we're a world-wide community, so you might not be communicating in someone else's primary language.
* **Be respectful**: Not all of us will agree all the time, but disagreement is no excuse for poor behaviour and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. Its important to remember that a community where people feel uncomfortable or threatened is not a productive one.
* **Be careful in the words that we choose**: Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behaviour aren't acceptable.
* **Try to understand why we disagree**: Disagreements, both social and technical, happen all the time. It is important that we resolve disagreements and differing views constructively. Remember that were different. The strength of our community comes from its diversity, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesnt mean that theyre wrong. Dont forget that it is human to err and blaming each other doesnt get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes.
|
## Definitions
Harassment includes, but is not limited to:
- Offensive comments related to gender, gender identity and expression, sexual orientation, disability, mental illness, neuro(a)typicality, physical appearance, body size, race, age, regional discrimination, political or religious affiliation
- Unwelcome comments regarding a persons lifestyle choices and practices, including those related to food, health, parenting, drugs, and employment
- Deliberate misgendering. This includes deadnaming or persistently using a pronoun that does not correctly reflect a person's gender identity. You must address people by the name they give you when not addressing them by their username or handle
- Physical contact and simulated physical contact (eg, textual descriptions like “*hug*” or “*backrub*”) without consent or after a request to stop
- Threats of violence, both physical and psychological
- Incitement of violence towards any individual, including encouraging a person to commit suicide or to engage in self-harm
- Deliberate intimidation
- Stalking or following
- Harassing photography or recording, including logging online activity for harassment purposes
- Sustained disruption of discussion
- Unwelcome sexual attention, including gratuitous or off-topic sexual images or behaviour
- Pattern of inappropriate social contact, such as requesting/assuming inappropriate levels of intimacy with others
- Continued one-on-one communication after requests to cease
- Deliberate “outing” of any aspect of a persons identity without their consent except as necessary to protect others from intentional abuse
- Publication of non-harassing private communication
|
We will not act on complaints regarding:
- Good faith and non-malicious conduct whose object is to ameliorate the conditions of disadvantaged individuals or groups including those that are disadvantaged because of race, national or ethnic origin, colour, religion, sex, age or mental or physical disability.
- Reasonable communication of boundaries, such as “leave me alone,” “go away,” or “Im not discussing this with you”
- Refusal to explain or debate social justice concepts
- Communicating in a tone you dont find congenial
- Criticizing racist, sexist, cissexist, or otherwise oppressive behaviour or assumptions
|
### Diversity Statement
We encourage everyone to participate and are committed to building a community for all. Although we will fail at times, we seek to treat everyone both as fairly and equally as possible. Whenever a participant has made a mistake, we expect them to take responsibility for it. If someone has been harmed or offended, it is our responsibility to listen carefully and respectfully, and do our best to right the wrong.
Although this list cannot be exhaustive, we explicitly honour diversity in age, gender, gender identity or expression, culture, ethnicity, language, national origin, political beliefs, profession, race, religion, sexual orientation, socioeconomic status, and technical ability. We will not tolerate discrimination based on any of the protected
characteristics above, including participants with disabilities.
|
### Reporting Issues
If you experience or witness unacceptable behaviour — or have any other concerns — please report it by contacting us via abuse@matrix.org. All reports will be handled with discretion. In your report please include:
- Your contact information.
- Names (usernames and nicks, real names, and/or pseudonyms) of any individuals involved. If there are additional witnesses, please
include them as well. Your account of what occurred, and if you believe the incident is ongoing.
- The date and time of the incident (or start of incident).
- Any additional information that may be helpful.
After filing a report, a representative will contact you personally, review the incident, follow up with any additional questions, and make a decision as to how to respond. If the person who is harassing you is part of the response team, they will recuse themselves from handling your incident. If the complaint originates from a member of the response team, it will be handled by a different member of the response team. We will respect confidentiality requests for the purpose of protecting victims of abuse.
|
### Attribution & Acknowledgements
This Code of Conduct is based on the [TODO Group](https://twitter.com/todogroup)'s [Open Code of Conduct template](https://github.com/todogroup/opencodeofconduct), but with some modifications.

@ -1,37 +0,0 @@
---
layout: post
title: Let's Encrypt Matrix
categories: guides
---
====================
Let's Encrypt Matrix
====================
Let's Encrypt is a free Certificate Authority that makes it easy to secure your server's internet traffic. This makes it really easy to secure your Matrix homeserver, and this guide will explain exactly how you do this. Guide written by William A Stevens - thanks!
0: Prerequisites
================
* Install Synapse_.
* Install (or Download) certbot_
1: Get certificates
===================
When executing the Let's Encrypt client, it will ask for the domain name of your server, and your email address. The domain list can include multiple names and should include any domain you want to access the server from.
Also, the certificates will be in a folder under /etc/letsencrypt (see below) and owned by root.
::
# certbot certonly --standalone
A note about renewal
--------------------
These certificates will expire in 3 months. To renew certificates, run ```certbot renew```. It is recommended to create a cronjob, which attempts renewal twice a day. Depending on your distribution, that could be already configured.
2: Install Certificates
=======================
At the top of your homeserver.yaml there should be two keys, ```tls_certificate_path``` and ```tls_private_key_path```. These should be changed so that instead of pointing to the default keys, they now point to the Let's Encrypt keys. ```tls_certificate_path``` should point to ```/etc/letsencrypt/live/(your domain name)/fullchain.pem```. ```tls_private_key_path``` should point to ```/etc/letsencrypt/live/(your domain name)/privkey.pem```. ```tls_dh_params_path``` can stay the same as before.
.. _Synapse: https://github.com/matrix-org/synapse/blob/master/README.rst#synapse-installation
.. _certbot: https://certbot.eff.org/

@ -1,114 +0,0 @@
---
layout: post
title: Migrating from Client Server API v1
categories: guides
---
Migrating from client-server API v1
===================================
This guide assists developers of API clients that target the ``v1`` version of
the API to migrate their code to the later ``r0``. It does not aim to introduce
new concepts that were added in ``r0`` except where these replace things that
were removed since ``v1``.
Updated Version In Path
=======================
The new version of the API is ``r0``; this should be used in paths where
``v1`` used to appear. Additionally, the ``/api`` path component has now been
removed. API endpoint paths are now::
POST /_matrix/client/r0/register
GET /_matrix/client/r0/login
etc...
New Registration and Login Endpoints
====================================
The ``r0`` version of the ``/register`` and ``/login`` endpoints is different
to the ``v1`` version. See the updated API documentation for details on how the
new API works. In brief, the changes are that the new version returns extra
information in the form of the ``params`` object, and that a sequence of
multiple calls may be statefully chained together by the ``session`` parameter.
Additionally, whereas in ``v1`` the client performed a ``GET`` request to
discover the list of supported flows for ``/register``, in ``r0`` this is done
by sending a ``POST`` request with an empty data body. The ``/login`` endpoint
continues to use the ``GET`` method as before.
Deprecated Endpoints
====================
The following endpoints are now deprecated and replaced by the ``/sync`` API::
/initialSync
/events
/rooms/:roomId/initialSync
The new ``/sync`` API takes an optional ``since`` parameter to distinguish the
initial sync from subsequent updates for more events.
The return value takes a different structure to that from the previous
``/initialSync`` API. For full details see the API documentation, but the
following summary may be useful to compare with ``v1``:
* ``/initialSync`` returned a ``state`` key containing the most recent state
in the room, whereas the new ``/sync`` API's ``state`` corresponds to the
room state at the start of the returned timeline. This makes it easier for
clients to represent state changes that occur within the region of returned
timeline.
* In ``/events``, if more events occurred since the ``since`` token than the
``limit`` parameter allowed, then events from the start of this range were
returned and the client had to perform another fetch to incrementally obtain
more of them. In the ``/sync`` API the result always contains the most
recent events, creating a gap if this would be more events than the
requested limit. If this occurs then the client can use the ``prev_batch``
token as a reference to obtaining more.
* The ``state`` contained in the response to a ``/sync`` request that has a
``since`` parameter will contain only keys that have changed since the
basis given in the ``since`` parameter, rather than containing a full set
values.
The ``/initialSync`` API allowed a parameter called ``limit`` to limit the
number of events returned. To apply this limit to the new ``/sync`` API, you
can supply an ad-hoc filter::
GET .../sync?filter={"room":{"timeline":{"limit:$limit}}}
There is no direct replacement for the per-room ``/rooms/:roomId/initialSync``
endpoint, but the behaviour can be recreated by applying an ad-hoc filter using
the ``filter`` parameter to ``/sync`` that selects only the required room ID::
GET .../sync?filter={"room":{"rooms":[$room_id]}}
However, the way that the new ``/sync`` API works should remove any need to do
this kind of query, in the situations where the ``v1`` API needed it.
Specifically, on joining a new room the initial information about that room is
sent in the next ``/sync`` batch, so it should not be necessary to query that
one room specially.
The following endpoint is deprecated and has no direct replacement::
/events/:eventId
However, if the client knows the room ID of the room that the event is in, it
can use the ``/rooms/:roomId/context/:eventId`` request to fetch the event
itself. By giving the ``limit`` parameter of ``0`` the client can save using
extra bandwidth by actually returning additional context events around the
requested one.
Removed POST Endpoint
=====================
The room message sending API endpoint in ``v1`` accepted both ``PUT`` and
``POST`` methods, where the client could specify a message ID in the ``PUT``
path for de-duplication purposes, or have the server allocate one during
``POST``. In ``r0`` this latter form no longer exists. Clients will now have
to generate these IDs locally.
The following URLs have therefore been removed::
POST .../rooms/:roomId/send/:messageType

@ -1,394 +0,0 @@
---
layout: post
title: Client Server API
categories: guides
---
.. TODO kegan
Room config (specifically: message history,
public rooms).
How to use the client-server API
================================
.. NOTE::
The git version of this document is ``{% project_version %}``
This guide focuses on how the client-server APIs *provided by the reference
homeserver* can be used. Since this is specific to a homeserver
implementation, there may be variations in relation to registering/logging in
which are not covered in extensive detail in this guide.
If you haven't already, get a homeserver up and running on
``https://localhost:8448``.
Accounts
========
Before you can send and receive messages, you must **register** for an account.
If you already have an account, you must **login** into it.
.. NOTE::
`Try out the fiddle`__
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/matrix-doc/tree/master/supporting-docs/howtos/jsfiddles/register_login
Registration
------------
The aim of registration is to get a user ID and access token which you will need
when accessing other APIs::
curl -XPOST -d '{"username":"example", "password":"wordpass", "auth": {"type":"m.login.dummy"}}' "https://localhost:8448/_matrix/client/r0/register"
{
"access_token": "QGV4YW1wbGU6bG9jYWxob3N0.AqdSzFmFYrLrTmteXc",
"home_server": "localhost",
"user_id": "@example:localhost"
}
NB: If a ``user`` is not specified, one will be randomly generated for you.
If you do not specify a ``password``, you will be unable to login to the account
if you forget the ``access_token``.
Implementation note: The matrix specification does not enforce how users
register with a server. It just specifies the URL path and absolute minimum
keys. The reference homeserver uses a username/password to authenticate user,
but other homeservers may use different methods. This is why you need to
specify the ``type`` of method.
Login
-----
The aim when logging in is to get an access token for your existing user ID::
curl -XGET "https://localhost:8448/_matrix/client/r0/login"
{
"flows": [
{
"type": "m.login.password"
}
]
}
curl -XPOST -d '{"type":"m.login.password", "user":"example", "password":"wordpass"}' "https://localhost:8448/_matrix/client/r0/login"
{
"access_token": "QGV4YW1wbGU6bG9jYWxob3N0.vRDLTgxefmKWQEtgGd",
"home_server": "localhost",
"user_id": "@example:localhost"
}
Implementation note: Different homeservers may implement different methods for
logging in to an existing account. In order to check that you know how to login
to this homeserver, you must perform a ``GET`` first and make sure you
recognise the login type. If you do not know how to login, you can
``GET /login/fallback`` which will return a basic webpage which you can use to
login. The reference homeserver implementation support username/password login,
but other homeservers may support different login methods (e.g. OAuth2).
Communicating
=============
In order to communicate with another user, you must **create a room** with that
user and **send a message** to that room.
.. NOTE::
`Try out the fiddle`__
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/matrix-doc/tree/master/supporting-docs/howtos/jsfiddles/create_room_send_msg
Creating a room
---------------
If you want to send a message to someone, you have to be in a room with them. To
create a room::
curl -XPOST -d '{"room_alias_name":"tutorial"}' "https://localhost:8448/_matrix/client/r0/createRoom?access_token=YOUR_ACCESS_TOKEN"
{
"room_alias": "#tutorial:localhost",
"room_id": "!asfLdzLnOdGRkdPZWu:localhost"
}
The "room alias" is a human-readable string which can be shared with other users
so they can join a room, rather than the room ID which is a randomly generated
string. You can have multiple room aliases per room.
.. TODO(kegan)
How to add/remove aliases from an existing room.
Sending messages
----------------
You can now send messages to this room::
curl -XPOST -d '{"msgtype":"m.text", "body":"hello"}' "https://localhost:8448/_matrix/client/r0/rooms/%21asfLdzLnOdGRkdPZWu:localhost/send/m.room.message?access_token=YOUR_ACCESS_TOKEN"
{
"event_id": "YUwRidLecu"
}
The event ID returned is a unique ID which identifies this message.
NB: There are no limitations to the types of messages which can be exchanged.
The only requirement is that ``"msgtype"`` is specified. The Matrix
specification outlines the following standard types: ``m.text``, ``m.image``,
``m.audio``, ``m.video``, ``m.location``, ``m.emote``. See the specification for
more information on these types.
Users and rooms
===============
Each room can be configured to allow or disallow certain rules. In particular,
these rules may specify if you require an **invitation** from someone already in
the room in order to **join the room**. In addition, you may also be able to
join a room **via a room alias** if one was set up.
.. NOTE::
`Try out the fiddle`__
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/matrix-doc/tree/master/supporting-docs/howtos/jsfiddles/room_memberships
Inviting a user to a room
-------------------------
You can directly invite a user to a room like so::
curl -XPOST -d '{"user_id":"@myfriend:localhost"}' "https://localhost:8448/_matrix/client/r0/rooms/%21asfLdzLnOdGRkdPZWu:localhost/invite?access_token=YOUR_ACCESS_TOKEN"
This informs ``@myfriend:localhost`` of the room ID
``!CvcvRuDYDzTOzfKKgh:localhost`` and allows them to join the room.
Joining a room via an invite
----------------------------
If you receive an invite, you can join the room::
curl -XPOST -d '{}' "https://localhost:8448/_matrix/client/r0/rooms/%21asfLdzLnOdGRkdPZWu:localhost/join?access_token=YOUR_ACCESS_TOKEN"
NB: Only the person invited (``@myfriend:localhost``) can change the membership
state to ``"join"``. Repeatedly joining a room does nothing.
Joining a room via an alias
---------------------------
Alternatively, if you know the room alias for this room and the room config
allows it, you can directly join a room via the alias::
curl -XPOST -d '{}' "https://localhost:8448/_matrix/client/r0/join/%21asfLdzLnOdGRkdPZWu:localhost?access_token=YOUR_ACCESS_TOKEN"
{
"room_id": "!CvcvRuDYDzTOzfKKgh:localhost"
}
You will need to use the room ID when sending messages, not the room alias.
NB: If the room is configured to be an invite-only room, you will still require
an invite in order to join the room even though you know the room alias. As a
result, it is more common to see a room alias in relation to a public room,
which do not require invitations.
Getting events
==============
An event is some interesting piece of data that a client may be interested in.
It can be a message in a room, a room invite, etc. There are many different ways
of getting events, depending on what the client already knows.
.. NOTE::
`Try out the fiddle`__
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/matrix-doc/tree/master/supporting-docs/howtos/jsfiddles/event_stream
Getting all state
-----------------
If the client doesn't know any information on the rooms the user is
invited/joined on, they can get all the user's state for all rooms::
curl -XGET "https://localhost:8448/_matrix/client/r0/sync?access_token=YOUR_ACCESS_TOKEN"
{
"account_data": {
"events": [
{
...
}
]
},
"next_batch": "s9_3_0_1_1_1",
"presence": {
"events": [
{
"content": {
"currently_active": true,
"last_active_ago": 19,
"presence": "online"
},
"sender": "@example:localhost",
"type": "m.presence"
}
]
},
"rooms": {
"invite": {},
"join": {
"!asfLdzLnOdGRkdPZWu:localhost": {
"account_data": {
"events": []
},
"ephemeral": {
"events": []
},
"state": {
"events": []
},
"timeline": {
"events": [
{
"content": {
"creator": "@example:localhost"
},
"event_id": "$14606534990LhqHt:localhost",
"origin_server_ts": 1460653499699,
"sender": "@example:localhost",
"state_key": "",
"type": "m.room.create",
"unsigned": {
"age": 239192
}
},
{
"content": {
"avatar_url": null,
"displayname": null,
"membership": "join"
},
"event_id": "$14606534991nsZKk:localhost",
"membership": "join",
"origin_server_ts": 1460653499727,
"sender": "@example:localhost",
"state_key": "@example:localhost",
"type": "m.room.member",
"unsigned": {
"age": 239164
}
},
...
],
"limited": false,
"prev_batch": "s9_3_0_1_1_1"
},
"unread_notifications": {}
}
},
"leave": {}
}
}
This returns all the room information the user is invited/joined on, as well as
all of the presences relevant for these rooms. This can be a LOT of data. You
may just want the most recent event for each room. This can be achieved by
applying a filter that asks for a limit of 1 timeline event per room::
curl --globoff -XGET 'https://localhost:8448/_matrix/client/r0/sync?filter={"room":{"timeline":{"limit":1}}}&access_token=YOUR_ACCESS_TOKEN'
{
...
"rooms": {
"invite": {},
"join": {
"!asfLdzLnOdGRkdPZWu:localhost": {
...
"timeline": {
"events": [
{
"content": {
"body": "hello",
"msgtype": "m.text"
},
"event_id": "$14606535757KCGXo:localhost",
"origin_server_ts": 1460653575105,
"sender": "@example:localhost",
"type": "m.room.message",
"unsigned": {
"age": 800348
}
}
],
"limited": true,
"prev_batch": "t8-8_7_0_1_1_1"
},
"unread_notifications": {}
}
},
"leave": {}
}
}
(additionally we have to ask ``curl`` not to try to interpret any ``{}``
characters in the URL, which is what the ``--globoff`` option is for)
Getting live state
------------------
In the response to this ``sync`` request the server includes a token that can
be used to obtain updates since this point under the object key ``next_batch``.
To use this token, specify its value as the ``since`` parameter to another
``/sync`` request.::
curl -XGET "https://localhost:8448/_matrix/client/r0/sync?since=s9_7_0_1_1_1&access_token=YOUR_ACCESS_TOKEN"
{
"account_data": {
"events": []
},
"next_batch": "s9_9_0_1_1_1",
"presence": {
"events": [
{
"content": {
"currently_active": true,
"last_active_ago": 12,
"presence": "online"
},
"sender": "@example:localhost",
"type": "m.presence"
}
]
},
"rooms": {
"invite": {},
"join": {},
"leave": {}
}
}
By default this request will not wait in the server, always returning a value
even if nothing interesting happened. However, by applying the ``timeout``
query parameter, which gives a duration in miliseconds, we can ask the server
to wait for up to that amount of time before it returns. If no interesting
events have happened since then, the response will be relatively empty.::
curl -XGET "https://localhost:8448/_matrix/client/r0/sync?since=s9_13_0_1_1_1&access_token=YOUR_ACCESS_TOKEN"
{
"account_data": {
"events": []
},
"next_batch": "s9_13_0_1_1_1",
"presence": {
"events": []
},
"rooms": {
"invite": {},
"join": {},
"leave": {}
}
}
Example application
-------------------
The following example demonstrates registration and login, live event streaming,
creating and joining rooms, sending messages, getting member lists and getting
historical messages for a room. This covers most functionality of a messaging
application.
.. NOTE::
`Try out the fiddle`__
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/matrix-doc/tree/master/supporting-docs/howtos/jsfiddles/example_app

@ -1,797 +0,0 @@
---
layout: post
title: End-to-End Encryption implementation guide
categories: guides
---
Implementing End-to-End Encryption in Matrix clients
====================================================
This guide is intended for authors of Matrix clients who wish to add
support for end-to-end encryption. It is highly recommended that readers
be familiar with the Matrix protocol and the use of access tokens before
proceeding.
.. contents::
The libolm library
------------------
End-to-end encryption in Matrix is based on the Olm and Megolm
cryptographic ratchets. The recommended starting point for any client
authors is with the `libolm <http://matrix.org/git/olm>`__ library,
which contains implementations of all of the cryptographic primitives
required. The library itself is written in C/C++, but is architected in
a way which makes it easy to write wrappers for higher-level languages.
Devices
-------
We have a particular meaning for “device”. As a user, I might have
several devices (a desktop client, some web browsers, an Android device,
an iPhone, etc). When I first use a client, it should register itself as
a new device. If I log out and log in again as a different user, the
client must register as a new device. Critically, the client must create
a new set of keys (see below) for each “device”.
The longevity of devices will depend on the client. In the web client,
we create a new device every single time you log in. In a mobile client,
it might be acceptable to reuse the device if a login session expires,
**provided** the user is the same. **Never** share keys between
different users.
Devices are identified by their ``device_id`` (which is unique within
the scope of a given user). By default, the ``/login`` and ``/register``
endpoints will auto-generate a ``device_id`` and return it in the
response; a client is also free to generate its own ``device_id`` or, as
above, reuse a device, in which case the client should pass the
``device_id`` in the request body.
The lifetime of devices and ``access_token``\ s are closely related. In
the simple case where a new device is created each time you log in,
there is a one-to-one mapping between a ``device_id`` and an
``access_token``. If a client reuses a ``device_id`` when logging
in, there will be several ``access_token``\ s associated with a
given ``device_id`` - but still, we would expect only one of these to be
active at once (though we do not currently enforce that in Synapse).
Keys used in End-to-End encryption
----------------------------------
There are a number of keys involved in encrypted communication: a
summary of them follows.
Ed25519 fingerprint key pair
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Ed25519 is a public-key cryptographic system for signing messages. In
Matrix, each device has an Ed25519 key pair which serves to identify
that device. The private part of the key pair should never leave the
device, but the public part is published to the Matrix network.
Curve25519 identity key pair
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Curve25519 is a public-key cryptographic system which can be used to
establish a shared secret. In Matrix, each device has a long-lived
Curve25519 identity key which is used to establish Olm sessions with
that device. Again, the private key should never leave the device, but
the public part is signed with the Ed25519 fingerprint key and published
to the network.
Theoretically we should rotate the Curve25519 identity key from time to
time, but we haven't implemented this yet.
Curve25519 one-time keys
~~~~~~~~~~~~~~~~~~~~~~~~
As well as the identity key, each device creates a number of Curve25519
key pairs which are also used to establish Olm sessions, but can only be
used once. Once again, the private part remains on the device.
At startup, Alice creates a number of one-time key pairs, and publishes
them to her homeserver. If Bob wants to establish an Olm session with
Alice, he needs to claim one of Alices one-time keys, and creates a new
one of his own. Those two keys, along with Alices and Bobs identity
keys, are used in establishing an Olm session between Alice and Bob.
Megolm encryption keys
~~~~~~~~~~~~~~~~~~~~~~
The Megolm key is used to encrypt group messages (in fact it is used to
derive an AES-256 key, and an HMAC-SHA-256 key). It is initialised with
random data. Each time a message is sent, a hash calculation is done on
the Megolm key to derive the key for the next message. It is therefore
possible to share the current state of the Megolm key with a user,
allowing them to decrypt future messages but not past messages.
Ed25519 Megolm signing key pair
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When a sender creates a Megolm session, he also creates another Ed25519
signing key pair. This is used to sign messages sent via that Megolm
session, to authenticate the sender. Once again, the private part of the
key remains on the device. The public part is shared with other devices
in the room alongside the encryption key.
Creating and registering device keys
------------------------------------
This process only happens once, when a device first starts.
It must create the Ed25519 fingerprint key pair and the Curve25519
identity key pair. This is done by calling ``olm_create_account`` in
libolm. The (base64-encoded) keys are retrieved by calling
``olm_account_identity_keys``. The account should be stored for future
use.
It should then publish these keys to the homeserver. To do this, it
should construct a JSON object as follows:
.. code:: json
{
"algorithms": ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
"device_id": "<device_id>",
"keys": {
"curve25519:<device_id>": "<curve25519_key>",
"ed25519:<device_id>": "<ed25519_key>"
},
"user_id: <user_id>"
}
The object should be formatted as `Canonical
JSON <http://matrix.org/docs/spec/server_server/unstable.html#canonical-json>`__,
then signed with ``olm_account_sign``; the signature should be added to
the JSON as ``signatures.<user_id>.ed25519:<device_id>``.
The signed JSON is then uploaded via
``POST /_matrix/client/unstable/keys/upload``.
Creating and registering one-time keys
--------------------------------------
At first start, and at regular intervals
thereafter\ [#]_, the client should check how
many one-time keys the homeserver has stored for it, and, if necessary,
generate and upload some more.
.. [#] Every 10 minutes is suggested.
The number of one-time keys currently stored is returned by
``POST /_matrix/client/unstable/keys/upload``. (Post an empty JSON object
``{}`` if you dont want to upload the device keys.)
The maximum number of active keys supported by libolm is returned by
``olm_account_max_number_of_one_time_keys``. The client should try to
maintain about half this number on the homeserver.
To generate new one-time keys:
* Call ``olm_account_generate_one_time_keys`` to generate new keys.
* Call ``olm_account_one_time_keys`` to retrieve the unpublished keys. This
returns a JSON-formatted object with the single property ``curve25519``,
which is itself an object mapping key id to base64-encoded Curve25519
key. For example:
.. code:: json
{
"curve25519": {
"AAAAAA": "wo76WcYtb0Vk/pBOdmduiGJ0wIEjW4IBMbbQn7aSnTo",
"AAAAAB": "LRvjo46L1X2vx69sS9QNFD29HWulxrmW11Up5AfAjgU"
}
}
* Each key should be signed with the account key. To do this:
* Construct a JSON object as follows:
.. code:: json
{
"key": "<curve25519_key>"
}
* Call ``olm_account_sign`` to calculate the signature.
* Add the signature should be added to the JSON as
``signatures.<user_id>.ed25519:<device_id>``.
* The complete key object should now look like:
.. code:: json
{
"key": "wo76WcYtb0Vk/pBOdmduiGJ0wIEjW4IBMbbQn7aSnTo",
"signatures": {
"@alice:example.com": {
"ed25519:JLAFKJWSCS": "dSO80A01XiigH3uBiDVx/EjzaoycHcjq9lfQX0uWsqxl2giMIiSPR8a4d291W1ihKJL/a+myXS367WT6NAIcBA"
}
}
}
* Aggregate all the signed one-time keys into a single JSON object as follows:
.. code:: json
{
"one_time_keys": {
"signed_curve25519:<key_id>": {
"key": "<curve25519_key>",
"signatures": {
"<user_id>": {
"ed25519:<device_id>": "<signature>"
}
}
},
"signed_curve25519:<key_id>": {
...
},
...
}
}
* Upload the object via ``POST /_matrix/client/unstable/keys/upload``.
* Call ``olm_account_mark_keys_as_published`` to tell the olm library not to
return the same keys from a future call to ``olm_account_one_time_keys``.
Configuring a room to use encryption
------------------------------------
To enable encryption in a room, a client should send a state event of
type ``m.room.encryption``, and content ``{ "algorithm":
"m.megolm.v1.aes-sha2" }``.
.. |m.room.encryption| replace:: ``m.room.encryption``
.. _`m.room.encryption`:
Handling an ``m.room.encryption`` state event
---------------------------------------------
When a client receives an ``m.room.encryption`` event as above, it
should set a flag to indicate that messages sent in the room should be
encrypted.
This flag should **not** be cleared if a later ``m.room.encryption``
event changes the configuration. This is to avoid a situation where a
MITM can simply ask participants to disable encryption. In short: once
encryption is enabled in a room, it can never be disabled.
The event should contain an ``algorithm`` property which defines which
encryption algorithm should be used for encryption. Currently only
``m.megolm.v1-aes-sha2`` is permitted here.
The event may also include other settings for how messages sent in the room
should be encrypted (for example, ``rotation_period_ms`` to define how often
the session should be replaced).
Handling an ``m.room.encrypted`` event
--------------------------------------
Encrypted events have a type of ``m.room.encrypted``. They have a
content property ``algorithm`` which gives the encryption algorithm in
use, as well as other properties specific to the algorithm [#]_.
.. [#] Note that a redacted event will have an empty content, and hence the
content will have no ``algorithm`` property. Thus a client should check
whether an event is redacted before checking for the ``algorithm`` property.
The encrypted payload is a JSON object with the properties ``type``
(giving the decrypted event type), and ``content`` (giving the decrypted
content). Depending on the algorithm in use, the payload may contain
additional keys.
There are currently two defined algorithms:
``m.olm.v1.curve25519-aes-sha2``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Encrypted events using this algorithm should have a ``sender_key`` and a
``ciphertext`` property.
The ``sender_key`` property of the event content gives the Curve25519
identity key of the sender. Clients should maintain a list of known Olm
sessions for each device they speak to; it is recommended to index them
by Curve25519 identity key.
Olm messages are encrypted separately for each recipient device.
``ciphertext`` is an object mapping from the Curve25519 identity key for
the recipient device. The receiving client should, of course, look for
its own identity key in this object. (If it isn't listed, the message
wasn't sent for it, and the client can't decrypt it; it should show an
error instead, or similar).
This should result in an object with the properties ``type`` and
``body``. Messages of type '0' are 'prekey' messages which are used to
establish a new Olm session between two devices; type '1' are normal
messages which are used once a message has been received on the session.
When a message (of either type) is received, a client should first
attempt to decrypt it with each of the known sessions for that sender.
There are two steps to this:
- If (and only if) ``type==0``, the client should call
``olm_matches_inbound_session`` with the session and ``body``. This
returns a flag indicating whether the message was encrypted using
that session.
- The client calls ``olm_decrypt``, with the session, ``type``, and
``body``. If this is successful, it returns the plaintext of the
event.
If the client was unable to decrypt the message using any known sessions
(or if there are no known sessions yet), **and** the message had type 0,
**and** ``olm_matches_inbound_session`` wasn't true for any existing
sessions, then the client can try establishing a new session. This is
done as follows:
- Call ``olm_create_inbound_session_from`` using the olm account, and
the ``sender_key`` and ``body`` of the message.
- If the session was established successfully:
- call ``olm_remove_one_time_keys`` to ensure that the same
one-time-key cannot be reused.
- Call ``olm_decrypt`` with the new session
- Store the session for future use
At the end of this, the client will hopefully have successfully
decrypted the payload.
As well as the ``type`` and ``content`` properties, the payload should
contain a number of other properties. Each of these should be checked as
follows [#]_.
``sender``
The user ID of the sender. The client should check that this matches the
``sender`` in the event.
``recipient``
The user ID of the recipient. The client should check that this matches the
local user ID.
``keys``
an object with a property ``ed25519``, The client should check that the
value of this property matches the sender's fingerprint key when `marking
the event as verified`_\ .
``recipient_keys``
an object with a property ``ed25519``. The client should check that the
value of this property matches its own fingerprint key.
.. [#] These tests prevent an attacker publishing someone else's curve25519
keys as their own and subsequently claiming to have sent messages which they
didn't.
``m.megolm.v1.aes-sha2``
~~~~~~~~~~~~~~~~~~~~~~~~
Encrypted events using this algorithm should have ``sender_key``,
``session_id`` and ``ciphertext`` content properties. If the
``room_id``, ``sender_key`` and ``session_id`` correspond to a known
Megolm session (see `below`__), the ciphertext can be
decrypted by passing the ciphertext into ``olm_group_decrypt``.
__ `m.room_key`_
In order to avoid replay attacks a client should remember the megolm
``message_index`` returned by ``olm_group_decrypt`` of each event they decrypt
for each session. If the client decrypts an event with the same
``message_index`` as one that it has already received using that session then
it should treat the message as invalid. However, care must be taken when an
event is decrypted multiple times that it is not flagged as a replay attack.
For example, this may happen when the client decrypts an event, the event gets
purged from the client's cache, and then the client backfills and re-decrypts
the event. One way to handle this case is to ensure that the record of
``message_index``\ es is appropriately purged when the client's cache of events
is purged. Another way is to remember the event's ``event_id`` and
``origin_server_ts`` along with its ``message_index``. When the client decrypts
an event with a ``message_index`` matching that of a previously-decrypted
event, it can then compare the ``event_id`` and ``origin_server_ts`` that it
remembered for that ``message_index``, and if those fields match, then the
message should be decrypted as normal.
The client should check that the sender's fingerprint key matches the
``keys.ed25519`` property of the event which established the Megolm session
when `marking the event as verified`_.
.. _`m.room_key`:
Handling an ``m.room_key`` event
--------------------------------
These events contain key data to allow decryption of other messages.
They are sent to specific devices, so they appear in the ``to_device``
section of the response to ``GET /_matrix/client/r0/sync``. They will
also be encrypted, so will need decrypting as above before they can be
seen. (These events are generated by other clients - see `starting a megolm
session`_).
The event content will contain an 'algorithm' property, indicating the
encryption algorithm the key data is to be used for. Currently, this
will always be ``m.megolm.v1.aes-sha2``.
Room key events for Megolm will also have ``room_id``, ``session_id``, and
``session_key`` keys. They are used to establish a Megolm session. The
``room_id`` identifies which room the session will be used in. The ``room_id``,
together with the ``sender_key`` of the ``room_key`` event before it was
decrypted, and the ``session_id``, uniquely identify a Megolm session. If they
do not represent a known session, the client should start a new inbound Megolm
session by calling ``olm_init_inbound_group_session`` with the ``session_key``.
The client should remember the value of the keys property of the payload
of the encrypted ``m.room_key`` event and store it with the inbound
session. This is used as above when marking the event as verified.
.. _`download the device list`:
Downloading the device list for users in the room
-------------------------------------------------
Before an encrypted message can be sent, it is necessary to retrieve the
list of devices for each user in the room. This can be done proactively,
or deferred until the first message is sent. The information is also
required to allow users to `verify or block devices`__.
__ `blocking`_
The client should build a JSON query object as follows:
.. code:: json
{
"device_keys": {
"<user_id>": {},
[...]
}
}
Each member in the room should be included in the query. This is then
sent via ``POST /_matrix/client/unstable/keys/query.``
The result includes, for each listed user id, a map from device ID to an
object containing information on the device, as follows:
.. code:: json
{
"algorithms": [...],
"device_id": "<device_id>",
"keys": {
"curve25519:<device_id>": "<curve25519_key>",
"ed25519:<device_id>": "<ed25519_key>"
},
"signatures": {
"<userId>": {
"ed25519:<device_id>": "<signature>"
},
},
"unsigned": {
"device_display_name": "<display name>"
},
"user_id: <user_id>"
}
The client should first check the signature on this object. To do this,
it should remove the ``signatures`` and ``unsigned`` properties, format
the remainder as Canonical JSON, and pass the result into
``olm_ed25519_verify``, using the Ed25519 key for the ``key`` parameter,
and the corresponding signature for the ``signature`` parameter. If the
signature check fails, no further processing should be done on the
device.
The client must also check that the ``user_id`` and ``device_id`` fields in the
object match those in the top-level map [#]_.
The client should check if the ``user_id``/``device_id`` correspond to a device
it had seen previously. If it did, the client **must** check that the Ed25519
key hasn't changed. Again, if it has changed, no further processing should be
done on the device.
Otherwise the client stores the information about this device.
.. [#] This prevents a malicious or compromised homeserver replacing the keys
for the device with those of another.
Sending an encrypted event
--------------------------
When sending a message in a room `configured to use encryption`__, a client
first checks to see if it has an active outbound Megolm session. If not, it
first `creates one as per below`__. If an outbound session exists, it should
check if it is time to `rotate`__ it, and create a new one if so.
__ `Configuring a room to use encryption`_
__ `Starting a Megolm session`_
__ `Rotating Megolm sessions`_
The client then builds an encryption payload as follows:
.. code:: json
{
"type": "<event type>",
"content": "<event content>",
"room_id": "<id of destination room>"
}
and calls ``olm_group_encrypt`` to encrypt the payload. This is then packaged
into event content as follows:
.. code:: json
{
"algorithm": "m.megolm.v1.aes-sha2",
"sender_key": "<our curve25519 device key>",
"ciphertext": "<encrypted payload>",
"session_id": "<outbound group session id>",
"device_id": "<our device ID>"
}
Finally, the encrypted event is sent to the room with ``POST
/_matrix/client/r0/rooms/<room_id>/send/m.room.encrypted/<txn_id>``.
Starting a Megolm session
~~~~~~~~~~~~~~~~~~~~~~~~~
When a message is first sent in an encrypted room, the client should
start a new outbound Megolm session. This should **not** be done
proactively, to avoid proliferation of unnecessary Megolm sessions.
To create the session, the client should call
``olm_init_outbound_group_session``, and store the details of the
outbound session for future use.
The client should then call ``olm_outbound_group_session_id`` to get the
unique ID of the new session, and ``olm_outbound_group_session_key`` to
retrieve the current ratchet key and index. It should store these
details as an inbound session, just as it would when `receiving them via
an m.room_key event`__.
__ `m.room_key`_
The client must then share the keys for this session with each device in the
room. It must therefore `download the device list`_ if it hasn't already done
so, and for each device in the room which has not been `blocked`__, the client
should:
__ `blocking`_
* Build a content object as follows:
.. code:: json
{
"algorithm": "m.megolm.v1.aes-sha2",
"room_id": "<id of destination room>",
"session_id": "<session id>",
"session_key": "<session_key>"
}
- Encrypt the content as an ``m.room_key`` event using Olm, as below.
Once all of the key-sharing event contents have been assembled, the
events should be sent to the corresponding devices via
``PUT /_matrix/client/unstable/sendToDevice/m.room.encrypted/<txnId>``.
Rotating Megolm sessions
~~~~~~~~~~~~~~~~~~~~~~~~
Megolm sessions may not be reused indefinitely.
The number of messages which can be sent before a session should be rotated is
given by the ``rotation_period_msgs`` property of the |m.room.encryption|_
event, or ``100`` if that property isn't present.
Similarly, the maximum age of a megolm session is given, in milliseconds, by
the ``rotation_period_ms`` property of the ``m.room.encryption``
event. ``604800000`` (a week) is the recommended default here.
Once either the message limit or time limit have been reached, the client
should start a new session before sending any more messages.
Encrypting an event with Olm
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Olm is not used for encrypting room events, as it requires a separate
copy of the ciphertext for each device, and because the receiving device
can only decrypt received messages once. However, it is used for
encrypting key-sharing events for Megolm.
When encrypting an event using Olm, the client should:
- Build an encryption payload as follows:
.. code:: json
{
"type": "<event type>",
"content": "<event content>",
"sender": "<our user ID>",
"sender_device": "<our device ID>",
"keys": {
"ed25519": "<our ed25519 fingerprint key>"
},
"recipient": "<recipient user ID>",
"recipient_keys": {
"ed25519": "<recipient's ed25519 fingerprint key>"
},
}
- Check if it has an existing Olm session; if it does not, `start a new
one`__. If it has several (as may happen due to
races when establishing sessions), it should use the one with the
first session_id when sorted by their ASCII codepoints (ie, 'A'
would be before 'Z', which would be before 'a').
__ `Starting an Olm session`_
- Encrypt the payload by calling ``olm_encrypt``.
- Package the payload into event content as follows:
.. code:: json
{
"algorithm": "m.olm.v1.curve25519-aes-sha2",
"sender_key": "<our curve25519 identity key>",
"ciphertext": "<encrypted payload>"
}
Starting an Olm session
~~~~~~~~~~~~~~~~~~~~~~~
To start a new Olm session with another device, a client must first
claim one of the other device's one-time keys. To do this, it should
create a query object as follows:
.. code:: json
{
"<user id>": {
"<device_id>": "signed_curve25519",
...
},
...
}
and send this via ``POST /_matrix/client/unstable/keys/claim``. Claims
for multiple devices should be aggregated into a single request.
This will return a result as follows:
.. code:: json
{
"one_time_keys": {
"<user id>": {
"<device_id>": {
"signed_curve25519:<key_id>": {
"key": "<curve25519_key>",
"signatures": {
"<user_id>": {
"ed25519:<device_id>": "<signature>"
}
}
},
},
...
},
...
}
}
The client should first check the signatures on the signed key objects. As with
checking the signatures on the device keys, it should remove the ``signatures``
and (if present) ``unsigned`` properties, format the remainder as Canonical
JSON, and pass the result into ``olm_ed25519_verify``, using the Ed25519 device
key for the ``key`` parameter.
Provided the key object passes verification, the client should then pass the
key, along with the Curve25519 Identity key for the remote device, into
``olm_create_outbound_session``.
Handling membership changes
---------------------------
The client should monitor rooms which are configured to use encryption for
membership changes.
When a member leaves a room, the client should invalidate any active outbound
Megolm session, to ensure that a new session is used next time the user sends a
message.
When a new member joins a room, the client should first `download the device
list`_ for the new member, if it doesn't already have it.
After giving the user an opportunity to `block`__ any suspicious devices, the
client should share the keys for the outbound Megolm session with all the new
member's devices. This is done in the same way as `creating a new session`__,
except that there is no need to start a new Megolm session: due to the design
of the Megolm ratchet, the new user will only be able to decrypt messages
starting from the current state. The recommended method is to maintain a list
of members who are waiting for the session keys, and share them when the user
next sends a message.
__ `blocking`_
__ `Starting a Megolm session`_
Sending New Device announcements
--------------------------------
When a user logs in on a new device, it is necessary to make sure that
other devices in any rooms with encryption enabled are aware of the new
device. This is done as follows.
Once the initial call to the ``/sync`` API completes, the client should
iterate through each room where encryption is enabled. For each user
(including the client's own user), it should build a content object as
follows:
.. code:: json
{
"device_id": "<our device ID>",
"rooms": ["<shared room id 1>", "<room id 2>", ... ]
}
Once all of these have been constructed, they should be sent to all of the
relevant user's devices (using the wildcard ``*`` in place of the
``device_id``) via ``PUT
/_matrix/client/unstable/sendToDevice/m.new_device/<txnId>.``
Handling an ``m.new_device`` event
----------------------------------
As with ``m.room_key`` events, these will appear in the ``to_device``
section of the ``/sync`` response.
The client should `download the device list`_ of the sender, to get the details
of the new device.
The event content will contain a ``rooms`` property, as well as the
``device_id`` of the new device. For each room in the list, the client
should check if encryption is enabled, and if the sender of the event is
a member of that room. If so, the client should share the keys for the
outbound Megolm session with the new device, in the same way as
`handling a new user in the room`__.
__ `Handling membership changes`_
.. _`blocking`:
Blocking / Verifying devices
----------------------------
It should be possible for a user to mark each device belonging to
another user as 'Blocked' or 'Verified'.
When a user chooses to block a device, this means that no further
encrypted messages should be shared with that device. In short, it
should be excluded when sharing room keys when `starting a new Megolm
session <#_p5d1esx6gkrc>`__. Any active outbound Megolm sessions whose
keys have been shared with the device should also be invalidated so that
no further messages are sent over them.
Verifying a device involves ensuring that the device belongs to the
claimed user. Currently this must be done by showing the user the
Ed25519 fingerprint key for the device, and prompting the user to verify
out-of-band that it matches the key shown on the other user's device.
.. _`marking the event as verified`:
Marking events as 'verified'
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Once a device has been verified, it is possible to verify that events
have been sent from a particular device. See the section on `Handling an
m.room.encrypted event`_ for notes on how to do this
for each algorithm. Events sent from a verified device can be decorated
in the UI to show that they have been sent from a verified device.

@ -1,79 +0,0 @@
---
layout: post
version: v1.0
title: Types of Bridging
categories: guides
---
<link href="/docs/css/faq.css" type="text/css" rel="stylesheet" />
# Types of bridging
Bridges come in many flavours, and we need consistent terminology within the Matrix community to ensure everyone (users, developers, core team) is on the same page. This post is primarily intended for bridge developers to refer to when building bridges.
*The most recent version of this document is [here](https://matrix.org/docs/guides/types-of-bridging.html) ([source](https://github.com/matrix-org/matrix-doc/blob/master/supporting-docs/guides/2017-03-11-types-of-bridging.md)) but were also posting it as a blog post for visibility.*
## Types of rooms
### Portal rooms
Bridges can register themselves as controlling chunks of room aliases namespace, letting Matrix users join remote rooms transparently if they /join #freenode_#wherever:matrix.org or similar. The resulting Matrix room is typically automatically bridged to the single target remote room. Access control for Matrix users is typically managed by the remote networks side of the room. This is called a portal room, and is useful for jumping into remote rooms without any configuration needed whatsoever - using Matrix as a bouncer for the remote network.
### Plumbed rooms
Alternatively, an existing Matrix room can be can plumbed into one or more specific remote rooms by configuring a bridge (which can be run by anyone). For instance, #matrix:matrix.org is plumbed into #matrix on Freenode, matrixdotorg/#matrix on Slack, etc. Access control for Matrix users is necessarily managed by the Matrix side of the room. This is useful for using Matrix to link together different communities.
Migrating rooms between a portal & plumbed room is currently a bit of a mess, as theres not yet a way for users to remove portal rooms once theyre created, so you can end up with a mix of portal & plumbed users bridged into a room, which looks weird from both the Matrix and non-Matrix viewpoints. https://github.com/matrix-org/matrix-appservice-irc/issues/387 tracks this.
## Types of bridges (simplest first):
### Bridgebot-based bridges
The simplest way to exchange messages with a remote network is to have the bridge log into the network using one or more predefined users called bridge bots - typically called MatrixBridge or MatrixBridge[123] etc. These relay traffic on behalf of the users on the other side, but its a terrible experience as all the metadata about the messages and senders is lost. This is how the [telematrix](https://github.com/SijmenSchoon/telematrix) matrix<->telegram bridge currently works.
### Bot-API (aka Virtual user) based bridges
Some remote systems support the idea of injecting messages from fake or virtual users, which can be used to represent the Matrix-side users as unique entities in the remote network. For instance, Slacks inbound webhooks lets remote bots be created on demand, letting Matrix users be shown cosmetically correctly in the timeline as virtual users. However, the resulting virtual users arent real users on the remote system, so dont have presence/profile and cant be tab-completed or direct-messaged etc. They also have no way to receive typing notifs or other richer info which may not be available via bot APIs. This is how the current [matrix-appservice-slack](https://github.com/matrix-org/matrix-appservice-slack) bridge works.
### Simple puppeted bridge
This is a richer form of bridging, where the bridge logs into the remote service as if it were a real 3rd party client for that service. As a result, the Matrix user has to already have a valid account on the remote system. In exchange, the Matrix user puppets their remote user, such that other users on the remote system arent even aware they are speaking to a user via Matrix. The full semantics of the remote system are available to the bridge to expose into Matrix. However, the bridge has to handle the authentication process to log the user into the remote bridge.
This is essentially how the current [matrix-appservice-irc](https://github.com/matrix-org/matrix-appservice-irc) bridge works (if you configure it to log into the remote IRC network as your real IRC nickname). [matrix-appservice-gitter](https://github.com/matrix-org/matrix-appservice-gitter) is being extended to support both puppeted and bridgebot-based operation. Its how the experimental [matrix-appservice-tg](https://github.com/matrix-org/matrix-appservice-tg) bridge works.
Going forwards were aiming for all bridges to be at least simple puppeted, if not double-puppeted.
### Double-puppeted bridge
A simple puppeted bridge allows the Matrix user to control their account on their remote network. However, ideally this puppeting should work in both directions, so if the user logs into (say) their native telegram client and starts conversations, sends messages etc, these should be reflected back into Matrix as if the user had done them there. This requires the bridge to be able to puppet the Matrix side of the bridge on behalf of the user.
This is the holy-grail of bridging because both the Matrix account and the third party account are accurately represented on their respective networks, with all user metadata intact. This is in contrast to a relaybot which would appear as a separate user from whom it represents.
Several obstacles exist to the proper implementation of double-puppeted bridges. On the Matrix side, we need an elegant way of having the bridge auth with Matrix as the matrix user (which requires some kind of scoped access_token delegation). On the third-party network, unique problems exist depending on the limitations of that particular network network. For example, many third party networks will lack the ability to represent other Matrix users than the one being puppeted (see hybrid relaybot).
[matrix-puppet-bridge](https://github.com/matrix-hacks/matrix-puppet-bridge) is a community project that tries to facilitate development of double-puppeted bridges, having done so, without a bridgebot feature, for [several networks](https://github.com/matrix-hacks/matrix-puppet-bridge#examples). A downside to their approach is the assumption that an individual will run the bridge on their own homeserver, thus working around the problem of sharing auth credentials on a shared homeserver.
### Hybrid Relaybot Puppet Bridge
This type of bridge is a combination single or double puppet bridge which tries to solve the problem of representing other users by means of the bridgebot technique. [matrix-appservice-gitter](https://github.com/matrix-org/matrix-appservice-gitter) works in this way.
### Server-to-server bridging
Some remote protocols (IRC, XMPP, SIP, SMTP, NNTP, GnuSocial etc) support federation - either open or closed. The most elegant way of bridging to these protocols would be to have the bridge participate in the federation as a server, directly bridging the entire namespace into Matrix.
Were not aware of anyone whos done this yet.
### One-way bridging
One-way bridging is rare, but can be used to represent a bridge that is bridging from the remote system into matrix. This is common when the remote system does not permit message posting, or is simply not capable of handling posting outside their system. The users bridged from the remote system often appear as virtual users in matrix, as is the case with [matrix-appservice-instagram](https://github.com/turt2live/matrix-appservice-instagram).
### Sidecar bridge
Finally: the types of bridging described above assume that you are synchronising the conversation history of the remote system into Matrix, so it may be decentralised and exposed to multiple users within the wider Matrix network.
This can cause problems where the remote system may have arbitrarily complicated permissions (ACLs) controlling access to the history, which will then need to be correctly synchronised with Matrixs ACL model, without introducing security issues such as races. We already see some problems with this on the IRC bridge, where history visibility for +i and +k channels have to be carefully synchronised with the Matrix rooms.
You can also hit problems with other network-specific features not yet having equivalent representation in the Matrix protocol (e.g. ephemeral messages, or op-only messages - although arguably thats a type of ACL).
One solution could be to support an entirely different architecture of bridging, where the Matrix client-server API is mapped directly to the remote service, meaning that ACL decisions are delegated to the remote service, and conversations are not exposed into the wider Matrix. This is effectively using the bridge purely as a 3rd party client for the network (similar to Bitlbee). The bridge is only available to a single user, and conversations cannot be shared with other Matrix users as they arent actually Matrix rooms. (Another solution could be to use Active Policy Servers at last as a way of centralising and delegating ACLs for a room)
This is essentially an entirely different product to the rest of Matrix, and whilst it could be a solution for some particularly painful ACL problems, were focusing on non-sidecar bridges for now.

@ -1,11 +0,0 @@
---
layout: project
title: Try Matrix Now!
categories: howtos
---
<html>
<head>
<meta http-equiv="refresh" content="0; url=../guides/client-server.html" />
</head>
</html>

@ -1,17 +0,0 @@
.loggedin {
visibility: hidden;
}
p {
font-family: monospace;
}
table
{
border-spacing:5px;
}
th,td
{
padding:5px;
}

@ -1,30 +0,0 @@
<div>
<p>This room creation / message sending demo requires a homeserver to be running on http://localhost:8008</p>
</div>
<form class="loginForm">
<input type="text" id="userLogin" placeholder="Username"></input>
<input type="password" id="passwordLogin" placeholder="Password"></input>
<input type="button" class="login" value="Login"></input>
</form>
<div class="loggedin">
<form class="createRoomForm">
<input type="text" id="roomAlias" placeholder="Room alias (optional)"></input>
<input type="button" class="createRoom" value="Create Room"></input>
</form>
<form class="sendMessageForm">
<input type="text" id="roomId" placeholder="Room ID"></input>
<input type="text" id="messageBody" placeholder="Message body"></input>
<input type="button" class="sendMessage" value="Send Message"></input>
</form>
<table id="rooms">
<tbody>
<tr>
<th>Room ID</th>
<th>My state</th>
<th>Room Alias</th>
<th>Latest message</th>
</tr>
</tbody>
</table>
</div>

@ -1,113 +0,0 @@
var accountInfo = {};
var showLoggedIn = function(data) {
accountInfo = data;
getCurrentRoomList();
$(".loggedin").css({visibility: "visible"});
};
$('.login').live('click', function() {
var user = $("#userLogin").val();
var password = $("#passwordLogin").val();
$.ajax({
url: "http://localhost:8008/_matrix/client/api/v1/login",
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
dataType: "json",
success: function(data) {
showLoggedIn(data);
},
error: function(err) {
var errMsg = "To try this, you need a homeserver running!";
var errJson = $.parseJSON(err.responseText);
if (errJson) {
errMsg = JSON.stringify(errJson);
}
alert(errMsg);
}
});
});
var getCurrentRoomList = function() {
var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
$.getJSON(url, function(data) {
var rooms = data.rooms;
for (var i=0; i<rooms.length; ++i) {
rooms[i].latest_message = rooms[i].messages.chunk[0].content.body;
addRoom(rooms[i]);
}
}).fail(function(err) {
alert(JSON.stringify($.parseJSON(err.responseText)));
});
};
$('.createRoom').live('click', function() {
var roomAlias = $("#roomAlias").val();
var data = {};
if (roomAlias.length > 0) {
data.room_alias_name = roomAlias;
}
$.ajax({
url: "http://localhost:8008/_matrix/client/api/v1/createRoom?access_token="+accountInfo.access_token,
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(data),
dataType: "json",
success: function(data) {
data.membership = "join"; // you are automatically joined into every room you make.
data.latest_message = "";
addRoom(data);
},
error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText)));
}
});
});
var addRoom = function(data) {
row = "<tr>" +
"<td>"+data.room_id+"</td>" +
"<td>"+data.membership+"</td>" +
"<td>"+data.room_alias+"</td>" +
"<td>"+data.latest_message+"</td>" +
"</tr>";
$("#rooms").append(row);
};
$('.sendMessage').live('click', function() {
var roomId = $("#roomId").val();
var body = $("#messageBody").val();
var msgId = $.now();
if (roomId.length === 0 || body.length === 0) {
return;
}
var url = "http://localhost:8008/_matrix/client/api/v1/rooms/$roomid/send/m.room.message?access_token=$token";
url = url.replace("$token", accountInfo.access_token);
url = url.replace("$roomid", encodeURIComponent(roomId));
var data = {
msgtype: "m.text",
body: body
};
$.ajax({
url: url,
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(data),
dataType: "json",
success: function(data) {
$("#messageBody").val("");
// wipe the table and reload it. Using the event stream would be the best
// solution but that is out of scope of this fiddle.
$("#rooms").find("tr:gt(0)").remove();
getCurrentRoomList();
},
error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText)));
}
});
});

@ -1,17 +0,0 @@
.loggedin {
visibility: hidden;
}
p {
font-family: monospace;
}
table
{
border-spacing:5px;
}
th,td
{
padding:5px;
}

@ -1,23 +0,0 @@
<div>
<p>This event stream demo requires a homeserver to be running on http://localhost:8008</p>
</div>
<form class="loginForm">
<input type="text" id="userLogin" placeholder="Username"></input>
<input type="password" id="passwordLogin" placeholder="Password"></input>
<input type="button" class="login" value="Login"></input>
</form>
<div class="loggedin">
<form class="sendMessageForm">
<input type="button" class="sendMessage" value="Send random message"></input>
</form>
<p id="streamErrorText"></p>
<table id="rooms">
<tbody>
<tr>
<th>Room ID</th>
<th>Latest message</th>
</tr>
</tbody>
</table>
</div>

@ -1,145 +0,0 @@
var accountInfo = {};
var eventStreamInfo = {
from: "END"
};
var roomInfo = [];
var longpollEventStream = function() {
var url = "http://localhost:8008/_matrix/client/api/v1/events?access_token=$token&from=$from";
url = url.replace("$token", accountInfo.access_token);
url = url.replace("$from", eventStreamInfo.from);
$.getJSON(url, function(data) {
eventStreamInfo.from = data.end;
var hasNewLatestMessage = false;
for (var i=0; i<data.chunk.length; ++i) {
if (data.chunk[i].type === "m.room.message") {
for (var j=0; j<roomInfo.length; ++j) {
if (roomInfo[j].room_id === data.chunk[i].room_id) {
roomInfo[j].latest_message = data.chunk[i].content.body;
hasNewLatestMessage = true;
}
}
}
}
if (hasNewLatestMessage) {
setRooms(roomInfo);
}
$("#streamErrorText").text("");
longpollEventStream();
}).fail(function(err) {
$("#streamErrorText").text("Event stream error: "+JSON.stringify($.parseJSON(err.responseText)));
setTimeout(longpollEventStream, 5000);
});
};
var showLoggedIn = function(data) {
accountInfo = data;
longpollEventStream();
getCurrentRoomList();
$(".loggedin").css({visibility: "visible"});
};
$('.login').live('click', function() {
var user = $("#userLogin").val();
var password = $("#passwordLogin").val();
$.ajax({
url: "http://localhost:8008/_matrix/client/api/v1/login",
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
dataType: "json",
success: function(data) {
$("#rooms").find("tr:gt(0)").remove();
showLoggedIn(data);
},
error: function(err) {
var errMsg = "To try this, you need a homeserver running!";
var errJson = $.parseJSON(err.responseText);
if (errJson) {
errMsg = JSON.stringify(errJson);
}
alert(errMsg);
}
});
});
var getCurrentRoomList = function() {
$("#roomId").val("");
var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
$.getJSON(url, function(data) {
var rooms = data.rooms;
for (var i=0; i<rooms.length; ++i) {
if ("messages" in rooms[i]) {
rooms[i].latest_message = rooms[i].messages.chunk[0].content.body;
}
}
roomInfo = rooms;
setRooms(roomInfo);
}).fail(function(err) {
alert(JSON.stringify($.parseJSON(err.responseText)));
});
};
$('.sendMessage').live('click', function() {
if (roomInfo.length === 0) {
alert("There is no room to send a message to!");
return;
}
var index = Math.floor(Math.random() * roomInfo.length);
sendMessage(roomInfo[index].room_id);
});
var sendMessage = function(roomId) {
var body = "jsfiddle message @" + $.now();
if (roomId.length === 0) {
return;
}
var url = "http://localhost:8008/_matrix/client/api/v1/rooms/$roomid/send/m.room.message?access_token=$token";
url = url.replace("$token", accountInfo.access_token);
url = url.replace("$roomid", encodeURIComponent(roomId));
var data = {
msgtype: "m.text",
body: body
};
$.ajax({
url: url,
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(data),
dataType: "json",
success: function(data) {
$("#messageBody").val("");
},
error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText)));
}
});
};
var setRooms = function(roomList) {
// wipe existing entries
$("#rooms").find("tr:gt(0)").remove();
var rows = "";
for (var i=0; i<roomList.length; ++i) {
row = "<tr>" +
"<td>"+roomList[i].room_id+"</td>" +
"<td>"+roomList[i].latest_message+"</td>" +
"</tr>";
rows += row;
}
$("#rooms").append(rows);
};

@ -1,43 +0,0 @@
.roomListDashboard, .roomContents, .sendMessageForm {
visibility: hidden;
}
.roomList {
background-color: #909090;
}
.messageWrapper {
background-color: #EEEEEE;
height: 400px;
overflow: scroll;
}
.membersWrapper {
background-color: #EEEEEE;
height: 200px;
width: 50%;
overflow: scroll;
}
.textEntry {
width: 100%
}
p {
font-family: monospace;
}
table
{
border-spacing:5px;
}
th,td
{
padding:5px;
}
.roomList tr:not(:first-child):hover {
background-color: orange;
cursor: pointer;
}

@ -1,7 +0,0 @@
name: Example Matrix Client
description: Includes login, live event streaming, creating rooms, sending messages and viewing member lists.
authors:
- matrix.org
resources:
- http://matrix.org
normalize_css: no

@ -1,56 +0,0 @@
<div class="signUp">
<p>Matrix example application: Requires a local homeserver running at http://localhost:8008</p>
<form class="registrationForm">
<p>No account? Register:</p>
<input type="text" id="userReg" placeholder="Username"></input>
<input type="password" id="passwordReg" placeholder="Password"></input>
<input type="button" class="register" value="Register"></input>
</form>
<form class="loginForm">
<p>Got an account? Login:</p>
<input type="text" id="userLogin" placeholder="Username"></input>
<input type="password" id="passwordLogin" placeholder="Password"></input>
<input type="button" class="login" value="Login"></input>
</form>
</div>
<div class="roomListDashboard">
<form class="createRoomForm">
<input type="text" id="roomAlias" placeholder="Room alias"></input>
<input type="button" class="createRoom" value="Create Room"></input>
</form>
<table id="rooms" class="roomList">
<tbody>
<tr>
<th>Room</th>
<th>My state</th>
<th>Latest message</th>
</tr>
</tbody>
</table>
</div>
<div class="roomContents">
<p id="roomName">Select a room</p>
<div class="messageWrapper">
<table id="messages">
<tbody>
</tbody>
</table>
</div>
<form class="sendMessageForm">
<input type="text" class="textEntry" id="body" placeholder="Enter text here..." onkeydown="javascript:if (event.keyCode == 13) document.getElementById('sendMsg').focus()"></input>
<input type="button" class="sendMessage" id="sendMsg" value="Send"></input>
</form>
</div>
<div>
<p>Member list:</p>
<div class="membersWrapper">
<table id="members">
<tbody>
</tbody>
</table>
</div>
</div>

@ -1,327 +0,0 @@
var accountInfo = {};
var eventStreamInfo = {
from: "END"
};
var roomInfo = [];
var memberInfo = [];
var viewingRoomId;
// ************** Event Streaming **************
var longpollEventStream = function() {
var url = "http://localhost:8008/_matrix/client/api/v1/events?access_token=$token&from=$from";
url = url.replace("$token", accountInfo.access_token);
url = url.replace("$from", eventStreamInfo.from);
$.getJSON(url, function(data) {
eventStreamInfo.from = data.end;
var hasNewLatestMessage = false;
var updatedMemberList = false;
var i=0;
var j=0;
for (i=0; i<data.chunk.length; ++i) {
if (data.chunk[i].type === "m.room.message") {
console.log("Got new message: " + JSON.stringify(data.chunk[i]));
if (viewingRoomId === data.chunk[i].room_id) {
addMessage(data.chunk[i]);
}
for (j=0; j<roomInfo.length; ++j) {
if (roomInfo[j].room_id === data.chunk[i].room_id) {
roomInfo[j].latest_message = data.chunk[i].content.body;
hasNewLatestMessage = true;
}
}
}
else if (data.chunk[i].type === "m.room.member") {
if (viewingRoomId === data.chunk[i].room_id) {
console.log("Got new member: " + JSON.stringify(data.chunk[i]));
addMessage(data.chunk[i]);
for (j=0; j<memberInfo.length; ++j) {
if (memberInfo[j].state_key === data.chunk[i].state_key) {
memberInfo[j] = data.chunk[i];
updatedMemberList = true;
break;
}
}
if (!updatedMemberList) {
memberInfo.push(data.chunk[i]);
updatedMemberList = true;
}
}
if (data.chunk[i].state_key === accountInfo.user_id) {
getCurrentRoomList(); // update our join/invite list
}
}
else {
console.log("Discarding: " + JSON.stringify(data.chunk[i]));
}
}
if (hasNewLatestMessage) {
setRooms(roomInfo);
}
if (updatedMemberList) {
$("#members").empty();
for (i=0; i<memberInfo.length; ++i) {
addMember(memberInfo[i]);
}
}
longpollEventStream();
}).fail(function(err) {
setTimeout(longpollEventStream, 5000);
});
};
// ************** Registration and Login **************
var onLoggedIn = function(data) {
accountInfo = data;
longpollEventStream();
getCurrentRoomList();
$(".roomListDashboard").css({visibility: "visible"});
$(".roomContents").css({visibility: "visible"});
$(".signUp").css({display: "none"});
};
$('.login').live('click', function() {
var user = $("#userLogin").val();
var password = $("#passwordLogin").val();
$.ajax({
url: "http://localhost:8008/_matrix/client/api/v1/login",
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
dataType: "json",
success: function(data) {
onLoggedIn(data);
},
error: function(err) {
alert("Unable to login: is the homeserver running?");
}
});
});
$('.register').live('click', function() {
var user = $("#userReg").val();
var password = $("#passwordReg").val();
$.ajax({
url: "http://localhost:8008/_matrix/client/api/v1/register",
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
dataType: "json",
success: function(data) {
onLoggedIn(data);
},
error: function(err) {
var msg = "Is the homeserver running?";
var errJson = $.parseJSON(err.responseText);
if (errJson !== null) {
msg = errJson.error;
}
alert("Unable to register: "+msg);
}
});
});
// ************** Creating a room ******************
$('.createRoom').live('click', function() {
var roomAlias = $("#roomAlias").val();
var data = {};
if (roomAlias.length > 0) {
data.room_alias_name = roomAlias;
}
$.ajax({
url: "http://localhost:8008/_matrix/client/api/v1/createRoom?access_token="+accountInfo.access_token,
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(data),
dataType: "json",
success: function(response) {
$("#roomAlias").val("");
response.membership = "join"; // you are automatically joined into every room you make.
response.latest_message = "";
roomInfo.push(response);
setRooms(roomInfo);
},
error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText)));
}
});
});
// ************** Getting current state **************
var getCurrentRoomList = function() {
var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
$.getJSON(url, function(data) {
var rooms = data.rooms;
for (var i=0; i<rooms.length; ++i) {
if ("messages" in rooms[i]) {
rooms[i].latest_message = rooms[i].messages.chunk[0].content.body;
}
}
roomInfo = rooms;
setRooms(roomInfo);
}).fail(function(err) {
alert(JSON.stringify($.parseJSON(err.responseText)));
});
};
var loadRoomContent = function(roomId) {
console.log("loadRoomContent " + roomId);
viewingRoomId = roomId;
$("#roomName").text("Room: "+roomId);
$(".sendMessageForm").css({visibility: "visible"});
getMessages(roomId);
getMemberList(roomId);
};
var getMessages = function(roomId) {
$("#messages").empty();
var url = "http://localhost:8008/_matrix/client/api/v1/rooms/" +
encodeURIComponent(roomId) + "/messages?access_token=" + accountInfo.access_token + "&from=END&dir=b&limit=10";
$.getJSON(url, function(data) {
for (var i=data.chunk.length-1; i>=0; --i) {
addMessage(data.chunk[i]);
}
});
};
var getMemberList = function(roomId) {
$("#members").empty();
memberInfo = [];
var url = "http://localhost:8008/_matrix/client/api/v1/rooms/" +
encodeURIComponent(roomId) + "/members?access_token=" + accountInfo.access_token;
$.getJSON(url, function(data) {
for (var i=0; i<data.chunk.length; ++i) {
memberInfo.push(data.chunk[i]);
addMember(data.chunk[i]);
}
});
};
// ************** Sending messages **************
$('.sendMessage').live('click', function() {
if (viewingRoomId === undefined) {
alert("There is no room to send a message to!");
return;
}
var body = $("#body").val();
sendMessage(viewingRoomId, body);
});
var sendMessage = function(roomId, body) {
var msgId = $.now();
var url = "http://localhost:8008/_matrix/client/api/v1/rooms/$roomid/send/m.room.message?access_token=$token";
url = url.replace("$token", accountInfo.access_token);
url = url.replace("$roomid", encodeURIComponent(roomId));
var data = {
msgtype: "m.text",
body: body
};
$.ajax({
url: url,
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(data),
dataType: "json",
success: function(data) {
$("#body").val("");
},
error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText)));
}
});
};
// ************** Navigation and DOM manipulation **************
var setRooms = function(roomList) {
// wipe existing entries
$("#rooms").find("tr:gt(0)").remove();
var rows = "";
for (var i=0; i<roomList.length; ++i) {
row = "<tr>" +
"<td>"+roomList[i].room_id+"</td>" +
"<td>"+roomList[i].membership+"</td>" +
"<td>"+roomList[i].latest_message+"</td>" +
"</tr>";
rows += row;
}
$("#rooms").append(rows);
$('#rooms').find("tr").click(function(){
var roomId = $(this).find('td:eq(0)').text();
var membership = $(this).find('td:eq(1)').text();
if (membership !== "join") {
console.log("Joining room " + roomId);
var url = "http://localhost:8008/_matrix/client/api/v1/rooms/$roomid/join?access_token=$token";
url = url.replace("$token", accountInfo.access_token);
url = url.replace("$roomid", encodeURIComponent(roomId));
$.ajax({
url: url,
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({membership: "join"}),
dataType: "json",
success: function(data) {
loadRoomContent(roomId);
getCurrentRoomList();
},
error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText)));
}
});
}
else {
loadRoomContent(roomId);
}
});
};
var addMessage = function(data) {
var msg = data.content.body;
if (data.type === "m.room.member") {
if (data.content.membership === undefined) {
return;
}
if (data.content.membership === "invite") {
msg = "<em>invited " + data.state_key + " to the room</em>";
}
else if (data.content.membership === "join") {
msg = "<em>joined the room</em>";
}
else if (data.content.membership === "leave") {
msg = "<em>left the room</em>";
}
else if (data.content.membership === "ban") {
msg = "<em>was banned from the room</em>";
}
}
if (msg === undefined) {
return;
}
var row = "<tr>" +
"<td>"+data.user_id+"</td>" +
"<td>"+msg+"</td>" +
"</tr>";
$("#messages").append(row);
};
var addMember = function(data) {
var row = "<tr>" +
"<td>"+data.state_key+"</td>" +
"<td>"+data.content.membership+"</td>" +
"</tr>";
$("#members").append(row);
};

@ -1,7 +0,0 @@
.loggedin {
visibility: hidden;
}
p {
font-family: monospace;
}

@ -1,20 +0,0 @@
<div>
<p>This registration/login demo requires a homeserver to be running on http://localhost:8008</p>
</div>
<form class="registrationForm">
<input type="text" id="user" placeholder="Username"></input>
<input type="password" id="password" placeholder="Password"></input>
<input type="button" class="register" value="Register"></input>
</form>
<form class="loginForm">
<input type="text" id="userLogin" placeholder="Username"></input>
<input type="password" id="passwordLogin" placeholder="Password"></input>
<input type="button" class="login" value="Login"></input>
</form>
<div class="loggedin">
<p id="welcomeText"></p>
<input type="button" class="testToken" value="Test token"></input>
<input type="button" class="logout" value="Logout"></input>
<p id="imSyncText"></p>
</div>

@ -1,79 +0,0 @@
var accountInfo = {};
var showLoggedIn = function(data) {
accountInfo = data;
$(".loggedin").css({visibility: "visible"});
$("#welcomeText").text("Welcome " + accountInfo.user_id+". Your access token is: " +
accountInfo.access_token);
};
$('.register').live('click', function() {
var user = $("#user").val();
var password = $("#password").val();
$.ajax({
url: "http://localhost:8008/_matrix/client/api/v1/register",
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
dataType: "json",
success: function(data) {
showLoggedIn(data);
},
error: function(err) {
var errMsg = "To try this, you need a homeserver running!";
var errJson = $.parseJSON(err.responseText);
if (errJson) {
errMsg = JSON.stringify(errJson);
}
alert(errMsg);
}
});
});
var login = function(user, password) {
$.ajax({
url: "http://localhost:8008/_matrix/client/api/v1/login",
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
dataType: "json",
success: function(data) {
showLoggedIn(data);
},
error: function(err) {
var errMsg = "To try this, you need a homeserver running!";
var errJson = $.parseJSON(err.responseText);
if (errJson) {
errMsg = JSON.stringify(errJson);
}
alert(errMsg);
}
});
};
$('.login').live('click', function() {
var user = $("#userLogin").val();
var password = $("#passwordLogin").val();
$.getJSON("http://localhost:8008/_matrix/client/api/v1/login", function(data) {
if (data.flows[0].type !== "m.login.password") {
alert("I don't know how to login with this type: " + data.type);
return;
}
login(user, password);
});
});
$('.logout').live('click', function() {
accountInfo = {};
$("#imSyncText").text("");
$(".loggedin").css({visibility: "hidden"});
});
$('.testToken').live('click', function() {
var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
$.getJSON(url, function(data) {
$("#imSyncText").text(JSON.stringify(data, undefined, 2));
}).fail(function(err) {
$("#imSyncText").text(JSON.stringify($.parseJSON(err.responseText)));
});
});

@ -1,17 +0,0 @@
.loggedin {
visibility: hidden;
}
p {
font-family: monospace;
}
table
{
border-spacing:5px;
}
th,td
{
padding:5px;
}

@ -1,37 +0,0 @@
<div>
<p>This room membership demo requires a homeserver to be running on http://localhost:8008</p>
</div>
<form class="loginForm">
<input type="text" id="userLogin" placeholder="Username"></input>
<input type="password" id="passwordLogin" placeholder="Password"></input>
<input type="button" class="login" value="Login"></input>
</form>
<div class="loggedin">
<form class="createRoomForm">
<input type="button" class="createRoom" value="Create Room"></input>
</form>
<form class="changeMembershipForm">
<input type="text" id="roomId" placeholder="Room ID"></input>
<input type="text" id="targetUser" placeholder="Target User ID"></input>
<select id="membership">
<option value="invite">invite</option>
<option value="join">join</option>
<option value="leave">leave</option>
</select>
<input type="button" class="changeMembership" value="Change Membership"></input>
</form>
<form class="joinAliasForm">
<input type="text" id="roomAlias" placeholder="Room Alias (#name:domain)"></input>
<input type="button" class="joinAlias" value="Join via Alias"></input>
</form>
<table id="rooms">
<tbody>
<tr>
<th>Room ID</th>
<th>My state</th>
<th>Room Alias</th>
</tr>
</tbody>
</table>
</div>

@ -1,141 +0,0 @@
var accountInfo = {};
var showLoggedIn = function(data) {
accountInfo = data;
getCurrentRoomList();
$(".loggedin").css({visibility: "visible"});
$("#membership").change(function() {
if ($("#membership").val() === "invite") {
$("#targetUser").css({visibility: "visible"});
}
else {
$("#targetUser").css({visibility: "hidden"});
}
});
};
$('.login').live('click', function() {
var user = $("#userLogin").val();
var password = $("#passwordLogin").val();
$.ajax({
url: "http://localhost:8008/_matrix/client/api/v1/login",
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
dataType: "json",
success: function(data) {
$("#rooms").find("tr:gt(0)").remove();
showLoggedIn(data);
},
error: function(err) {
var errMsg = "To try this, you need a homeserver running!";
var errJson = $.parseJSON(err.responseText);
if (errJson) {
errMsg = JSON.stringify(errJson);
}
alert(errMsg);
}
});
});
var getCurrentRoomList = function() {
$("#roomId").val("");
// wipe the table and reload it. Using the event stream would be the best
// solution but that is out of scope of this fiddle.
$("#rooms").find("tr:gt(0)").remove();
var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
$.getJSON(url, function(data) {
var rooms = data.rooms;
for (var i=0; i<rooms.length; ++i) {
addRoom(rooms[i]);
}
}).fail(function(err) {
alert(JSON.stringify($.parseJSON(err.responseText)));
});
};
$('.createRoom').live('click', function() {
var data = {};
$.ajax({
url: "http://localhost:8008/_matrix/client/api/v1/createRoom?access_token="+accountInfo.access_token,
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(data),
dataType: "json",
success: function(data) {
data.membership = "join"; // you are automatically joined into every room you make.
data.latest_message = "";
addRoom(data);
},
error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText)));
}
});
});
var addRoom = function(data) {
row = "<tr>" +
"<td>"+data.room_id+"</td>" +
"<td>"+data.membership+"</td>" +
"<td>"+data.room_alias+"</td>" +
"</tr>";
$("#rooms").append(row);
};
$('.changeMembership').live('click', function() {
var roomId = $("#roomId").val();
var member = $("#targetUser").val();
var membership = $("#membership").val();
if (roomId.length === 0) {
return;
}
var url = "http://localhost:8008/_matrix/client/api/v1/rooms/$roomid/$membership?access_token=$token";
url = url.replace("$token", accountInfo.access_token);
url = url.replace("$roomid", encodeURIComponent(roomId));
url = url.replace("$membership", membership);
var data = {};
if (membership === "invite") {
data = {
user_id: member
};
}
$.ajax({
url: url,
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(data),
dataType: "json",
success: function(data) {
getCurrentRoomList();
},
error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText)));
}
});
});
$('.joinAlias').live('click', function() {
var roomAlias = $("#roomAlias").val();
var url = "http://localhost:8008/_matrix/client/api/v1/join/$roomalias?access_token=$token";
url = url.replace("$token", accountInfo.access_token);
url = url.replace("$roomalias", encodeURIComponent(roomAlias));
$.ajax({
url: url,
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({}),
dataType: "json",
success: function(data) {
getCurrentRoomList();
},
error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText)));
}
});
});

@ -1,20 +0,0 @@
---
layout: project
title: Riot
categories: projects client
thumbnail: /docs/projects/images/riot-web-small.png
author: Riot.im
description: Riot is a glossy web client with an emphasis on performance and usability
maturity: Released
---
![screenshot](/docs/projects/images/riot-web-large.png "{{ page.title }}")
# {{ page.title }}
[Riot](https://riot.im) is a glossy Matrix client built on top of [matrix-react-sdk](http://matrix.org/docs/projects/sdk/matrix.org-react-sdk.html) with an emphasis on performance and usability.
You can use it at [https://riot.im/app](https://riot.im/app), read more at [https://riot.im](https://riot.im) and get the source from [github](https://github.com/vector-im/vector-web)!
There is also a desktop version, which is available at [riot.im](https://riot.im/desktop.html). Taw has created RPM package builds for Fedora, CentOS, and Red Hat Enterprise Linux which are available via [GitHub](https://github.com/taw00/riot-rpm/) and [FedoraCorp](https://copr.fedorainfracloud.org/coprs/taw/Riot/).
Josué Tille has contributed a [Yunohost app](https://github.com/Josue-T/riot_ynh).

@ -1,10 +0,0 @@
---
layout: project
title: Try Matrix Now!
---
<html>
<head>
<meta http-equiv="refresh" content="0; url=./riot.html" />
</head>
</html>

@ -1,32 +0,0 @@
---
layout: project
title: Synapse
categories: projects server
thumbnail: https://matrix.org/blog/wp-content/uploads/2015/04/Screen-Shot-2015-04-29-at-00.28.25-400x284.png
author: Matrix.org team
maturity: Late beta
---
![screenshot](https://matrix.org/blog/wp-content/uploads/2015/04/Screen-Shot-2015-04-29-at-00.28.25-1080x579.png "{{ page.title }}")
# {{ page.title }}
Matrix.orgs reference server Synapse: [https://github.com/matrix-org/synapse](https://github.com/matrix-org/synapse)
Apt repo: [https://matrix.org/packages/debian/](https://matrix.org/packages/debian/)
Dockerfile from Silvio Fricke: [https://registry.hub.docker.com/u/silviof/docker-matrix/](https://registry.hub.docker.com/u/silviof/docker-matrix/)
ArchLinux package from Ivan Shapovalov: [https://www.archlinux.org/packages/community/any/matrix-synapse/](https://www.archlinux.org/packages/community/any/matrix-synapse/)
There is a FreeBSD package on [freshports.org](http://www.freshports.org/net/py-matrix-synapse/).
Martin Giess has created an auto-deployment process with vagrant/ansible, tested with VirtualBox/AWS/DigitalOcean see [https://github.com/EMnify/matrix-synapse-auto-deploy](https://github.com/EMnify/matrix-synapse-auto-deploy) for details.
Synapse is also on the [Open Build Service](https://obs.infoserver.lv/project/show/matrix-synapse).
Synapse is available for the [Nix package manager](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/misc/matrix-synapse.nix).
You can get Synapse on [Yunohost](https://github.com/YunoHost-Apps/synapse_ynh)
There is also a [handy spreadsheet](http://matrix.org/docs/projects/other/hdd-space-calc-for-synapse.html) to calculate HDD space for your Synapse instance.

@ -1,11 +0,0 @@
---
layout: project
title: NEB (N. E. Bot)
categories: projects other
description: Our dear Matrix Bot
author: Kegsay
maturity: Late beta
---
# {{ page.title }}
Kegsay's general-purpose Python Matrix Bot framework ([github](https://github.com/Kegsay/Matrix-NEB))

@ -1,10 +0,0 @@
---
layout: project
title: Matrix.org Android SDK
categories: projects sdk
author: Matrix.org team
maturity: Late beta
---
# {{ page.title }}
Matrix.org's Android SDK ([github](https://github.com/matrix-org/matrix-android-sdk))

@ -1,10 +0,0 @@
---
layout: project
title: Matrix.org iOS SDK
categories: projects sdk
author: Matrix.org team
maturity: Late beta
---
# {{ page.title }}
Matrix.org's iOS SDK ([github](https://github.com/matrix-org/matrix-ios-sdk))

@ -1,13 +0,0 @@
---
layout: project
title: Pallium
categories: projects server
thumbnail: https://matrix.org/blog/wp-content/uploads/2015/04/pallium.png
author: KoFish
maturity: Alpha
---
![screenshot](https://matrix.org/blog/wp-content/uploads/2015/04/pallium.png "{{ page.title }}")
# {{ page.title }}
KoFishs homeserver written in Go Pallium ([github](https://github.com/KoFish/pallium))

@ -1,12 +0,0 @@
---
layout: project
title: Matrix.org AngularJS SDK
categories: projects sdk
author: Matrix.org team
maturity: DEPRECATED
---
# {{ page.title }}
AngularJS services from Matrix.org for building communication apps in Angular based on Matrix: [https://github.com/matrix-org/matrix-angular-sdk](https://github.com/matrix-org/matrix-angular-sdk)
These are no longer being actively maintained.

@ -1,11 +0,0 @@
---
layout: project
title: Perl Matrix-IRC Bridge
categories: projects other
description: The first Matrix/IRC bridge
author: Tom Molesworth / Paul Evans
maturity: Beta
---
# {{ page.title }}
The first Matrix/IRC bridge, originally written by Tom Molesworth using Paul Evans' Net::Async::Matrix Perl client SDK and then later maintained by Paul Evans. Nowadays superceded by the [matrix-appservice-irc Node IRC bridge AS](../as/irc-bridge.html).

@ -1,11 +0,0 @@
---
layout: project
title: ivar2 Matrix/IRC Bot
categories: projects other
description: IRC bot with native Matrix support
author: haste / Tor
maturity: Beta
---
# {{ page.title }}
ivar2 is a very comprehensive IRC bot written in Lua which has native support for Matrix! [https://github.com/torhve/ivar2](https://github.com/torhve/ivar2)

@ -1,11 +0,0 @@
---
layout: project
title: Try Matrix Now!
categories: projects
---
<html>
<head>
<meta http-equiv="refresh" content="0; url=./try-matrix-now.html" />
</head>
</html>

@ -1,10 +0,0 @@
---
layout: project
title: Matrix.org Python SDK
categories: projects sdk
author: Matrix.org team
maturity: Alpha
---
# {{ page.title }}
Matrix.org's Python SDK ([github](https://github.com/matrix-org/matrix-python-sdk))

@ -1,13 +0,0 @@
---
layout: project
title: OpenMarket's SMS Gateway
categories: projects as
thumbnail: https://matrix.org/blog/wp-content/uploads/2015/04/omlogo-400x284.jpg
author: OpenMarket
maturity: Late beta
---
![screenshot](https://matrix.org/blog/wp-content/uploads/2015/04/omlogo.jpg "{{ page.title }}")
# {{ page.title }}
OpenMarkets SMS Gateway is an Application Service that enables Matrix-users to send SMS. [More info here](http://matrix.org/blog/2015/02/26/welcoming-the-openmarket-matrix-gateway/).

@ -1,18 +0,0 @@
---
layout: project
title: WeeChat script
categories: projects client
thumbnail: https://matrix.org/blog/wp-content/uploads/2015/04/Screen-Shot-2015-04-22-at-21.43.01-400x284.png
description: For fans of command line interfaces, a nice Matrix script for WeeChat, an IRSSI like CLI
author: Tor
maturity: Late beta
---
![screenshot](https://matrix.org/blog/wp-content/uploads/2015/04/Screen-Shot-2015-04-22-at-21.43.01.png "{{ page.title }}")
# {{ page.title }}
[WeeChat](http://weechat.org/) is a super powerful CLI chat client that is extensible in many languages to allow for new protocols like Matrix. @torhve contributed this great WeeChat script for Matrix which is getting a good feedback from more geeky users.
To use it, you will need to sign up for a Matrix account on a server using the [Console Web](./matrix-console.html) or [Vector](./vector.html) clients, as WeeChat doesnt support registration yet.
To run it, grab the code from [@torhve's github repo](https://github.com/torhve/weechat-matrix-protocol-script).

@ -1,10 +0,0 @@
---
layout: project
title: Matrix.org AS Node SDK
categories: projects as
author: Matrix.org team
maturity: Early beta
---
# {{ page.title }}
A framework from Matrix.org for building Application Services in Node.js/Express: [https://github.com/matrix-org/matrix-appservice-node](https://github.com/matrix-org/matrix-appservice-node)

@ -1,10 +0,0 @@
---
layout: project
title: Matrix.org JS SDK
categories: projects sdk
author: Matrix.org team
maturity: Early beta
---
# {{ page.title }}
Matrix.org's JS SDK ([github](https://github.com/matrix-org/matrix-js-sdk))

@ -1,14 +0,0 @@
---
layout: project
title: Matrix.org IRC Bridge
categories: projects as
description:
author: Matrix.org team
maturity: Early beta
---
# {{ page.title }}
An application service gateway from Matrix.org for bridging between IRC networks and Matrix, written using [matrix-appservice-node](http://matrix.org/blog/project/matrix-appservice-node/). You can get the [code on github](https://github.com/matrix-org/matrix-appservice-irc).
Supports dynamically bridging IRC channels on demand; synchronised user-lists, and other goodness.

@ -1,10 +0,0 @@
---
layout: project
title: redpill IRC bridge
categories: projects as
author: Tjgillies
maturity: Alpha
---
# {{ page.title }}
Kodo's IRC<->matrix GW written in Ruby ([github](https://github.com/tjgillies/redpill))

@ -1,13 +0,0 @@
---
layout: project
title: jSynapse
categories: projects server
thumbnail: https://matrix.org/blog/wp-content/uploads/2015/04/jsynapse2.png
author: Swarmcom
maturity: Alpha
---
![screenshot](https://matrix.org/blog/wp-content/uploads/2015/04/jsynapse2.png "{{ page.title }}")
# {{ page.title }}
Swarmcoms Java homeserver jSynapse ([github](https://github.com/swarmcom/jSynapse/))

@ -1,10 +0,0 @@
---
layout: project
title: gomatrix IRC bridge
categories: projects as
author: Tor
maturity: Alpha
---
# {{ page.title }}
Tor's IRC<->Matrix bridge written in the Go language ([github](https://github.com/torhve/gomirc))

@ -1,10 +0,0 @@
---
layout: project
title: Matrix.org MatrixKit (iOS)
categories: projects sdk
author: Matrix.org team
maturity: Late beta
---
# {{ page.title }}
Matrix.org's reusable UI interfaces for iOS ([github](https://github.com/matrix-org/matrix-ios-kit))

@ -1,16 +0,0 @@
---
layout: project
title: Riot iOS
categories: projects client
thumbnail: /docs/projects/images/vector-iOS-small.png
description: Riot is a glossy client with an emphasis on performance and usability
author: Riot.im
maturity: Released
---
![screenshot](/docs/projects/images/vector-iOS-large.png "{{ page.title }}")
# {{ page.title }}
The iOS version of the [Riot](https://matrix.org/docs/projects/client/riot.html) web client. Riot is a glossy client with focus on performance and usability.
The code is available from [github](https://github.com/vector-im/vector-ios), and the app is available from the Apple [app store](https://itunes.apple.com/gb/app/vector.im/id1083446067?mt=8).

@ -1,10 +0,0 @@
---
layout: project
title: Try Matrix Now!
---
<html>
<head>
<meta http-equiv="refresh" content="0; url=./riot-ios.html" />
</head>
</html>

@ -1,15 +0,0 @@
---
layout: project
title: Project Clearwater / Matrix Gateway
categories: projects as
thumbnail: https://matrix.org/blog/wp-content/uploads/2015/04/clearwter-400x284.jpg
author: Matt Williams
maturity: Alpha
---
![screenshot](https://matrix.org/blog/wp-content/uploads/2015/04/clearwter.jpg "{{ page.title }}")
![demo](https://matrix.org/blog/wp-content/uploads/2015/04/projectclearwatermatrixgateway1.jpg "Matt presenting his hack at TADHack London")
# {{ page.title }}
Matt Williams IMS->Matrix gateway ([github](https://github.com/matt-williams/sprout/tree/matrix))

@ -1,10 +0,0 @@
---
layout: project
title: newlisp-matrix-client
categories: projects sdk
author: Ingo Hohmann
maturity: Early beta
---
# {{ page.title }}
Client SDK for newlisp available at [https://github.com/IngoHohmann/newlisp-matrix-client](https://github.com/IngoHohmann/newlisp-matrix-client)

@ -1,10 +0,0 @@
---
layout: project
title: matrix-erlang-sdk
categories: projects sdk
author: Andreas Hallberg
maturity: Alpha
---
# {{ page.title }}
Andreas Hallberg's client SDK for Erlang [https://github.com/anhallbe/matrix-erlang-sdk](https://github.com/anhallbe/matrix-erlang-sdk)

@ -1,18 +0,0 @@
---
layout: project
title: Riot Android
categories: projects client
thumbnail: /docs/projects/images/vector-android-small.png
description: Riot is a glossy client with an emphasis on performance and usability
author: Riot.im
maturity: Released
---
![screenshot](/docs/projects/images/vector-android-large.png "{{ page.title }}")
# {{ page.title }}
The Android version of the [Riot](https://matrix.org/docs/projects/client/riot.html) web client. Riot is a glossy client with focus on performance and usability.
The code is available from [github](https://github.com/vector-im/vector-android), and the app is available from the [Google Play store](https://play.google.com/store/apps/details?id=im.vector.alpha) and [F-Droid](https://f-droid.org/repository/browse/?fdfilter=vector&fdid=im.vector.alpha).
If you want to help test the app, you can download development versions from [Jenkins](https://matrix.org/jenkins/job/VectorAndroidDevelop/).

@ -1,10 +0,0 @@
---
layout: project
title: Try Matrix Now!
---
<html>
<head>
<meta http-equiv="refresh" content="0; url=./riot-android.html" />
</head>
</html>

@ -1,11 +0,0 @@
---
layout: project
title: Matrix.org React SDK
categories: projects sdk
author: Matrix.org team
maturity: Early beta
---
# {{ page.title }}
[matrix-react-sdk](https://github.com/matrix-org/matrix-react-sdk) is our next-generation Web SDK for Matrix, providing both a large hierarchy of reusable UI components and a simple neutral reference application. It's suitable both for building standalone applications and embedding Matrix clients into existing web apps, irrespective of the host app's framework.

@ -1,14 +0,0 @@
---
layout: project
title: Tensor
categories: projects client
thumbnail: https://matrix.org/blog/wp-content/uploads/2015/11/tensor1-400x284.png
description: QML-based Matrix client
author: David A Roberts
maturity: Alpha
---
![screenshot](https://matrix.org/blog/wp-content/uploads/2015/11/tensor1.png "{{ page.title }}")
# {{ page.title }}
Tensor is a cross-platform native Matrix client, based on QtQuick/QML and libqmatrixclient (formerly matrix-js-sdk). It has been tested on Desktop GNU/Linux, OS X, Windows, Android, SailfishOS and Ubuntu Touch. Get it from [github](https://github.com/davidar/tensor).

@ -1,14 +0,0 @@
---
layout: project
title: Quaternion
categories: projects client
thumbnail: https://raw.githubusercontent.com/QMatrixClient/Quaternion/master/quaternion.png
author: Felix Rohrbach, Kitsune Ral
description: Quaternion is an IM client for the Matrix protocol
maturity: Late alpha
---
![screenshot](https://raw.githubusercontent.com/QMatrixClient/Quaternion/master/quaternion.png "{{ page.title }}")
# {{ page.title }}
Quaternion is a Qt/QML-based IM client for the Matrix protocol in development. ([github](https://github.com/QMatrixClient/Quaternion))

@ -1,10 +0,0 @@
---
layout: project
title: xmpptrix
categories: projects as
author: SkaveRat
maturity: Alpha
---
# {{ page.title }}
XMPP<->Matrix gateway application service written in Node JS: [https://github.com/SkaveRat/xmpptrix](https://github.com/SkaveRat/xmpptrix).

@ -1,12 +0,0 @@
---
layout: project
title: matrix-appservice-bridge
categories: projects as
author: Kegsay
maturity: Early beta
---
# {{ page.title }}
This library sits on top of the [core application service library](https://github.com/matrix-org/matrix-appservice-node) and provides an API for setting up bridges quickly.
Get it from [github](https://github.com/matrix-org/matrix-appservice-bridge) and have a look at the [HOW-TO](https://github.com/matrix-org/matrix-appservice-bridge/blob/master/HOWTO.md) for a step-by-step tutorial on setting up a new bridge!

@ -1,12 +0,0 @@
---
layout: project
title: matrix-appservice-respoke
categories: projects as
author: Matrix.org team
maturity: Alpha
---
# {{ page.title }}
An incredibly hacky Matrix --> Asterisk bridge via chan_respoke.
Get it from [github](https://github.com/matrix-org/matrix-appservice-respoke)!

@ -1,10 +0,0 @@
---
layout: project
title: Vertobridge
categories: projects as
author: Matthew / Kegan
maturity: Alpha
---
# {{ page.title }}
A Matrix <--> Verto bridge, designed for conferencing via Freeswitch. [https://github.com/matrix-org/matrix-appservice-verto](https://github.com/matrix-org/matrix-appservice-verto)

@ -1,10 +0,0 @@
---
layout: project
title: bullettime
categories: projects server
author: Patrik Oldsberg
maturity: Alpha
---
# {{ page.title }}
An experimental golang Matrix homeserver ([github](https://github.com/matrix-org/bullettime))

@ -1,12 +0,0 @@
---
layout: project
title: matrix-appservice-slack
categories: projects as
author: illicitonion
maturity: Early beta
---
# {{ page.title }}
This project bridges [Slack](https://slack.com) to Matrix, all in a 100 lines of code! It's currently quite barebones, but at the same time it shows how quickly a bridge can be written.
Get it from [github](https://github.com/matrix-org/matrix-appservice-slack)!

@ -1,12 +0,0 @@
---
layout: project
title: Matrix-XMPP Bridge
categories: projects as
author: jfrederickson
maturity: Alpha
---
# {{ page.title }}
This project creates a bridge between a Matrix room and an XMPP MUC: [https://github.com/jfrederickson/matrix-xmpp-bridge](https://github.com/jfrederickson/matrix-xmpp-bridge)
Update: there is also a forked and refactored version at [pztrn's GitHub space](https://github.com/pztrn/matrix-xmpp-bridge)

@ -1,11 +0,0 @@
---
layout: project
title: node-purple
categories: projects as
author: Matrix.org team / tjfontaine
maturity: Early beta
---
# {{ page.title }}
[node-purple](https://github.com/tjfontaine/node-purple) provides basic FFI bindings for libpurple. In our fork, [appservice](https://github.com/matrix-org/node-purple/tree/master/appservice) connects node-purple to the AS API, so you can talk from Matrix to libpurple's connections.

@ -1,12 +0,0 @@
---
layout: project
title: slackbridge
categories: projects as
author: illicitonion
maturity: Alpha
---
# {{ page.title }}
illicitonion's early Slack bridge, written in Go.
Check it out on [github](https://github.com/illicitonion/slackbridge)

@ -1,13 +0,0 @@
---
layout: project
title: purple-matrix
categories: projects client
description: A plugin for libpurple
author: Matrix.org team
maturity: Alpha
---
# {{ page.title }}
This project is a plugin for [libpurple](https://developer.pidgin.im/wiki/WhatIsLibpurple) which adds the ability to communicate with matrix.org homeservers to any libpurple-based clients (such as [Pidgin](http://www.pidgin.im/)).
Get it at [github](https://github.com/matrix-org/purple-matrix/).

@ -1,11 +0,0 @@
---
layout: project
title: Bender
categories: projects other
description: A simple/flexible bot framework
author: Dylan Griffith
maturity: Alpha
---
# {{ page.title }}
Bender is a Matrix bot framework written in Elixir: [https://github.com/DylanGriffith/bender](https://github.com/DylanGriffith/bender)

@ -1,15 +0,0 @@
---
layout: project
title: Mero
categories: projects as
description: NodeJS based XMPP facade bridge for matrix.org
author: SkaveRat
maturity: Alpha
---
# {{ page.title }}
NodeJS based XMPP facade bridge for matrix.org
This Matrix Application Server will pretend to be a XMPP (jabber) server and mirror all actions to and from Matrix.
Check it out on [github](https://github.com/SkaveRat/mero)

@ -1,13 +0,0 @@
---
layout: project
title: MatrixTool
categories: projects other
description: Commands to interact with a Matrix homeserver
author: LeoNerd
maturity: Alpha
---
# {{ page.title }}
The tool provides a wrapper around a number of sub-commands that provide useful interactions with a Matrix homeserver.
You can grab it from [CPAN](http://search.cpan.org/~pevans/App-MatrixTool/).

@ -1,11 +0,0 @@
---
layout: project
title: Ruma
categories: projects server
author: Jimmy Cuadra
maturity: Alpha
---
# {{ page.title }}
Ruma is a server written in Rust ([github](https://github.com/ruma/ruma))

@ -1,11 +0,0 @@
---
layout: project
title: Hubot-Matrix
categories: projects other
description: A Matrix-adapter for Hubot
author: David A Roberts
maturity: Alpha
---
# {{ page.title }}
A Matrix-adapter for Hubot: [https://github.com/davidar/hubot-matrix/](https://github.com/davidar/hubot-matrix/)

@ -1,10 +0,0 @@
---
layout: project
title: Matrix Client SDK for GLib
categories: projects sdk
author: Gergely Polonkai
maturity: Alpha
---
# {{ page.title }}
A Matrix.org client-server SDK for GLib >= 2.40. It contains both raw API calls and a signal based asynchronous client. Get it from [github](https://github.com/gergelypolonkai/matrix-glib-sdk)!

@ -1,15 +0,0 @@
---
layout: project
title: Perpetually Talking Online (PTO)
categories: projects client
description: PTO is an IRC frontend to the federated Matrix network.
author: tdfischer
maturity: Abandoned
---
# {{ page.title }}
Perpetually Talking Online (PTO) is an IRC frontend to the federated Matrix network. It aims to enable as many people as possible to use an existing Matrix homeserver with their existing IRC clients, and provides a radically expanded feature set for existing IRC communities looking to migrate to Matrix.
See [pto.im](http://pto.im) for more info - or grab the code from [GitHub](https://github.com/tdfischer/pto).
Unfortunately, it appears that this project is no longer being worked on. Check out [matrix-ircd](./matrix-ircd.html) instead!

@ -1,20 +0,0 @@
---
layout: project
title: Matrix Console iOS
categories: projects client
thumbnail: /docs/projects/images/matrix-console-ios-2016-02-16-cropped.png
description: A neutral iOS client showcasing Matrix capabilities and implementation.
author: Matrix.org team
maturity: No longer maintained
---
![screenshot](/docs/projects/images/matrix-console-ios-2016-02-16-large.png "{{ page.title }}")
# {{ page.title }}
Matrix.org's reference iOS client.
This client is meant to be a showcase of Matrix capabilities, a reference implementation of the Matrix standard and an easy entry to the Matrix ecosystem from iOS devices for less techy users, with a relatively neutral branding. This client is built using [MatrixKit](http://matrix.org/blog/project/matrix-ios-matrixkit/).
The code can be retrieved from [github](https://github.com/matrix-org/matrix-ios-console).
Also available from the Apple [app store](https://itunes.apple.com/gb/app/matrix-console/id970074271?mt=8).

@ -1,22 +0,0 @@
---
layout: project
title: Matrix Console Android
categories: projects client
thumbnail: /docs/projects/images/matrix-console-android-2016-02-16-cropped.png
description: A neutral Android client showcasing Matrix capabilities and implementation.
author: Matrix.org team
maturity: No longer maintained
---
![screenshot](/docs/projects/images/matrix-console-android-2016-02-16-large.png "{{ page.title }}")
# {{ page.title }}
Matrix.orgs reference Android client.
This client is meant to be a showcase of Matrix capabilities, a reference implementation of the Matrix standard and an easy entry to the Matrix ecosystem from Android devices for less techy users, with a relatively neutral branding.
The code can be retrieved from [github](https://github.com/matrix-org/matrix-android-console).
Also available from the [Google Play store](https://play.google.com/store/apps/details?id=org.matrix.androidsdk.alpha).
Folks wanting to live on the bleeding edge can also pull APKs of the develop (and master) branches straight out of the [Jenkins build server](http://matrix.org/jenkins/job/AndroidConsoleDevelop/).

@ -1,21 +0,0 @@
---
layout: project
title: Matrix Console Web
categories: projects client
thumbnail: https://matrix.org/blog/wp-content/uploads/2015/04/syweb2-400x284.png
description: Matrix.orgs legacy AngularJS web client.
author: Matrix.org team
maturity: No longer maintained
---
![screenshot](https://matrix.org/blog/wp-content/uploads/2015/04/syweb2-1080x806.png "{{ page.title }}")
# {{ page.title }}
Matrix.orgs original reference AngularJS webclient. The implementation has major performance issues and is not being actively maintained. Please see [Vector](https://matrix.org/blog/project/vector) and [matrix-react-sdk](https://matrix.org/blog/project/matrix-react-sdk) for better alternatives.
The code can be retrieved from [github](https://github.com/matrix-org/matrix-angular-sdk).
Public hosts running this client:
[https://matrix.org/beta](https://matrix.org/beta)

@ -1,14 +0,0 @@
---
layout: project
title: Glowing Bear + WeeChat Script
categories: projects client
thumbnail: https://matrix.org/blog/wp-content/uploads/2015/04/Screen-Shot-2015-04-21-at-17.36.43-400x284.png
author: glowing-bear.org
description: A great IRC-style web interface to Matrix.
maturity: Late beta
---
![screenshot](https://matrix.org/blog/wp-content/uploads/2015/04/Screen-Shot-2015-04-21-at-17.36.43-1080x581.png "{{ page.title }}")
# {{ page.title }}
[Glowing Bear](http://glowing-bear.org/) combined with [@torhves Matrix script for WeeChat](./weechat.html) makes for a great IRC-style web interface to Matrix.

@ -1,12 +0,0 @@
---
layout: project
title: Dendron
categories: projects server
author: Matrix.org team
maturity: Obsolete
---
# {{ page.title }}
Dendron was the start of an experimental Matrix homeserver written in Go, built as a 'strangler pattern' implementation on top of Synapse. It was discontinued in favour of Dendrite.
The code lives on at [github](https://github.com/matrix-org/dendron)!

@ -1,13 +0,0 @@
---
layout: project
title: Rocket Chat Federation
categories: projects as
thumbnail: https://raw.githubusercontent.com/Sing-Li/bbug/master/images/rcsnynapse.png
author: Rocket.Chat
maturity: Alpha
---
![screenshot](https://raw.githubusercontent.com/Sing-Li/bbug/master/images/rcsnynapse.png "{{ page.title }}")
# {{ page.title }}
Rocket.Chat have written a bot called Freddie that pairs a Synapse server with a Rocket.Chat server ([github](https://github.com/RocketChat/Rocket.Chat.Federation/tree/develop/matrix.org/hubot-freddie))

@ -1,14 +0,0 @@
---
layout: project
title: Unplug
categories: projects client
thumbnail: https://matrix.org/blog/wp-content/uploads/2015/04/unplug-0.1.5-400x284.png
description: Experimental Kotlin client
author: hrjet
maturity: Alpha
---
![screenshot](https://matrix.org/blog/wp-content/uploads/2015/04/unplug-0.1.5.png "{{ page.title }}")
# {{ page.title }}
hrjet's Kotlin/JVM client unplug ([github](https://github.com/UprootLabs/unplug))

@ -1,13 +0,0 @@
---
layout: project
title: feedbot
categories: projects other
description: Connects to RSS and Twitter feeds
author: Ryan Rix
maturity: Early beta
---
# {{ page.title }}
Connect to RSS and Twitter feeds via Ryan Rix's feedbot!
Check it out from [Ryan's git repo](https://fort.kickass.systems/git/rrix/matrix-feedbot)

@ -1,13 +0,0 @@
---
layout: project
title: Lightrix
categories: projects other
description: Drive Adafruit Neopixels over Matrix
author: Ryan Rix
maturity: Early beta
---
# {{ page.title }}
Control Adafruit Neopixels via Matrix!
Check it out from [Ryan's git repo](https://fort.kickass.systems/git/rrix/lightrix) or watch it in action on [YouTube](https://www.youtube.com/watch?v=4YG9Fk5aP24)

@ -1,13 +0,0 @@
---
layout: project
title: mcat
categories: projects other
description: Pipe to/from a Matrix room via the Python SDK
author: Ryan Rix
maturity: Early beta
---
# {{ page.title }}
Pipe in to and out of a Matrix room using the [Matrix Python SDK](https://github.com/matrix-org/matrix-python-sdk).
Check it out from [Ryan's git repo](https://fort.kickass.systems/git/rrix/matrix-cat)

@ -1,13 +0,0 @@
---
layout: project
title: Polynomial
description: A Decentralized Webring
categories: projects other
author: Ryan Rix
maturity: Early beta
---
# {{ page.title }}
Webring software built on top of Matrix.org which means it doesn't have a central point of failure.
Check it out at [Ryan's blog](http://whatthefuck.computer/blog/2015/12/06/polynomial-a-decentralized-webring/) and grab the code from [Ryan's git repo](https://fort.kickass.systems/git/rrix/polynomial)

@ -1,14 +0,0 @@
---
layout: project
title: redpill
categories: projects client
thumbnail: https://matrix.org/blog/wp-content/uploads/2015/04/redpill0.7-400x284.png
description: A Python2 CLI client
author: oddvar
maturity: Alpha
---
![screenshot](https://matrix.org/blog/wp-content/uploads/2015/04/redpill0.7.png "{{ page.title }}")
# {{ page.title }}
Oddvar's Python2 CLI client redpill ([github](https://github.com/oddvar/redpill))

@ -1,14 +0,0 @@
---
layout: project
title: Headjack
categories: projects client
thumbnail: https://matrix.org/blog/wp-content/uploads/2015/05/headjack-400x284.png
description: Experimental Chrome App client
author: SkaveRat
maturity: Alpha
---
![screenshot](https://matrix.org/blog/wp-content/uploads/2015/05/headjack.png "{{ page.title }}")
# {{ page.title }}
Experimental Chrome App desktop client for Matrix, using [matrix-angular-sdk](https://github.com/matrix-org/matrix-angular-sdk). Available from [github](https://github.com/SkaveRat/headjack).

@ -1,10 +0,0 @@
---
layout: project
title: Net::Async::Matrix (Perl)
categories: projects sdk
author: LeoNerd
maturity: Late beta
---
# {{ page.title }}
LeoNerd's Net::Async::Matrix client SDK library for Perl: [https://metacpan.org/release/Net-Async-Matrix](https://metacpan.org/release/Net-Async-Matrix)

@ -1,216 +0,0 @@
---
layout: project
title: Try Matrix Now!
categories: projects
---
<div class='font18'>
Matrix is a whole ecosystem of matrix-enabled clients, servers, gateways, application services, bots, etc.
</div>
|
<div class='font18 bold'>
The easiest way to get started is to pick a client that appeals and join #matrix:matrix.org:
</div>
<p>&nbsp;</p>
<table class='bigtable'>
<tr>
<td class='bigproject'>
<a href='./client/weechat.html' class='font18 bold'>
Weechat/Matrix
</a><br />
If you like command line clients, try the Weechat plugin.<br /><br />
<a href='./client/weechat.html'>
<img src='https://matrix.org/blog/wp-content/uploads/2015/04/Screen-Shot-2015-08-07-at-13.31.29-300x209.png' class='featured_screenshot'>
</a>
</td>
<td class='bigproject'>
<a href='./client/riot.html' class='font18 bold'>
Riot
</a><br />
If you like glossy and feature-rich web clients, try Riot.<br /><br />
<a href='./client/riot.html'>
<img src='/docs/projects/images/riot-web-featured.png' class='featured_screenshot'>
</a>
</td>
<td class='bigproject'>
<a href='./client/riot-ios.html' class='font18 bold'>
Riot iOS
</a><br />
You can also access Matrix on your iOS phone via Riot iOS.<br /><br />
<a href='./client/riot-ios.html'>
<img src='/docs/projects/images/vector-iOS-featured.png' class='featured_screenshot'>
</a>
</td>
<td class='bigproject'>
<a href='./client/riot-android.html' class='font18 bold'>
Riot Android
</a><br />
Riot is also available on Android devices!<br /><br />
<a href='./client/riot-android.html'>
<img src='/docs/projects/images/vector-android-featured.png' class='featured_screenshot'>
</a>
</td>
</tr>
</table>
This page aims to collect all known Matrix projects - if you want to add a new one (or update an existing one), you can submit a PR to the [matrix-doc](https://github.com/matrix-org/matrix-doc) project on github - the existing projects can be found [here](https://github.com/matrix-org/matrix-doc/tree/master/supporting-docs/projects) - or just let us know in the #matrix:matrix.org room.
|
<div class='font18'>
Projects using Matrix:
</div>
* TOC
{:toc .toc}
|
Clients
=======
<table>
{% assign post_nr = '0' %}
{% for post in site.categories.client reversed limit:100 %}
{% assign add_new_row_test = post_nr | modulo:6 %}
{% if add_new_row_test == 0 %}<tr>{% endif %}
<td class='project'>
<a href='/docs{{ BASE_PATH }}{{ post.url }}'>
<img class='thumbnail' src='{{ post.thumbnail }}'>
</a>
<br />
<a href='/docs{{ BASE_PATH }}{{ post.url }}'>
{{ post.title }}
</a><br />
<div style='margin-bottom: 8px;'>
{{ post.description }}
</div>
Author: {{ post.author }}<br />
Maturity: {{ post.maturity }}
</td>
{% assign post_nr = post_nr | plus: '1' %}
{% assign add_new_row_test = post_nr | modulo:6 %}
{% if add_new_row_test == 0 %}</tr>{% endif %}
{% endfor %}
</tr>
</table>
|
Servers
=======
<table>
{% assign post_nr = '0' %}
{% for post in site.categories.server reversed limit:100 %}
{% assign add_new_row_test = post_nr | modulo:6 %}
{% if add_new_row_test == 0 %}<tr>{% endif %}
<td class='project'>
<a href='/docs{{ BASE_PATH }}{{ post.url }}'>
{{ post.title }}
</a><br />
<div style='margin-bottom: 8px;'>
{{ post.description }}
</div>
Author: {{ post.author }}<br />
Maturity: {{ post.maturity }}
</td>
{% assign post_nr = post_nr | plus: '1' %}
{% assign add_new_row_test = post_nr | modulo:6 %}
{% if add_new_row_test == 0 %}</tr>{% endif %}
{% endfor %}
</tr>
</table>
|
Application Services
====================
<table>
{% assign post_nr = '0' %}
{% for post in site.categories.as reversed limit:100 %}
{% assign add_new_row_test = post_nr | modulo:6 %}
{% if add_new_row_test == 0 %}<tr>{% endif %}
<td class='project'>
<a href='/docs{{ BASE_PATH }}{{ post.url }}'>
{{ post.title }}
</a><br />
<div style='margin-bottom: 8px;'>
{{ post.description }}
</div>
Author: {{ post.author }}<br />
Maturity: {{ post.maturity }}
</td>
{% assign post_nr = post_nr | plus: '1' %}
{% assign add_new_row_test = post_nr | modulo:6 %}
{% if add_new_row_test == 0 %}</tr>{% endif %}
{% endfor %}
</tr>
</table>
|
Client SDKs
===========
<table>
{% assign post_nr = '0' %}
{% for post in site.categories.sdk reversed limit:100 %}
{% assign add_new_row_test = post_nr | modulo:6 %}
{% if add_new_row_test == 0 %}<tr>{% endif %}
<td class='project'>
<a href='/docs{{ BASE_PATH }}{{ post.url }}'>
{{ post.title }}
</a><br />
<div style='margin-bottom: 8px;'>
{{ post.description }}
</div>
Author: {{ post.author }}<br />
Maturity: {{ post.maturity }}
</td>
{% assign post_nr = post_nr | plus: '1' %}
{% assign add_new_row_test = post_nr | modulo:6 %}
{% if add_new_row_test == 0 %}</tr>{% endif %}
{% endfor %}
</tr>
</table>
|
Other
=====
<table>
{% assign post_nr = '0' %}
{% for post in site.categories.other reversed limit:100 %}
{% assign add_new_row_test = post_nr | modulo:6 %}
{% if add_new_row_test == 0 %}<tr>{% endif %}
<td class='project'>
<a href='/docs{{ BASE_PATH }}{{ post.url }}'>
{{ post.title }}
</a><br />
<div style='margin-bottom: 8px;'>
{{ post.description }}
</div>
Author: {{ post.author }}<br />
Maturity: {{ post.maturity }}
</td>
{% assign post_nr = post_nr | plus: '1' %}
{% assign add_new_row_test = post_nr | modulo:6 %}
{% if add_new_row_test == 0 %}</tr>{% endif %}
{% endfor %}
</tr>
</table>

@ -1,10 +0,0 @@
---
layout: project
title: Vector Desktop
description: Desktop version of Vector
author: Steven Hammerton
maturity: Alpha
---
# {{ page.title }}
Desktop version of the [Vector](./vector.html) web client. Basically it's Vector wrapped in an [Electron](https://github.com/atom/electron) app. Source here: [https://github.com/stevenhammerton/vector-desktop](https://github.com/stevenhammerton/vector-desktop)

@ -1,11 +0,0 @@
---
layout: project
title: Matrix Blog
categories: projects client
description: Read-only blog-style Matrix interface
author: simeng
maturity: Alpha
---
# {{ page.title }}
An example read-only blog-style interface to a Matrix room ([github](https://github.com/simeng/matrix-blog)).

@ -1,12 +0,0 @@
---
layout: project
title: matrix-appservice-gitter
categories: projects as
author: LeoNerd
maturity: Early beta
---
# {{ page.title }}
This project bridges [Gitter](https://gitter.im) to Matrix, via the AS API on the Matrix side, and a Gitter user on the Gitter side.
Get it from [github](https://github.com/matrix-org/matrix-appservice-gitter).

@ -1,12 +0,0 @@
---
layout: project
title: Matrix .NET SDK
categories: projects sdk
author: Half-Shot
maturity: Alpha
---
# {{ page.title }}
The .NET SDK provides an object oriented library to interact with Matrix. It is currently mature enough to be used for simple clients and bots.
[Github](https://github.com/Half-Shot/matrix-dotnet-sdk)

@ -1,17 +0,0 @@
---
layout: project
title: MPD DJ
categories: projects other
description: A bot for controlling MPD over matrix.
author: Half-Shot
maturity: Alpha
---
# {{ page.title }}
Ever wanted to control a MPD instance over matrix? Now you can!
Supports a selection (with more coming soon) of mpd commands with the ability to play Youtube links.
Development is steadily ongoing at [Github](https://github.com/Half-Shot/matrix-mpd-dj)

@ -1,12 +0,0 @@
---
layout: project
title: libqmatrixclient
categories: projects sdk
author: Kitsune Ral, Felix Rohrbach
maturity: Late alpha
---
# {{ page.title }}
libqmatrixclient is a Qt-based library to make IM clients for the Matrix protocol. It is used by [Quaternion](https://matrix.org/docs/projects/client/quaternion.html) and is a part of the QMatrixClient project. Recent builds of [Tensor](https://matrix.org/docs/projects/client/tensor.html) are also made on top of libqmatrixclient.
The project lives in QMatrixClient [github space](https://github.com/QMatrixClient/libqmatrixclient).

@ -1,12 +0,0 @@
---
layout: project
title: Concourse/Matrix notification resource
categories: projects other
thumbnail: /docs/projects/images/concourse-ci-logo.png
description: Post notifications from Concourse CI jobs
author: freelock
maturity: beta
---
# {{ page.title }}
Create a Concourse custom resource type using [freelock/matrix-notification-resource](https://hub.docker.com/r/freelock/matrix-notification-resource/) from Docker Hub, or fork/contribute on [github](https://github.com/freelock/matrix-notification-resource)

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save