From 2b0102afc33f54192abafe1db09debdea9f14d0e Mon Sep 17 00:00:00 2001 From: Mary Anthony Date: Sun, 8 Dec 2019 21:03:02 +0000 Subject: [PATCH 1/5] Adding radiks to the docs Adding in base radiks files Work in progress Signed-off-by: Mary Anthony --- _data/navigation_learn.yml | 8 + _develop/overview_auth.md | 1 - _develop/radiks-collaborate.md | 82 ++++++++++ _develop/radiks-intro.md | 74 +++++++++ _develop/radiks-models.md | 245 ++++++++++++++++++++++++++++++ _develop/radiks-server-extras.md | 129 ++++++++++++++++ _develop/radiks-setup.md | 247 +++++++++++++++++++++++++++++++ _develop/storage.md | 4 + _develop/zero_to_dapp_2.md | 2 +- _develop/zero_to_dapp_2_win.md | 2 +- _develop/zero_to_dapp_3.md | 2 +- _develop/zero_to_dapp_3_win.md | 2 +- _includes/architecture.md | 18 +-- _includes/commandline.md | 2 +- _includes/question.html | 6 +- _org/wallet-install.md | 2 +- _storage/overview.md | 35 +---- 17 files changed, 813 insertions(+), 48 deletions(-) create mode 100644 _develop/radiks-collaborate.md create mode 100644 _develop/radiks-intro.md create mode 100644 _develop/radiks-models.md create mode 100644 _develop/radiks-server-extras.md create mode 100644 _develop/radiks-setup.md diff --git a/_data/navigation_learn.yml b/_data/navigation_learn.yml index d2d738bc..3fd88578 100644 --- a/_data/navigation_learn.yml +++ b/_data/navigation_learn.yml @@ -16,6 +16,14 @@ docs: - develop/collections - develop/collection-type + +- title: Sharing data + docs: + - develop/radiks-intro + - develop/radiks-setup + - develop/radiks-models + - develop/radiks-collaborate + - develop/radiks-server-extras - title: Try it! Zero to DApp docs: diff --git a/_develop/overview_auth.md b/_develop/overview_auth.md index eabbba24..4ce0d9f8 100644 --- a/_develop/overview_auth.md +++ b/_develop/overview_auth.md @@ -4,7 +4,6 @@ permalink: /:collection/:path.html --- # Understand Blockstack Auth - {:.no_toc} Blockstack Auth provides single sign on and authentication without third parties or remote servers. On this page, you'll get an overview of authentication from a developer and user perspective. The following topics are covered: diff --git a/_develop/radiks-collaborate.md b/_develop/radiks-collaborate.md new file mode 100644 index 00000000..70c5ab13 --- /dev/null +++ b/_develop/radiks-collaborate.md @@ -0,0 +1,82 @@ +--- +layout: learn +permalink: /:collection/:path.html +--- +# Collaboration + +A key feature of Radiks is support for private collaboration between multiple users. Supporting collaboration with client-side encryption and user-owned storage can be complicated, but the patterns to implement it are generally the same for different apps. Radiks provides out-of-the box for collaboration, making it easy to build private, collaborative apps. + +Radiks is built in a way that provides maximum privacy and security for collaborative groups. Radiks-server and external users have no knowledge about who is in a group. + +### UserGroup Model + +The key model behind a collaborative group is `UserGroup`. By default, it only has one attribute, `name`, which is encrypted. You can create multiple subclasses of `UserGroup` later on with different attributes, if you need to. + +### General Workflow + +The general workflow for creating a collaborative group that can share and edit encrypted models is as follows: + +1. The admin of the group creates a new `UserGroup`, which acts as the 'hub' and controls the logic around inviting and removing users. +2. The admin invites one or more other users to a group: + 1. The admin specifies the username of the user they want to invite + 2. Radiks looks up the user's public key + 3. Radiks creates an 'invitation' that is encrypted with the user's public key, and contains information about the `UserGroup` + 4. When the invited user 'activates' an invitation, they create a `GroupMembership`, which they can later use to reference information (such as private keys and signing keys) related to the group. +3. Later on, members of the group can create and update models that are related to the group. These models **must** contain a reference to the group, using the attribute `userGroupId`. This allows Radiks to know which keys to use for encryption and signing. +4. The admin of the group can later remove a user from a group. They do this by creating a new private key for signing and encryption, and updating the `GroupMembership` of all users _except_ the user they just removed. +5. After a key is rotated, all new and updated models must use the new key for signing. Radiks-server validates all group-related models to ensure that they're signed with the most up-to-date key. + +#### Creating a UserGroup + +~~~javascript +import { UserGroup } from 'radiks'; + +// ... + +const group = new UserGroup({ name: 'My Group Name' }); +await group.create(); +~~~ + +Calling `create` on a new `UserGroup` will create the group and activate an invitation for the creator of the group. + +#### Inviting a User + +Use the `makeGroupMembership` method on a `UserGroup` instance to invite a user. The only argument passed to this method is the username of the user you want to invite. + +~~~javascript +import { UserGroup } from 'radiks'; + +const group = await UserGroup.findById(myGroupId); +const usernameToInvite = 'hankstoever.id'; +const invitation = await group.makeGroupMembership(usernameToInvite); +console.log(invitation._id); // the ID used to later activate an invitation +~~~ + +#### Accepting an invitation + +Use the `activate` method on a `GroupInvitation` instance to activate an invitation: + +~~~javascript +import { GroupInvitation } from 'radiks'; + +const invitation = await GroupInvitation.findById(myInvitationID); +await invitation.activate(); +~~~ + +#### Viewing all activated UserGroups for the current user + +Call `UserGroup.myGroups` to fetch all groups that the current user is a member of: + +~~~javascript +import { UserGroup } from 'radiks'; + +const groups = await UserGroup.myGroups(); +~~~ + +#### Finding a UserGroup + +Use the method `UserGroup.find(id)` when fetching a specific UserGroup. This method has extra boilerplate to handle decrypting the model, because the private keys may need to be fetched from different models. + +~~~javascript +const group = await UserGroup.find('my-id-here'); +~~~ \ No newline at end of file diff --git a/_develop/radiks-intro.md b/_develop/radiks-intro.md new file mode 100644 index 00000000..282b33a7 --- /dev/null +++ b/_develop/radiks-intro.md @@ -0,0 +1,74 @@ +--- +layout: learn +permalink: /:collection/:path.html +--- +# Radiks the data indexer + +The Blockstack Radiks feature enables Blockstack decentralized applications (DApps) to index and store across data belonging to multiple users. Radiks works with Blockstack's Gaia Storage System. Using Radiks, you can build multi-player DApps that: + +- index, store, and query complex application data +- query a user's publicly saved data +- display real-time updates that reflect in progress changes +- support collaboration among sets of users + +{% include question.html content="I added the word application to the complex data bullet above. Your origianl text seemed to make a distinction between publicly saved data and complex data. I assumed applciation data was the something Radiks could store and that was intended here. Was this your intent?"%} + + + +## Why use Radiks? + +Many applications server data that users create to share publicly with others, Facebook, Twitter, and Instagram are examples of such applications. Decentralized applications that want to create comparable multi-user experiences must ensure that anything a user creates for public sharing is likewise still under control the creator in the user's Gaia storage. + +For example, if Twitter were a decentralized application where many different users creating their own tweets and those tweets are stored in each user's own Gaia storage. In such a situation, developer need a way to track of everyone's tweets, display tweets in user timelines, and perform searches across tweets. Radiks exists to support these kind of scenarios in that it allows applications to query across multiple user data, using complicated queries like text search, joins, and filters. Radiks allows applications to query data in a performant and flexible way. + +{% include question.html content="Can one Radiks server support multiple client applications or is a single server required for each application?"%} + +## How Radiks works with application data + +Radiks consists of a database, a pre-built server, and a client. Each application adds Radiks client library to their application. With this library a developer models their application data. The model defines an applications data schema for the Radiks server. Then, you can use calls to this model to write and query data that uses that schema. When ever an application saves or updates data on behalf of a user, Radiks follows this process: + +1. Encrypts private user data on the client-side. +2. Saves a raw JSON of this encrypted data in the user's Gaia storage. +3. Stores the encrypted data on the Radiks server. + +{% include question.html content="You didn't state this in the original but I am assuming the data is user data not just general application data. Is this correct? See the following paragraph which was taken from your server docs, in particular you had the last sentence regarding unencrypted data. Still isn't clear to me when is data in unencrypted and how it is identified from data the user wants signed." %} + +Radiks stores both public data and sensitive, non-private data. Radiks encrypts sensitive data before it leaves the client. Your application can query Radiks for public data and then decrypt the sensitive information on the client. This means that the server is only able to return queries for unencrypted data. + + +## How Radiks authorizes writes + +Radiks must ensure that user's own any data they are writing. To ensure this, Radiks creates and manages *signing keys*. These keys sign all writes that a user performs. Radiks-server validates all signatures before performing a write. This guarantees that a user is not able to over-write another user's data. + +Radiks-server also is built to support writes in a collaborative, but private, situation. For example, consider a collaborative document editing application, where users can create organizations and invite users to that organization. All users in that organization have read and write privileges to the organization data. These organizations have single shared key that is used to sign and encrypt data. + +When an organization administrator needs to remove a user from the group, they'll revoke a previous key and create a new one. Radiks is aware of these relationships, and will only support writes that are signed with the current active key related to an organization. + +## Is Radiks decentralized + +Although Radiks applications rely on a centrally-hosted database, an application using Radiks remains fundamentally decentralized. A DApp that uses Radiks has these characteristics. + + + + + + + + + + + + + + + + + +
Built on decentralized authentication Radiks is deeply tied to Blockstack authentication, which uses a blockchain and Gaia to give you full control over your user data. +
No data lock-in

All user data is first stored in Gaia before encrypted with the user's keys and stored in Radiks. This process means the user still controls their data for as long as they need to. If the application's Radiks server shuts down, the user can still access their data. And, without application to the user's signing keys, the application cannot decrypt the data. Users may also backup or migrate their application data from Gaia. +

Censorship resistance

All data is also stored in Gaia, no third-party can revoke access to this data. +

Maximum privacy

All data is encrypted on the client side before being stored anywhere using Blockstack authorization. The application host cannot inspect, sell, or use user data in any way that a user doesn't explicitly authorize. +

