Syncto

travis Coverage Documentation Status

Syncto is a server allowing you to store and retrieve Firefox Sync user data attached to your Firefox account using a subset of the Kinto API in order to be able to use Kinto.js for that task.

Table of content

Overview

_images/overview-use-cases.png

Syncto is a bridge service that let you interract with the Firefox Sync infrastructure using Kinto clients such as Kinto.js or Kinto.py.

FAQ

Is Syncto secure?

Syncto caches encrypted credentials for five minutes for a given BrowserID assertion.

In addition all the data coming from Firefox Sync are encrypted and Syncto will never have access to the encryption key (only final clients have).

By default Syncto is a read-only proxy, to enable write permissions for some collection, it has to be configured explicitely. (history/password/tabs/etc.)

Is it web scale?

Syncto is simply a proxy that relies on the Firefox Sync infrastructure.

Syncto doesn’t even necessarily need a shared cache between its nodes.

Also it is better that nodes from the same loadbalancer uses the same cache or that client are always affected to the same one with regards to their Authorization header.

What is Cliquet? What is the difference between Cliquet, Syncto and Kinto ?

Cliquet is a toolkit for designing micro-services. Kinto and Syncto are servers built using that toolkit.

Syncto uses the same protocol as Kinto does to name and interract with collection records. This enable developers to use Kinto clients to fetch Firefox Sync collection records.

I am seeing an Exception error, what’s wrong?

Have a look at the Troubleshooting section to see what to do.

Installation

Run locally

Syncto is based on top of the cliquet project, and as such, you may wanto to refer to cliquet’s documentation for more details.

For development

By default, Syncto persists internal cache in Redis.

git clone https://github.com/mozilla-services/syncto
cd syncto
make serve
note:OSX users are warned that supplementary steps are needed to ensure proper installation of cryptographic dependencies is properly done; see dedicated note.

If you already installed Syncto earlier and you want to recreate a full environment (because of errors when running make serve), please run:

make maintainer-clean serve

Authentication

Syncto relies on Firefox Account BrowserID assertion to authenticate users to the Token Server.

Note that you will need to pass through a BrowserID assertion with the https://token.services.mozilla.com/ audience for Syncto to be able to read Firefox Sync server credentials.

This can be made using HTTPie and PyFxA.

To install them:

$ pip install httpie PyFxA

To build a Firefox Account Browser ID assertion for an existing user:

$ BID_AUDIENCE=https://token.services.mozilla.com/ \
    BID_WITH_CLIENT_STATE=True \
      http GET 'https://syncto.dev.mozaws.net/v1/buckets/syncto/collections/history/records'
        --auth-type fxa-browserid --auth "user@email.com:US3R_P4S5W0RD" -v

Once you have got a BrowserID Assertion, you can also reuse it to benefit from Syncto cache features:

$ http GET 'https://syncto.dev.mozaws.net/v1/buckets/syncto/collections/history/records' \
    Authorization:"BrowserID eyJhbGciOiJSUzI1NiJ9..." \
    X-Client-State:64e8bc35e90806f9a67c0ef8fef63...

Cryptography libraries

Linux

On Debian / Ubuntu based systems:

apt-get install libffi-dev libssl-dev

On RHEL-derivatives:

apt-get install libffi-devel openssl-devel

OS X

Assuming brew is installed:

brew install libffi openssl pkg-config

Warning

Apple having dropped support for OpenSSL and moving to their own library recently, you have to force its usage to properly install cryptography-related dependencies:

$ env LDFLAGS="-L$(brew --prefix openssl)/lib" \
      CFLAGS="-I$(brew --prefix openssl)/include" \
          .venv/bin/pip install cryptography
$ make serve

Running in production

Enable write access

By default, collections are read-only. In order to enable write operations on remote Sync collections, add some settings in the configuration with the collection name:

syncto.record_tabs_put_enabled = true
syncto.record_tabs_delete_enabled = true
syncto.record_passwords_put_enabled = true
syncto.record_passwords_delete_enabled = true
syncto.record_bookmarks_put_enabled = true
syncto.record_bookmarks_delete_enabled = true
syncto.record_history_put_enabled = true
syncto.record_history_delete_enabled = true

Monitoring

# Heka
syncto.logging_renderer = cliquet.logs.MozillaHekaRenderer

# StatsD
syncto.statsd_url = udp://carbon.server:8125

Application output should go to stdout, and message format should have no prefix string:

[handler_console]
class = StreamHandler
args = (sys.stdout,)
level = INFO
formater = heka

