An authenticated, realtime, offline-first microstorage that's synced across devices.
Built-in support for most common data-sharing patterns and makes securely managing your user's and app's data simple.
Provides multiple data patterns, out of the box:
inkdb.public
Anonymous, public, read-only.inkdb.user.app.readonly
User-specific data the app backend has read-write access to. User read-only. Authenticated, encrypted.inkdb.user.app.readwrite
User-specific data the app backend has read-write access to. User read-write. Authenticated, encrypted. (Needs new name?)inkdb.user.private
User-specific data the app backend promises not to access. Can be zero-knowledge encrypted to make sure that holds true. User read-write. Authenticated, encrypted, can be zero-knowledge encrypted.inkdb.device_only
Encrypted, read-write storage that never leaves the device. Unlike the above, it is never synced.
Key features:
- Everything encrypted. Use whatever backend you'd like. Your user's data is encrypted.
- Agnostic to your backend. Pluggable backends for storage (Kinto, Firestore, DynamoDB, Hasura/Postgres, etc), and authentication (Django, Rails, etc.)
- Bundled with Kinto storage backend, and a Python/Django app for authentication.
inkdb.public.foo
"bar"
inkdb.public.theme.daily_button_color
"#454545"
> inkdb.user.app.readwrite.foo
{'error': 'User not authenticated'}
// Authenticates with server. Securely fetches user's encryption key, verifies, and decrypts.
> inkdb.authenticate('myusername', 'mypassword')
> inkdb.user.app.readwrite.foo
"Hi, I'm data."
> inkdb.user.app.readonly.purchases
{
"product-id-1": {
purchased_on: 1928312981023,
title: "My Great Product",
url: "https://mycompany.com/products/great-product-1",
}
}
> inkdb.device_only.myvar = 1
1
> inkdb.device_only.myvar
1
> inkdb.public.foo = "I want to override this"
{'error': 'Public is read-only.'}
> inkdb.public.foo
"bar"
> inkdb.user.app.readwrite.bar = "ack";
{'error': 'User not authenticated'}
> inkdb.authenticate('myusername', 'mypassword')
> inkdb.user.app.readwrite.bar = "ack";
> inkdb.user.app.readwrite
{
"foo": "Hi, I'm data.",
"bar": "ack",
}
> inkdb.user.app.readonly.purchases.product2.purchased = True
{'error': 'Vault is read-only'}
> inkdb.user.app.readwrite.favorite_color = "#454545";
> inkdb.user.app.readwrite.favorite_color
"#454545";
inkdb.register('mynewusername', 'mynewpassword')
inkdb.authenticate('mynewusername', 'mynewpassword')
inkdb.user.change_password('mynewpassword') // Only works if authenticated.
inkdb.reset_password('mynewpassword') // Starts email-based password recovery flow.
// Won't help with zero-knowledge encrypted private data.
// Authenticates with a server. If user encryption user-managed, simply fetches the encrypted data.
> inkdb.authenticate('myusername', 'mypassword')
> inkdb.private.bar
{"error": "Zero-knowledge encryption enabled, and user vault is not decrypted."}
// Decrypt zero-knowledge encrypted data
> inkdb.private.decrypt(prompt("What is your vault encryption key"?))
> inkdb.private.bar
'ack'
// Enable zero-knowledge
> inkdb.private.enable_zero_knowledge_encryption('myzeroknowledgeencryptionkey')
// Disable zero-knowledge
> inkdb.private.disable_zero_knowledge_encryption('myzeroknowledgeencryptionkey')
- Do we need to even do pub/private pairs? We only have one real party who needs access, symmetric encryption might be a better fit.
- Is it worth bothering to use the server's key pair to verify, since we implicitly trust SSL? Protection against heartbleed-type attacks, sure. But worth the cost?
Given:
- The server has a secure public/private key pair.
- The client is passed the server's public key via SSL
Account Creation:
- Create account on server for user from username/pass
- Generate pub/priv pair for user on server. Encrypt with server's private key, store in db.
- Return pub/priv via SSL. Verify using server's public key (needed since we trust SSL?).
- Store pub/priv in browser local storage
Using a second device/logging in:
- Authenticate with the server over SSL.
- Recieve pub/priv pair via SSL and session token (that stays for the time you set.)
- Follow 1-5 above.
Data access/reads and writes:
- Access user data on data store at /publickey
- Bind data to local var
- Watch var for changes.
- On changes (and first run), recursively decrypt the tree with the user's private key.
- On user data add, save to local sync'd object, see in watch, encrypt with the public key and put into data store.
Zero-knowledge path:
- Follow 1-4 from the standard account list.
- Generate local zero-knowledge zeropub/zeropriv
- Ask the user to provide a zero-knowledge password.
- Encrypt local zeropub/zeropriv pair using the PBKDF(zero-knowledge-pass).
- Send encrypted zeropair to server, save.
- (Optional) Save working zero-knowledge pair into localstorage (If user chooses when entering password. Default to not save.)
- Use the zero-knowledge pair instead of passed pair to encrypt/decrypt.
Zero-knowledge second device:
- Authenticate with the server over SSL.
- Recieve pub/priv and zeropub/zeropriv over SSL.
- Provide zeropub/zeropriv decryption key.
- Unlock zeropub/zeropriv keys.
- Follow 5-6 above.
Zero-knowledge upgrade:
- Use current pub/priv pair to recursively decrypt data
- Use new zeropub/zeropriv pair to recursively encrypt data
- Force-write encrypted data to root node.
In the server's environment:
- The server's public/private keys
In the database and in database backups:
- Each client's default public/private pair, encrypted with the server's private key
- Zero-knowledge clients' zeropub/zeropair, encrypted with a passphrase that the server doesn't know.
In a regular client's browser memory:
- The server's public key
- The client's default public/private pair
In a regular client's local storage:
- A session token
- The client's default public/private pair
In a zero-knowledge client's browser memory:
- The server's public key
- The client's default public/private pair
- The client's zeropub/zeropriv
In a zero-knowledge client's local storage:
- Nothing
In transit, over TLS/SSL:
- The client's username and password
- The server's public key
- The client's default public/private pair
- The client's encrypted zeropub/zeropriv
In transit, over insecured HTTP:
- Nothing
In transit between server and database, over TLS/SSL:
- Each client's default public/private pair, encrypted with the server's private key
- Zero-knowledge clients' zeropub/zeropair, encrypted with a passphrase that the server doesn't know.
Tech notes: