Browse Source

feat: improve data storage guide

feat/build-apps
Mark Hendrickson 4 years ago
parent
commit
b8f8d67c03
  1. 2
      src/pages/build-apps/guides/authentication.md
  2. 224
      src/pages/build-apps/guides/data-storage.md
  3. 2
      src/pages/build-apps/guides/transaction-signing.md

2
src/pages/build-apps/guides/authentication.md

@ -17,7 +17,7 @@ Authentication provides a way for users to identify themselves to an app while r
Users who register for your app can subsequently authenticate to any other app with support for the [Blockchain Naming System](/build-apps/references/bns) and vice versa. Users who register for your app can subsequently authenticate to any other app with support for the [Blockchain Naming System](/build-apps/references/bns) and vice versa.
See [the Todos app tutorial](/build-apps/tutorials/todos) for a concrete example of this functionality in practice. [See the Todos app tutorial](/build-apps/tutorials/todos) for a concrete example of this functionality in practice.
## How it works ## How it works

224
src/pages/build-apps/guides/data-storage.md

@ -15,199 +15,145 @@ This guide explains how to save and retrieve data for users with [Gaia](/build-a
Data storage provides a way for users to save both public and private data off-chain while retaining complete control over it. Data storage provides a way for users to save both public and private data off-chain while retaining complete control over it.
Storing data off of the blockchain ensures that apps can provide users with high performance and high availability for data reads and writes without the involvement of centralized parties that could comprise their privacy or accessibility. Storing data off the Stacks blockchain ensures that apps can provide users with high performance and high availability for data reads and writes without the involvement of centralized parties that could compromise their privacy or accessibility.
See [the Todos app tutorial](/build-apps/tutorials/todos) for a concrete example of this functionality in practice. [See the Todos app tutorial](/build-apps/tutorials/todos) for a concrete example of this functionality in practice.
## Authentication ## Initiate session
TODO: Add indicator that authentication guide should be followed first Users must authenticate to an app before the `storage` package will work to save or retrieve data on their behalf.
## How data is stored [See the authentication guide](/build-apps/guides/authentication) before proceeding to integrate the following data storage capabilities in cases where `userSession.isUserSignedIn()` returns `true`.
Gaia storage is a key-value store. ## Save data for session user
## Creating a file Gaia serves as a key-value store in which data is saved and retrieved as files to and from Gaia hubs owned by, or managed for, users.
Use the [Storage.putFile](https://blockstack.github.io/stacks.js/classes/storage.html#putfile) method: The default Gaia hub for users who authenticate to apps with [the Stacks Wallet](https://blockstack.org/wallet) is run by Hiro PBC at `https://gaia.blockstack.org/`. It supports files up to 25 megabytes in size.
```tsx -> We recommend breaking data instances greater than 25 MB into several files, saving them individually, and recomposing them on retrieval.
const userSession = new UserSession();
const storage = new Storage({ userSession });
const options: PutFileOptions = {
encrypt: false,
};
userSession.putFile('hello.txt', 'hello world', options).then(() => {
// hello.txt exists now, and has the contents "hello world"
});
```
## Creating an encrypted file These files can comprise any type of data such as text, image, video or binary.
Use the [Storage.putFile](https://blockstack.github.io/stacks.js/classes/storage.html#putfile) method and Files are often saved as strings that represent stringified JSON objects and contain a variety of properties for a particular model.
pass `encrypt: true` within the options object. See the [`PutFileOptions` type definition here](https://blockstack.github.io/stacks.js/interfaces/putfileoptions.html#encrypt)
```tsx To save a file, first instantiate a `storage` object using the `userSession` object for an authenticated user. Then proceed to call its `putFile` method with relevant parameters:
const userSession = new UserSession();
const options: PutFileOptions = { ```js
encrypt: true, import { AppConfig, UserSession } from '@stacks/connect';
}; import { Storage } from '@stacks/storage';
userSession.putFile('message.txt', 'Secret hello', options).then(() => { const appConfig = new AppConfig(['store_write', 'publish_data']);
// message.txt exists now, and has the contents "Secret hello" const userSession = new UserSession({ appConfig });
}); const storage = new Storage({ userSession });
```
## Reading a file
Use the [Storage.getFile](https://blockstack.github.io/stacks.js/classes/storage.html#getfile) method: let fileName = 'car.json';
```tsx let fileData = {
const userSession = new UserSession(); color: 'blue'
const storage = new Storage({ userSession }); electric: true,
purchaseDate: '2019-04-03',
};
const options: GetFileOptions = { let options = {
decrypt: false, encrypt: true,
}; };
storage.getFile('hello.txt', options).then(fileContents => { storage.putFile(fileName, JSON.stringify(fileData), options).then(() => {
// get the contents of the file hello.txt // Handle any execution after data has been saved
assert(fileContents === 'hello world!');
}); });
``` ```
## Reading an encrypted file The `options` parameter object contains an `encrypt` property that when set to `true` indicates that the data should be encrypted with the user's app private key before saved to their Gaia hub. All data will be encrypted as such by default if the `encrypt` property or the `options` object itself is omitted entirely.
Use the [Storage.getFile](https://blockstack.github.io/stacks.js/classes/storage.html#getfile) method and pass If the `encrypt` property is set to `false`, the data will be saved completely unencrypted and available to everyone online with public access to the user's Gaia hub.
`decrypt: true` within the options object. See the [`GetFileOptions` type definition here](https://blockstack.github.io/stacks.js/interfaces/getfileoptions.html#decrypt)
```tsx Whereas saving privately encrypted data is possible for all authenticated apps with the [`store_write`](https://blockstack.github.io/stacks.js/enums/authscope.html#store_write) scope, the user must have previously granted the [`publish_data`](https://blockstack.github.io/stacks.js/enums/authscope.html#publish_data) scope as well during authentication for the app to save publicly unencrypted data.
const userSession = new UserSession();
const storage = new Storage({ userSession });
const options: GetFileOptions = { -> Note that you'll need to save an entirely new string of modified data using `putFile` with the same `fileName` every time you want to update a record. There is no separate update method.
decrypt: true,
};
storage.getFile('message.txt', options).then(fileContents => { ## Get data for session user
// get & decrypt the contents of the file /message.txt
assert(fileContents === 'Secret hello!'); To retrieve data previously saved for a user with an app, call the `getFile` method available from the `storage` object:
});
```
## Reading another user's unencrypted file ```js
import { AppConfig, UserSession } from '@stacks/connect';
import { Storage } from '@stacks/storage';
In order for files to be publicly readable, the app must request const appConfig = new AppConfig(['store_write', 'publish_data']);
the [`publish_data` scope](https://blockstack.github.io/stacks.js/enums/authscope.html#publish_data) during authentication. const userSession = new UserSession({ appConfig });
const storage = new Storage({ userSession });
let fileName = 'car.json';
```jsx
const options = { const options = {
user: 'ryan.id', // the Stacks ID of the user for which to lookup the file decrypt: true,
app: 'https://BlockstackApp.com', // origin of the app this file is stored for
decrypt: false,
}; };
const userSession = new UserSession(); storage.getFile(fileName, options).then(fileData => {
storage.getFile('hello.txt', options).then(fileContents => { // Handle any execution that uses decrypted fileData
// get the contents of the file /message.txt
assert(fileContents === 'hello world!');
}); });
``` ```
## Delete a file Note how the `decrypt` property in the `options` object here should implement the same boolean value as used for `encrypt` initially upon saving the data with `putFile`. The `decrypt` property will default to `true` if omitted.
Use the [`UserSession.deleteFile`](https://blockstack.github.io/stacks.js/classes/storage.html#deletefile) from the application's data store. Encrypted files need `decrypt` set to `true` so the app knows to decrypt the data with the user's app private key before made available in the callback here as `fileData`.
```jsx ## Get data for other user
const userSession = new UserSession();
const storage = new Storage({ userSession });
storage.deleteFile('hello.txt').then(() => {
// hello.txt is now removed.
});
```
## Write-to and Read-from URL Guarantees Apps can also retrieve public data saved by users other than the one with the active session, granted those users have registered usernames via the [Blockchain Naming System](/build-apps/references/bns).
Gaia is built on a driver model that supports many storage services. So, with Simply indicate the username of such a user in the `options` object:
very few lines of code, you can interact with providers on Amazon S3, Dropbox,
and so forth. The simple `getFile()` and `putFile()` interfaces are kept simple
because Stacks assumes and wants to encourage a community of
open-source-data-management libraries.
The performance and simplicity-oriented guarantee of the Gaia specification is ```js
that when an application submits a write-to import { AppConfig, UserSession } from '@stacks/connect';
`https://myhub.service.org/store/foo/bar` URL, the application is guaranteed to import { Storage } from '@stacks/storage';
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 const appConfig = new AppConfig(['store_write', 'publish_data']);
encrypted format means only the user that stored it can view it. For applications that want other users to const userSession = new UserSession({ appConfig });
view data, the application should set the `encrypt` option to `false`. And, corresponding, the `decrypt` const storage = new Storage({ userSession });
option on `getFile()` should also be `false`.
Consistent, identical suffixes allow an application to know _exactly_ where a let fileName = 'car.json';
written file can be read from, given the read prefix. The Gaia service defines a `hub_info` endpoint to obtain
that read prefix:
```bash const options = {
GET /hub_info/ username: 'markmhendrickson.id.blockstack',
``` };
The endpoint returns a JSON object with a `read_url_prefix`, for example, if my service returns:
```jsx storage.getFile(fileName, options).then(fileData => {
{ ..., // Handle any execution that uses decrypted fileData
"read_url_prefix": "https://myservice.org/read/" });
}
``` ```
The data be read with this `getFile()` and this address: This `getFile` call will retrieve data found at the given `fileName` path from the storage bucket of the Gaia hub that maps to the user who registered the given `username` and this particular app as hosted at the current domain.
```
https://myservice.org/read/1DHvWDj834zPAkwMhpXdYbCYh4PomwQfzz/0/profile.json
```
The application is guaranteed that the profile is written with `putFile()` this request address: Set an additional `app` property within `options` to retrieve data for a user as saved by an app hosted at a separate domain:
```js
const options = {
app: 'example.org'
username: 'markmhendrickson.id.blockstack',
};
``` ```
https://myservice.org/store/1DHvWDj834zPAkwMhpXdYbCYh4PomwQfzz/0/profile.json
```
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 This will cause the `getFile` call to retrieve data found in a separate storage bucket for the indicated app on the user's Gaia hub.
Access control in a Gaia storage hub is performed on a per-address basis. ## Delete data for session user
Writes to URLs `/store/<address>/<file>` are allowed only if the writer can
demonstrate that they control _that_ address. This is achieved via the
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. Call the `deleteFile` method on `storage` to remove data found at a particular file path for the active session user:
For example, here's how you would get data from the [Banter](https://banter.pub/) app, stored under the
Stacks ID `gavin.id`.
### Step 1: Get the bucket URL ```js
import { AppConfig, UserSession } from '@stacks/connect';
import { Storage } from '@stacks/storage';
```bash const appConfig = new AppConfig(['store_write', 'publish_data']);
BUCKET_URL="$(curl -sL https://core.blockstack.org/v1/users/gavin.id | jq -r '."gavin.id"["profile"]["apps"]["https://banter.pub"]')"  const userSession = new UserSession({ appConfig });
echo "$BUCKET_URL"  https://gaia.blockstack.org/hub/16E485MVpR3QpmjVkRgej7ya2Vnzu3jyTR/ const storage = new Storage({ userSession });
```
### Step 2: Get the data let fileName = 'car.json';
```bash storage.deleteFile(fileName).then(() => {
curl -sL "${BUCKET_URL%%/}/Message/3e866af471d0-4072-beba-06ad1e7ad4bd" // Handle any execution after file has been deleted
``` });
```bash
{"content":"Anyone here?","votes":[],"createdBy":"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. -> Apps can save and delete data only for the active session user.

2
src/pages/build-apps/guides/transaction-signing.md

@ -17,7 +17,7 @@ Transaction signing provides a way for users execute [Clarity smart contracts](/
Users can sign transactions that exchange fungible or non-fungible tokens with upfront guarantees while retaining complete control over their digital assets. Users can sign transactions that exchange fungible or non-fungible tokens with upfront guarantees while retaining complete control over their digital assets.
See [the public registry tutorial](/build-apps/tutorials/public-registry) for a concrete example of this functionality in practice. [See the public registry tutorial](/build-apps/tutorials/public-registry) for a concrete example of this functionality in practice.
## How it works ## How it works

Loading…
Cancel
Save