[formatter_heka]
format = %(message)s

Adapt the logging configuration in order to plug Sentry:

[loggers]
keys = root, sentry

[handlers]
keys = console, sentry

[formatters]
keys = generic

[logger_root]
level = INFO
handlers = console, sentry

[logger_sentry]
level = WARN
handlers = console
qualname = sentry.errors
propagate = 0

[handler_console]
class = StreamHandler
args = (sys.stdout,)
level = INFO
formater = heka

[formatter_heka]
format = %(message)s

[handler_sentry]
class = raven.handlers.logging.SentryHandler
args = ('http://public:secret@example.com/1',)
level = WARNING
formatter = generic

[formatter_generic]
format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

Running with uWsgi

To run the application using uWsgi, an app.wsgi file is provided. This command can be used to run it:

uwsgi --ini config/syncto.ini

uWsgi configuration can be tweaked in the ini file in the dedicated [uwsgi] section.

Here’s an example:

[uwsgi]
wsgi-file = app.wsgi
enable-threads = true
socket = /run/uwsgi/syncto.sock
chmod-socket = 666
cheaper-algo = busyness
cheaper = 5
cheaper-initial = 9
workers = 14
cheaper-step = 1
cheaper-overload = 30
cheaper-busyness-verbose = true
master = true
module = syncto
harakiri = 120
uid = ubuntu
gid = ubuntu
virtualenv = /data/venvs/syncto
lazy = true
lazy-apps = true
single-interpreter = true
buffer-size = 65535
post-buffering = 65535

To use a different ini file, the SYNCTO_INI environment variable should be present with a path set to it.

API Endpoints

Resource endpoints

Get a list of records for a collection

Requires authentication

Returns all records of the current user for this collection.

Collection can be one of the Firefox Sync collections:

The default collections used by Firefox to store sync data are:

  • bookmarks
  • history
  • forms
  • prefs
  • tabs
  • passwords

The following additional collections are used for internal management purposes by the storage client:

  • clients
  • crypto
  • keys
  • meta

The returned value is a JSON mapping containing:

  • data: the list of records, with exhaustive fields;

A Total-Records response header indicates the total number of records in the collection.

A Last-Modified response header provides a human-readable (rounded to second) version of the current collection timestamp.

For cache and concurrency control, an ETag response header gives the value that consumers can provide in subsequent requests using If-Match and If-None-Match headers (see the section about timestamps).

GET /buckets/syncto/collections/(collection_id)/records

Example request:

$ BID_AUDIENCE=https://token.services.mozilla.com/ BID_WITH_CLIENT_STATE=True \
    http GET https://syncto.dev.mozaws.net/v1/buckets/syncto/collections/history/records \
        --auth-type fxa-browserid --auth "user@email.com:P4S5W0RD" -v

GET /v1/buckets/syncto/collections/history/records HTTP/1.1
Authorization: BrowserID eyJhbGciOiJSUzI1NiJ9...FHGg
Host: syncto.dev.mozaws.net
User-Agent: HTTPie/0.9.2
X-Client-State: 64e8bc35e90806f9a67c0ef8fef63...

Example response:

HTTP/1.1 200 OK
Access-Control-Expose-Headers: Content-Length, Quota-Remaining, Alert, \
    Retry-After, Last-Modified, Total-Records, ETag, Backoff, Next-Page
Content-Length: 1680
Content-Type: application/json; charset=UTF-8
Date: Tue, 06 Oct 2015 13:57:24 GMT
ETag: "1442849064460"
Last-Modified: Mon, 21 Sep 2015 15:24:24 GMT
Total-Records: 6

{
    "data": [
        {
            "id": "VLkOS7iT5C94",
            "last_modified": 1441868927070,
            "payload": "{\"ciphertext\":\"Wf2AoZiOly...\",\"IV\":\"jW7JFPf...\",\"hmac\":\"989352d9b5e0c6...\"}",
            "sortindex": -1
        },
        {
            "id": "qYYobAN_p9vS",
            "last_modified": 1441868927070,
            "payload": "{\"ciphertext\":\"3upjoLrO...7\",\"IV\":\"3O/nPq82xUT...\",\"hmac\":\"addce0f9d3024ed9fd0042b...\"}",
            "sortindex": 100
        },
        ...
    ]
}
Status Codes:
Filtering and Sorting