+ +If you are not familiar with Gaia or Blockstack authentication, see +[read the Gaia documentation](({{site.baseurl}}/storage/overview.html) and [start with the overview of Blockstack auth](overview_auth.html). diff --git a/_develop/radiks-models.md b/_develop/radiks-models.md new file mode 100644 index 00000000..eb43fab4 --- /dev/null +++ b/_develop/radiks-models.md @@ -0,0 +1,245 @@ +--- +layout: learn +permalink: /:collection/:path.html +--- +# Create and use models + +To create a model class, first import the `Model` class from radiks. Then, create a class that extends this model, and provide a schema. + +**Important**: Make sure you add a static `className` property to your class. This is used when storing and querying information. If you don't add this, radiks will default to the actual model's class name. However, in production, your code will likely be minified, and the actual class name will be different. For this reason, it's highly recommended that you define the `className` manually. + +#### Schema + +The first static property you'll need to define is a schema. Create a static `schema` property on your class to define it. Each `key` in this object is the name of the field. The value is whatever type you want the field to be, or you can pass some options. + +If you don't want to include any options, just pass the class for that field, like `String`, `Boolean`, or `Number`. + +To include options, pass an object, with a mandatory `type` field. The only supported option right now is `decrypted`. This defaults to `false`, but if you provide `true`, then this field will not be encrypted before storing data publicly. This is useful if you want to be able to query this field when fetching data. + +**Important**: do not add the `decrypted` option to fields that contain sensitive user data. Remember, because this is decentralized storage, anyone can read the user's data. That's why encrypting it is so important. If you want to be able to filter sensitive data, then you should do it on the client-side, after decrypting it. A good use-case for storing decrypted fields is to store a `foreignId` that references a different model, for a "belongs-to" type of relation. + +#### Defaults + +Include an optional `defaults` static property to define default values for a field. + +#### Example + +~~~javascript +import { Model } from 'radiks'; + +class Person extends Model { + static className = 'Person'; + + static schema = { + name: String, + age: Number, + isHuman: Boolean, + likesDogs: { + type: Boolean, + decrypted: true // all users will know if this record likes dogs! + } + } + + static defaults = { + likesDogs: true + } +} +~~~ + +### Using models + +All model instances have an `_id` attribute. If you don't pass an `_id` to the model (when constructing it), then an `_id` will be created automatically using [`uuid/v4`](https://github.com/kelektiv/node-uuid). This `_id` is used as a primary key when storing data, and would be used for fetching this model in the future. + +In addition to automatically creating an `_id` attribute, radiks also creates a `createdAt` and `updatedAt` property when creating and saving models. + +#### Constructing a model + +To create an instance of a model, pass some attributes to the constructor of that class: + +~~~javascript +const person = new Person({ + name: 'Hank', + isHuman: false, + likesDogs: false // just an example, I love dogs! +}) +~~~ + +#### Fetching a model + +To fetch an existing model, first construct it with a required `id` property. Then, call the `fetch()` function, which returns a promise. + +~~~javascript +const person = await Person.findById('404eab3a-6ddc-4ba6-afe8-1c3fff464d44'); +~~~ + +After calling `fetch`, radiks will automatically decrypt all encrypted fields. + +#### Accessing model attributes + +All attributes (other than `id`) are stored in an `attrs` property on the model. + +~~~javascript +const { name, likesDogs } = person.attrs; +console.log(`Does ${name} like dogs?`, likesDogs); +~~~ + +#### Updating a model + +To quickly update multiple attributes of a model, pass those attributes to the `update` function. + +~~~javascript +const newAttributes = { + likesDogs: false, + age: 30 +} +person.update(newAttributes) +~~~ + +Note that calling `update` does **not** save the model. + +#### Saving a model + +To save a model to Gaia and MongoDB, call the `save` function. First, it encrypts all attributes that do not have the `decrypted` option in their schema. Then, it saves a JSON representation of the model in Gaia, as well as in MongoDB. `save` returns a promise. + +~~~javascript +await person.save(); +~~~ + +#### Deleting a model + +To delete a model, just call the `destroy` method on it. + +~~~javascript +await person.destroy(); +~~~ + +### Querying models + +To fetch multiple records that match a certain query, use the class's `fetchList` function. This method creates an HTTP query to Radiks-server, which then queries the underlying database. Radiks-server uses the [`query-to-mongo`](https://github.com/pbatey/query-to-mongo) package to turn an HTTP query into a MongoDB query. Read the documentation for that package to learn how to do complex querying, sorting, limiting, etc. + +Here are some examples: + +~~~javascript +const dogHaters = await Person.fetchList({ likesDogs: false }); +~~~ + +Or, imagine a `Task` model with a `name`, a boolean for `completed`, and an `order` attribute. + +~~~javascript +class Task extends Model { + static className = 'Task'; + + static schema = { + name: String, + completed: { + type: Boolean, + decrypted: true, + }, + order: { + type: Number, + decrypted: true, + } + } +} + +const tasks = await Task.fetchList({ + completed: false, + sort: '-order' +}) +~~~ + +### Counting models + +You can also get the count record directly. +~~~javascript +const dogHaters = await Person.count({ likesDogs: false }); +// dogHaters is the count number +~~~ + +### Fetching models created by the current user + +Use the `fetchOwnList` method to find models that were created by the current user. By using this method, you can preserve privacy, because Radiks uses a `signingKey` that only the current user knows. + +```javascript +const tasks = await Task.fetchOwnList({ + completed: false +}); +``` + +### Managing relational data + +It is common for applications to have multiple different models, where some reference another. For example, imagine a task-tracking application where a user has multiple projects, and each project has multiple tasks. Here's what those models might look like: + +~~~javascript +class Project extends Model { + static className = 'Project'; + static schema = { name: String } +} + +class Task extends Model { + static className = 'Task'; + static schema = { + name: String, + projectId: { + type: String, + decrypted: true, + } + completed: Boolean + } +} +~~~ + +Whenever you save a task, you'll want to save a reference to the project it's in: + +~~~javascript +const task = new Task({ + name: 'Improve radiks documentation', + projectId: project._id +}) +await task.save(); +~~~ + +Then, later you'll want to fetch all tasks for a certain project: + +~~~javascript +const tasks = await Task.fetchList({ + projectId: project._id, +}) +~~~ + +Radiks lets you define an `afterFetch` method, which you can use to automatically fetch child records when you fetch the parent instance. + +~~~javascript +class Project extends Model { + static className = 'Project'; + static schema = { name: String } + + async afterFetch() { + this.tasks = await Task.fetchList({ + projectId: this.id, + }) + } +} + +const project = await Project.findById('some-id-here'); +console.log(project.tasks); // will already have fetched and decrypted all related tasks +~~~ + +### Extending the user model + +You can extend the default user model to add your own fields. + +~~~javascript +import { User } from 'radiks'; + +// For example I want to add a public name on my user model +class MyAppUserModel extends User { + static schema = { + ...User.schema, + name: { + type: String, + decrypted: true, + }, + }; +} +~~~ \ No newline at end of file diff --git a/_develop/radiks-server-extras.md b/_develop/radiks-server-extras.md new file mode 100644 index 00000000..504e5972 --- /dev/null +++ b/_develop/radiks-server-extras.md @@ -0,0 +1,129 @@ +--- +layout: learn +permalink: /:collection/:path.html +--- +# Other ways to use Radiks + + +### Running a custom Radiks-server + +If you're using an [express.js](https://expressjs.com/) server to run your application, it's probably easiest to use the Radiks-server middleware. This way, you won't have to run a separate application server and Radiks server. + +Radiks-server includes an easy-to-use middleware that you can include in your application: + +```javascript +const express = require('express'); + +const { setup } = require('radiks-server'); + +const app = express(); + +setup().then(RadiksController => { + app.use('/radiks', RadiksController); +}); +``` + +The `setup` method returns a promise, and that promise resolves to the actual middleware that your server can use. This is because it first connects to MongoDB, and then sets up the middleware with that database connection. + +The `setup` function accepts an `options` object as the first argument. Right now, the only option supported is `mongoDBUrl`. If you aren't using environment variables, you can explicitly pass in a MongoDB URL here: + +```javascript +setup({ + mongoDBUrl: 'mongodb://localhost:27017/my-custom-database', +}).then(RadiksController => { + app.use('/radiks', RadiksController); +}); +``` + +### Accessing the MongoDB Collection + +#### Using `getDB` to manually connecting to the MongoDB collection + +Radiks-server keeps all models inside of a collection. You can use the `getDB` function to access this collection. [See the MongoDB Collection reference](https://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html) for documentation about how you can interact with this collection. + +```js +const { getDB } = require('radiks-server'); + +const mongo = await getDB(MONGODB_URL); +``` + +#### Migration from Firebase (or anywhere else) + +Migrating data from Firebase to Radiks-server is simple and painless. You can create a script file to fetch all the firebase data using their API. Then, you can use your MONGOD_URI config to use the `mongodb` npm package. + +```js +// Script for transfering users from Firebase to Radiks-server + +const { getDB } = require('radiks-server'); +const { mongoURI } = require('......'); // How you import/require your mongoURI is up to you + +const migrate = async () => { + // `mongo` is a reference to the MongoDB collection that radiks-server uses. + // You can add or edit or update data as necessary. + const mongo = await getDB(mongoURI); + + /** + * Call code to get your users from firebase + * const users = await getUsersFromFirebase(); + * OR grab the Firebase JSON file and set users to that value + * How you saved your user data will proably be different than the example below + */ + + const users = { + '-LV1HAQToANRvhysSClr': { + blockstackId: '1N1DzKgizU4rCEaxAU21EgMaHGB5hprcBM', + username: 'kkomaz.id', + }, + }; + + const usersToInsert = Object.values(users).map(user => { + const { username } = user; + const doc = { + username, + _id: username, + radiksType: 'BlockstackUser', + }; + const op = { + updateOne: { + filter: { + _id: username, + }, + update: { + $setOnInsert: doc, + }, + upsert: true, + }, + }; + return op; + }); + + await mongo.bulkWrite(usersToInsert); +}; + +migrate() + .then(() => { + console.log('Done!'); + process.exit(); + }) + .catch(error => { + console.error(error); + process.exit(); + }); +``` + +### Options + +You can specify some options while initiating the Radiks server. + +```javascript +const { setup } = require('radiks-server'); + +setup({ + ...myOptions, +}); +``` + +Available options: + +- `mongoDBUrl` - The MongoDB URL for the Radiks server +- `maxLimit` - The maximum `limit` field used inside the mongo queries - default to 1000 diff --git a/_develop/radiks-setup.md b/_develop/radiks-setup.md new file mode 100644 index 00000000..8fbf9e8a --- /dev/null +++ b/_develop/radiks-setup.md @@ -0,0 +1,247 @@ +--- +layout: learn +permalink: /:collection/:path.html +--- +# Set-up Radiks for your DApp +{:.no_toc} + +Using Radiks with your application requires a Radiks server and a client application constructed to use the server. In this article, you learn how to install, setup, and run a pre-packaged Radiks server that connects to MongoDB. You also learn how to establish your DApp application as a client for that server. + +* TOC +{:toc} + +## Task 1. Set up your Radiks server + +Radiks-server is a `node.js` application that uses [MongoDB](https://www.mongodb.com/) as an underlying database. + +### Install and configure MongoDB + +In the future, Radiks-server will support various different databases, but right now only MongoDB 3.6 or higher is supported. MongoDB 3.6 and higher contains fixes required for naming patterns in keys. + +{% include note.html content="The steps assumes you want to install and run the MongoDB software locally on your workstation for testing and development. If you are deploying for a production application, you would install MongoDB on your application server or on a server connected to it. " %} + +1. Download and install MongoDB 3.6 or higher on your workstation. + + You can also install MongoDB using your favorite package manager, for example, Homebrew is recommended for macOS. If you are testing on a local workstation, you can use a `docker` image instead of installing locally. + +2. Start the MongoDB service and verify it is running. + +3. On your MongoDB instance, create a database for your application data. + + You can use the Mongo shell to do this or you can install the MongoDB Compass software to explore and work with MongoDB data. + +4. Create a username/password combination with `root` privileges on your new database. + + +### Install and start the Radiks server + +The easiest way to run `radiks-server` is to use the pre-packaged `node.js` server. + +1. Install the `radiks-server` on a workstation or server. + + ```bash + npm install -g radiks-server + ``` + Or, if you prefer `yarn`: + + ```bash + yarn global add radiks-server + ``` + The default port for Mongodb is `27017`, your instance may be configured differently. By default, Radiks-server will use `'MongoDB://localhost:27017/radiks-server'` as the `MongoDB_URI` value. This is suitable for local testing, but in production, you'll want to change the hostname and possible the database name. + +3. Start the `radiks-server` in the command line to confirm your installation. + + ``` + $ radiks-server + (node:37750) DeprecationWarning: current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor. + radiks-server is ready on http://localhost:1260 + ``` + + The `radiks-server` defaults to running on port `1260`. To change the default port, specify the `PORT` environment variable in your environment. + +4. By default the server is running at `http://localhost:1260` + +4. Stop the `radiks` server process after you confirm it runs and your installation was a success. + +## Task 2. Set up your application + +You must set up your application to use Radiks. This requires installing the `radiks` client package and then configuring your application to connect to your Radiks server. + +### Install the radiks client software + +If you are using `blockstack.js` version 18 or earlier, you must use the Radiks version 0.1.\*, otherwise if you're using `blockstack.js` version 19 or higher, use Radiks 0.2.\* . + +1. Change directory to the root of you application code. +2. Install the install the `radiks` client package. + + + + + + + + + + + + + + +
Use npmUse yarn
npm install --save radiksyarn add radiks
+ +### Configure the MongoDB for your application + +1. Start the mongo shell application. + + ``` + $ mongo + MongoDB shell version v4.2.0 + connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb + Implicit session: session { "id" : UUID("8d43cf80-490d-4cac-8bd6-40eec5c128de") } + MongoDB server version: 4.2.0 + .... + + To enable free monitoring, run the following command: db.enableFreeMonitoring() + To permanently disable this reminder, run the following command: db.disableFreeMonitoring() + > + ``` + + 2. Create a new database for your application. + + ``` + > show dbs + admin 0.000GB + config 0.000GB + local 0.000GB + > use test1 + switched to db test1 + > show dbs + admin 0.000GB + config 0.000GB + local 0.000GB + > db.createUser({user: "admin", pwd:"foobar1",roles: ["readWrite","dbAdmin"]}); + Successfully added user: { "user" : "admin", "roles" : [ "readWrite", "dbAdmin" ] } + ``` + +3. Add a user with administrative rights to the database. + + ``` + > db.createUser({user: "admin", pwd:"foobar1",roles: ["readWrite","dbAdmin"]}); + Successfully added user: { "user" : "admin", "roles" : [ "readWrite", "dbAdmin" ] } + ``` + +### Configure your application to use + + + +3. Configure your application to use your `radiks-server`. + + To configure your applciation as a `radiks` client, use code that looks like this when starting up your application: + + ```js + import { UserSession, AppConfig } from 'blockstack'; + import { configure } from 'radiks'; + + const userSession = new UserSession({ + appConfig: new AppConfig(['store_write', 'publish_data']) + }) + + configure({ + apiServer: 'http://localhost:1260', + userSession + }); + ``` + + For more information on configuring and writing a Radiks a client application, see [the Radiks client](https://github.com/blockstack-radiks/radiks) repository. + + +4. Create an `MONGODB_URI` environment variable on the same machine where you are running the `radiks-server`. + + Use the `mongodb://username:password@host:port/db_name` format for your variable. For example, to set this variable in a `bash` shell: + + ```bash + export MONGODB_URI="mongodb://admin:foobar1@localhost:27017/test1" + ``` + + mongodb://admin:foobar1@127.0.0.1:27017/test1 + +5. Build and run your application. + + +### Configuration + +To set up radiks.js, you only need to configure the URL that your Radiks-server instance is running on. If you're using the pre-built Radiks server, this will be `http://localhost:1260`. If you're in production or are using a custom Radiks server, you'll need to specify exactly which URL it's available at. + +Radiks also is compatible with version 19 of blockstack.js, which requires you to configure a `UserSession` object to handle all user-data-related methods. You'll need to define this and pass it to your Radiks configuration, so that Radiks can know how to fetch information about the current logged in user. + +To configure radiks, use code that looks like this when starting up your application: + +~~~javascript +import { UserSession, AppConfig } from 'blockstack'; +import { configure } from 'radiks'; + +const userSession = new UserSession({ + appConfig: new AppConfig(['store_write', 'publish_data']) +}) + +configure({ + apiServer: 'http://my-radiks-server.com', + userSession +}); +~~~ + +### Authentication + +Most of your code will be informed by following [Blockstack's authentication documentation](https://github.com/blockstack/blockstack.js/blob/master/src/auth/README.md). + +After your user logs in with Blockstack, you'll have some code to save the user's data in localStorage. You'll want to use the same `UserSession` you configured with Radiks, which can be fetched from the `getConfig` method. + +~~~javascript +import { User, getConfig } from 'radiks'; + +const handleSignIn = () => { + const { userSession } = getConfig(); + if (userSession.isSignInPending()) { + await userSession.handlePendingSignIn(); + await User.createWithCurrentUser(); + } +} +~~~ + +Calling `User.createWithCurrentUser` will do a few things: + +1. Fetch user data that Blockstack.js stores in `localStorage` +2. Save the user's public data (including their public key) in Radiks-server +3. Find or create a signing key that is used to authorize writes on behalf of this user +4. Cache the user's signing key (and any group-related signing keys) to make signatures and decryption happen quickly later on + +## Models + +Creating models for your application's data is where radiks truly becomes helpful. We provide a `Model` class that you can extend to easily create, save, and fetch models. + +### Quick start + +```javascript +import { Model, User } from 'radiks'; + +class Todo extends Model { + static className = 'Todo'; + static schema = { // all fields are encrypted by default + title: String, + completed: Boolean, + } +}; + +// after authentication: +const todo = new Todo({ title: 'Use Radiks in an app' }); +await todo.save(); +todo.update({ + completed: true, +}); +await todo.save(); + +const incompleteTodos = await Todo.fetchOwnList({ // fetch todos that this user created + completed: false +}); +console.log(incompleteTodos.length); // 0 +``` \ No newline at end of file diff --git a/_develop/storage.md b/_develop/storage.md index 12a5f6fe..c6bef379 100644 --- a/_develop/storage.md +++ b/_develop/storage.md @@ -14,6 +14,10 @@ The Blockstack Platform stores application data in the Gaia Storage System. Tran {% include note.html content="" %} +## How data is stored + +Gaia storage is a key-value store. + ## Creating a file diff --git a/_develop/zero_to_dapp_2.md b/_develop/zero_to_dapp_2.md index 13f08823..6622bffa 100644 --- a/_develop/zero_to_dapp_2.md +++ b/_develop/zero_to_dapp_2.md @@ -7,7 +7,7 @@ image: /assets/img/zero-to-dapp.png # 2 - Learn about the platform {:.no_toc} -**Zero-to-DApp 2 of 4 for MacOS/Linux (or [Windows](zero_to_dapp_2_win.html))** +**Zero-to-DApp 2 of 4 for macOS/Linux (or [Windows](zero_to_dapp_2_win.html))** In this part, you learn how the Blockstack platform lowers the barriers to building with blockchain technology. You'll set up all the prerequisites you diff --git a/_develop/zero_to_dapp_2_win.md b/_develop/zero_to_dapp_2_win.md index c74ad634..ceaec60b 100644 --- a/_develop/zero_to_dapp_2_win.md +++ b/_develop/zero_to_dapp_2_win.md @@ -7,7 +7,7 @@ image: /assets/img/zero-to-dapp.png # 2 - Learn about the platform (Windows) {:.no_toc} -**Zero-to-DApp 2 of 4 for Windows (or [MacOS/Linux](zero_to_dapp_2.html))** +**Zero-to-DApp 2 of 4 for Windows (or [macOS/Linux](zero_to_dapp_2.html))** In this part, you learn how the Blockstack platform lowers the barriers to building with blockchain technology. You'll set up all the prerequisites you diff --git a/_develop/zero_to_dapp_3.md b/_develop/zero_to_dapp_3.md index 2177a601..65705304 100644 --- a/_develop/zero_to_dapp_3.md +++ b/_develop/zero_to_dapp_3.md @@ -7,7 +7,7 @@ image: /assets/img/zero-to-dapp.png # 3 - Customize your kingdom {:.no_toc} - **Zero to DAPP 3 of 4 for MacOS/Linux (or [Windows](zero_to_dapp_3_win.html))** + **Zero to DAPP 3 of 4 for macOS/Linux (or [Windows](zero_to_dapp_3_win.html))** In this page, you examine and modify the Animal Kingdom DApp [you built in part 2](zero_to_dapp_2.html). You'll review the underlying code and locate the diff --git a/_develop/zero_to_dapp_3_win.md b/_develop/zero_to_dapp_3_win.md index b4fa3c9a..2faa8544 100644 --- a/_develop/zero_to_dapp_3_win.md +++ b/_develop/zero_to_dapp_3_win.md @@ -7,7 +7,7 @@ image: /assets/img/zero-to-dapp.png # 3 - Customize your kingdom (Windows) {:.no_toc} - **Zero to DAPP 3 of 4 for Windows (or [MacOS/Linux](zero_to_dapp_3.html))** + **Zero to DAPP 3 of 4 for Windows (or [macOS/Linux](zero_to_dapp_3.html))** In this page, you examine and modify the Animal Kingdom DApp [you built in part 2](zero_to_dapp_2.html). You'll review the underlying code and locate the diff --git a/_includes/architecture.md b/_includes/architecture.md index b7ad07ac..0239b6de 100644 --- a/_includes/architecture.md +++ b/_includes/architecture.md @@ -3,20 +3,14 @@ Blockchains require consensus among large numbers of people, so they can be slow. Additionally, a blockchain is not designed to hold a lot of data. This means using a blockchain for every bit of data a user might write and store is expensive. For example, imagine if an application were storing every tweet in the chain. -Blockstack addresses blockchain performance problems using a layered approach. At the base of the system is a blockchain and the Blockstack Naming System (BNS). The blockchain governs ownership of names (identities) in the system, names such as domain names, usernames, and application names. +Blockstack addresses blockchain performance problems using a layered approach. The base layer consists of the Stacks blockchain and the Blockstack Naming System (BNS). The blockchain governs ownership of identities in the Blockstack network. Identities can be names such as domain names, usernames, or application names. -Names in Blockstack correspond to routing data in the OSI stack. The routing data is stored in the Atlas Peer Network, the second layer. Every core node that joins the Blockstack Network is able to obtain an entire copy of this routing data. Blockstack uses the routing data to associate names (usernames, domains, and application names) with a particular storage location. +When an identity is created, its creation is recorded in the Stacks blockchain. Identities make up the primary data stored into the Stacks blockchain. These identities correspond to routing data in the OSI stack. The routing data is stored in the Atlas Peer Network, the second layer. Every core node that joins the Blockstack Network is able to obtain an entire copy of this routing data. Blockstack uses the routing data to associate identities (domain names, user names, and application names) with a particular storage location in the final layer, the Gaia Storage System. -The final layer is the Gaia Storage System. A Gaia system consists of a _hub -service_ and storage resource on a cloud software provider such as Azure, -DigitalOcean, Amazon EC2, and so forth. Typically the compute resource and the -storage resource belong to same cloud vendor. Gaia currently has -driver support for S3 and Azure Blob Storage, but the driver model allows for -other backend support as well. +A Gaia Storage System consists of a _hub service_ and storage resource on a cloud software provider. The storage provider can be any commercial provider such as Azure, DigitalOcean, Amazon EC2, and so forth. Typically the compute resource and the storage resource reside same cloud vendor, though this is not a requirement. Gaia currently has driver support for S3 and Azure Blob Storage, but the driver model allows for other backend support as well. -Because Gaia stores application and user data off the blockchain, a Blockstack -DApp is typically more performant than DApps created on other blockchains. -Moreover, users choose where their data lives, and Gaia enables applications -to access that user data via a uniform API. When the user logs in, +Gaia stores data as a simple key-value store. When an identity is created, a corresponding data store is associated with that identity on Gaia. When a user logs into a dApp, the authentication process gives the application the URL of a Gaia hub, which then writes to storage on behalf of that user. + +Within Blockstack, then, the Stacks blockchain stores only identity data. Data created by the actions of an identity is stored in a Gaia Storage System. Each user has profile data. When a user interacts with a decentralized dApp that application stores application data on behalf of the user. Because Gaia stores user and application data off the blockchain, a Blockstack DApp is typically more performant than DApps created on other blockchains. diff --git a/_includes/commandline.md b/_includes/commandline.md index 15d172a0..a62e1799 100644 --- a/_includes/commandline.md +++ b/_includes/commandline.md @@ -60,7 +60,7 @@ To see the usage and options for the command in general, enter `blockstack-cli` ## How to install the command line {:.no_toc} -You must have [Node.js](https://nodejs.org/en/download/) v8 or higher (v10 recommended). MacOS and Linux users can avoid `sudo` or [permissions problems](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally) or by using [`nvm`](https://github.com/nvm-sh/nvm). These instructions assume you are using a macOS or Linux system. +You must have [Node.js](https://nodejs.org/en/download/) v8 or higher (v10 recommended). macOS and Linux users can avoid `sudo` or [permissions problems](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally) or by using [`nvm`](https://github.com/nvm-sh/nvm). These instructions assume you are using a macOS or Linux system. To install the command line, do the following: diff --git a/_includes/question.html b/_includes/question.html index 7ec5bd08..e91af501 100644 --- a/_includes/question.html +++ b/_includes/question.html @@ -1 +1,5 @@ -
QUESTION FOR REVIEWERS: {{include.content}}
\ No newline at end of file +<<<<<<< HEAD +
QUESTION FOR REVIEWERS: {{include.content}}
+======= +
QUESTION FOR REVIEWERS: {{include.content}}
+>>>>>>> 6350bd1... Adding in base radiks files diff --git a/_org/wallet-install.md b/_org/wallet-install.md index 287e8438..e68c203c 100644 --- a/_org/wallet-install.md +++ b/_org/wallet-install.md @@ -21,7 +21,7 @@ by Blockstack PBC." %} ## Mac Installation 1. Go to the wallet download page in your browser. -2. Select the **MacOS Download** button. +2. Select the **macOS Download** button. This button downloads the software to your computer. diff --git a/_storage/overview.md b/_storage/overview.md index 6193e357..8405f723 100644 --- a/_storage/overview.md +++ b/_storage/overview.md @@ -23,18 +23,14 @@ The following diagram depicts the Blockstack architecture and Gaia's place in it ## User control or how is Gaia decentralized? -A Gaia hub runs as a service which writes to data storage. The hub service +A Gaia hub runs as a service which writes to data storage. The storage itself is a simple key-value store. The hub service writes to data storage by requiring a valid authentication token from a requestor. Typically, the hub service runs on a compute resource and the storage itself on separate, dedicated storage resource. Typically, both resources belong to the same cloud computing provider. ![Gaiastorage](/storage/images/gaia-storage.png) -Gaia's approach to decentralization focuses on user control of data and its -storage. If a user can choose which Gaia hub provider to use, then that choice -is all the decentralization required to enable user-controlled applications. +Gaia's approach to decentralization focuses on user control of data and its storage. Users can choose a Gaia hub provider. If a user can choose which Gaia hub provider to use, then that choice is all the decentralization required to enable user-controlled applications. Moreover, Gaia a uniform API to access for applications to access that data. -The control of user data lies in the way that user data is accessed. -When an application fetches a file `data.txt` for a given user `alice.id`, the -lookup will follow these steps: +The control of user data lies in the way that user data is accessed. When an application fetches a file `data.txt` for a given user `alice.id`, the lookup will follow these steps: 1. Fetch the `zonefile` for `alice.id`. 2. Read her profile URL from her `zonefile`. @@ -43,35 +39,18 @@ lookup will follow these steps: 5. Read the `gaiaHubUrl` (e.g. `https://gaia.alice.org/`) out of the profile 6. Fetch the file from `https://gaia.alice.org/data.txt`. -Because `alice.id` has access to her zonefile, she can change where her profile -is stored. For example, she may do this if the current profile's service or -storage is compromised. To change where her profile is stored, she changes her -Gaia hub URL to another Gaia hub URL from another hub provider. If Alice has -sufficient compute and storage resources herself, she may run her own Gaia -Storage System and bypass a commercial Gaia hub provider all together. +Because `alice.id` has access to her zonefile, she can change where her profile is stored. For example, she may do this if the current profile's service provider or storage is compromised. To change where her profile is stored, she changes her Gaia hub URL to another Gaia hub URL. If a user has sufficient compute and storage resources, a user may run their own Gaia Storage System and bypass a commercial Gaia hub provider all together. {% include note.html content="Users with existing identities cannot yet migrate their data from one hub to another." %} -Applications writing directly on behalf of Alice do not need to perform a -lookup. Instead, the Blockstack authentication flow provides Alice's chosen application -root URL to the application. This authentication flow _is also_ within Alice's -control because Alice's browser _must_ generate the authentication response. - +Applications writing directly on behalf of `alice.id` do not need to perform a lookup. Instead, the Blockstack authentication flow provides Alice's chosen application root URL to the application. This authentication flow _is also_ within Alice's control because Alice's browser _must_ generate the authentication response. ## Understand data storage -A Gaia hub stores the written data _exactly_ as given. It offers minimal -guarantees about the data. It does not ensure that data is validly formatted, -contains valid signatures, or is encrypted. Rather, the design philosophy is -that these concerns are client-side concerns. - -Client libraries (such as `blockstack.js`) are capable of providing these -guarantees. Blockstack used a liberal definition of the end-to-end principle to -guide this design decision. +A Gaia hub stores the written data _exactly_ as given. It offers minimal guarantees about the data. It does not ensure that data is validly formatted, contains valid signatures, or is encrypted. Rather, the design philosophy is that these concerns are client-side concerns. +Client libraries (such as `blockstack.js`) are capable of providing these guarantees. Blockstack used a liberal definition of the end-to-end principle to guide this design decision. ## Gaia versus other storage systems From d1de26c01e28cea848ddf31bdfd92c1d2d0b9c71 Mon Sep 17 00:00:00 2001 From: Mary Anthony Date: Mon, 27 Jan 2020 20:31:19 -0800 Subject: [PATCH 2/5] wip Signed-off-by: Mary Anthony --- _develop/radiks-models.md | 38 ++++++++++++ _develop/radiks-setup.md | 126 ++++++++++++-------------------------- _includes/question.html | 4 -- 3 files changed, 76 insertions(+), 92 deletions(-) diff --git a/_develop/radiks-models.md b/_develop/radiks-models.md index eb43fab4..2d75835c 100644 --- a/_develop/radiks-models.md +++ b/_develop/radiks-models.md @@ -3,11 +3,49 @@ layout: learn permalink: /:collection/:path.html --- # Create and use models +{:.no_toc} + +* TOC +{:toc} + + +### The Model class + To create a model class, first import the `Model` class from radiks. Then, create a class that extends this model, and provide a schema. **Important**: Make sure you add a static `className` property to your class. This is used when storing and querying information. If you don't add this, radiks will default to the actual model's class name. However, in production, your code will likely be minified, and the actual class name will be different. For this reason, it's highly recommended that you define the `className` manually. + +We provide a `Model` class that you can extend to easily create, save, and fetch models. + + + +```javascript +import { Model, User } from 'radiks'; + +class Todo extends Model { + static className = 'Todo'; + static schema = { // all fields are encrypted by default + title: String, + completed: Boolean, + } +}; + +// after authentication: +const todo = new Todo({ title: 'Use Radiks in an app' }); +await todo.save(); +todo.update({ + completed: true, +}); +await todo.save(); + +const incompleteTodos = await Todo.fetchOwnList({ // fetch todos that this user created + completed: false +}); +console.log(incompleteTodos.length); // 0 +``` + #### Schema The first static property you'll need to define is a schema. Create a static `schema` property on your class to define it. Each `key` in this object is the name of the field. The value is whatever type you want the field to be, or you can pass some options. diff --git a/_develop/radiks-setup.md b/_develop/radiks-setup.md index 8fbf9e8a..95ecff7c 100644 --- a/_develop/radiks-setup.md +++ b/_develop/radiks-setup.md @@ -130,31 +130,6 @@ If you are using `blockstack.js` version 18 or earlier, you must use the Radiks Successfully added user: { "user" : "admin", "roles" : [ "readWrite", "dbAdmin" ] } ``` -### Configure your application to use - - - -3. Configure your application to use your `radiks-server`. - - To configure your applciation as a `radiks` client, use code that looks like this when starting up your application: - - ```js - import { UserSession, AppConfig } from 'blockstack'; - import { configure } from 'radiks'; - - const userSession = new UserSession({ - appConfig: new AppConfig(['store_write', 'publish_data']) - }) - - configure({ - apiServer: 'http://localhost:1260', - userSession - }); - ``` - - For more information on configuring and writing a Radiks a client application, see [the Radiks client](https://github.com/blockstack-radiks/radiks) repository. - - 4. Create an `MONGODB_URI` environment variable on the same machine where you are running the `radiks-server`. Use the `mongodb://username:password@host:port/db_name` format for your variable. For example, to set this variable in a `bash` shell: @@ -163,85 +138,60 @@ If you are using `blockstack.js` version 18 or earlier, you must use the Radiks export MONGODB_URI="mongodb://admin:foobar1@localhost:27017/test1" ``` - mongodb://admin:foobar1@127.0.0.1:27017/test1 -5. Build and run your application. - - -### Configuration +## Task 3. Add startup code and build your application To set up radiks.js, you only need to configure the URL that your Radiks-server instance is running on. If you're using the pre-built Radiks server, this will be `http://localhost:1260`. If you're in production or are using a custom Radiks server, you'll need to specify exactly which URL it's available at. Radiks also is compatible with version 19 of blockstack.js, which requires you to configure a `UserSession` object to handle all user-data-related methods. You'll need to define this and pass it to your Radiks configuration, so that Radiks can know how to fetch information about the current logged in user. -To configure radiks, use code that looks like this when starting up your application: - -~~~javascript -import { UserSession, AppConfig } from 'blockstack'; -import { configure } from 'radiks'; - -const userSession = new UserSession({ - appConfig: new AppConfig(['store_write', 'publish_data']) -}) - -configure({ - apiServer: 'http://my-radiks-server.com', - userSession -}); -~~~ +### Configure your application to use your `radiks-server`. -### Authentication +To configure your application as a `radiks` client, do the following: -Most of your code will be informed by following [Blockstack's authentication documentation](https://github.com/blockstack/blockstack.js/blob/master/src/auth/README.md). +1. Start your application so that a `UserSession` allows the app to both write and publish data: -After your user logs in with Blockstack, you'll have some code to save the user's data in localStorage. You'll want to use the same `UserSession` you configured with Radiks, which can be fetched from the `getConfig` method. - -~~~javascript -import { User, getConfig } from 'radiks'; + ```js + import { UserSession, AppConfig } from 'blockstack'; + import { configure } from 'radiks'; -const handleSignIn = () => { - const { userSession } = getConfig(); - if (userSession.isSignInPending()) { - await userSession.handlePendingSignIn(); - await User.createWithCurrentUser(); - } -} -~~~ + const userSession = new UserSession({ + appConfig: new AppConfig(['store_write', 'publish_data']) + }) -Calling `User.createWithCurrentUser` will do a few things: + configure({ + apiServer: 'http://localhost:1260', + userSession + }); + ``` + + 2. Add authentication to your application -1. Fetch user data that Blockstack.js stores in `localStorage` -2. Save the user's public data (including their public key) in Radiks-server -3. Find or create a signing key that is used to authorize writes on behalf of this user -4. Cache the user's signing key (and any group-related signing keys) to make signatures and decryption happen quickly later on + After your user logs in with Blockstack, you'll have some code to save the user's data in your applications `localStorage`. You'll want to use the same `UserSession` you configured with Radiks, which can be fetched from the `getConfig` method. -## Models + ```js + import { User, getConfig } from 'radiks'; + + const handleSignIn = () => { + const { userSession } = getConfig(); + if (userSession.isSignInPending()) { + await userSession.handlePendingSignIn(); + await User.createWithCurrentUser(); + } + } + ``` -Creating models for your application's data is where radiks truly becomes helpful. We provide a `Model` class that you can extend to easily create, save, and fetch models. + Calling `User.createWithCurrentUser` does the following: -### Quick start + * Fetch user data that Blockstack.js stores in `localStorage` + * Save the user's public data (including their public key) in Radiks-server + * Find or create a signing key that is used to authorize writes on behalf of this user + * Cache the user's signing key (and any group-related signing keys) to make signatures and decryption happen quickly later on -```javascript -import { Model, User } from 'radiks'; +### Build and run your application -class Todo extends Model { - static className = 'Todo'; - static schema = { // all fields are encrypted by default - title: String, - completed: Boolean, - } -}; +After you have added Radiks to your application, build and run the application. Test the application by logging in with your Blockstack ID. Create some data using the application. If you inspect the MongoDB database, you should see the encrypted data stored in the database. -// after authentication: -const todo = new Todo({ title: 'Use Radiks in an app' }); -await todo.save(); -todo.update({ - completed: true, -}); -await todo.save(); +## Where to go next -const incompleteTodos = await Todo.fetchOwnList({ // fetch todos that this user created - completed: false -}); -console.log(incompleteTodos.length); // 0 -``` \ No newline at end of file +Creating models for your application's data is where radiks truly becomes helpful. To learn how to use models, see the [Create and use models](radiks-models.html) section. \ No newline at end of file diff --git a/_includes/question.html b/_includes/question.html index e91af501..079f0c52 100644 --- a/_includes/question.html +++ b/_includes/question.html @@ -1,5 +1 @@ -<<<<<<< HEAD
QUESTION FOR REVIEWERS: {{include.content}}
-======= -
QUESTION FOR REVIEWERS: {{include.content}}
->>>>>>> 6350bd1... Adding in base radiks files From 552f48d41d2c6828e228111ee80dfd2502f0a264 Mon Sep 17 00:00:00 2001 From: Mary Anthony Date: Wed, 29 Jan 2020 21:27:39 -0800 Subject: [PATCH 3/5] Updating all the Radiks Signed-off-by: Mary Anthony --- _develop/radiks-collaborate.md | 83 ++++++++----- _develop/radiks-models.md | 200 ++++++++++++++++++------------- _develop/radiks-server-extras.md | 52 ++++---- _develop/radiks-setup.md | 15 +++ 4 files changed, 205 insertions(+), 145 deletions(-) diff --git a/_develop/radiks-collaborate.md b/_develop/radiks-collaborate.md index 70c5ab13..484bcf69 100644 --- a/_develop/radiks-collaborate.md +++ b/_develop/radiks-collaborate.md @@ -2,81 +2,100 @@ layout: learn permalink: /:collection/:path.html --- -# Collaboration +# Collaborate with groups +{:.no_toc} -A key feature of Radiks is support for private collaboration between multiple users. Supporting collaboration with client-side encryption and user-owned storage can be complicated, but the patterns to implement it are generally the same for different apps. Radiks provides out-of-the box for collaboration, making it easy to build private, collaborative apps. +A key feature of Radiks is support for private collaboration between multiple users. Supporting collaboration with client-side encryption and user-owned storage can be complicated but the patterns to implement it are generally the same among most applications. Radiks provides out-of-the box for collaboration, making it easy to build private, collaborative apps. -Radiks is built in a way that provides maximum privacy and security for collaborative groups. Radiks-server and external users have no knowledge about who is in a group. +You use the UserGroup class to build a collaborative group with Radiks. In this section, you learn about this class. -### UserGroup Model +* TOC +{:toc} -The key model behind a collaborative group is `UserGroup`. By default, it only has one attribute, `name`, which is encrypted. You can create multiple subclasses of `UserGroup` later on with different attributes, if you need to. +## Understand the UserGroup workflow -### General Workflow +The key model behind a collaborative group is `UserGroup`. By default, it only has one attribute, `name`, which is encrypted. You can subclass `UserGroup` with different attributes as needed. The general workflow for creating a collaborative group that can share and edit encrypted models is as follows: -1. The admin of the group creates a new `UserGroup`, which acts as the 'hub' and controls the logic around inviting and removing users. +1. The admin of the group creates a new `UserGroup`. + This group acts as the 'hub' and controls the logic around inviting and removing users. 2. The admin invites one or more other users to a group: - 1. The admin specifies the username of the user they want to invite - 2. Radiks looks up the user's public key - 3. Radiks creates an 'invitation' that is encrypted with the user's public key, and contains information about the `UserGroup` - 4. When the invited user 'activates' an invitation, they create a `GroupMembership`, which they can later use to reference information (such as private keys and signing keys) related to the group. -3. Later on, members of the group can create and update models that are related to the group. These models **must** contain a reference to the group, using the attribute `userGroupId`. This allows Radiks to know which keys to use for encryption and signing. -4. The admin of the group can later remove a user from a group. They do this by creating a new private key for signing and encryption, and updating the `GroupMembership` of all users _except_ the user they just removed. -5. After a key is rotated, all new and updated models must use the new key for signing. Radiks-server validates all group-related models to ensure that they're signed with the most up-to-date key. + * The admin specifies the username of the user they want to invite + * Radiks looks up the user's public key + * Radiks creates an 'invitation' that is encrypted with the user's public key, and contains information about the `UserGroup` +3. When the invited user 'activates' an invitation, they create a `GroupMembership`. + They use this membership instance to reference information (such as private keys and signing keys) related to the group. -#### Creating a UserGroup +As they participate in a group, the group's members can create and update models that are related to the group. These models **must** contain a `userGroupId` attribute used to reference the group. This allows Radiks to know which keys to use for encryption and signing. -~~~javascript +When needed, the group admin can remove a user from a group. To remove a user from the group, the admin creates a new private key for signing and encryption. Then, the admin updates the `GroupMembership` of all users _except_ the user they just removed. This is also known as rotating the key. + +After a key is rotated, all new and updated models must use the new key for signing. Radiks-server validates all group-related models to ensure that they're signed with the most up-to-date key. + +## Work with a UserGroup + +This section details the methods on the UserGroup class you can use to create, add members to, and query a group. + +### Create a UserGroup + +To create a `UserGroup`, your application must import the class from `radiks`: + +```javascript import { UserGroup } from 'radiks'; // ... +``` +Calling `create` on a new `UserGroup` will create the group and activate an invitation for the group's creator. + + +```javascript const group = new UserGroup({ name: 'My Group Name' }); await group.create(); -~~~ +``` + +A group's creator is also the group's admin. -Calling `create` on a new `UserGroup` will create the group and activate an invitation for the creator of the group. -#### Inviting a User +### Invite users to become members -Use the `makeGroupMembership` method on a `UserGroup` instance to invite a user. The only argument passed to this method is the username of the user you want to invite. +Use the `makeGroupMembership` method on a `UserGroup` instance to invite a user. The only argument passed to this method is the user's `username`. -~~~javascript +```javascript import { UserGroup } from 'radiks'; const group = await UserGroup.findById(myGroupId); const usernameToInvite = 'hankstoever.id'; const invitation = await group.makeGroupMembership(usernameToInvite); console.log(invitation._id); // the ID used to later activate an invitation -~~~ +``` -#### Accepting an invitation +### Accept an invitation -Use the `activate` method on a `GroupInvitation` instance to activate an invitation: +Use the `activate` method on a `GroupInvitation` instance to activate an invitation on behalf of a user: -~~~javascript +```javascript import { GroupInvitation } from 'radiks'; const invitation = await GroupInvitation.findById(myInvitationID); await invitation.activate(); -~~~ +``` -#### Viewing all activated UserGroups for the current user +## View all activated UserGroups for the current user Call `UserGroup.myGroups` to fetch all groups that the current user is a member of: -~~~javascript +```javascript import { UserGroup } from 'radiks'; const groups = await UserGroup.myGroups(); -~~~ +``` -#### Finding a UserGroup +## Find a UserGroup Use the method `UserGroup.find(id)` when fetching a specific UserGroup. This method has extra boilerplate to handle decrypting the model, because the private keys may need to be fetched from different models. -~~~javascript +```javascript const group = await UserGroup.find('my-id-here'); -~~~ \ No newline at end of file +``` \ No newline at end of file diff --git a/_develop/radiks-models.md b/_develop/radiks-models.md index 2d75835c..e01d2a5d 100644 --- a/_develop/radiks-models.md +++ b/_develop/radiks-models.md @@ -5,21 +5,26 @@ permalink: /:collection/:path.html # Create and use models {:.no_toc} +Radiks allows you to model your client data. You can then query this data and display it for user in multi-player applications. For example, a social application where users want to see the comments of other users. This page explains how to create a model in your distributed application using Radiks. + * TOC {:toc} +## Overview of Model class extension -### The Model class - - -To create a model class, first import the `Model` class from radiks. Then, create a class that extends this model, and provide a schema. +Blockstack provides a `Model` class you should extend to easily create, save, and fetch models. To create a model class, import the `Model` class from `radiks` into your application. -**Important**: Make sure you add a static `className` property to your class. This is used when storing and querying information. If you don't add this, radiks will default to the actual model's class name. However, in production, your code will likely be minified, and the actual class name will be different. For this reason, it's highly recommended that you define the `className` manually. +```javascript +import { Model, User } from 'radiks'; +``` + Then, create a class that extends this model, and provide a schema. Refer to the Model class in the `radiks` repo to get an overview of the class functionality. -We provide a `Model` class that you can extend to easily create, save, and fetch models. +Your new class must define a static `className` property manually. This property is used when storing and querying information. If you don't add a `className`, Radiks defaults to the actual model's class name. In production, your code should be minified, and the actual class name will be different. For this reason, it's highly recommended that you define the `className` manually. +{% include question.html content="The sentence with the statement that the actual class name will be different doesn't parse. The actual class name will be different from the model class name? the className as defined --- what? Also, what do you mean by defining the class name manually -- you mean in the code as opposed to default to the model class name"%} +The example class code extends `Model` to create a class named `Todo`: ```javascript import { Model, User } from 'radiks'; @@ -46,23 +51,37 @@ const incompleteTodos = await Todo.fetchOwnList({ // fetch todos that this user console.log(incompleteTodos.length); // 0 ``` -#### Schema +## How to create your own Model -The first static property you'll need to define is a schema. Create a static `schema` property on your class to define it. Each `key` in this object is the name of the field. The value is whatever type you want the field to be, or you can pass some options. +The following sections guide you through the steps in defining your own class. + +### Define a class schema + +Every class must have a static `schema` property which defines the attributes of a model using field/value pairs, for example: + +```javascript +class Todo extends Model { + static className = 'Todo'; + static schema = { // all fields are encrypted by default + title: String, + completed: Boolean, + } +}; +``` -If you don't want to include any options, just pass the class for that field, like `String`, `Boolean`, or `Number`. +The `key` in this object is the field name and the value, for example, `String`, `Boolean`, or `Number`. In this case, the `title` is a `String` field. Alternatively, you can pass options instead of a type. -To include options, pass an object, with a mandatory `type` field. The only supported option right now is `decrypted`. This defaults to `false`, but if you provide `true`, then this field will not be encrypted before storing data publicly. This is useful if you want to be able to query this field when fetching data. +To define options, pass an object, with a mandatory `type` field. The only supported option right now is `decrypted`. This defaults to `false`, meaning the field is encrypted before the data is stored publicly. If you specify `true`, then the field is not encrypted. -**Important**: do not add the `decrypted` option to fields that contain sensitive user data. Remember, because this is decentralized storage, anyone can read the user's data. That's why encrypting it is so important. If you want to be able to filter sensitive data, then you should do it on the client-side, after decrypting it. A good use-case for storing decrypted fields is to store a `foreignId` that references a different model, for a "belongs-to" type of relation. +Storing unencrypted fields is useful if you want to be able to query the field when fetching data. A good use-case for storing decrypted fields is to store a `foreignId` that references a different model, for a "belongs-to" type of relation. -#### Defaults +**Never add the `decrypted` option to fields that contain sensitive user data.** Blockstack data is stored in a decentralized Gaia storage and anyone can read the user's data. That's why encrypting it is so important. If you want to filter sensitive data, then you should do it on the client-side, after decrypting it. -Include an optional `defaults` static property to define default values for a field. +### Include defaults -#### Example +You may want to include an optional `defaults` static property for some field values. For example in the class below the `likesDogs` field is a `Boolean` and the default is `true`. -~~~javascript +```javascript import { Model } from 'radiks'; class Person extends Model { @@ -82,88 +101,121 @@ class Person extends Model { likesDogs: true } } -~~~ +``` -### Using models +If you wanted to add a default for `isHuman`, you would simply add it to the `defaults` as well. Separate each field with a comma. -All model instances have an `_id` attribute. If you don't pass an `_id` to the model (when constructing it), then an `_id` will be created automatically using [`uuid/v4`](https://github.com/kelektiv/node-uuid). This `_id` is used as a primary key when storing data, and would be used for fetching this model in the future. +### Extend the User model -In addition to automatically creating an `_id` attribute, radiks also creates a `createdAt` and `updatedAt` property when creating and saving models. +Radiks also supplies a default User model. You can also extend this model to add your own attributes. -#### Constructing a model +```javascript +import { User } from 'radiks'; + +// For example I want to add a public name on my user model +class MyAppUserModel extends User { + static schema = { + ...User.schema, + displayName: { + type: String, + decrypted: true, + }, + }; +} +``` + +The default `User` model defines a `username` but you can add a `displayName` to allow the user to define a name unique in your app. + +## Use a model you have defined + +In this section, you learn how to use a model you have defined. + +### About the _id attribute + +All model instances have an `_id` attribute. An `_id` is used as a primary key when storing data, and is used for fetching a model. Radiks also creates a `createdAt` and `updatedAt` property when creating and saving models. + +If, when constructing a model, you don't pass an `_id`, Radiks creates an `_id` for you automatically. This automatically created id uses the [`uuid/v4`](https://github.com/kelektiv/node-uuid) format. + + +### Construct a model instance To create an instance of a model, pass some attributes to the constructor of that class: -~~~javascript +```javascript const person = new Person({ name: 'Hank', isHuman: false, likesDogs: false // just an example, I love dogs! }) -~~~ +``` -#### Fetching a model +{% include question.html content="I don't see an example of the _id attribute in the above construction. How does the user get the id to pass to findByID below then?"%} -To fetch an existing model, first construct it with a required `id` property. Then, call the `fetch()` function, which returns a promise. +### Fetch an instance -~~~javascript +To fetch an existing instance of a model, you need the instance's `id` property. Then, call the `fetch()` function, which returns a promise. + +```javascript const person = await Person.findById('404eab3a-6ddc-4ba6-afe8-1c3fff464d44'); -~~~ +``` -After calling `fetch`, radiks will automatically decrypt all encrypted fields. +{% include question.html content="You mention calling fetch but your example uses findById. it looks like either will work but do you want to use fetch in this example?"%} -#### Accessing model attributes -All attributes (other than `id`) are stored in an `attrs` property on the model. +After calling `fetch`, Radiks automatically decrypts all encrypted fields. -~~~javascript +### Access attributes + +Other than `id`, all attributes are stored in an `attrs` property on the model. + +```javascript const { name, likesDogs } = person.attrs; console.log(`Does ${name} like dogs?`, likesDogs); -~~~ +``` -#### Updating a model +### Update attributes To quickly update multiple attributes of a model, pass those attributes to the `update` function. -~~~javascript +```javascript const newAttributes = { likesDogs: false, age: 30 } person.update(newAttributes) -~~~ +``` Note that calling `update` does **not** save the model. -#### Saving a model +### Save changes -To save a model to Gaia and MongoDB, call the `save` function. First, it encrypts all attributes that do not have the `decrypted` option in their schema. Then, it saves a JSON representation of the model in Gaia, as well as in MongoDB. `save` returns a promise. +To save a model to Gaia and MongoDB, call the `save()` method which returns a promise. This method encrypts all attributes that do not have the `decrypted` option in their schema. Then, it saves a JSON representation of the model in Gaia, as well as in the MongoDB. -~~~javascript +```javascript await person.save(); -~~~ +``` -#### Deleting a model +### Delete an instance To delete a model, just call the `destroy` method on it. -~~~javascript +```javascript await person.destroy(); -~~~ +``` -### Querying models +## Query a model -To fetch multiple records that match a certain query, use the class's `fetchList` function. This method creates an HTTP query to Radiks-server, which then queries the underlying database. Radiks-server uses the [`query-to-mongo`](https://github.com/pbatey/query-to-mongo) package to turn an HTTP query into a MongoDB query. Read the documentation for that package to learn how to do complex querying, sorting, limiting, etc. +To fetch multiple records that match a certain query, use the class's `fetchList()` function. This method creates an HTTP query to Radiks-server, which then queries the underlying database. Radiks-server uses the `query-to-mongo` package to turn an HTTP query into a MongoDB query. Here are some examples: -~~~javascript +```javascript const dogHaters = await Person.fetchList({ likesDogs: false }); -~~~ +``` Or, imagine a `Task` model with a `name`, a boolean for `completed`, and an `order` attribute. -~~~javascript +```javascript class Task extends Model { static className = 'Task'; @@ -184,17 +236,20 @@ const tasks = await Task.fetchList({ completed: false, sort: '-order' }) -~~~ +``` -### Counting models +You can read the [`query-to-mongo`](https://github.com/pbatey/query-to-mongo) package documentation to learn how to do complex querying, sorting, limiting, and so forth. -You can also get the count record directly. -~~~javascript +## Count models + +You can also get a model's `count` record directly. + +```javascript const dogHaters = await Person.count({ likesDogs: false }); // dogHaters is the count number -~~~ +``` -### Fetching models created by the current user +## Fetch models created by the current user Use the `fetchOwnList` method to find models that were created by the current user. By using this method, you can preserve privacy, because Radiks uses a `signingKey` that only the current user knows. @@ -204,11 +259,11 @@ const tasks = await Task.fetchOwnList({ }); ``` -### Managing relational data +## Manage relational data It is common for applications to have multiple different models, where some reference another. For example, imagine a task-tracking application where a user has multiple projects, and each project has multiple tasks. Here's what those models might look like: -~~~javascript +```javascript class Project extends Model { static className = 'Project'; static schema = { name: String } @@ -225,29 +280,29 @@ class Task extends Model { completed: Boolean } } -~~~ +``` -Whenever you save a task, you'll want to save a reference to the project it's in: +Whenever you save a task, you should save a reference to the project it's in: -~~~javascript +```javascript const task = new Task({ name: 'Improve radiks documentation', projectId: project._id }) await task.save(); -~~~ +``` Then, later you'll want to fetch all tasks for a certain project: -~~~javascript +```javascript const tasks = await Task.fetchList({ projectId: project._id, }) -~~~ +``` -Radiks lets you define an `afterFetch` method, which you can use to automatically fetch child records when you fetch the parent instance. +Radiks lets you define an `afterFetch` method. Use this method to automatically fetch child records when you fetch the parent instance. -~~~javascript +```javascript class Project extends Model { static className = 'Project'; static schema = { name: String } @@ -261,23 +316,4 @@ class Project extends Model { const project = await Project.findById('some-id-here'); console.log(project.tasks); // will already have fetched and decrypted all related tasks -~~~ - -### Extending the user model - -You can extend the default user model to add your own fields. - -~~~javascript -import { User } from 'radiks'; - -// For example I want to add a public name on my user model -class MyAppUserModel extends User { - static schema = { - ...User.schema, - name: { - type: String, - decrypted: true, - }, - }; -} -~~~ \ No newline at end of file +``` diff --git a/_develop/radiks-server-extras.md b/_develop/radiks-server-extras.md index 504e5972..28901853 100644 --- a/_develop/radiks-server-extras.md +++ b/_develop/radiks-server-extras.md @@ -2,10 +2,25 @@ layout: learn permalink: /:collection/:path.html --- -# Other ways to use Radiks +# Radiks server Tips and tricks +In this section, you'll find some tips and tricks you can use to work with a Radiks server. -### Running a custom Radiks-server + +## Access the MongoDB collection + +Radiks-server keeps all models inside of a collection. You can use the `getDB` function to access this collection from inside your application. + +```js +const { getDB } = require('radiks-server'); + +const mongo = await getDB(MONGODB_URL); +``` + +[See the MongoDB Collection reference](https://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html) for documentation about how you can interact with this collection. + + +## Run a custom Radiks-server If you're using an [express.js](https://expressjs.com/) server to run your application, it's probably easiest to use the Radiks-server middleware. This way, you won't have to run a separate application server and Radiks server. @@ -25,7 +40,7 @@ setup().then(RadiksController => { The `setup` method returns a promise, and that promise resolves to the actual middleware that your server can use. This is because it first connects to MongoDB, and then sets up the middleware with that database connection. -The `setup` function accepts an `options` object as the first argument. Right now, the only option supported is `mongoDBUrl`. If you aren't using environment variables, you can explicitly pass in a MongoDB URL here: +The `setup` function accepts an `options` object as the first argument. If you aren't using environment variables, you can explicitly pass in a MongoDB URL here: ```javascript setup({ @@ -35,21 +50,12 @@ setup({ }); ``` -### Accessing the MongoDB Collection - -#### Using `getDB` to manually connecting to the MongoDB collection - -Radiks-server keeps all models inside of a collection. You can use the `getDB` function to access this collection. [See the MongoDB Collection reference](https://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html) for documentation about how you can interact with this collection. - -```js -const { getDB } = require('radiks-server'); +Currently, only the `mongoDBUrl` option is supported. -const mongo = await getDB(MONGODB_URL); -``` -#### Migration from Firebase (or anywhere else) +## Migrate from Firebase (or anywhere else) -Migrating data from Firebase to Radiks-server is simple and painless. You can create a script file to fetch all the firebase data using their API. Then, you can use your MONGOD_URI config to use the `mongodb` npm package. +Migrating data from Firebase to Radiks-server is simple and painless. You can create a script file to fetch all the Firebase data using their API. Then, you can use your `MONGOD_URI` config to use the `mongodb` npm package. ```js // Script for transfering users from Firebase to Radiks-server @@ -111,19 +117,3 @@ migrate() }); ``` -### Options - -You can specify some options while initiating the Radiks server. - -```javascript -const { setup } = require('radiks-server'); - -setup({ - ...myOptions, -}); -``` - -Available options: - -- `mongoDBUrl` - The MongoDB URL for the Radiks server -- `maxLimit` - The maximum `limit` field used inside the mongo queries - default to 1000 diff --git a/_develop/radiks-setup.md b/_develop/radiks-setup.md index 95ecff7c..670c9f6f 100644 --- a/_develop/radiks-setup.md +++ b/_develop/radiks-setup.md @@ -192,6 +192,21 @@ To configure your application as a `radiks` client, do the following: After you have added Radiks to your application, build and run the application. Test the application by logging in with your Blockstack ID. Create some data using the application. If you inspect the MongoDB database, you should see the encrypted data stored in the database. + +You can specify the `mongoDBUrl` or the `maxLimit` option when initiating the Radiks server in your application. + +```javascript +const { setup } = require('radiks-server'); + +setup({ + ...myOptions, +}); +``` + +The `mongoDBUrl` option is the MongoDB URL for the Radiks server +The `maxLimit` option is the maximum `limit` field used inside the mongo queries. The default is 1000. + + ## Where to go next Creating models for your application's data is where radiks truly becomes helpful. To learn how to use models, see the [Create and use models](radiks-models.html) section. \ No newline at end of file From d2737e10c5ace42cb1336d9e030067adc5d39768 Mon Sep 17 00:00:00 2001 From: Mary Anthony Date: Wed, 29 Jan 2020 22:11:01 -0800 Subject: [PATCH 4/5] Entering comments from talk with Hank Signed-off-by: Mary Anthony --- _develop/radiks-intro.md | 13 ++++--------- _develop/radiks-models.md | 28 +++++++++++----------------- _develop/radiks-server-extras.md | 2 +- 3 files changed, 16 insertions(+), 27 deletions(-) diff --git a/_develop/radiks-intro.md b/_develop/radiks-intro.md index 282b33a7..28dcc68e 100644 --- a/_develop/radiks-intro.md +++ b/_develop/radiks-intro.md @@ -6,22 +6,20 @@ permalink: /:collection/:path.html The Blockstack Radiks feature enables Blockstack decentralized applications (DApps) to index and store across data belonging to multiple users. Radiks works with Blockstack's Gaia Storage System. Using Radiks, you can build multi-player DApps that: -- index, store, and query complex application data +- index, store, and query application data - query a user's publicly saved data - display real-time updates that reflect in progress changes - support collaboration among sets of users -{% include question.html content="I added the word application to the complex data bullet above. Your origianl text seemed to make a distinction between publicly saved data and complex data. I assumed applciation data was the something Radiks could store and that was intended here. Was this your intent?"%} - ## Why use Radiks? Many applications server data that users create to share publicly with others, Facebook, Twitter, and Instagram are examples of such applications. Decentralized applications that want to create comparable multi-user experiences must ensure that anything a user creates for public sharing is likewise still under control the creator in the user's Gaia storage. -For example, if Twitter were a decentralized application where many different users creating their own tweets and those tweets are stored in each user's own Gaia storage. In such a situation, developer need a way to track of everyone's tweets, display tweets in user timelines, and perform searches across tweets. Radiks exists to support these kind of scenarios in that it allows applications to query across multiple user data, using complicated queries like text search, joins, and filters. Radiks allows applications to query data in a performant and flexible way. +For example, if Twitter were a decentralized application where many different users creating their own tweets and those tweets are stored in each user's own Gaia storage. In such a situation, developer need a way to track of everyone's tweets, display tweets in user timelines, and perform searches across tweets. Radiks exists to support these kind of scenarios in that it allows applications to query across multiple user data, using complicated queries like text search, joins, and filters. -{% include question.html content="Can one Radiks server support multiple client applications or is a single server required for each application?"%} +Radiks allows applications to query data in a performant and flexible way. Each application that wishes to index and query in this way requires its own Radiks server. ## How Radiks works with application data @@ -31,10 +29,7 @@ Radiks consists of a database, a pre-built server, and a client. Each applicatio 2. Saves a raw JSON of this encrypted data in the user's Gaia storage. 3. Stores the encrypted data on the Radiks server. -{% include question.html content="You didn't state this in the original but I am assuming the data is user data not just general application data. Is this correct? See the following paragraph which was taken from your server docs, in particular you had the last sentence regarding unencrypted data. Still isn't clear to me when is data in unencrypted and how it is identified from data the user wants signed." %} - -Radiks stores both public data and sensitive, non-private data. Radiks encrypts sensitive data before it leaves the client. Your application can query Radiks for public data and then decrypt the sensitive information on the client. This means that the server is only able to return queries for unencrypted data. - +Radiks stores both public data and sensitive, non-private data. Radiks encrypts sensitive data by default before the data leaves the client. Your application can query Radiks for public data and then decrypt the sensitive information on the client. This means that the Radiks server is only able to return queries for unencrypted data. ## How Radiks authorizes writes diff --git a/_develop/radiks-models.md b/_develop/radiks-models.md index e01d2a5d..aee39e95 100644 --- a/_develop/radiks-models.md +++ b/_develop/radiks-models.md @@ -20,9 +20,7 @@ import { Model, User } from 'radiks'; Then, create a class that extends this model, and provide a schema. Refer to the Model class in the `radiks` repo to get an overview of the class functionality. -Your new class must define a static `className` property manually. This property is used when storing and querying information. If you don't add a `className`, Radiks defaults to the actual model's class name. In production, your code should be minified, and the actual class name will be different. For this reason, it's highly recommended that you define the `className` manually. - -{% include question.html content="The sentence with the statement that the actual class name will be different doesn't parse. The actual class name will be different from the model class name? the className as defined --- what? Also, what do you mean by defining the class name manually -- you mean in the code as opposed to default to the model class name"%} +Your new class must define a static `className` property. This property is used when storing and querying information. If you fail to add a `className`, Radiks defaults to the actual model's class name (`foobar.ts`) and your application will behave unpredictably. The example class code extends `Model` to create a class named `Todo`: @@ -134,12 +132,12 @@ In this section, you learn how to use a model you have defined. All model instances have an `_id` attribute. An `_id` is used as a primary key when storing data, and is used for fetching a model. Radiks also creates a `createdAt` and `updatedAt` property when creating and saving models. -If, when constructing a model, you don't pass an `_id`, Radiks creates an `_id` for you automatically. This automatically created id uses the [`uuid/v4`](https://github.com/kelektiv/node-uuid) format. +If, when constructing a model's instance, you don't pass an `_id`, Radiks creates an `_id` for you automatically. This automatically created id uses the [`uuid/v4`](https://github.com/kelektiv/node-uuid) format. This automatic `_id` is returned by the constructor. ### Construct a model instance -To create an instance of a model, pass some attributes to the constructor of that class: +To create an instance, pass some attributes to the constructor of that class: ```javascript const person = new Person({ @@ -149,24 +147,20 @@ const person = new Person({ }) ``` -{% include question.html content="I don't see an example of the _id attribute in the above construction. How does the user get the id to pass to findByID below then?"%} - ### Fetch an instance -To fetch an existing instance of a model, you need the instance's `id` property. Then, call the `fetch()` function, which returns a promise. +To fetch an existing instance of an instance, you need the instance's `id` property. Then, call the `findById()` method or the `fetch()` method, which returns a promise. ```javascript const person = await Person.findById('404eab3a-6ddc-4ba6-afe8-1c3fff464d44'); ``` -{% include question.html content="You mention calling fetch but your example uses findById. it looks like either will work but do you want to use fetch in this example?"%} - -After calling `fetch`, Radiks automatically decrypts all encrypted fields. +After calling these methods, Radiks automatically decrypts all encrypted fields. ### Access attributes -Other than `id`, all attributes are stored in an `attrs` property on the model. +Other than `id`, all attributes are stored in an `attrs` property on the instance. ```javascript const { name, likesDogs } = person.attrs; @@ -175,7 +169,7 @@ console.log(`Does ${name} like dogs?`, likesDogs); ### Update attributes -To quickly update multiple attributes of a model, pass those attributes to the `update` function. +To quickly update multiple attributes of an instance, pass those attributes to the `update` method. ```javascript const newAttributes = { @@ -185,11 +179,11 @@ const newAttributes = { person.update(newAttributes) ``` -Note that calling `update` does **not** save the model. +Important, calling `update` does **not** save the instance. ### Save changes -To save a model to Gaia and MongoDB, call the `save()` method which returns a promise. This method encrypts all attributes that do not have the `decrypted` option in their schema. Then, it saves a JSON representation of the model in Gaia, as well as in the MongoDB. +To save an instance to Gaia and MongoDB, call the `save()` method which returns a promise. This method encrypts all attributes that do not have the `decrypted` option in their schema. Then, it saves a JSON representation of the model in Gaia, as well as in the MongoDB. ```javascript await person.save(); @@ -197,7 +191,7 @@ await person.save(); ### Delete an instance -To delete a model, just call the `destroy` method on it. +To delete an instance, just call the `destroy` method on it. ```javascript await person.destroy(); @@ -251,7 +245,7 @@ const dogHaters = await Person.count({ likesDogs: false }); ## Fetch models created by the current user -Use the `fetchOwnList` method to find models that were created by the current user. By using this method, you can preserve privacy, because Radiks uses a `signingKey` that only the current user knows. +Use the `fetchOwnList` method to find instances that were created by the current user. By using this method, you can preserve privacy, because Radiks uses a `signingKey` that only the current user knows. ```javascript const tasks = await Task.fetchOwnList({ diff --git a/_develop/radiks-server-extras.md b/_develop/radiks-server-extras.md index 28901853..a97a66b1 100644 --- a/_develop/radiks-server-extras.md +++ b/_develop/radiks-server-extras.md @@ -2,7 +2,7 @@ layout: learn permalink: /:collection/:path.html --- -# Radiks server Tips and tricks +# Radiks server tips and tricks In this section, you'll find some tips and tricks you can use to work with a Radiks server. From 8b82873a85e156e1c10b720c58abe0f73b0eb1a2 Mon Sep 17 00:00:00 2001 From: Mary Anthony Date: Thu, 30 Jan 2020 09:42:06 -0800 Subject: [PATCH 5/5] Adding in copy check Signed-off-by: Mary Anthony --- _data/navigation_learn.yml | 2 +- _develop/radiks-collaborate.md | 6 +++--- _develop/radiks-intro.md | 18 +++++++++--------- _develop/radiks-models.md | 12 ++++++------ _develop/radiks-server-extras.md | 2 +- _develop/radiks-setup.md | 22 +++++++++++----------- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/_data/navigation_learn.yml b/_data/navigation_learn.yml index 3fd88578..c92e71bb 100644 --- a/_data/navigation_learn.yml +++ b/_data/navigation_learn.yml @@ -17,7 +17,7 @@ - develop/collections - develop/collection-type -- title: Sharing data +- title: Radiks data docs: - develop/radiks-intro - develop/radiks-setup diff --git a/_develop/radiks-collaborate.md b/_develop/radiks-collaborate.md index 484bcf69..e71f459b 100644 --- a/_develop/radiks-collaborate.md +++ b/_develop/radiks-collaborate.md @@ -5,7 +5,7 @@ permalink: /:collection/:path.html # Collaborate with groups {:.no_toc} -A key feature of Radiks is support for private collaboration between multiple users. Supporting collaboration with client-side encryption and user-owned storage can be complicated but the patterns to implement it are generally the same among most applications. Radiks provides out-of-the box for collaboration, making it easy to build private, collaborative apps. +A key feature of Radiks is support for private collaboration between multiple users. Supporting collaboration with client-side encryption and user-owned storage can be complicated, but the patterns to implement it are generally the same among most applications. Radiks supplies interfaces for collaboration, making it easy to build private, collaborative apps. You use the UserGroup class to build a collaborative group with Radiks. In this section, you learn about this class. @@ -29,7 +29,7 @@ The general workflow for creating a collaborative group that can share and edit As they participate in a group, the group's members can create and update models that are related to the group. These models **must** contain a `userGroupId` attribute used to reference the group. This allows Radiks to know which keys to use for encryption and signing. -When needed, the group admin can remove a user from a group. To remove a user from the group, the admin creates a new private key for signing and encryption. Then, the admin updates the `GroupMembership` of all users _except_ the user they just removed. This is also known as rotating the key. +When needed, the group admin can remove a user from a group. To remove a user from the group, the admin creates a new private key for signing and encryption. Then, the admin updates the `GroupMembership` of all users _except_ the user they just removed. This update-and-remove action is also known as rotating the key. After a key is rotated, all new and updated models must use the new key for signing. Radiks-server validates all group-related models to ensure that they're signed with the most up-to-date key. @@ -39,7 +39,7 @@ This section details the methods on the { * Call code to get your users from firebase * const users = await getUsersFromFirebase(); * OR grab the Firebase JSON file and set users to that value - * How you saved your user data will proably be different than the example below + * How you saved your user data will probably be different than the example below */ const users = { diff --git a/_develop/radiks-setup.md b/_develop/radiks-setup.md index 670c9f6f..a62c0ca0 100644 --- a/_develop/radiks-setup.md +++ b/_develop/radiks-setup.md @@ -16,19 +16,19 @@ Radiks-server is a `node.js` application that uses [MongoDB](https://www.mongodb ### Install and configure MongoDB -In the future, Radiks-server will support various different databases, but right now only MongoDB 3.6 or higher is supported. MongoDB 3.6 and higher contains fixes required for naming patterns in keys. +In the future, Radiks-server will support various different databases, but right now, only MongoDB 3.6 or higher is supported. MongoDB 3.6 and higher contains fixes required for naming patterns in keys. -{% include note.html content="The steps assumes you want to install and run the MongoDB software locally on your workstation for testing and development. If you are deploying for a production application, you would install MongoDB on your application server or on a server connected to it. " %} +{% include note.html content="The steps assume you want to install and run the MongoDB software locally on your workstation for testing and development. If you are deploying for a production application, you will install MongoDB on your application server or on a server connected to it. " %} 1. Download and install MongoDB 3.6 or higher on your workstation. - You can also install MongoDB using your favorite package manager, for example, Homebrew is recommended for macOS. If you are testing on a local workstation, you can use a `docker` image instead of installing locally. + You can also install MongoDB using your favorite package manager; for example, Homebrew is recommended for macOS. If you are testing on a local workstation, you can use a `docker` image instead of installing locally. 2. Start the MongoDB service and verify it is running. 3. On your MongoDB instance, create a database for your application data. - You can use the Mongo shell to do this or you can install the MongoDB Compass software to explore and work with MongoDB data. + You can use the Mongo shell to do this, or you can install the MongoDB Compass software to explore and work with MongoDB data. 4. Create a username/password combination with `root` privileges on your new database. @@ -47,21 +47,21 @@ The easiest way to run `radiks-server` is to use the pre-packaged `node.js` serv ```bash yarn global add radiks-server ``` - The default port for Mongodb is `27017`, your instance may be configured differently. By default, Radiks-server will use `'MongoDB://localhost:27017/radiks-server'` as the `MongoDB_URI` value. This is suitable for local testing, but in production, you'll want to change the hostname and possible the database name. + The default port for Mongodb is `27017`; your instance may be configured differently. By default, Radiks-server will use `'MongoDB://localhost:27017/radiks-server'` as the `MongoDB_URI` value. This is suitable for local testing, but in production, you'll want to change the hostname and possibly the database name. 3. Start the `radiks-server` in the command line to confirm your installation. ``` $ radiks-server - (node:37750) DeprecationWarning: current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor. + (node:37750) DeprecationWarning: current Server Discovery and Monitoring engine is deprecated and will be removed in a future version. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor. radiks-server is ready on http://localhost:1260 ``` The `radiks-server` defaults to running on port `1260`. To change the default port, specify the `PORT` environment variable in your environment. -4. By default the server is running at `http://localhost:1260` +4. By default, the server is running at `http://localhost:1260` -4. Stop the `radiks` server process after you confirm it runs and your installation was a success. +4. Stop the `radiks` server process after you confirm it runs, and your installation was a success. ## Task 2. Set up your application @@ -72,7 +72,7 @@ You must set up your application to use Radiks. This requires installing the `ra If you are using `blockstack.js` version 18 or earlier, you must use the Radiks version 0.1.\*, otherwise if you're using `blockstack.js` version 19 or higher, use Radiks 0.2.\* . 1. Change directory to the root of you application code. -2. Install the install the `radiks` client package. +2. Install the `radiks` client package. @@ -141,9 +141,9 @@ If you are using `blockstack.js` version 18 or earlier, you must use the Radiks ## Task 3. Add startup code and build your application -To set up radiks.js, you only need to configure the URL that your Radiks-server instance is running on. If you're using the pre-built Radiks server, this will be `http://localhost:1260`. If you're in production or are using a custom Radiks server, you'll need to specify exactly which URL it's available at. +To set up radiks.js, you only need to configure the URL that your Radiks-server instance is running on. If you're using the pre-built Radiks server, this will be `http://localhost:1260`. If you're in production or are using a custom Radiks server, you'll need to specify the exact URL where it's available. -Radiks also is compatible with version 19 of blockstack.js, which requires you to configure a `UserSession` object to handle all user-data-related methods. You'll need to define this and pass it to your Radiks configuration, so that Radiks can know how to fetch information about the current logged in user. +Radiks also is compatible with version 19 of blockstack.js, which requires you to configure a `UserSession` object to handle all user-data-related methods. You'll need to define this and pass it to your Radiks configuration so that Radiks can know how to fetch information about the current logged in user. ### Configure your application to use your `radiks-server`.