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.
The command line is intended for developers only. Developers can use the command
line to test and debug Blockstack applications in ways that the Blockstack
Browser does not yet support. Using the command line, developers can:
- Generate and Broadcast all supported types of Blockstack transactions
- Load, store, and list data in Gaia hubs
- Generate owner, payment and application keys from a seed phrase
- Query Stacks Nodes
- Implement a minimum viable authentication flow
{% include warning.html content="Many of the commands operate on unencrypted
private keys. For this reason, DO NOT use this tool for day-to-day tasks as you
risk the security of your keys." %}
You must <ahref="#installCommandLine">install the command line</a> before you
can use the commands.
## List of commands
{:.no_toc}
To see the usage and options for the command in general, enter `blockstack-cli` without any subcommands. To see a list of subcommands enter `blockstack-cli help`. Enter `blockstack-cli SUBCOMMAND_NAME help` to see a subcommand with its usage. The following are the available subcommands:
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:
1. <ahref="https://github.com/blockstack/cli-blockstack"target="\_blank">Download or `git clone` the command line repository code</a>.
Downloading or cloning the repo creates a `cli-blockstack` repository on your system.
2. Change directory into the `cli-blockstack` directory.
```
cd cli-blockstack
```
```
3. Install the dependencies with `npm`.
```
npm install
```
4. Build the command line command.
```
npm run build
```
5. Link the command.
```
sudo npm link
```
### Troubleshooting the CLI installation
If you run into `EACCES` permissions errors, try the following:
* See https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally.
* Use [`Node Version Manager`](https://github.com/nvm-sh/nvm).
description: Single-page application with Blockstack
redirect_from:
- /develop/zero_to_dapp_1.html
- /browser/hello-blockstack.html
---
# Tutorial for App Integration
# Integrating Blockstack
In this tutorial, you will learn about Blockstack authentication and storage by installing, running and reviewing the code for a "Todos" web app built with Blockstack and [React](https://reactjs.org/).
In this tutorial, you will learn about Blockstack authentication and storage by installing,
running and reviewing the code for a "Todos" web app built with Blockstack and [React](https://reactjs.org/).
This app highlights the following platform functionality:
@ -20,157 +22,196 @@ This app highlights the following platform functionality:
Existing familiarity with React is recommended for reviewing this app's code.
### Install and run the app
## Install and run the app
You must have recent versions of Git and [Node.js](https://nodejs.org/en/download/) (v12.10.0 or greater) installed already.
You must have recent versions of Git and [Node.js](https://nodejs.org/en/download/)
3. Open your local browser to [`http://localhost:3000`](http://localhost:3000) if it doesn't open automatically.
### Step 3: Open your local browser to [`http://localhost:3000`](http://localhost:3000) if it doesn't open automatically.
You should see the app's landing page:
You should see the app's landing page:
![](images/todos-home.png)
![](images/todos-home.png)
### Onboard into your first Blockstack app
## Onboard into your first Blockstack app
1. Choose **Get started** to start onboarding into the app.
### Step 1: Choose **Get started** to start onboarding into the app.
The app displays a standardized introductory modal using [Blockstack Connect](https://github.com/blockstack/ux/tree/master/packages/connect), a JavaScript library that makes it easy to integrate Blockstack into the UI of any web app.
The app displays a standardized introductory modal using
[Blockstack Connect](https://github.com/blockstack/ux/tree/master/packages/connect), a JavaScript
library that makes it easy to integrate Blockstack into the UI of any web app.
![](images/todos-intro.png)
![](images/todos-intro.png)
Below, you can see the relevant parts of the [React component](https://reactjs.org/docs/react-component.html) that triggers this modal in [`src/components/Signin.jsx`](https://github.com/blockstack/blockstack-todos/blob/master/src/components/Signin.jsx):
Below, you can see the relevant parts of the [React component](https://reactjs.org/docs/react-component.html)
that triggers this modal in [`src/components/Signin.jsx`](https://github.com/blockstack/blockstack-todos/blob/master/src/components/Signin.jsx):
This component imports the [React hook](https://reactjs.org/docs/hooks-overview.html) [`useConnect`](https://github.com/blockstack/ux/blob/master/packages/connect/src/react/hooks/use-connect.ts) from the Blockstack Connect library.
`useConnect` returns many helper functions such as [`doOpenAuth`](https://github.com/blockstack/ux/blob/master/packages/connect/src/react/hooks/use-connect.ts#L33), which triggers this modal upon click of the "Get started" button.
This component imports the [React hook](https://reactjs.org/docs/hooks-overview.html)
The modal is designed to prepare new users for a different type of relationship with Blockstack apps, one in which they authenticate with a _Secret Key_ that's used to encrypt their private data.
`useConnect` returns many helper functions such as
which triggers this modal upon click of the "Get started" button.
The modal displays the app's name and icon as configured in [`src/components/App.jsx`](https://github.com/blockstack/blockstack-todos/blob/master/src/components/App.jsx#L26):
The modal is designed to prepare new users for a different type of relationship with
Blockstack apps, one in which they authenticate with a _Secret Key_ that's used to encrypt
their private data.
```js
The modal displays the app's name and icon as configured in
This component loads the [`UserSession`](https://blockstack.github.io/blockstack.js/classes/usersession.html) module from a second Blockstack library called [blockstack.js](https://github.com/blockstack/blockstack.js/), which complements Blockstack Connect by providing an API for many protocol-level operations, such as for authentication and storage.
```
```js
import { UserSession } from 'blockstack';
import { appConfig } from '../assets/constants';
This component loads the [`UserSession`](https://blockstack.github.io/blockstack.js/classes/usersession.html)
module from a second Blockstack library called [blockstack.js](https://github.com/blockstack/blockstack.js/),
which complements Blockstack Connect by providing an API for many protocol-level operations, such as for
authentication and storage.
// ...
```js
import { UserSession } from 'blockstack';
import { appConfig } from '../assets/constants';
const userSession = new UserSession({ appConfig });
```
// ...
This module handles user session operations and is initiated using the [`appConfig`](https://github.com/blockstack/blockstack-todos/blob/master/src/assets/constants.js#L3) object, which contains an array of [scopes](/develop/overview_auth.html#scopes) that indicate just what permissions to grant during authentication:
const userSession = new UserSession({ appConfig });
```
```js
export const appConfig = new AppConfig(['store_write', 'publish_data']);
```
This module handles user session operations and is initiated using the
which contains an array of [scopes](/develop/overview_auth.html#scopes) that indicate just what permissions
to grant during authentication:
The `appDetails` and `userSession` objects are joined by the callback function [`finished`](https://github.com/blockstack/blockstack-todos/blob/master/src/components/App.jsx#L31) in configuring Blockstack Connect for authentication with the `authOptions` object:
in configuring Blockstack Connect for authentication with the `authOptions` object:
This function simply saves data about the user into the app's state upon authentication.
```js
// src/components/App.jsx
Further down in the component we see in [`componentDidMount`](https://github.com/blockstack/blockstack-todos/blob/master/src/components/App.jsx#L46) that it checks upon mount to either process completion of authentication with `userSession.handlePendingSignIn()` or otherwise load session data into app state as above with `userSession.isUserSignedIn()`:
2) Choose **Get started** to generate a _Secret Key_.
### Step 2: Choose **Get started** to generate a _Secret Key_.
The app triggers a popup window in which [the Blockstack App](https://github.com/blockstack/ux/tree/master/packages/app) is loaded from [`app.blockstack.org`](http://app.blockstack.org/) and begins generating a new _Secret Key_.
The app triggers a popup window in which [the Blockstack App](https://github.com/blockstack/ux/tree/master/packages/app)
is loaded from [`app.blockstack.org`](http://app.blockstack.org/) and begins generating a new _Secret Key_.
![](images/todos-generation.svg)
![](images/todos-generation.svg)
3) Choose **Copy Secret Key** to copy your _Secret Key_ to the clipboard.
### Step 3: Choose **Copy Secret Key** to copy your _Secret Key_ to the clipboard.
The _Secret Key_ is a unique 12-word [mnemonic phrase](https://en.bitcoinwiki.org/wiki/Mnemonic_phrase) that empowers the user not only to access Blockstack apps securely and independently. It's also used to encrypt all of the private data they create and manage with Blockstack apps.
The _Secret Key_ is a unique 12-word [mnemonic phrase](https://en.bitcoinwiki.org/wiki/Mnemonic_phrase) that
empowers the user not only to access Blockstack apps securely and independently. It's also used to encrypt
all of the private data they create and manage with Blockstack apps.
_Secret Keys_ are like strong passwords. However, they can never be recovered if lost or reset if stolen. As such, it's paramount that users handle them with great care.
_Secret Keys_ are like strong passwords. However, they can never be recovered if lost or reset if stolen.
As such, it's paramount that users handle them with great care.
![](images/todos-copy-secret-key.svg)
![](images/todos-copy-secret-key.svg)
4) Choose **I've saved it** to confirm you've secured your _Secret Key_ in a suitable place.
### Step 4: Choose **I've saved it** to confirm you've secured your _Secret Key_ in a suitable place.
![](images/todos-ive-saved-it.svg)
![](images/todos-ive-saved-it.svg)
5) Enter a username value and choose **Continue**
### Step 5: Enter a username value and choose **Continue**
The username will be used by the app to generate a URL for sharing your todos, should you choose to make them public.
The username will be used by the app to generate a URL for sharing your todos, should you choose to make them public.
It is registered on the Stacks blockchain with the [Blockstack Naming System (BNS)](/core/naming/introduction.html) and associated with your _Secret Key_.
It is registered on the Stacks blockchain with the [Blockstack Naming System (BNS)](/core/naming/introduction)
and associated with your _Secret Key_.
![](images/todos-username.svg)
![](images/todos-username.svg)
6) You've now completed onboarding into the app!
### Done: You've now completed onboarding into the app!
### Add, edit and delete todos privately
## Add, edit and delete todos privately
Once you've authenticated the app, you can can start adding todos by entering values into the "Write your to do" field and hitting "Enter".
Once you've authenticated the app, you can can start adding todos by entering values into the "Write your to do"
field and hitting "Enter".
![](images/todos-home-authenticated.svg)
The data for all todos are saved as JSON to the Gaia hub linked to your Secret Key using the [`putFile`](http://blockstack.github.io/blockstack.js/globals.html#putfile) method of the `userSession` object in the [`src/assets/data-store.js`](https://github.com/blockstack/blockstack-todos/blob/master/src/assets/data-store.js#L26) module:
The data for all todos are saved as JSON to the Gaia hub linked to your Secret Key using the
[`putFile`](http://blockstack.github.io/blockstack.js/globals.html#putfile) method of the `userSession` object in the
These todos are subsequently loaded using the [`getFile`](http://blockstack.github.io/blockstack.js/globals.html#getfile) method of the same object in the same module:
These todos are subsequently loaded using the [`getFile`](http://blockstack.github.io/blockstack.js/globals.html#getfile)
By default, the `putFile` and `getFile` methods automatically encrypt data when saved and decrypt it when retrieved, using the user's Secret Key. This ensures that only the user has the ability to view this data.
By default, the `putFile` and `getFile` methods automatically encrypt data when saved and decrypt it when retrieved,
using the user's Secret Key. This ensures that only the user has the ability to view this data.
When deleting a todo, the same `putFile` method is used to save a new JSON array of todos that excludes the deleted todo.
### Publish your todos publicly
## Publish your todos publicly
Select "Make public" to make your todos accessible to the public for sharing via URL.
@ -206,19 +249,27 @@ This will call `saveTasks` with the `isPublic` parameter set to `true`, which is
The app will now show all of your todos to anyone who visits the URL displayed with your Blockstack username as a suffix.
### Sign out and see your public tasks
## Sign out and see your public tasks
Select "Sign out" to deauthenticate the app with your Blockstack account.
This triggers an event, which [under the hood](https://github.com/blockstack/blockstack-todos/blob/master/src/components/Header.jsx#L47) calls the [`signUserOut` method](https://blockstack.github.io/blockstack.js/classes/usersession.html#signuserout) of the `UserSession` object.
This triggers an event, which
[under the hood](https://github.com/blockstack/blockstack-todos/blob/master/src/components/Header.jsx#L47)
calls the [`signUserOut` method](https://blockstack.github.io/blockstack.js/classes/usersession.html#signuserout)
of the `UserSession` object.
Now, visit the URL that was provided to you when you made your tasks public. This url is of the format `/todos/:username`, so if your username is `jane_doe.id.blockstack`, the URL would be [`localhost:3000/todos/jane_doe.id.blockstack`](http://localhost:3000/todos/jane_doe.id.blockstack).
Now, visit the URL that was provided to you when you made your tasks public. This url is of the format `/todos/:username`,
so if your username is `jane_doe.id.blockstack`, the URL would be
When you visit this page, the `TodoList.jsx` component detects that there is a username in the URL. When there is a username, it calls `fetchTasks`, this time providing the `username` argument. This `username` option is then passed to `getFile`, which will lookup where that user's tasks are stored.
When you visit this page, the `TodoList.jsx` component detects that there is a username in the URL.
When there is a username, it calls `fetchTasks`, this time providing the `username` argument. This `username`
option is then passed to `getFile`, which will lookup where that user's tasks are stored.
### Sign back in
## Sign back in
At this point, you will be logged out from the app but not you'll still have an active session with the Blockstack app itself on [app.blockstack.org](https://app.blockstack.org). Navigate to app.blockstack.org and select "Sign out" there if you want to deauthenticate the Blockstack app as well.
At this point, you will be logged out from the app but not you'll still have an active session with the Blockstack
app itself on [app.blockstack.org](https://app.blockstack.org). Navigate to app.blockstack.org and select "Sign out" there if you want to deauthenticate the Blockstack app as well.
Once signed out, select "Sign in" to sign back in with your _Secret Key_.
@ -228,7 +279,8 @@ If you've previously deauthenticated the Blockstack app, you'll see a prompt to
The above screen will be ommitted if you have an active session with the Blockstack app already.
Then you'll be presented with the option to select an existing username associated with your _Secret Key_ or create a new one if you wish to authenticate the app with a different identity and data set:
Then you'll be presented with the option to select an existing username associated with your _Secret Key_ or
create a new one if you wish to authenticate the app with a different identity and data set:
![](images/todos-choose-account.svg)
@ -236,4 +288,6 @@ You'll now see your todos as an authenticated user for the username you've chose
## Learn more
Read [the Blockstack Connect guide](/develop/connect/get-started.html) and [the blockstack.js reference](https://blockstack.github.io/blockstack.js/) to learn more about the libraries used in this tutorial.
Read [the Blockstack Connect guide](/develop/connect/get-started) and
[the blockstack.js reference](https://blockstack.github.io/blockstack.js/) to learn more about the
@ -13,7 +13,7 @@ all chunks are available to clients.
This document is aimed at developers and technical users. The following
concepts are discussed:
The reader of this document is expected to be familiar with the [Blockstack Naming Service]({{site.baseurl}}/core/naming/introduction.html)(BNS), as well as Blockstack's
The reader of this document is expected to be familiar with the [Blockstack Naming Service](/core/naming/introduction)(BNS), as well as Blockstack's
storage system [Gaia](https://github.com/blockstack/gaia). We advise the reader
to familiarize themselves with both systems before approaching this document.
import { CLIReferenceTable } from '@components/cli-reference'
# Blockstack CLI reference
The command line is intended for developers only. Developers can use the command
line to test and debug Blockstack applications in ways that the Blockstack
Browser does not yet support. Using the command line, developers can:
- Generate and Broadcast all supported types of Blockstack transactions
- Load, store, and list data in Gaia hubs
- Generate owner, payment and application keys from a seed phrase
- Query Stacks Nodes
- Implement a minimum viable authentication flow
!> Many of the commands operate on unencrypted private keys. For this reason,
DO NOT use this tool for day-to-day tasks as you risk the security of your keys.
You must install the command line before you can use the commands.
## How to install the command line
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:
### Step 1: [Download or `git clone` the command line repository code](https://github.com/blockstack/cli-blockstack).
Downloading or cloning the repo creates a `cli-blockstack` repository on your system.
### Step 2: Change directory into the `cli-blockstack` directory.
```bash
cd cli-blockstack
```
### Step 3: Install the dependencies with `npm`.
```bash
npm install
```
### Step 4: Build the command line command.
```bash
npm run build
```
### Step 5: Link the command.
```bash
sudo npm link
```
### Troubleshooting the CLI installation
If you run into `EACCES` permissions errors, try the following:
- See https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally.
- Use [`Node Version Manager`](https://github.com/nvm-sh/nvm).
## List of commands
To see the usage and options for the command in general, enter `blockstack-cli` without any subcommands. To see a list of subcommands enter `blockstack-cli help`. Enter `blockstack-cli SUBCOMMAND_NAME help` to see a subcommand with its usage. The following are the available subcommands:
This document lists frequently-asked questions developers about Blockstack application development. If you are new to Blockstack, you should read the [general questions]({{site.baseurl}}/faqs/allFAQs.html) first.
This document lists frequently-asked questions developers about Blockstack application development. If you are new to Blockstack, you should read the [general questions](/faqs/allFAQs) first.
For more technical FAQs about Stacks nodes, the Stacks blockchain, and other architectural elements, see the [entire set of technical FAQs]({{site.baseurl}}/core/faq_technical.html).
For more technical FAQs about Stacks nodes, the Stacks blockchain, and other architectural elements, see the [entire set of technical FAQs](/core/faq_technical).
If you have a technical question that gets frequently asked on the
[forum](https://forum.blockstack.org) or [Slack](https://blockstack.slack.com),
This document lists frequently-asked questions by developers interested in working with Blockstack application and core components. If you are new to Blockstack, you should read the [general questions]({{site.baseurl}}/faqs/allFAQs.html) first.
This document lists frequently-asked questions by developers interested in working with Blockstack application and core components. If you are new to Blockstack, you should read the [general questions](/faqs/allFAQs) first.
If you have a technical question that gets frequently asked on the
[forum](https://forum.blockstack.org) or [Slack](https://blockstack.slack.com),
@ -4,7 +4,7 @@ description: 'Blockstack naming service (BNS)'
# Creating a Namespace
Making a namespace is very expensive. Given the large amount of cryptocurrency at stake in name creation, developers wanting to create their own namespaces should read [Understand Namespaces]({{ site.baseurl }}/core/naming/namespaces.html) first. You should also read this document thoroughly before creating a namespace.
Making a namespace is very expensive. Given the large amount of cryptocurrency at stake in name creation, developers wanting to create their own namespaces should read [Understand Namespaces](/core/naming/namespaces) first. You should also read this document thoroughly before creating a namespace.
A recent consensus hash is required to create a `NAMESPACE_PREORDER` transaction. The reference
BNS clients do this automatically. See the [transaction format]({{ site.baseurl }}/core/wire-format.html)
document for details on how the consensus hash is used to construct the
transaction.
BNS clients do this automatically. See the [transaction format](/core/wire-format)
document for details on how the consensus hash is used to construct the transaction.
## Create a namespace
There are four steps to creating a namespace:
1. **Send a `NAMESPACE_PREORDER` transaction** ([live example](https://www.blocktrail.com/BTC/tx/5f00b8e609821edd6f3369ee4ee86e03ea34b890e242236cdb66ef6c9c6a1b28)).
This is the first step. This registers the _salted hash_ of the namespace with BNS nodes, and burns the
requisite amount of cryptocurrency. In addition, it proves to the
BNS nodes that user has honored the BNS consensus rules by including
a recent _consensus hash_ in the transaction
(see the section on [BNS forks](#bns-forks) for details).
2. **Send a `NAMESPACE_REVEAL` transaction** ([live example](https://www.blocktrail.com/BTC/tx/ab54b1c1dd5332dc86b24ca2f88b8ca0068485edf0c322416d104c5b84133a32)).
This is the second step. This reveals the salt and the namespace ID (pairing it with its
`NAMESPACE_PREORDER`), it reveals how long names last in this namespace before
they expire or must be renewed, and it sets a _price function_ for the namespace
that determines how cheap or expensive names its will be. The price function takes
a name in this namespace as input, and outputs the amount of cryptocurrency the
name will cost (i.e. by examining how long the name is, and whether or not it
has any vowels or non-alphabet characters). The namespace creator
has the option to collect name registration fees for the first year of the
namespace's existence by setting a _namespace creator address_.
3. **Seed the namespace with `NAME_IMPORT` transactions** ([live example](https://www.blocktrail.com/BTC/tx/c698ac4b4a61c90b2c93dababde867dea359f971e2efcf415c37c9a4d9c4f312)).
Once the namespace has been revealed, the user has the option to populate it with a set of
names. Each imported name is given both an owner and some off-chain state.
This step is optional---namespace creators are not required to import names.
4. **Send a `NAMESPACE_READY` transaction** ([live example](https://www.blocktrail.com/BTC/tx/2bf9a97e3081886f96c4def36d99a677059fafdbd6bdb6d626c0608a1e286032)).
This is the final step of the process. It _launches_ the namespace, which makes it available to the
public. Once a namespace is ready, anyone can register a name in it if they
pay the appropriate amount of cryptocurrency (according to the price funtion
revealed in step 2).
### There are four steps to creating a namespace:
#### Step 1: Send a `NAMESPACE_PREORDER` transaction
This is the first step. This registers the _salted hash_ of the namespace with BNS nodes, and burns the
requisite amount of cryptocurrency. In addition, it proves to the BNS nodes that user has honored the
BNS consensus rules by including a recent _consensus hash_ in the transaction (see the section on
[BNS forks](#bns-forks) for details).
See `NAMESPACE_PREORDER` ([live example](https://www.blocktrail.com/BTC/tx/5f00b8e609821edd6f3369ee4ee86e03ea34b890e242236cdb66ef6c9c6a1b28)).
#### Step 2: Send a `NAMESPACE_REVEAL` transaction
This is the second step. This reveals the salt and the namespace ID (pairing it with its
`NAMESPACE_PREORDER`), it reveals how long names last in this namespace before
they expire or must be renewed, and it sets a _price function_ for the namespace
that determines how cheap or expensive names its will be. The price function takes
a name in this namespace as input, and outputs the amount of cryptocurrency the
name will cost (i.e. by examining how long the name is, and whether or not it
has any vowels or non-alphabet characters). The namespace creator
has the option to collect name registration fees for the first year of the
namespace's existence by setting a _namespace creator address_.
See `NAMESPACE_REVEAL` ([live example](https://www.blocktrail.com/BTC/tx/ab54b1c1dd5332dc86b24ca2f88b8ca0068485edf0c322416d104c5b84133a32)).
#### Step 3: Seed the namespace with `NAME_IMPORT` transactions
Once the namespace has been revealed, the user has the option to populate it with a set of
names. Each imported name is given both an owner and some off-chain state.
This step is optional---namespace creators are not required to import names.
See `NAME_IMPORT` ([live example](https://www.blocktrail.com/BTC/tx/c698ac4b4a61c90b2c93dababde867dea359f971e2efcf415c37c9a4d9c4f312)).
#### Step 4: Send a `NAMESPACE_READY` transaction
This is the final step of the process. It _launches_ the namespace, which makes it available to the
public. Once a namespace is ready, anyone can register a name in it if they
pay the appropriate amount of cryptocurrency (according to the price funtion
revealed in step 2).
See `NAMESPACE_READY` ([live example](https://www.blocktrail.com/BTC/tx/2bf9a97e3081886f96c4def36d99a677059fafdbd6bdb6d626c0608a1e286032)).
---
The reason for the `NAMESPACE_PREORDER/NAMESPACE_REVEAL` pairing is to prevent
frontrunning. The BNS consensus rules require a `NAMESPACE_REVEAL` to be
@ -149,6 +169,6 @@ Once the user issues the `NAMESPACE_PREORDER` and `NAMESPACE_REVEAL`, they have
do this, then the namespace they created disappears (along with all the names
they imported).
Developers wanting to create their own namespaces should read the [namespace creation section](/core/naming/namespaces.html) document. It is highly recommended that
Developers wanting to create their own namespaces should read the [namespace creation section](/core/naming/namespaces) document. It is highly recommended that
developers request individual support before creating their own space, given the large amount of
- **Step 3:** Fetch the data for the .id namespace and respective profiles. Note, you may want to redirect stderr to a file, as there is a lot of debug output.
When the registrar wakes up to prepare a transaction, it packs the queued
@ -146,20 +144,22 @@ GET /status/{subdomain}
The registrar checks if the subdomain has propagated (i.e., the
registration is completed), in which case the following is returned:
```
{"status": "Subdomain already propagated"}
```json
{"status": "Subdomain already propagated"}
```
Or, if the subdomain has already been submitted in a transaction:
```
{"status": "Your subdomain was registered in transaction 09a40d6ea362608c68da6e1ebeb3210367abf7aa39ece5fd57fd63d269336399 -- it should propagate on the network once it has 6 confirmations."}
```json
{
"status": "Your subdomain was registered in transaction 09a40d6ea362608c68da6e1ebeb3210367abf7aa39ece5fd57fd63d269336399 -- it should propagate on the network once it has 6 confirmations."
}
```
If the subdomain still hasn't been submitted yet:
```
{"status": "Subdomain is queued for update and should be announced within the next few blocks."}
```json
{"status": "Subdomain is queued for update and should be announced within the next few blocks."}
```
If an error occurred trying to submit the `UPDATE` transaction, this endpoint will return an error
@ -193,7 +193,7 @@ This means that search is _not_ yet supported.
The lookups work just like normal -- it returns the user's
@ -45,6 +45,6 @@ Note some of the key Clarity language rules and limitations.
## Learning Clarity
The tutorials are ordered from "beginner" to "advanced." Start with the [Hello World tutorial](tutorial.html), then learn how to construct [a counter](tutorial-counter.html), and finally, learn how to [test your smart contracts](tutorial-test.html) using Mocha.
The tutorials are ordered from "beginner" to "advanced." Start with the [Hello World tutorial](tutorial), then learn how to construct [a counter](tutorial-counter), and finally, learn how to [test your smart contracts](tutorial-test) using Mocha.
Once you've got the hang of the general workflow, environment, and language syntax, you should be ready to start writing contracts, referring to the [Clarity language reference](clarityRef.html) as you go.
Once you've got the hang of the general workflow, environment, and language syntax, you should be ready to start writing contracts, referring to the [Clarity language reference](clarityRef) as you go.
@ -17,7 +17,7 @@ In this tutorial, you learn how to implement a smart contract that stores and ma
## Prerequisites
Before you get started, you should complete the [Hello World tutorial](tutorial.html).
Before you get started, you should complete the [Hello World tutorial](tutorial).
## Step 1: Downloading counter starter project
@ -36,7 +36,7 @@ You must now select a template. Type `counter` and hit ENTER:
? Template - one of [hello-world, counter]: counter
```
Finally, the project dependencies are installed and your project is ready for development. Because you already completed the [Hello World tutorial](tutorial.html), the project structure is familiar to you. The main difference is that we have additional tests for a new counter smart contract.
Finally, the project dependencies are installed and your project is ready for development. Because you already completed the [Hello World tutorial](tutorial), the project structure is familiar to you. The main difference is that we have additional tests for a new counter smart contract.
## Step 2: Running tests
@ -75,7 +75,7 @@ Let's get familiar with the tests to understand what the new smart contract shou
1. Take a quick look at the test file associated with the counter smart contract:
```shell
```bash
cat test/counter.ts
```
@ -112,7 +112,7 @@ Let's get familiar with the tests to understand what the new smart contract shou
3. Run the tests and review the results:
```shell
```bash
npm test
```
@ -137,7 +137,7 @@ Let's get familiar with the tests to understand what the new smart contract shou
Done? Great! Run the tests and make sure all of them are passing. You are looking for 4 passed tests:
@ -20,7 +20,7 @@ Clarity, Blockstack's smart contracting language, is based on [LISP](<https://en
To complete the tutorial, you should have [NodeJS](https://nodejs.org/en/download/) installed on your workstation. To install and run the starter project, you need to have at least version `8.12.0`. You can verify your installation by opening up your terminal and run the following command:
@ -11,7 +11,7 @@ description: Get Started Writing Smart Contracts with Clarity
In the world of smart contracts, everything is a blockchain transaction. You use tokens in your wallet to deploy a smart contract in a transaction, and each call to that contract after it's published is a transaction, too. That means that at each step, tokens are being exchanged as transaction fees. This tutorial introduces you to this mode of programming, which transforms blockchains into powerful state machines capable of executing complex logic.
Clarity, Blockstack's smart contracting language, is based on LISP and uses its parenthesized notation. Clarity is an [interpreted language](https://en.wikipedia.org/wiki/Interpreted_language), and [decidable](https://en.wikipedia.org/wiki/Recursive_language). To learn more basics about the language, see the [Introduction to Clarity](overview.html) topic.
Clarity, Blockstack's smart contracting language, is based on LISP and uses its parenthesized notation. Clarity is an [interpreted language](https://en.wikipedia.org/wiki/Interpreted_language), and [decidable](https://en.wikipedia.org/wiki/Recursive_language). To learn more basics about the language, see the [Introduction to Clarity](overview) topic.
By the end of this tutorial, you will:
@ -26,7 +26,7 @@ By the end of this tutorial, you will:
You will need [NodeJS](https://nodejs.org/en/download/) `8.12.0` or higher to complete this tutorial. You can verify your installation by opening up your terminal and run the following command:
```shell
```bash
node --version
```
@ -63,7 +63,7 @@ Select **File** > **Add Folder to Workspace** in VS Code, and add the `hello-wor
You will see that the program and each statement is enclosed in `()` (parentheses), and the smart contract consists of two functions.
```cl
```clarity
(define-public (say-hi)
(ok "hello world"))
@ -156,43 +156,43 @@ The following set of commands will achieve the same goals as the above workflow.
Install an early release of the new Blockstack CLI for Stacks 2.0.
Collections is the feature designed to make data portable among Blockstack applications. Sharing is accomplished by storing a user's data in a standardized format at a known, Gaia storage location. Collections associate user data with a user's decentralized ID. When users move among apps, the same data is available to each application the user authorizes.
Collections is the feature designed to make data portable among Blockstack applications. Sharing is accomplished by
storing a user's data in a standardized format at a known, Gaia storage location. Collections associate user data with
a user's decentralized ID. When users move among apps, the same data is available to each application the user authorizes.
On this page, you learn what collections are and how to use them. You'll learn about the `Contact` collection in particular. The following topics are covered:
On this page, you learn what collections are and how to use them. You'll learn about the `Contact` collection in
particular. The following topics are covered:
{% include note.html content="This is a preview release of the <code>Contact</code> collections. This release allows developers to try out the new collections functionality and we are interested in collecting feedback. Please feel free to report issues or request enhancements with collections or <code>Contacts</code> themselves on the <ahref='https://github.com/blockstack/blockstack-collections/issues/new'target='_blank'>blockstack/blockstack-collections</a> repository. If you encounter problems with <code>blockstack.js</code> you can <ahref='https://github.com/blockstack/blockstack.js/issues/new'target='_blank'>file issues or request enhancements on its repo</a>." %}
> #### Preview release
>
> This is a preview release of the `Contact` collections. This release allows developers to try out the new collections
> functionality and we are interested in collecting feedback. Please feel free to report issues or request enhancements
> with collections or `Contacts` themselves on the [blockstack-collections repository](https://github.com/blockstack/blockstack-collections/issues/new).
>
> If you encounter problems with `blockstack.js` you can [file issues or request enhancements on its repo](https://github.com/blockstack/blockstack.js/issues/new).
## Understand how collections work
One of Blockstack's goals is to give users true data ownership by enabling _data portability_. Data portability allows users to login with their digital ID on any app and have access to the same data. For example, if a user adds a photo of a Hawaiian vacation in one app, that photo enters the user's data pool. Then, when the user opens a second app, that same photo is available to the second app because the user data, including the photo, is shared via the user's decentralized ID.
One of Blockstack's goals is to give users true data ownership by enabling _data portability_. Data portability allows
users to login with their digital ID on any app and have access to the same data. For example, if a user adds a photo of
a Hawaiian vacation in one app, that photo enters the user's data pool. Then, when the user opens a second app, that
same photo is available to the second app because the user data, including the photo, is shared via the user's
decentralized ID.
How do collections work? Blockstack builds a library containing commonly used data schemes. Developers use these classes and objects instead of creating their own, unique data schemes. Using a class from the collections library guarantees class data is stored in Gaia in that format; And, when retrieved, guarantees the same format is returned. This pre-release provides the `Contact` collection. A contact schema produces this structure:
How do collections work? Blockstack builds a library containing commonly used data schemes. Developers use these classes
and objects instead of creating their own, unique data schemes. Using a class from the collections library guarantees
class data is stored in Gaia in that format; And, when retrieved, guarantees the same format is returned. This
pre-release provides the `Contact` collection. A contact schema produces this structure:
```
```json
{
"lastName": "jeffries",
"firstName": "sally",
"blockstackID": "",
"email": "",
"website": "",
"telephone": "",
"identifier": "sally jeffries"
"lastName": "jeffries",
"firstName": "sally",
"blockstackID": "",
"email": "",
"website": "",
"telephone": "",
"identifier": "sally jeffries"
}
```
A collection schema is neither validated or enforced. The goal is to incentivize collection use rather that enforce use.
Because malicious apps or apps with poor security controls may damage user data, Blockstack believes collections should include the ability for users to roll-back changes. For this reason, Blockstack supports an event log and rollback mechanisms in collections. To support this rollback in the pre-release, collections data store is conceptually an event log. Every data write an app makes is stored as a separate file. By placing data in files it ensures that data is never lost and files can be returned back to any previous state.
Because malicious apps or apps with poor security controls may damage user data, Blockstack believes collections should
include the ability for users to roll-back changes. For this reason, Blockstack supports an event log and rollback
mechanisms in collections. To support this rollback in the pre-release, collections data store is conceptually an event
log. Every data write an app makes is stored as a separate file. By placing data in files it ensures that data is never
lost and files can be returned back to any previous state.
##### The Future of Collections Envisioned
<divclass="uk-card uk-card-default uk-card-body">
<h5class="uk-card-title">The Future of Collections Envisioned</h5>
<p>Blockstack believes that collections should enable true data portability across applications for each decentralized ID. The goal is to develop simple user interfaces to allow users to manage of application access and permissions to collection data. For example, in the future, users can rollback data to previous versions using management interfaces.</p>
<p>For developers, collections can incentivize user adoption by reducing user friction. Users can easily try new apps and move to them without the overhead or barrier of re-entering data. You are <ahref="https://forum.blockstack.org/t/feedback-wanted-collections-design/7752"target="_blank">welcome to review and comment</a> on the current design document.</p>
</div>
Blockstack believes that collections should enable true data portability across applications for each decentralized ID.
The goal is to develop simple user interfaces to allow users to manage of application access and permissions to collection
data. For example, in the future, users can rollback data to previous versions using management interfaces.
For developers, collections can incentivize user adoption by reducing user friction. Users can easily try new apps and
move to them without the overhead or barrier of re-entering data. You are [welcome to review and comment](https://forum.blockstack.org/t/feedback-wanted-collections-design/7752)
on the current design document.
## Build a Contact Manager demo app
@ -135,7 +158,7 @@ In this section, you learn how to add `Contact` collection functionality to an e
This scope grants your app permission to read and write to the user’s `Contact` collection.
```javascript
```jsx
import { UserSession, AppConfig, makeAuthRequest } from 'blockstack';
import { Contact } from '`blockstack-collections';
@ -151,7 +174,7 @@ Collection storage was designed around an ORM-like interface. This approach ensu
Then, you can use API methods under the `blockstackConnect` global variable:
```javascript
```jsx
const authOptions = {
/** See docs above for options */
};
@ -158,7 +160,7 @@ To make sure your app handles this gracefully, you'll need to handle the case wh
To finalize authentication with this flow, you'll need to utilize the `UserSession` methods `isSignInPending()` and `handlePendingSignIn()`. For more information, check out the [blockstack.js API reference](https://blockstack.github.io/blockstack.js/).
@ -27,16 +27,18 @@ functions, the default is to request `['store_write']`.
Decentralized apps have a manifest file. This file is based on the [W3C web app manifest specification](https://w3c.github.io/manifest/). The following is an example manifest file.
description: A key feature of Radiks is support for private collaboration between multiple users.
---
# Collaborate with groups
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.
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 <ahref="https://github.com/blockstack/radiks/blob/master/src/models/user-group.ts"target="_blank"><code>UserGroup</code></a> class to build a collaborative group with Radiks. In this section, you learn about this class.
You use the [`UserGroup`](https://github.com/blockstack/radiks/blob/master/src/models/user-group.ts) class to build a
collaborative group with Radiks. In this section, you learn about this class.
## Understand the UserGroup 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 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:
@ -22,21 +29,27 @@ The general workflow for creating a collaborative group that can share and edit
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.
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.
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 update-and-remove action 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.
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 <ahref="https://github.com/blockstack/radiks/blob/master/src/models/user-group.ts"target="_blank"><code>UserGroup</code></a> class you can use to create, add members to, and query a group.
This section details the methods on the [`UserGroup`](https://github.com/blockstack/radiks/blob/master/src/models/user-group.ts)
class you can use to create, add members to, and query a group.
### Create a UserGroup
To create a `UserGroup`, you must import the class into your application from `radiks`:
```javascript
```jsx
import { UserGroup } from 'radiks';
// ...
@ -44,7 +57,7 @@ import { UserGroup } from 'radiks';
Calling `create` on a new `UserGroup` will create the group and activate an invitation for the group's creator.
```javascript
```jsx
const group = new UserGroup({ name: 'My Group Name' });
await group.create();
```
@ -53,9 +66,10 @@ A group's creator is also the group's admin.
### 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 user's `username`.
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
```jsx
import { UserGroup } from 'radiks';
const group = await UserGroup.findById(myGroupId);
@ -66,9 +80,11 @@ console.log(invitation._id); // the ID used to later activate an invitation
#### Generic invitation
You can also create a generic invitation that any user can activate, if they are provided with randomly generated secret key, which should be used to decrypt the invitation. The key is generated when the generic invitation is being created.
You can also create a generic invitation that any user can activate, if they are provided with randomly generated
secret key, which should be used to decrypt the invitation. The key is generated when the generic invitation
is being created.
```javascript
```jsx
import { GenericGroupInvitation, UserGroup } from 'radiks';
const group = await UserGroup.findById(myGroupId);
// Creating generic invitation
@ -81,7 +97,7 @@ console.log(genericInvitation.secretCode); // the secretCode used to later activ
Use the `activate` method on a `GroupInvitation` instance to activate an invitation on behalf of a user:
````javascript
```jsx
import { GroupInvitation, GenericGroupInvitation } from 'radiks';
## View all activated UserGroups for the current user
Call `UserGroup.myGroups` to fetch all groups that the current user is a member of:
```javascript
```jsx
import { UserGroup } from 'radiks';
const groups = await UserGroup.myGroups();
````
```
## 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.
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.
Radiks allows you to model your client data. You can then query this data and display it for a user in multi-player applications. A social application where users want to see the comments of other users is an example of a multi-player application. This page explains how to create a model in your distributed application using Radiks.
Radiks allows you to model your client data. You can then query this data and display it for a
user in multi-player applications. A social application where users want to see the comments of
other users is an example of a multi-player application. This page explains how to create a model
in your distributed application using Radiks.
## Overview of Model class extension
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.
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.
```javascript
```jsx
import { Model, User } from 'radiks';
```
Then, create a class that extends this model, and provide a schema. Refer to <ahref="https://github.com/blockstack/radiks/blob/master/src/model.ts"target="_blank">the <code>Model</code> class</a> in the `radiks` repo to get an overview of the class functionality.
Then, create a class that extends this model, and provide a schema. Refer to
[the `Model` class](https://github.com/blockstack/radiks/blob/master/src/model.ts) in the
`radiks` repo to get an overview of the class functionality.
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.
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`:
```javascript
```jsx
import { Model, User } from 'radiks';
class Todo extends Model {
@ -52,9 +60,10 @@ 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:
Every class must have a static `schema` property which defines the attributes of a model
using field/value pairs, for example:
```javascript
```jsx
class Todo extends Model {
static className = 'Todo';
static schema = {
@ -65,19 +74,28 @@ class Todo extends Model {
}
```
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.
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 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.
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.
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 relationship.
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 relationship.
**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.
**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 defaults
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`.
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
```jsx
import { Model } from 'radiks';
class Person extends Model {
@ -99,13 +117,15 @@ class Person extends Model {
}
```
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.
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.
### Extend the User model
Radiks also supplies <ahref="https://github.com/blockstack/radiks/blob/master/src/models/user.ts"target="_blank">a default <code>User</code> model</a>. You can also extend this model to add your own attributes.
Radiks also supplies [a default `User` model](https://github.com/blockstack/radiks/blob/master/src/models/user.ts).
You can also extend this model to add your own attributes.
```javascript
```jsx
import { User } from 'radiks';
// For example I want to add a public name on my user model
@ -120,7 +140,8 @@ class MyAppUserModel extends User {
}
```
The default `User` model defines a `username`, but you can add a `displayName` to allow the user to set unique name in your app.
The default `User` model defines a `username`, but you can add a `displayName` to allow the user to
set unique name in your app.
## Use a model you have defined
@ -128,15 +149,18 @@ 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.
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'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.
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, pass some attributes to the constructor of that class:
```javascript
```jsx
const person = new Person({
name: 'Hank',
isHuman: false,
@ -146,9 +170,10 @@ const person = new Person({
### Fetch an instance
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.
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
```jsx
const person = await Person.findById('404eab3a-6ddc-4ba6-afe8-1c3fff464d44');
```
@ -158,7 +183,7 @@ After calling these methods, Radiks automatically decrypts all encrypted fields.
Other than `id`, all attributes are stored in an `attrs` property on the instance.
```javascript
```jsx
const { name, likesDogs } = person.attrs;
console.log(`Does ${name} like dogs?`, likesDogs);
```
@ -167,7 +192,7 @@ console.log(`Does ${name} like dogs?`, likesDogs);
To quickly update multiple attributes of an instance, pass those attributes to the `update` method.
```javascript
```jsx
const newAttributes = {
likesDogs: false,
age: 30,
@ -179,9 +204,11 @@ Important, calling `update` does **not** save the instance.
### Save changes
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.
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
```jsx
await person.save();
```
@ -189,23 +216,25 @@ await person.save();
To delete an instance, just call the `destroy` method on it.
```javascript
```jsx
await person.destroy();
```
## 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` package to turn an HTTP query into a MongoDB query.
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.
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 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.
## Count models
You can also get a model's `count` record directly.
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.
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.
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:
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
```jsx
class Project extends Model {
static className = 'Project';
static schema = { name: String }
@ -274,7 +307,7 @@ class Task extends Model {
Whenever you save a task, you should save a reference to the project it's in:
```javascript
```jsx
const task = new Task({
name: 'Improve radiks documentation',
projectId: project._id,
@ -284,15 +317,16 @@ await task.save();
Then, later you'll want to fetch all tasks for a certain project:
```javascript
```jsx
const tasks = await Task.fetchList({
projectId: project._id,
});
```
Radiks lets you define an `afterFetch` method. Use this method 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
@ -9,7 +9,7 @@ In this section, you'll find some tips and tricks you can use to work with a Rad
Radiks-server keeps all models inside of a collection. You can use the `getDB` function to access this collection from inside your application.
```js
```jsx
const { getDB } = require('radiks-server');
const mongo = await getDB(MONGODB_URL);
@ -23,7 +23,7 @@ If you're using an [express.js](https://expressjs.com/) server to run your appli
Radiks-server includes an easy-to-use middleware that you can include in your application:
```javascript
```jsx
const express = require('express');
const { setup } = require('radiks-server');
@ -39,7 +39,7 @@ The `setup` method returns a promise, and that promise resolves to the actual mi
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:
Sometimes, you need to save some data on behalf of the user that only the server is able to see. A common use case for this is when you want to notify a user, and you need to store, for example, their email. This should be updatable only by the user, and only the server (or that user) should be able to see it. Radiks provides the `Central` API to handle this:
@ -47,7 +47,7 @@ The easiest way to run `radiks-server` is to use the pre-packaged `node.js` serv
2. Start the `radiks-server` in the command line to confirm your installation.
```
```bash
$ 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
@ -89,7 +89,7 @@ If you are using `blockstack.js` version 18 or earlier, you must use the Radiks
1. Start the mongo shell application.
```
```bash
$ mongo
MongoDB shell version v4.2.0
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
@ -104,7 +104,7 @@ If you are using `blockstack.js` version 18 or earlier, you must use the Radiks
2. Create a new database for your application.
```
```bash
> show dbs
admin 0.000GB
config 0.000GB
@ -121,7 +121,7 @@ If you are using `blockstack.js` version 18 or earlier, you must use the Radiks
3. Add a user with administrative rights to the database.
@ -189,7 +189,7 @@ After you have added Radiks to your application, build and run the application.
You can specify the `mongoDBUrl` or the `maxLimit` option when initiating the Radiks server in your application.
```javascript
```jsx
const { setup } = require('radiks-server');
setup({
@ -202,4 +202,4 @@ The `maxLimit` option is the maximum `limit` field used inside the mongo queries
## 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.
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) section.
The Blockstack Platform stores application data in the Gaia Storage System. Transactional metadata is stored on the Blockstack blockchain and user application data is stored in Gaia storage. Storing data off of the blockchain ensures that Blockstack applications can provide users with high performance and high availability for data reads and writes without introducing central trust parties.
{% include note.html content="<ul><li>Blockstack Gaia Storage APIs and on-disk format will change in upcoming pre-releases breaking backward compatibility. File encryption is currently opt-in on a file by file basis.</li><li>Certain storage features such as collections are not implemented in the current version. These features will be rolled out in future updates.</li></ul>" %}
> Blockstack Gaia Storage APIs and on-disk format will change in upcoming pre-releases breaking backward compatibility. File encryption is currently opt-in on a file by file basis.
> Certain storage features such as collections are not implemented in the current version. These features will be rolled out in future updates.
## How data is stored
@ -15,68 +16,64 @@ Gaia storage is a key-value store.
You use the <ahref="https://blockstack.github.io/blockstack.js/classes/usersession.html#putfile"target="_blank">UserSession.putFile</a>
You use the <ahref="https://blockstack.github.io/blockstack.js/classes/usersession.html#deletefile"target="_blank">UserSession.deleteFile</a> from the application's data store.
```JavaScript
```jsx
const userSession = new UserSession();
var userSession = new UserSession()
userSession.deleteFile("/hello.txt")
.then(() => {
// /hello.txt is now removed.
})
userSession.deleteFile('/hello.txt').then(() => {
// /hello.txt is now removed.
});
```
## Related Information
To learn more about the guarantees provided by Gaia, see [Storage write and read]({{ site.baseurl }}/storage/write-to-read.html#)
To learn more about the guarantees provided by Gaia, see [Storage write and read](/storage/write-to-read)
This page describes how to use the Stacks Wallet software to manager your Stacks (STX) tokens. This page contains the following topics:
The Stacks Wallet software is installed on your computer, it is not a web application. You should have already [downloaded, verified, and installed the wallet software](wallet-install.html).
The Stacks Wallet software is installed on your computer, it is not a web application. You should have already [downloaded, verified, and installed the wallet software](wallet-install).
@ -4,32 +4,32 @@ description: 'Storing user data with Blockstack'
# Configure a hub on Amazon EC2
This teaches you how to run a Gaia hub on Amazon EC2. Amazon EC2 is an affordable and convenient cloud computing provider. This example uses Amazon EC2 instance together with an [EBS](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AmazonEBS.html) disk for file storage.
<divclass="uk-card uk-card-default uk-card-body">
<h5>Is this tutorial for you?</h5>
<p>This documentation is appropriate for advanced power users who are familiar with command line tools, <code>ssh</code>, and basic editing configuration files.</p>
<p>If you are planning on running an <em>open-membership hub</em> or an <em>application-specific hub</em>, see <ahref="hub-operation.html">the section on Hub Operation</a>.</p>
</div>
This teaches you how to run a Gaia hub on Amazon EC2. Amazon EC2 is an affordable and convenient cloud computing provider.
This example uses Amazon EC2 instance together with an [EBS](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AmazonEBS.html)
disk for file storage.
> #### Is this tutorial for you?
>
> This documentation is appropriate for advanced power users who are familiar with command line tools, `ssh`, and
> basic editing configuration files.
>
> If you are planning on running an _open-membership hub_ or an _application-specific hub_, see
> [the section on Hub Operation](hub-operation).
## Prerequisites you need
This procedure uses Amazon AWS to choose and configure an Amazon Machine Image
(AMI) running a Gaia service. For this reason, you should have an AWS account
on the <ahref="https://aws.amazon.com/free/"target="\_blank">Amazon AWS free
tier</a>, personal account, or corporate account. These instructions assume you
are using a free tier account.
on the [Amazon AWS free tier](https://aws.amazon.com/free/), personal account,
or corporate account. These instructions assume you are using a free tier account.
These instructions assume you have already created a free <a
href="https://www.freenom.com" target="\_blank">domain through the freenom
service</a>. If you have another domain, you can use that instead.
These instructions assume you have already created a free [domain through the freenom service](https://www.freenom.com).
If you have another domain, you can use that instead.
Finally, setting up the SSL certificates on your EC2 instance requires you to use the terminal command line on your workstation. Make sure you have the `watch` command installed using the `which` command.
Finally, setting up the SSL certificates on your EC2 instance requires you to use the terminal
command line on your workstation. Make sure you have the `watch` command installed using the `which` command.
```
```bash
$ which watch
/usr/local/bin/watch
```
@ -38,7 +38,7 @@ If `watch` is not located, install it on your workstation.
## Task 1: Launch an EC2 instance
1. Visit the <ahref="https://aws.amazon.com/free/"target="\_blank">AWS Free Tier page</a> and choose **Sign in to the Console**.
1. Visit the [AWS Free Tier page](https://aws.amazon.com/free/) and choose **Sign in to the Console**.
![](/storage/images/aws-console.png)
@ -67,15 +67,18 @@ If `watch` is not located, install it on your workstation.
You can choose an image that uses **ephemeral** or **EBS** storage. The ephemeral
storage is very small but free. Only choose this if you plan to test or use
a personal hub. Otherwise, choose the AMI for elastic block storage (EBS) which provides a persistent data store on a separate disk backed by [EBS](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AmazonEBS.html).
a personal hub. Otherwise, choose the AMI for elastic block storage (EBS) which provides a persistent data store on
a separate disk backed by [EBS](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AmazonEBS.html).
So, the `blockstack-gaia_hub-ephemeral-2.5.3-hvm - ami-0c8fc48c10a42737e` image uses ephemeral storage, is at version `2.5.3` and has the `0c8fc48c10a42737e` tag.
So, the `blockstack-gaia_hub-ephemeral-2.5.3-hvm - ami-0c8fc48c10a42737e` image uses ephemeral storage, is at
version `2.5.3` and has the `0c8fc48c10a42737e` tag.
6. Select the most recent version image with the storage you want. The images are not sorted; The most recent images is not necessarily at the top of the list.
6. Select the most recent version image with the storage you want. The images are not sorted; The most recent images
is not necessarily at the top of the list.
After you select an image, the system displays **Step 2: Choose an Instance Type** page.
After you select an image, the system displays **Step 2: Choose an Instance Type** page.
![](/storage/images/tier-2-image.png)
![](/storage/images/tier-2-image.png)
7. Select **t2.micro** and choose **Next: Configure Instance Details**.
@ -6,13 +6,13 @@ description: 'Storing user data with Blockstack'
{% include note.html content="The functionality described in this tutorial has been deprecated with the Blockstack Browser. It will continue working only for apps that have not yet upgraded to Blockstack Connect." %}
In this tutorial, you build on the <ahref="{{ site.baseurl }}/browser/hello-blockstack.html"target="\_blank">Hello, Blockstack Tutorial</a>. You'll modify the authentication code so that it prompts users who have not yet created a Blockstack identity, to choose a hub URL.
In this tutorial, you build on the <ahref="/browser/hello-blockstack.html"target="\_blank">Hello, Blockstack Tutorial</a>. You'll modify the authentication code so that it prompts users who have not yet created a Blockstack identity, to choose a hub URL.
{% include note.html content="This tutorial was written on macOS High Sierra 10.13.4. If you use a Windows or Linux system, you can still follow along. However, you will need to \"translate\" appropriately for your operating system. Additionally, this tutorial assumes you are accessing the Blockstack Browser web application via Chrome. The application you build will also work with a local installation and/or with browsers other than Chrome. " %}
## About this tutorial and the prerequisites you need
This tutorial assumes you already set up your environment and tooling as specified in the <ahref="{{ site.baseurl }}/browser/hello-blockstack.html"target="\_blank">Hello, Blockstack Tutorial</a>. You should also review that tutorial for basic information about
This tutorial assumes you already set up your environment and tooling as specified in the <ahref="/browser/hello-blockstack.html"target="\_blank">Hello, Blockstack Tutorial</a>. You should also review that tutorial for basic information about
## Task 1: Generate an initial Blockstack application
@ -148,7 +148,7 @@ To replace the default login, do the following:
2. Locate the `redirectToSignIn()` method at line 4.
3. Replace `redirectToSignIn()` method with the `blockstack.UserSession.redirectToSignInWithAuthRequest(authRequest)` method.
@ -4,7 +4,9 @@ description: 'Storing user data with Blockstack'
# Storage write and read
Once a user authenticates and a DApp obtains authentication, the application interacts with Gaia through the blockstack.js library. There are two simple methods for working with data in Gaia hub: the `putFile()` and `getFile()` methods. This section goes into greater detail about the methods, how they interact with a hub, and how to use them.
Once a user authenticates and a DApp obtains authentication, the application interacts with Gaia through the
blockstack.js library. There are two simple methods for working with data in Gaia hub: the `putFile()` and `getFile()`
methods. This section goes into greater detail about the methods, how they interact with a hub, and how to use them.
## Write-to and Read-from URL Guarantees
@ -21,18 +23,22 @@ be able to read from the `https://myreads.com/foo/bar` URL. Note that, while the
prefix in the write-to url (for example,`myhub.service.org/store`) and the read-from URL
(`https://myreads.com`) are different, the `foo/bar` suffixes are the same.
By default, `putFile()` encrypts information while `getFile()` decrypts it by default. Data stored in an encrypted format means only the user that stored it can view it. For applications that want other users to view data, the application should set the `encrypt` option to `false`. And, corresponding, the `decrypt` option on `getFile()` should also be `false`.
By default, `putFile()` encrypts information while `getFile()` decrypts it by default. Data stored in an
encrypted format means only the user that stored it can view it. For applications that want other users to
view data, the application should set the `encrypt` option to `false`. And, corresponding, the `decrypt`
option on `getFile()` should also be `false`.
Consistent, identical suffixes allow an application to know _exactly_ where a
written file can be read from, given the read prefix. The Gaia service defines a `hub_info` endpoint to obtain that read prefix:
written file can be read from, given the read prefix. The Gaia service defines a `hub_info` endpoint to obtain
that read prefix:
```
```bash
GET /hub_info/
```
The endpoint returns a JSON object with a `read_url_prefix`, for example, if my service returns:
```javascript
```jsx
{ ...,
"read_url_prefix": "https://myservice.org/read/"
}
@ -50,7 +56,9 @@ The application is guaranteed that the profile is written with `putFile()` this
When you use the `putFile()` method it takes the user data and POSTs it to the user's Gaia storage hub. The data POSTs directly to the hub, the blockchain is not used and no data is stored there. The limit on file upload is currently 25mb.
When you use the `putFile()` method it takes the user data and POSTs it to the user's Gaia storage hub.
The data POSTs directly to the hub, the blockchain is not used and no data is stored there. The limit on
file upload is currently 25mb.
## Address-based access-control
@ -61,15 +69,22 @@ authentication token which is a message _signed_ by the private key associated
with that address. The message itself is a challenge text, returned via the
`/hub_info/` endpoint.
Reads can be done by everybody. The URLs to a user's app data are in a canonical location in their profile. For example, here's how you would get data from the
 [Banter](https://banter.pub/) app, stored under the Blockstack ID `gavin.id`.
This data is public and unencrypted. The same works for encrypted data. Only the holder of the private key used for encryption would be able to decrypt the data.