Firefox Sync filtering options are exposed in syncto.

  • _since with the ETag value to fetch changes from the previous time.
  • _sort can be either newest, oldest, or index (as well as -last_modified, last_modified, and -sortindex).
  • _limit to limit the number of items per pages (no limit by default).
  • in_ids to define the list of requested records IDs.
Counting

Contrary to what Kinto does, the Total-Records only counts the number of records contained in the current request for now.

You may ask the request without the _limit parameter to get all the records at once.

Polling for changes

The _since parameter is provided as an alias for gt_last_modified. (Greater than last_modified)

If the request header If-None-Match is provided as described in the section about timestamps and if the collection was not changed, a 304 Not Modified response is returned.

Additionnal headers

The Quota-Remaining header is not part of the Kinto protocol yet but is passed through if present in Firefox Sync responses.

Its value is in Kilobyte (KB).

Get a collection record

Requires authentication

Returns a specific record by its id. The GET response body is a JSON mapping containing:

  • data: the record with exhaustive schema fields;

IDs are kept between Firefox Sync and Syncto.

Firefox Sync IDs are generated on client side as 9 random Bytes encoded in urlsafe base64 (+ and / are replaced with - and _).

If the request header If-None-Match is provided, and if the record has not changed meanwhile, a 304 Not Modified is returned.

GET /buckets/syncto/collections/(collection_id)/records/(record_id)

Example request:

$ http GET \
    https://syncto.dev.mozaws.net/v1/buckets/syncto/collections/history/records/d2X1O6-DyeFS \
    Authorization:"BrowserID eyJhbGciOiJSUzI1NiJ9...i_dQ" \
    X-Client-State:64e8bc35e90806f9a67c0ef8fef63...

GET /v1/buckets/syncto/collections/history/records/d2X1O6-DyeFS HTTP/1.1
Authorization: BrowserID eyJhbGciOiJSUzI1NiJ9...i_dQ
Host: syncto.dev.mozaws.net
User-Agent: HTTPie/0.9.2
X-Client-State: 64e8bc35e90806f9a67c0ef8fef63...

Example response:

HTTP/1.1 200 OK
Access-Control-Expose-Headers: Content-Length, Alert, Retry-After, Last-Modified, ETag, Backoff
Content-Length: 289
Content-Type: application/json; charset=UTF-8
Date: Tue, 06 Oct 2015 14:18:40 GMT
ETag: "1441868927070"
Last-Modified: Thu, 10 Sep 2015 07:08:47 GMT

{
    "data": {
        "id": "d2X1O6-DyeFS",
        "last_modified": 1441868927070,
        "payload": "{\"ciphertext\":\"75IcW3P4WxUJipehWryevc+ygK5vojh3n...\",\"IV\":\"Sj3U2Nkk2IjE...\",\"hmac\":\"c6a530f348...b68b610351\"}",
        "sortindex": 2000
    }
}
Status Codes:

Delete a record

Requires authentication

Delete a specific record by its id.

Note that contrary to what Kinto does, Firefox Sync count on clients to create deleted records tombstones. Moreover Firefox Sync tombstones are encrypted and look like real records for Syncto.

This endpoint should not be used to create tombstones but to remove the record when the client decides that all clients already fetched the tombstone.

By default this endpoint is deactivated and should be activated on a per collection basis.

DELETE /buckets/syncto/collections/(collection_id)/records/(record_id)

Example request:

$ http DELETE \
    https://syncto.dev.mozaws.net/v1/buckets/syncto/collections/history/records/d2X1O6-DyeFS \
    Authorization:"BrowserID eyJhbGciOiJSUzI1NiJ9...i_dQ" \
    X-Client-State:64e8bc35e90806f9a67c0ef8fef63...

DELETE /v1/buckets/syncto/collections/history/records/d2X1O6-DyeFS HTTP/1.1
Authorization: BrowserID eyJhbGciOiJSUzI1NiJ9...i_dQ
Host: syncto.dev.mozaws.net
User-Agent: HTTPie/0.9.2
X-Client-State: 64e8bc35e90806f9a67c0ef8fef63...

Example response:

HTTP/1.1 204 No Content
Access-Control-Expose-Headers: Content-Length, Alert, Retry-After, Last-Modified, ETag, Backoff
Content-Length: 0
Date: Tue, 06 Oct 2015 14:18:40 GMT
Status Codes:

Create or Update a record

Requires authentication

Create or replace a record with its id. The PUT body is a JSON mapping containing:

  • data: the values of the resource schema fields;

Because IDs are created on client side for Firefox Sync, this is the only endpoint that you can use either to create new record or to update them.

