README.md 14.7 KB
Newer Older
1 2
# Briar REST API

3
This is a headless Briar peer that exposes a REST API
4
with an integrated HTTP server instead of a traditional user interface.
5
You can use this API to script the peer behavior
6 7 8 9
or to develop your own user interface for it.

## How to use

10
The REST API peer comes as a `jar` file
11
and needs a Java Runtime Environment (JRE) that supports at least Java 8.
12 13
It currently works only on GNU/Linux operating systems.

14 15 16 17
To build the `jar` file, you can do this:

    $ ./gradlew --configure-on-demand briar-headless:jar

18
You can start the peer (and its API server) like this:
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

    $ java -jar briar-headless/build/libs/briar-headless.jar

It is possible to put parameters at the end.
Try `--help` for a list of options.

On the first start, it will ask you to create a user account:

    $ java -jar briar-headless.jar
    No account found. Let's create one!

    Nickname: testuser
    Password:

After entering a password, it will start up without further output.
Use the `-v` option if you prefer more verbose logging.

By default, Briar creates a folder `~/.briar` where it stores its database and other files.
There you also find the authentication token which is required to interact with the API:

    $ cat ~/.briar/auth_token
40
    DZbfoUie8sjap7CSDR9y6cgJCojV+xUITTIFbgtAgqk=
41 42 43

You can test that things work as expected by running:

44
    $ curl -H "Authorization: Bearer DZbfoUie8sjap7CSDR9y6cgJCojV+xUITTIFbgtAgqk=" http://127.0.0.1:7000/v1/contacts
45 46 47
    []

The answer is an empty JSON array, because you don't have any contacts.
48
Note that the HTTP request sets an `Authorization` header with the bearer token.
49 50 51 52 53 54 55 56 57 58 59 60 61
A missing or wrong token will result in a `401` response.

## REST API

### Listing all contacts

`GET /v1/contacts`

Returns a JSON array of contacts:

```json
{
    "author": {
62
        "formatVersion": 1,
63 64 65 66 67
        "id": "y1wkIzAimAbYoCGgWxkWlr6vnq1F8t1QRA/UMPgI0E0=",
        "name": "Test",
        "publicKey": "BDu6h1S02bF4W6rgoZfZ6BMjTj/9S9hNN7EQoV05qUo="
    },
    "contactId": 1,
68 69
    "alias" : "A local nickname",
    "handshakePublicKey": "XnYRd7a7E4CTqgAvh4hCxh/YZ0EPscxknB9ZcEOpSzY=",
70
    "verified": true,
71
    "lastChatActivity": 1557838312175,
72 73
    "connected": false,
    "unreadCount": 7
74 75 76
}
```

77 78
Note that the key `alias` isn't guaranteed to be in the response.

79 80
### Adding a contact

81
The first step is to get your own link:
82

83 84
`GET /v1/contacts/add/link`

85
This returns a JSON object with a `briar://` link that needs to be sent to the contact you want to add
86 87 88 89 90 91 92 93 94 95 96
outside of Briar via an external channel.

```json
{
    "link": "briar://wvui4uvhbfv4tzo6xwngknebsxrafainnhldyfj63x6ipp4q2vigy"
}
```

Once you have received the link of your future contact, you can add them
by posting the link together with an arbitrary nickname (or alias):

97
`POST /v1/contacts/add/pending`
98 99 100 101 102 103 104 105 106 107

The link and the alias should be posted as a JSON object:

```json
{
    "link": "briar://ddnsyffpsenoc3yzlhr24aegfq2pwan7kkselocill2choov6sbhs",
    "alias": "A nickname for the new contact"
}
```

108
Adding a pending contact starts the process of adding the contact.
109 110 111 112 113 114 115 116 117 118
Until it is completed, a pending contact is returned as JSON:

```json
{
    "pendingContactId": "jsTgWcsEQ2g9rnomeK1g/hmO8M1Ix6ZIGWAjgBtlS9U=",
    "alias": "ztatsaajzeegraqcizbbfftofdekclatyht",
    "timestamp": 1557838312175
}
```

119 120
Possible errors when adding a pending contact are:

121 122 123 124 125 126 127 128
#### 400: Pending contact's link is invalid

```json
{
    "error": "INVALID_LINK"
}
```

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
#### 400: Pending contact's handshake public key is invalid

```json
{
    "error": "INVALID_PUBLIC_KEY"
}
```

