Userbase is a tool for developers to build secure and private apps. We launched 1 year ago [1], and have worked hard to widen its use cases. Userbase offers built-in user accounts and authentication, an end-to-end encrypted zero-management database, file storage, streaming and sharing, and logic to process and manage subscriptions (via Stripe). All Userbase features are accessible through a simple JavaScript SDK, directly from the client. 100% open source, and all platforms are supported (browser, iOS, Android, desktop).
Today, Userbase can be used in apps that don’t require end-to-end encryption, for those who like that Userbase can handle authentication, data storage and real-time syncing in a few lines of code.
We also completed a security review by an independent team [2], and wrote up a comprehensive specification of our architecture [3].
Personally I joined in working on Userbase to store end-to-end encrypted data in a performant way for an accounting app. Under the hood, each write to a Userbase database is an append-only transaction to a log stored in DynamoDB (therefore constant time), which is then pushed to connected clients over a Web Socket. Each client then decrypts and applies this transaction to its local state of the database in memory (real-time syncing is provided out of the box). In this process, the server ensures each client receives transactions in a consistent order, 100% of the time. This is unlike some of the (very awesome) decentralized alternatives that exist today (OrbitDB, GunDB, Scuttlebot), which generally rely on CRDTs to stay in sync, and CRDTs can be pushed in any order. For certain applications, the consistent ordering guarantee a central server provides may be extremely useful (such as in an accounting app), on top of the added reliability and performance.
openDatabase loads the database's state into memory from the server, and then keeps it in sync with the server using the Web Socket. When you insert a transaction via one of the database operations, the server assigns the transaction a monotonically increasing sequence number, and broadcasts the transaction along with its sequence number out to the clients connected to a database. Clients then apply transactions in sequential order to the local state, and keep track of the latest sequence number applied. When a client goes offline and comes back on, it automatically reconnects the Web Socket and re-requests any transactions that it may have missed above its currently applied sequence number. We handle reconnection logic automatically under the hood, retrying on failure with backup delays.
I'm also working on an offline-first Google docs alternative that will write to IndexedDB, and stay in sync with Userbase using CRDTs. The tutorial on how to do it will be here: https://userbase.com/docs/
>Does it download the entire history of all transactions and replays them into the local database?
This is what clients do initially, until the database grows in size. Every time the transaction log increases 50 KB, the client takes a snapshot of the database's state at a particular point in time, compresses and encrypts it, and uploads this state to the server. We call this a "bundle". This way when clients reopen a database, they load from the bundle first, and then apply any new transactions that come after it. Rather than needing to query for the history of all transactions and decrypting them individually and reapplying.
>Hot do concurrent modification get resolved (several clients try to modify the shared stage at the same time)?
The server assigns each transaction a distinct sequence number via an atomic operation. So clients always apply transactions with the same distinct sequence number, in sequential order. The client relies on this to enforce uniqueness and versioning. Only the lowest sequence number itemId gets applied to a database if 2 clients insert with the same itemId at the same time, and similarly, only the lowest sequence number version of an item gets updated or deleted if 2 clients update or delete the same item at the same time.
With regards to bundling, it's a bit more complicated and there are layers to our approach in safely handling it under high concurrency. When a client uploads a bundle, the database records what sequence number the bundling took place at so clients can use it to retrieve the latest bundle. And the server retains copies of bundles at prior sequence numbers. This way if two clients attempt to open a database right around the moment a bundling process completes (client 1 receives a bundle at lower sequence number, and client 2 receives a bundle at a higher sequence number), both clients receive the same set of transactions regardless. The server sends all transactions in the log after the bundle sequence number, so client 1 just needs to decrypt and apply more individual transactions to rebuild the state compared to client 2.
Some may find this interesting too -- we specifically test for safe concurrent behavior across 2 clients using a makeshift testing framework that opens 2 browsers at the same time and does some neat tests: https://github.com/smallbets/userbase/tree/master/test
If you clone the repo and run `npm run test:concurrency`, it will run those tests and output test results to the consoles of the 2 browsers.
Userbase is a tool for developers to build secure and private apps. We launched 1 year ago [1], and have worked hard to widen its use cases. Userbase offers built-in user accounts and authentication, an end-to-end encrypted zero-management database, file storage, streaming and sharing, and logic to process and manage subscriptions (via Stripe). All Userbase features are accessible through a simple JavaScript SDK, directly from the client. 100% open source, and all platforms are supported (browser, iOS, Android, desktop).
Today, Userbase can be used in apps that don’t require end-to-end encryption, for those who like that Userbase can handle authentication, data storage and real-time syncing in a few lines of code.
We also completed a security review by an independent team [2], and wrote up a comprehensive specification of our architecture [3].
Personally I joined in working on Userbase to store end-to-end encrypted data in a performant way for an accounting app. Under the hood, each write to a Userbase database is an append-only transaction to a log stored in DynamoDB (therefore constant time), which is then pushed to connected clients over a Web Socket. Each client then decrypts and applies this transaction to its local state of the database in memory (real-time syncing is provided out of the box). In this process, the server ensures each client receives transactions in a consistent order, 100% of the time. This is unlike some of the (very awesome) decentralized alternatives that exist today (OrbitDB, GunDB, Scuttlebot), which generally rely on CRDTs to stay in sync, and CRDTs can be pushed in any order. For certain applications, the consistent ordering guarantee a central server provides may be extremely useful (such as in an accounting app), on top of the added reliability and performance.
[1] https://hackernews.hn/item?id=22145168
[2] https://userbase.com/announcements/#1-security-review
[3] https://github.com/smallbets/userbase/blob/master/docs/userb...