If you want to make sure that you don’t erase an existing record when creating one, you can use the If-None-Match: "*" header value.

The PUT response body is a JSON mapping containing:

  • data: the newly created/updated record, if all posted values are valid;

If the request header If-Match is provided, and if the record has changed meanwhile, a 412 Precondition failed error is returned.

There are no validation nor on the id format nor on the payload body.

By default this endpoint is deactivated and should be activated on a per collection basis.

PUT /buckets/syncto/collections/(collection_id)/records/(record_id)

Example request:

$ echo '{
     "data": {
         "payload": "{\"ciphertext\":\"75IcW3P4WxUJipehWryevc+ygK5vojh3nOadu7YSX9zJSm3eBHu5lNIg1UtDyt3b\",\"IV\":\"Sj3U2Nkk2IjE2S59hv0m7Q==\",\"hmac\":\"c6a530f3486142d1069f80bfaff907e0cc077a892cf7f9bd62f943b68b610351\"}",
         "sortindex": 2000
     }
 }' | http PUT \
    https://syncto.dev.mozaws.net/v1/buckets/syncto/collections/history/records/d2X1O6-DyeFS \
    Authorization:"BrowserID eyJhbGciOiJSUzI1NiJ9...i_dQ" \
    X-Client-State:64e8bc35e90806f9a67c0ef8fef63...

PUT /v1/buckets/syncto/collections/history/records/d2X1O6-DyeFS HTTP/1.1
Authorization: BrowserID eyJhbGciOiJSUzI1NiJ9...i_dQ
Content-Length: 275
Content-Type: application/json
Host: syncto.dev.mozaws.net
User-Agent: HTTPie/0.9.2
X-Client-State: 64e8bc35e90806f9a67c0ef8fef63...

{
    "data": {
        "payload": "{\"ciphertext\":\"75IcW3P4WxUJipehWryevc+ygK5vojh3nOadu7YSX9zJSm3eBHu5lNIg1UtDyt3b\",\"IV\":\"Sj3U2Nkk2IjE2S59hv0m7Q==\",\"hmac\":\"c6a530f3486142d1069f80bfaff907e0cc077a892cf7f9bd62f943b68b610351\"}",
        "sortindex": 2000
    }
}

Example response:

HTTP/1.1 200 OK
Access-Control-Expose-Headers: Content-Length, Alert, Retry-After, Last-Modified, ETag, Backoff
Connection: keep-alive
Content-Length: 289
Content-Type: application/json; charset=UTF-8
Date: Fri, 09 Oct 2015 10:04:13 GMT
ETag: "1444385059190"
Last-Modified: Fri, 09 Oct 2015 10:04:19 GMT

{
    "data": {
        "id": "d2X1O6-DyeFS",
        "last_modified": 1444385059190,
        "payload": "{\"ciphertext\":\"75IcW3P4WxUJipehWryevc+ygK5vojh3nOadu7YSX9zJSm3eBHu5lNIg1UtDyt3b\",\"IV\":\"Sj3U2Nkk2IjE2S59hv0m7Q==\",\"hmac\":\"c6a530f3486142d1069f80bfaff907e0cc077a892cf7f9bd62f943b68b610351\"}",
        "sortindex": 2000
    }
}
Status Codes:

API versioning

The API versioning is based on the application version deployed. It follows the semver specifications.

During development the server will be 0.X.X, the server endpoint will be prefixed by /v0.

Each non retro-compatible API change will imply the major version number to be incremented. Everything will be made to avoid retro incompatible changes.

The / endpoint will redirect to the last API version.

Warning

The version prefix will be implied throughout the rest of the API reference, to improve readability. For example, the / endpoint should be understood as /v0/.

Batch operations

POST /batch

Requires authentication

The POST body is a mapping, with the following attributes:

  • requests: the list of requests
  • defaults: (optional) default requests values in common for all requests
Each request is a JSON mapping, with the following attribute:
  • method: HTTP verb
  • path: URI
  • body: a mapping
  • headers: (optional), otherwise take those of batch request
{
  "defaults": {
    "method" : "POST",
    "path" : "/v0/articles",
    "headers" : {
      ...
    }
  },
  "requests": [
    {
      "body" : {
        "title": "MoFo",
        "url" : "http://mozilla.org",
        "added_by": "FxOS",
      }
    },
    {
      "body" : {
        "title": "MoCo",
        "url" : "http://mozilla.com"
        "added_by": "FxOS",
      }
    },
    {
      "method" : "PATCH",
      "path" : "/articles/409",
      "body" : {
        "read_position" : 3477
      }
    }
  ]
}