#### 403: A contact with the same handshake public key already exists

This error may be caused by someone attacking the user with the goal
of discovering the contacts of the user.

In the Android client, upon encountering this issue a message dialog
is shown that asks whether the contact and the just added pending contact
are the same person. If that's the case, a message is shown that the
contact already exists and the pending contact isn't added.
If that's not the case and they are two different persons, the Android
client
[shows the following message](https://code.briarproject.org/briar/briar/-/blob/beta-1.2.14/briar-android/src/main/res/values/strings.xml#L271)
when this happens:
> [Alice] and [Bob] sent you the same link.
>
> One of them may be trying to discover who your contacts are.
>
> Don't tell them you received the same link from someone else.

```json
{
158 159
    "error": "CONTACT_EXISTS",
    "remoteAuthorName": "Bob"
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
}
```

#### 403: A pending contact with the same handshake public key already exists

This error, too, may be caused by someone attacking the user with the goal
of discovering the contacts of the user.

Just like above, upon encountering this issue a message dialog is shown in
the Android client that asks whether the contact and the just added pending
contact are the same person. If that's the case, the pending contact gets
updated. If that's not the case and they are two different persons, the
Android client shows the same message as above, warning the user about the
possible attack.

```json
{
177
    "error": "PENDING_EXISTS",
178
    "pendingContactId": "jsTgWcsEQ2g9rnomeK1g/hmO8M1Ix6ZIGWAjgBtlS9U=",
179
    "pendingContactAlias": "Alice"
180 181 182 183
}
```
-----------

184 185 186 187 188 189 190 191
Before users can send messages to contacts, they become pending contacts.
In this state Briar still needs to do some work in the background (e.g.
spinning up a dedicated hidden service and letting the contact connect to it).
Pending contacts aren't shown in the Android's client contact list.
Note that the `pendingContactId` differs from the `authorId` the contact will get later.
The `pendingContactId` is the hash of their public handshake key, so it is the
same if another device is trying to add the same contact.

192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
It is possible to get a list of all pending contacts:

`GET /v1/contacts/add/pending`

This will return a JSON array of pending contacts and their states:

```json
{
    "pendingContact": {
        "pendingContactId": "jsTgWcsEQ2g9rnomeK1g/hmO8M1Ix6ZIGWAjgBtlS9U=",
        "alias": "ztatsaajzeegraqcizbbfftofdekclatyht",
        "timestamp": 1557838312175
    },
    "state": "adding_contact"
}
```

209 210
The state can be one of these values:

211 212 213 214 215 216 217 218 219 220 221 222
  * `waiting_for_connection`: Briar is still waiting to establish a connection
  via the dedicated Tor hidden service(s). Each contact creates one and whoever
  connects first wins. Making the hidden services available and establishing a
  connection to them can take some time.
  * `offline`: Briar went offline before the contact could be added.
  * `connecting`: Briar made a connection and can now start the process of
  adding the contact.
  * `adding_contact`: The contact will be added. Once this is complete the
  pending contact will be removed and replaced by a "real" contact.
  * `failed`: Briar tried for two days to connect, but couldn't get a
  connection, so it will stop trying. The pending contact will stick around as
  failed until removed.
223

224
If you want to be informed about state changes,
225 226 227 228
you can use the Websocket API (below) to listen for events.

The following events are relevant here:

229
  * `PendingContactAddedEvent`
230 231
  * `PendingContactStateChangedEvent`
  * `PendingContactRemovedEvent`
akwizgran's avatar
akwizgran committed
232
  * `ContactAddedEvent` (when the pending contact becomes an actual contact)
233 234 235 236 237 238 239 240 241 242 243 244 245

To remove a pending contact and abort the process of adding it:

`DELETE /v1/contacts/add/pending`

The `pendingContactId` of the pending contact to delete
needs to be provided in the request body as follows:

```json
{
    "pendingContactId": "jsTgWcsEQ2g9rnomeK1g/hmO8M1Ix6ZIGWAjgBtlS9U="
}
```
246

247 248 249 250
Note that it's also possible to add contacts nearby via Bluetooth/Wifi or
introductions. In these cases contacts omit the `pendingContact` state and
directly become `contact`s.

251 252 253 254 255 256 257 258 259 260 261 262
### Changing alias of a contact

`PUT /v1/contacts/{contactId}/alias`

The alias should be posted as a JSON object:

```json
{
    "alias": "A nickname for the new contact"
}
```

263 264 265 266 267 268 269
### Removing a contact

`DELETE /v1/contacts/{contactId}`

The `{contactId}` is the `contactId` of the contact (`1` in the example above).
It returns with a status code `200`, if removal was successful.

270 271
### Listing all private messages

272
`GET /v1/messages/{contactId}`
273 274 275 276 277 278 279 280 281 282 283 284 285

The `{contactId}` is the `contactId` of the contact (`1` in the example above).
It returns a JSON array of private messages:

```json
{
    "contactId": 1,
    "groupId": "oRRvCri85UE2XGcSloAKt/u8JDcMkmDc26SOMouxr4U=",
    "id": "ZGDrlpCxO9v7doO4Bmijh95QqQDykaS4Oji/mZVMIJ8=",
    "local": true,
    "read": true,
    "seen": true,
    "sent": true,
286
    "text": "test",
287
    "timestamp": 1537376633850,
288
    "type": "PrivateMessage"
289 290 291
}
```

292
If `local` is `true`, the message was sent by the Briar peer instead of its remote contact.
293

294 295 296 297 298 299
A message is `read` when the local user has read it i.e. it was displayed on their screen.
This only makes sense for incoming messages (which are not `local`).
`sent` and `seen` are only useful for outgoing (`local`) messages.
`sent` means that we offered the message to the contact (one tick) and `seen` (two ticks)
means that they confirmed the delivery of the message.

300
Attention: There can messages of other `type`s where the message `text` is `null`.
301 302 303

### Writing a private message

304
`POST /v1/messages/{contactId}`
305

306 307 308 309 310 311 312
The text of the message should be posted as JSON:

```json
{
  "text": "Hello World!"
}
```
313

314 315 316 317 318 319 320 321 322 323 324 325 326
### Marking private messages as read

`POST /v1/messages/{contactId}/read`

The `messageId` of the message to be marked as read
needs to be provided in the request body as follows:

```json
{
    "messageId": "+AIMMgOCPFF8HDEhiEHYjbfKrg7v0G94inKxjvjYzA8="
}
```

327 328 329 330 331 332
### Deleting all private messages

`DELETE /v1/messages/{contactId}/all`

It returns with a status code `200`, if removal was successful.

333 334 335 336 337 338 339 340 341
### Listing blog posts

`GET /v1/blogs/posts`

Returns a JSON array of blog posts:

```json
{
    "author": {
342
        "formatVersion": 1,
343 344 345 346 347 348 349 350 351
        "id": "VNKXkaERPpXmZuFbHHwYT6Qc148D+KNNxQ4hwtx7Kq4=",
        "name": "Test",
        "publicKey": "NbwpQWjS3gHMjjDQIASIy/j+bU6NRZnSRT8X8FKDoN4="
    },
    "authorStatus": "ourselves",
    "id": "X1jmHaYfrX47kT5OEd0OD+p/bptyR92IvuOBYSgxETM=",
    "parentId": null,
    "read": true,
    "rssFeed": false,
352
    "text": "Test Post Content",
353 354 355 356 357 358
    "timestamp": 1535397886749,
    "timestampReceived": 1535397886749,
    "type": "post"
}
```

359 360 361 362 363 364 365 366 367 368 369 370
`authorStatus` indicates what we know about the author of a blog post. Its possible values
are:

  * `none`: This is only used for RSS feed blog posts where Briar can't link
  the author to one of its contacts.
  * `unknown`: The author has broadcasted their identity but we don't know them.
  * `unverified`: The author is one of our contacts but we didn't verify their
  identity key. This happens for contacts added remotely or via introduction.
  * `verified`: The author is one of our contacts and we verified their identity key.
  * `ourselves`: The user is the author of the blog post.
  * `anonymous`: This status is deprecated and no longer used. It will be removed in future versions.

371 372 373 374
### Writing a blog post

`POST /v1/blogs/posts`

375 376 377 378 379 380 381
The text of the blog post should be posted as JSON:

```json
{
  "text": "Hello Blog World!"
}
```
382 383 384

## Websocket API

385
The Briar peer uses a websocket to notify a connected API client about new events.
386 387 388

`WS /v1/ws`

389 390 391
Immediately after making the connection,
you must send the authentication token as a message to the websocket.
If you fail to do this, you will not receive messages on that socket.
392

393
In JavaScript, it would look like this:
394

395 396 397 398 399 400
```javascript
var token = "DZbfoUie8sjap7CSDR9y6cgJCojV+xUITTIFbgtAgqk=";
var socket = new WebSocket("ws://localhost:7000/v1/ws");
socket.onopen = function(event) { socket.send(token); };
socket.onmessage = function(event) { console.log(event.data); }
```
401

402 403
### Receiving new private messages

404
When the Briar peer receives a new private message,
405 406 407 408 409 410 411 412 413 414 415 416
it will send a JSON object to connected websocket clients:

```json
{
    "data": {
        "contactId": 1,
        "groupId": "oRRvCri85UE2XGcSloAKt/u8JDcMkmDc26SOMouxr4U=",
        "id": "JBc+ogQIok/yr+7XtxN2iQgNfzw635mHikNaP5QOEVs=",
        "local": false,
        "read": false,
        "seen": false,
        "sent": false,
417
        "text": "Test Message",
418
        "timestamp": 1537389146088,
419
        "type": "PrivateMessage"
420
    },
421
    "name": "ConversationMessageReceivedEvent",
422 423 424 425 426 427
    "type": "event"
}
```

Note that the JSON object in `data` is exactly what the REST API returns
when listing private messages.
428

akwizgran's avatar
akwizgran committed
429
### A new contact was added
430

431 432 433
This means that a new contact was either added directly or that it has left
the pending state.

434 435 436
```json
{
    "data": {
akwizgran's avatar
akwizgran committed
437 438
        "contactId": 1,
        "verified": false
439
    },
akwizgran's avatar
akwizgran committed
440
    "name": "ContactAddedEvent",
441 442 443 444
    "type": "event"
}
```

445 446
### A pending contact was added

447 448 449
This means that a new `pendingContact` was added and Briar will try to add it
as a real `contact`.

450 451 452 453 454 455 456 457 458 459 460 461 462 463
```json
{
    "data": {
        "pendingContact": {
            "pendingContactId": "jsTgWcsEQ2g9rnomeK1g/hmO8M1Ix6ZIGWAjgBtlS9U=",
            "alias": "ztatsaajzeegraqcizbbfftofdekclatyht",
            "timestamp": 1557838312175
        }
    },
    "name": "PendingContactAddedEvent",
    "type": "event"
}

```
464 465 466 467 468
### A pending contact changed its state

```json
{
    "data": {
akwizgran's avatar
akwizgran committed
469 470
        "pendingContactId": "YqKjsczCuxScXohb5+RAYtFEwK71icoB4ldztV2gh7M=",
        "state": "waiting_for_connection"
471 472 473 474 475 476 477 478 479 480
    },
    "name": "PendingContactStateChangedEvent",
    "type": "event"
}
```

For a list of valid states, please see the section on adding contacts above.

### A pending contact was removed

481 482 483
This can happen when e.g. the user removed the pending contact manually. Briar
will no longer work on making this `pendingContact` become `contact`.

484 485 486 487 488 489 490 491 492
```json
{
    "data": {
        "pendingContactId": "YqKjsczCuxScXohb5+RAYtFEwK71icoB4ldztV2gh7M="
    },
    "name": "PendingContactRemovedEvent",
    "type": "event"
}
```
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508

### A contact connected or disconnected

When Briar establishes a connection to a contact (the contact comes online),
it sends a `ContactConnectedEvent`.
When the last connection is lost (the contact goes offline), it sends a `ContactDisconnectedEvent`.

```json
{
    "data": {
        "contactId": 1
    },
    "name": "ContactConnectedEvent",
    "type": "event"
}
```
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544

### A message was sent

When Briar sent a message to a contact, it sends a `MessagesSentEvent`. This is indicated in Briar
by showing one tick next to the message.

```json
{
    "data": {
        "contactId": 1,
        "messageIds": [
            "+AIMMgOCPFF8HDEhiEHYjbfKrg7v0G94inKxjvjYzA8="
        ]
    },
    "name": "MessagesSentEvent",
    "type": "event"
}
```

### A message was acknowledged

When a contact acknowledges that they received a message, Briar sends a `MessagesAckedEvent`.
This is indicated in Briar by showing two ticks next to the message.

```json
{
    "data": {
        "contactId": 1,
        "messageIds": [
            "+AIMMgOCPFF8HDEhiEHYjbfKrg7v0G94inKxjvjYzA8="
        ]
    },
    "name": "MessagesAckedEvent",
    "type": "event"
}
```