The response body is a list of all responses:

{
  "responses": [
    {
      "path" : "/articles/409",
      "status": 200,
      "body" : {
        "id": 409,
        "url": "...",
        ...
        "read_position" : 3477
      },
      "headers": {
        ...
      }
    },
    {
      "status": 201,
      "path" : "/articles",
      "body" : {
        "id": 411,
        "title": "MoFo",
        "url" : "http://mozilla.org",
        ...
      },
    },
    {
      "status": 201,
      "path" : "/articles",
      "body" : {
        "id": 412,
        "title": "MoCo",
        "url" : "http://mozilla.com",
        ...
      },
    },
  ]
}
HTTP Status Codes
  • 200 OK: The request has been processed
  • 400 Bad Request: The request body is invalid

Warning

Since the requests bodies are necessarily mappings, posting arbitrary data (like raw text or binary)is not supported.

Note

Responses are provided in the same order than requests.

Pros & Cons
  • This respects REST principles
  • This is easy for the client to handle, since it just has to pile up HTTP requests while offline
  • It looks to be a convention for several REST APIs (Neo4J, Facebook, Parse)
  • Payload of response can be heavy, especially while importing huge collections
  • Payload of response must all be iterated to look-up errors

Note

A form of payload optimization for massive operations is planned.

Server timestamps

In order to avoid race conditions, each change is guaranteed to increment the timestamp of the related collection. If two changes happen at the same millisecond, they will still have two different timestamps.

The ETag header with the current timestamp of the collection for the current user will be given on collection endpoints.

ETag: "1432208041618"

On record enpoints, the ETag header value will contain the timestamp of the record.

In order to bypass costly and error-prone HTTP date parsing, ETag headers are not HTTP date values.

A human readable version of the timestamp (rounded to second) is provided though in the Last-Modified response headers:

Last-Modified: Wed May 20 17:22:38 2015 +0200

Changed in version 2.0: In previous versions, cache and concurrency control was handled using If-Modified-Since and If-Unmodified-Since. But since the HTTP date does not include milliseconds, they contained the milliseconds timestamp as integer. The current version using ETag is HTTP compliant (see original discussion.)

Note

The client may send If-Unmodified-Since or If-Modified-Since requests headers, but in the current implementation, they will be ignored.

Cache control

In order to check that the client version has not changed, a If-None-Match request header can be used. If the response is 304 Not Modified then the cached version if still good.

Concurrency control

In order to prevent race conditions, like overwriting changes occured in the interim for example, a If-Match request header can be used. If the response is 412 Precondition failed then the resource has changed meanwhile.

The client can then choose to:

  • overwrite by repeating the request without If-Match;
  • reconcile the resource by fetching, merging and repeating the request.

Backoff indicators

Backoff header on heavy load

A Backoff header will be added to the success responses (>=200 and <400) when the server is under heavy load. It provides the client with a number of seconds during which it should avoid doing unnecessary requests.

Backoff: 30

Note

The back-off time is configurable on the server.

Note

In other implementations at Mozilla, there was X-Weave-Backoff and X-Backoff but the X- prefix for header has been deprecated since.

Retry-After indicators

A Retry-After header will be added to error responses (>=500), telling the client how many seconds it should wait before trying again.

Retry-After: 30

Error responses

Protocol description

Every response is JSON.

If the HTTP status is not OK (<200 or >=400), the response contains a JSON mapping, with the following attributes:

  • code: matches the HTTP status code (e.g 400)
  • errno: stable application-level error number (e.g. 109)
  • error: string description of error type (e.g. "Bad request")
  • message: context information (e.g. "Invalid request parameters")
  • info: online resource (e.g. URL to error details)
  • details: additional details (e.g. list of validation errors)

Example response

{
    "code": 412,
    "errno": 114,
    "error": "Precondition Failed",
    "message": "Resource was modified meanwhile",
    "info": "https://server/docs/api.html#errors",
}

Refer yourself to the ref:set of errors codes <errors>.

Precondition errors

As detailed in the timestamps section, it is possible to add concurrency control using ETag request headers.

When a concurrency error occurs, a 412 Precondition Failed error response is returned.

Additional information about the record currently stored on the server will be provided in the details field:

{
    "code": 412,
    "errno": 114,
    "error":"Precondition Failed"
    "message": "Resource was modified meanwhile",
    "details": {
        "existing": {
            "last_modified": 1436434441550,
            "id": "00dd028f-16f7-4755-ab0d-e0dc0cb5da92",
            "title": "Original title"
        }
    },
}

Conflict errors

When a record violates unicity constraints, a 409 Conflict error response is returned.

Additional information about conflicting record and field name will be provided in the details field.

{
    "code": 409,
    "errno": 122,
    "error": "Conflict",
    "message": "Conflict of field url on record eyjafjallajokull"
    "info": "https://server/docs/api.html#errors",
    "details": {
        "field": "url",
        "record": {
            "id": "eyjafjallajokull",
            "last_modified": 1430140411480,
            "url": "http://mozilla.org"
        }
    }
}

Validation errors

When multiple validation errors occur on a request, the first one is presented in the message.

The full list of validation errors is provided in the details field.

{
    "code": 400,
    "errno": 109,
    "error": "Bad Request",
    "message": "Invalid posted data",
    "info": "https://server/docs/api.html#errors",
    "details": [
        {
            "description": "42 is not a string: {'name': ''}",
            "location": "body",
            "name": "name"
        }
    ]
}

Deprecation

A track of the client version will be kept to know after which date each old version can be shutdown.

The date of the end of support is provided in the API root URL (e.g. /v0)

Using the Alert response header, the server can communicate any potential warning messages, information, or other alerts.

The value is JSON mapping with the following attributes:

  • code: one of the strings "soft-eol" or "hard-eol";
  • message: a human-readable message (optional);
  • url: a URL at which more information is available (optional).

A 410 Gone error response can be returned if the client version is too old, or the service had been remplaced with a new and better service using a new protocol version.

See details in Recommended settings to activate deprecation.

Syncto specific headers

Syncto also expose Firefox Sync information such as:

  • Quota-Remaining: Remaining KB for the user collection if Quota are enabled on the user sync server.

Troubleshooting

We are doing the best we can so you do not have to read this section.

That said, we have included solutions (or at least explanations) for some common problems below.

If you do not find a solution to your problem here, please ask for help!

Module object has no attribute ‘register_json’

Syncto uses the JSONBin feature of PostgreSQL, which is used to store native JSON objects efficiently. Support for this feature wasa added in PostgreSQL 9.4.

This is a hard requirement for postgresql backends, therefore you will either need to use PostgreSQL 9.4 (or greater), or use a different backend entirely.

OpenSSL error when installing on Mac OS X

#include <openssl/aes.h>
         ^
1 error generated.
error: command 'clang' failed with exit status 1

Apple has deprecated use of OpenSSL in favor of its own TLS and crypto libraries, please refer to the installation documentation to fix this.

Contributing

Thank you for considering to contribute to Syncto!

note:No contribution is too small; please submit as many fixes for typos and grammar bloopers as you can!
note:Open a pull-request even if your contribution is not ready yet! It can be discussed and improved collaboratively!

Run tests

make tests

IRC channel

Join #fxos-sync on irc.mozilla.org!

CHANGELOG

This document describes changes between each past release.

1.4.0 (2015-11-17)

  • Upgraded to Cliquet 2.11.0

New Features

  • Pass User-Agent header to sync. (#68)
  • Add trusted certificate pinning support. (#72)

See also *Cliquet* changes

1.3.0 (2015-10-27)

  • Upgraded to Cliquet 2.9.0

Protocol

  • Client-state id should now be provided through the bucket id in the URL (#62)

1.2.0 (2015-10-22)

  • Send Cache-Control: no-cache header (#54)
  • Make sure collection_list return an empty list (#56)

1.1.0 (2015-10-14)

  • Do not install postgresql dependencies by default.
  • Add statsd metrics on SyncClient response status_code. (#49)
  • Handle the new Firefox Sync sort=oldest parameter. (#46)
  • Rename ids to in_ids to reflect the Kinto protocol. (#50)
  • Make sure Next-Page header keeps QueryString parameters. (#47)
  • Add a Token server heartbeat. (#44)
  • Remove the not accurate Total-Records header when paginating. (#43)
  • Expose the now deprecated cliquet.batch_max_requests settings. (#48)

1.0.0 (2015-10-06)

  • First implementation of Syncto server.
  • Connection with Token server and Sync servers.
  • Encrypted credentials caching (#30, #31)
  • Collections are Read-only by default
  • Write permission on collection can be configured.
  • Statsd monitoring for backends calls.
  • Convert Syncto requests headers to Firefox Sync ones.
  • Convert Firefox Sync headers to Syncto ones.