mirror of https://github.com/lukechilds/docs.git
Browse Source
- Organize navigation - Rewrite overview page - Migrate and merge section pages into sub-directories - Update redirects - Add authentication guide page - Improve page meta data for related pages - Draft introductions for guide pages - Indicate TBD info areas - Update library references to @stacks/* - Update references to authenticator / Stacks Wallet - Rename authentication reference to Stacks Connectfeat/build-apps
Mark Hendrickson
4 years ago
23 changed files with 398 additions and 420 deletions
@ -1,91 +0,0 @@ |
|||||
--- |
|
||||
title: Building apps with Stacks |
|
||||
description: Resources for building apps on the Stacks blockchain |
|
||||
images: |
|
||||
large: /images/pages/build-apps.svg |
|
||||
sm: /images/pages/build-apps-sm.svg |
|
||||
--- |
|
||||
|
|
||||
## Introduction |
|
||||
|
|
||||
Prefer to jump right in? Get started with these tutorials such as creating a decentralized to-do list app |
|
||||
|
|
||||
[@page-reference | inline] |
|
||||
| /authentication/building-todo-app |
|
||||
|
|
||||
[@page-reference | inline] |
|
||||
| /authentication/building-with-angular |
|
||||
|
|
||||
## What are decentralized apps? |
|
||||
|
|
||||
Decentralized apps are apps that don’t depend on a centralized platform, server or database. Instead, they use a |
|
||||
decentralized network, built on the Stacks blockchain, for [authentication](/authentication/overview), [data storage](/data-storage/overview), |
|
||||
and [backend logic](/data-storage/indexing-overview). Just like Bitcoin, a decentralized network of applications is accessible to |
|
||||
anyone and not controlled by any central authority. |
|
||||
|
|
||||
To learn more about the Stacks network and decentralization, read the [Stacks overview](/ecosystem/overview). |
|
||||
|
|
||||
### User-owned data |
|
||||
|
|
||||
Data is stored with the user and encrypted with a key that only they own. Developers aren’t responsible for, or have to |
|
||||
host, their users’ data. This protects users against security breaches and keeps their data private. |
|
||||
|
|
||||
### Smart contracts |
|
||||
|
|
||||
Decentralized apps can use [smart contracts](/write-smart-contracts/overview) to make their backend logic public, open, and |
|
||||
permissionless. Once published on the blockchain, no one really owns or controls a smart contract. They will execute when |
|
||||
the terms are met, regardless of who interacts with it. |
|
||||
|
|
||||
### Compatible and extendable |
|
||||
|
|
||||
Decentralized apps are compatible by nature because they use the same data and shared state. You can build on top of |
|
||||
other apps without requiring permission or fear of being shut out. |
|
||||
|
|
||||
## Getting started |
|
||||
|
|
||||
To build your decentralized app, you’ll use [authentication](/authentication/overview), [data storage](/data-storage/overview), |
|
||||
[data indexing](/data-storage/indexing-overview) (optional), and [smart contracts](/write-smart-contracts/overview) (optional). |
|
||||
Get started with the documentation and tutorials below. |
|
||||
|
|
||||
### Authentication and data storage |
|
||||
|
|
||||
Like a regular app, yours will require user authentication and data storage, but decentralized. Get started with the documentation below or try the tutorial. |
|
||||
|
|
||||
[@page-reference | inline] |
|
||||
| /authentication/overview |
|
||||
|
|
||||
[@page-reference | inline] |
|
||||
| /data-storage/overview |
|
||||
|
|
||||
[@page-reference | inline] |
|
||||
| /authentication/building-todo-app |
|
||||
|
|
||||
### Data indexing |
|
||||
|
|
||||
If you need to store and index data shared by multiple users, such as messages or a shared document, read the Radiks |
|
||||
documentation. |
|
||||
|
|
||||
[@page-reference | inline] |
|
||||
| /data-storage/indexing-overview |
|
||||
|
|
||||
### Smart contracts |
|
||||
|
|
||||
You can use smart contracts to decentralize your app’s backend logic, making it open and permissionless. Smart contracts |
|
||||
on the Stacks blockchain are written in the [Clarity language](https://clarity-lang.org). View the smart contracts documentation or get started with a tutorial. |
|
||||
|
|
||||
[@page-reference | inline] |
|
||||
| /write-smart-contracts/overview |
|
||||
|
|
||||
[@page-reference | inline] |
|
||||
| /write-smart-contracts/hello-world-tutorial |
|
||||
|
|
||||
[@page-reference | inline] |
|
||||
| /write-smart-contracts/counter-tutorial |
|
||||
|
|
||||
[@page-reference | inline] |
|
||||
| /write-smart-contracts/public-registry-tutorial |
|
||||
|
|
||||
### Stacks.js |
|
||||
|
|
||||
[Stacks.js](https://blockstack.github.io/stacks.js/) is a collection of JavaScript library developed by Blockstack PBC that makes it easy to integrate authentication, data storage |
|
||||
and smart contracts functionality in a user-friendly way. |
|
@ -1,6 +1,6 @@ |
|||||
--- |
--- |
||||
title: Collections (Preview) |
title: Overview |
||||
description: Learn about the beta release of Collections and how you can start using it. |
description: Store data in standardized formats |
||||
--- |
--- |
||||
|
|
||||
## Introduction |
## Introduction |
@ -1,5 +1,6 @@ |
|||||
--- |
--- |
||||
title: Create a Collection type |
title: Types |
||||
|
description: Create new collection types |
||||
--- |
--- |
||||
|
|
||||
## Introduction |
## Introduction |
@ -0,0 +1,63 @@ |
|||||
|
--- |
||||
|
title: Authentication |
||||
|
description: Register and sign in users with identities on the Stacks blockchain |
||||
|
experience: beginners |
||||
|
tags: |
||||
|
- tutorial |
||||
|
images: |
||||
|
large: /images/pages/write-smart-contracts.svg |
||||
|
sm: /images/pages/write-smart-contracts-sm.svg |
||||
|
--- |
||||
|
|
||||
|
## Introduction |
||||
|
|
||||
|
This guide explains how to authenticate users with [the Stacks Connect protocol](/build-apps/references/stacks-connect) by implementing the `connect` package of [Stacks.js](https://blockstack.github.io/stacks.js/). |
||||
|
|
||||
|
Authentication provides a way for users to identify themselves to an app while retaining complete control over their credentials and personal details. It can be integrated alone or used in conjunction with [transaction signing](/build-apps/tutorials/transaction-signing) and [data storage](/build-apps/tutorials/data-storage), for which it is a prerequisite. |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
## Initiate authentication flow |
||||
|
|
||||
|
```js |
||||
|
import { AppConfig, UserSession, showConnect } from '@stacks/connect'; |
||||
|
|
||||
|
const appConfig = new AppConfig(['store_write', 'publish_data']); |
||||
|
const userSession = new UserSession({ appConfig }); |
||||
|
|
||||
|
function authenticate() { |
||||
|
showConnect({ |
||||
|
appDetails: { |
||||
|
name: 'My App', |
||||
|
icon: window.location.origin + '/my-app-logo.svg', |
||||
|
}, |
||||
|
redirectTo: '/', |
||||
|
finished: () => { |
||||
|
window.location.reload(); |
||||
|
}, |
||||
|
userSession: userSession, |
||||
|
}); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## Handle pending sign in (still needed??) |
||||
|
|
||||
|
```jsx |
||||
|
import { AppConfig, UserSession, showConnect } from '@stacks/connect'; |
||||
|
|
||||
|
const appConfig = new AppConfig(['store_write', 'publish_data']); |
||||
|
const userSession = new UserSession({ appConfig }); |
||||
|
|
||||
|
function componentDidMount() { |
||||
|
if (userSession.isSignInPending()) { |
||||
|
userSession.handlePendingSignIn().then(userData => { |
||||
|
window.history.replaceState({}, document.title, '/'); |
||||
|
this.setState({ userData: userData }); |
||||
|
}); |
||||
|
} else if (userSession.isUserSignedIn()) { |
||||
|
this.setState({ userData: userSession.loadUserData() }); |
||||
|
} |
||||
|
} |
||||
|
``` |
@ -0,0 +1,209 @@ |
|||||
|
--- |
||||
|
title: Data storage |
||||
|
description: Save and retrieve data for users with Gaia |
||||
|
experience: beginners |
||||
|
tags: |
||||
|
- tutorial |
||||
|
images: |
||||
|
large: /images/pages/write-smart-contracts.svg |
||||
|
sm: /images/pages/write-smart-contracts-sm.svg |
||||
|
--- |
||||
|
|
||||
|
## Introduction |
||||
|
|
||||
|
This guide explains how to save and retrieve data for users with [Gaia](/build-apps/references/gaia) by implementing the `connect` and `storage` packages of [Stacks.js](https://blockstack.github.io/stacks.js/). |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
See [the Todos app tutorial](/build-apps/tutorials/todos) for a concrete example of this functionality in practice. |
||||
|
|
||||
|
## How data is stored |
||||
|
|
||||
|
Gaia storage is a key-value store. |
||||
|
|
||||
|
## Creating a file |
||||
|
|
||||
|
Use the [Storage.putFile](https://blockstack.github.io/stacks.js/classes/storage.html#putfile) method: |
||||
|
|
||||
|
```tsx |
||||
|
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 |
||||
|
|
||||
|
Use the [Storage.putFile](https://blockstack.github.io/stacks.js/classes/storage.html#putfile) method and |
||||
|
pass `encrypt: true` within the options object. See the [`PutFileOptions` type definition here](https://blockstack.github.io/stacks.js/interfaces/putfileoptions.html#encrypt) |
||||
|
|
||||
|
```tsx |
||||
|
const userSession = new UserSession(); |
||||
|
|
||||
|
const options: PutFileOptions = { |
||||
|
encrypt: true, |
||||
|
}; |
||||
|
|
||||
|
userSession.putFile('message.txt', 'Secret hello', options).then(() => { |
||||
|
// message.txt exists now, and has the contents "Secret hello" |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
## Reading a file |
||||
|
|
||||
|
Use the [Storage.getFile](https://blockstack.github.io/stacks.js/classes/storage.html#getfile) method: |
||||
|
|
||||
|
```tsx |
||||
|
const userSession = new UserSession(); |
||||
|
const storage = new Storage({ userSession }); |
||||
|
|
||||
|
const options: GetFileOptions = { |
||||
|
decrypt: false, |
||||
|
}; |
||||
|
|
||||
|
storage.getFile('hello.txt', options).then(fileContents => { |
||||
|
// get the contents of the file hello.txt |
||||
|
assert(fileContents === 'hello world!'); |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
## Reading an encrypted file |
||||
|
|
||||
|
Use the [Storage.getFile](https://blockstack.github.io/stacks.js/classes/storage.html#getfile) method and pass |
||||
|
`decrypt: true` within the options object. See the [`GetFileOptions` type definition here](https://blockstack.github.io/stacks.js/interfaces/getfileoptions.html#decrypt) |
||||
|
|
||||
|
```tsx |
||||
|
const userSession = new UserSession(); |
||||
|
const storage = new Storage({ userSession }); |
||||
|
|
||||
|
const options: GetFileOptions = { |
||||
|
decrypt: true, |
||||
|
}; |
||||
|
|
||||
|
storage.getFile('message.txt', options).then(fileContents => { |
||||
|
// get & decrypt the contents of the file /message.txt |
||||
|
assert(fileContents === 'Secret hello!'); |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
## Reading another user's unencrypted file |
||||
|
|
||||
|
In order for files to be publicly readable, the app must request |
||||
|
the [`publish_data` scope](https://blockstack.github.io/stacks.js/enums/authscope.html#publish_data) during authentication. |
||||
|
|
||||
|
```jsx |
||||
|
const options = { |
||||
|
user: 'ryan.id', // the Stacks ID of the user for which to lookup the file |
||||
|
app: 'https://BlockstackApp.com', // origin of the app this file is stored for |
||||
|
decrypt: false, |
||||
|
}; |
||||
|
|
||||
|
const userSession = new UserSession(); |
||||
|
storage.getFile('hello.txt', options).then(fileContents => { |
||||
|
// get the contents of the file /message.txt |
||||
|
assert(fileContents === 'hello world!'); |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
## Delete a file |
||||
|
|
||||
|
Use the [`UserSession.deleteFile`](https://blockstack.github.io/stacks.js/classes/storage.html#deletefile) from the application's data store. |
||||
|
|
||||
|
```jsx |
||||
|
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 |
||||
|
|
||||
|
Gaia is built on a driver model that supports many storage services. So, with |
||||
|
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 |
||||
|
that when an application submits a write-to |
||||
|
`https://myhub.service.org/store/foo/bar` URL, the application is guaranteed to |
||||
|
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`. |
||||
|
|
||||
|
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: |
||||
|
|
||||
|
```bash |
||||
|
GET /hub_info/ |
||||
|
``` |
||||
|
|
||||
|
The endpoint returns a JSON object with a `read_url_prefix`, for example, if my service returns: |
||||
|
|
||||
|
```jsx |
||||
|
{ ..., |
||||
|
"read_url_prefix": "https://myservice.org/read/" |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
The data be read with this `getFile()` and this address: |
||||
|
|
||||
|
``` |
||||
|
https://myservice.org/read/1DHvWDj834zPAkwMhpXdYbCYh4PomwQfzz/0/profile.json |
||||
|
``` |
||||
|
|
||||
|
The application is guaranteed that the profile is written with `putFile()` this request address: |
||||
|
|
||||
|
``` |
||||
|
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 |
||||
|
|
||||
|
Access control in a Gaia storage hub is performed on a per-address basis. |
||||
|
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. |
||||
|
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 |
||||
|
|
||||
|
```bash |
||||
|
BUCKET_URL="$(curl -sL https://core.blockstack.org/v1/users/gavin.id | jq -r '."gavin.id"["profile"]["apps"]["https://banter.pub"]')"  |
||||
|
echo "$BUCKET_URL"  https://gaia.blockstack.org/hub/16E485MVpR3QpmjVkRgej7ya2Vnzu3jyTR/ |
||||
|
``` |
||||
|
|
||||
|
### Step 2: Get the data |
||||
|
|
||||
|
```bash |
||||
|
curl -sL "${BUCKET_URL%%/}/Message/3e866af471d0-4072-beba-06ad1e7ad4bd" |
||||
|
``` |
||||
|
|
||||
|
```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. |
@ -1,6 +1,6 @@ |
|||||
--- |
--- |
||||
title: Collaborate with groups |
title: Collaboration |
||||
description: A key feature of Radiks is support for private collaboration between multiple users. |
description: Support private collaboration between multiple users with Radiks |
||||
--- |
--- |
||||
|
|
||||
## Introduction |
## Introduction |
@ -1,6 +1,6 @@ |
|||||
--- |
--- |
||||
title: Create and use models |
title: Models |
||||
description: Model and query application data with Radiks. |
description: Model and query application data with Radiks |
||||
--- |
--- |
||||
|
|
||||
## Introduction |
## Introduction |
@ -1,6 +1,6 @@ |
|||||
--- |
--- |
||||
title: Indexing overview |
title: Overview |
||||
description: Using Radiks you can build multi-player apps that index, store, and query user data. |
description: Build multi-player apps that index, store, and query user data with Radiks |
||||
images: |
images: |
||||
large: /images/pages/radiks.svg |
large: /images/pages/radiks.svg |
||||
sm: /images/pages/radiks-sm.svg |
sm: /images/pages/radiks-sm.svg |
@ -1,6 +1,6 @@ |
|||||
--- |
--- |
||||
title: Server tips and tricks |
title: Server |
||||
description: Some tips and tricks for working with a Radiks server. |
description: Tips and tricks for working with Radiks server |
||||
--- |
--- |
||||
|
|
||||
## Access the MongoDB collection |
## Access the MongoDB collection |
@ -0,0 +1,35 @@ |
|||||
|
--- |
||||
|
title: Build apps with Stacks |
||||
|
description: Authenticate users, sign transactions and store data with the Stacks blockchain |
||||
|
images: |
||||
|
large: /images/pages/build-apps.svg |
||||
|
sm: /images/pages/build-apps-sm.svg |
||||
|
--- |
||||
|
|
||||
|
## Introduction |
||||
|
|
||||
|
Apps built with the Stacks blockchain give users control over their digital identities, assets and data. |
||||
|
|
||||
|
Unlike most cloud-based apps, they are "decentralized" since they don't depend on any centralized platform, server or database to function. Rather, they use the Stacks blockchain to authenticate users and facilitate read and write transactions for them without any single point of failure or trust. |
||||
|
|
||||
|
This page provides information on how to build such apps using [Stacks.js](https://github.com/blockstack/stacks.js) and other libraries that make integration of the Stacks blockchain easy for front-end developers. |
||||
|
|
||||
|
Three main types of integration are available: |
||||
|
|
||||
|
- **Authentication**: Register and sign in users with identities with the Stacks blockchain |
||||
|
- **Transaction signing**: Prompt users to sign and broadcast transactions to the Stacks blockchain |
||||
|
- **Data storage**: Save and retrieve data for users with Gaia |
||||
|
|
||||
|
All three of these types can be used together to create powerful new user experiences that rival or exceed those of traditional apps – all while protecting your users' digital rights. |
||||
|
|
||||
|
While integration is possible for any type of app, most of the resources available here are for web developers experienced with JavaScript. |
||||
|
|
||||
|
## Guides |
||||
|
|
||||
|
[@page-reference | grid] |
||||
|
| /build-apps/guides/authentication, /build-apps/guides/transaction-signing, /build-apps/guides/data-storage |
||||
|
|
||||
|
## Tutorials |
||||
|
|
||||
|
[@page-reference | grid] |
||||
|
| /build-apps/tutorials/todos, /build-apps/tutorials/public-registry, /build-apps/tutorials/angular, /build-apps/tutorials/radiks |
@ -1,12 +1,11 @@ |
|||||
--- |
--- |
||||
title: Overview |
title: Blockchain Naming System |
||||
description: 'Blockchain naming system (BNS)' |
description: Binds Stacks usernames to off-chain state |
||||
--- |
--- |
||||
|
|
||||
# Blockchain Naming System (BNS) |
Blockchain Naming System (BNS) is a network system that binds Stacks usernames |
||||
|
|
||||
BNS is a network system that binds names |
|
||||
to off-chain state without relying on any central points of control. |
to off-chain state without relying on any central points of control. |
||||
|
|
||||
The Stacks V1 blockchain implemented BNS through first-order name operations. |
The Stacks V1 blockchain implemented BNS through first-order name operations. |
||||
In Stacks V2, BNS is instead implemented through a smart-contract loaded |
In Stacks V2, BNS is instead implemented through a smart-contract loaded |
||||
during the genesis block. |
during the genesis block. |
@ -1,5 +1,5 @@ |
|||||
--- |
--- |
||||
title: Stacks Auth with Angular |
title: Angular |
||||
description: How to integrate authentication within an Angular application |
description: How to integrate authentication within an Angular application |
||||
experience: beginners |
experience: beginners |
||||
duration: 30 minutes |
duration: 30 minutes |
@ -1,6 +1,6 @@ |
|||||
--- |
--- |
||||
title: Integrate Radiks |
title: Radiks |
||||
description: Learn how to setup Radiks with your application |
description: Learn how to setup Radiks with your app |
||||
icon: BlockstackIcon |
icon: BlockstackIcon |
||||
duration: 1 hour |
duration: 1 hour |
||||
experience: intermediate |
experience: intermediate |
@ -1,5 +1,5 @@ |
|||||
--- |
--- |
||||
title: Building a public registry |
title: Public registry |
||||
description: Learn how to read state from the Stacks blockchain. |
description: Learn how to read state from the Stacks blockchain. |
||||
duration: 60 minutes |
duration: 60 minutes |
||||
experience: intermediate |
experience: intermediate |
@ -1,41 +0,0 @@ |
|||||
--- |
|
||||
description: 'Storing user data with Stacks' |
|
||||
--- |
|
||||
|
|
||||
## Introduction |
|
||||
|
|
||||
Stacks authentication is a bearer token-based authentication system. From an app user's perspective, |
|
||||
login similar to third-party authentication techniques that they're familiar with. For an app developer, |
|
||||
the flow is unlike the typical client-server flow of centralized sign-in services such as OAuth. With Stacks Auth |
|
||||
the authentication flow happens entirely client-side. |
|
||||
|
|
||||
In this section, you get an overview of the authentication system and learn how Gaia fits into it. |
|
||||
|
|
||||
## Authentication and Gaia |
|
||||
|
|
||||
A decentralized application (DApp) and the Stacks authenticator communicate during |
|
||||
the authentication flow by passing back and forth two tokens. The requesting |
|
||||
application sends the Stacks authenticator an `authRequest` token. Once a user |
|
||||
approves a sign-in, the Stacks authenticator responds to the application with an |
|
||||
`authResponse` token. These tokens are <a href="https://jwt.io/" target="\_blank">JSON Web Tokens</a>, and they are |
|
||||
passed via URL query strings. |
|
||||
|
|
||||
When a user chooses to "Sign in with Stacks Auth" on your DApp, the `redirectToSignIn()` method sends the user to the |
|
||||
Stacks authenticator. The browser responds with an authentication token and an _app private key_. |
|
||||
|
|
||||
![](/images/app-sign-in.png) |
|
||||
|
|
||||
The app private key is application-specific. It is generated from the user's identity address private key using the |
|
||||
`appDomain` as input. This key is deterministic, meaning that for a given Stacks ID and domain name, the same |
|
||||
private key is generated each time. The app private key is securely shared with the app on each authentication and |
|
||||
encrypted by the Stacks authenticator. The key serves three functions, it: |
|
||||
|
|
||||
- is used to create the credentials that give an app access to the Gaia hub storage bucket for that specific app |
|
||||
- is used in the end-to-end encryption of files stored for the app on the user's Gaia hub |
|
||||
- serves as a cryptographic secret that apps can use to perform other cryptographic functions |
|
||||
|
|
||||
When an application writes to a Gaia hub, the authentication token, key, and the data are passed to the Gaia hub. |
|
||||
|
|
||||
![Gaia writes](/images/gaia-writes.png) |
|
||||
|
|
||||
The token ensures the DApp has the authorization to write to the hub on the user's behalf. |
|
@ -1,121 +0,0 @@ |
|||||
--- |
|
||||
title: Guide to Stacks Storage |
|
||||
--- |
|
||||
|
|
||||
## Introduction |
|
||||
|
|
||||
The Stacks Platform stores application data in the Gaia Storage System. Transactional metadata is stored on the |
|
||||
Stacks blockchain and user application data is stored in Gaia storage. Storing data off of the blockchain ensures |
|
||||
that Stacks applications can provide users with high performance and high availability for data reads and writes |
|
||||
without introducing central trust parties. |
|
||||
|
|
||||
-> Stacks 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 |
|
||||
|
|
||||
Gaia storage is a key-value store. |
|
||||
|
|
||||
## Creating a file |
|
||||
|
|
||||
Use the [Storage.putFile](https://blockstack.github.io/stacks.js/classes/storage.html#putfile) method: |
|
||||
|
|
||||
```tsx |
|
||||
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 |
|
||||
|
|
||||
Use the [Storage.putFile](https://blockstack.github.io/stacks.js/classes/storage.html#putfile) method and |
|
||||
pass `encrypt: true` within the options object. See the [`PutFileOptions` type definition here](https://blockstack.github.io/stacks.js/interfaces/putfileoptions.html#encrypt) |
|
||||
|
|
||||
```tsx |
|
||||
const userSession = new UserSession(); |
|
||||
|
|
||||
const options: PutFileOptions = { |
|
||||
encrypt: true, |
|
||||
}; |
|
||||
|
|
||||
userSession.putFile('message.txt', 'Secret hello', options).then(() => { |
|
||||
// message.txt exists now, and has the contents "Secret hello" |
|
||||
}); |
|
||||
``` |
|
||||
|
|
||||
## Reading a file |
|
||||
|
|
||||
Use the [Storage.getFile](https://blockstack.github.io/stacks.js/classes/storage.html#getfile) method: |
|
||||
|
|
||||
```tsx |
|
||||
const userSession = new UserSession(); |
|
||||
const storage = new Storage({ userSession }); |
|
||||
|
|
||||
const options: GetFileOptions = { |
|
||||
decrypt: false, |
|
||||
}; |
|
||||
|
|
||||
storage.getFile('hello.txt', options).then(fileContents => { |
|
||||
// get the contents of the file hello.txt |
|
||||
assert(fileContents === 'hello world!'); |
|
||||
}); |
|
||||
``` |
|
||||
|
|
||||
## Reading an encrypted file |
|
||||
|
|
||||
Use the [Storage.getFile](https://blockstack.github.io/stacks.js/classes/storage.html#getfile) method and pass |
|
||||
`decrypt: true` within the options object. See the [`GetFileOptions` type definition here](https://blockstack.github.io/stacks.js/interfaces/getfileoptions.html#decrypt) |
|
||||
|
|
||||
```tsx |
|
||||
const userSession = new UserSession(); |
|
||||
const storage = new Storage({ userSession }); |
|
||||
|
|
||||
const options: GetFileOptions = { |
|
||||
decrypt: true, |
|
||||
}; |
|
||||
|
|
||||
storage.getFile('message.txt', options).then(fileContents => { |
|
||||
// get & decrypt the contents of the file /message.txt |
|
||||
assert(fileContents === 'Secret hello!'); |
|
||||
}); |
|
||||
``` |
|
||||
|
|
||||
## Reading another user's unencrypted file |
|
||||
|
|
||||
In order for files to be publicly readable, the app must request |
|
||||
the [`publish_data` scope](https://blockstack.github.io/stacks.js/enums/authscope.html#publish_data) during authentication. |
|
||||
|
|
||||
```jsx |
|
||||
const options = { |
|
||||
user: 'ryan.id', // the Stacks ID of the user for which to lookup the file |
|
||||
app: 'https://BlockstackApp.com', // origin of the app this file is stored for |
|
||||
decrypt: false, |
|
||||
}; |
|
||||
|
|
||||
const userSession = new UserSession(); |
|
||||
storage.getFile('hello.txt', options).then(fileContents => { |
|
||||
// get the contents of the file /message.txt |
|
||||
assert(fileContents === 'hello world!'); |
|
||||
}); |
|
||||
``` |
|
||||
|
|
||||
## Delete a file |
|
||||
|
|
||||
Use the [`UserSession.deleteFile`](https://blockstack.github.io/stacks.js/classes/storage.html#deletefile) from the application's data store. |
|
||||
|
|
||||
```jsx |
|
||||
const userSession = new UserSession(); |
|
||||
const storage = new Storage({ userSession }); |
|
||||
|
|
||||
storage.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](/data-storage/storage-write-read) |
|
@ -1,94 +0,0 @@ |
|||||
--- |
|
||||
description: 'Storing user data with Stacks' |
|
||||
--- |
|
||||
|
|
||||
## Introduction |
|
||||
|
|
||||
Once a user authenticates and a DApp obtains authentication, the application interacts with Gaia through the |
|
||||
`@stacks/auth` library. There are two simple methods in `@stacks/storage` 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 |
|
||||
|
|
||||
Gaia is built on a driver model that supports many storage services. So, with |
|
||||
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 |
|
||||
that when an application submits a write-to |
|
||||
`https://myhub.service.org/store/foo/bar` URL, the application is guaranteed to |
|
||||
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`. |
|
||||
|
|
||||
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: |
|
||||
|
|
||||
```bash |
|
||||
GET /hub_info/ |
|
||||
``` |
|
||||
|
|
||||
The endpoint returns a JSON object with a `read_url_prefix`, for example, if my service returns: |
|
||||
|
|
||||
```jsx |
|
||||
{ ..., |
|
||||
"read_url_prefix": "https://myservice.org/read/" |
|
||||
} |
|
||||
``` |
|
||||
|
|
||||
The data be read with this `getFile()` and this address: |
|
||||
|
|
||||
``` |
|
||||
https://myservice.org/read/1DHvWDj834zPAkwMhpXdYbCYh4PomwQfzz/0/profile.json |
|
||||
``` |
|
||||
|
|
||||
The application is guaranteed that the profile is written with `putFile()` this request address: |
|
||||
|
|
||||
``` |
|
||||
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 |
|
||||
|
|
||||
Access control in a Gaia storage hub is performed on a per-address basis. |
|
||||
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. |
|
||||
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 |
|
||||
|
|
||||
```bash |
|
||||
BUCKET_URL="$(curl -sL https://core.blockstack.org/v1/users/gavin.id | jq -r '."gavin.id"["profile"]["apps"]["https://banter.pub"]')"  |
|
||||
echo "$BUCKET_URL"  https://gaia.blockstack.org/hub/16E485MVpR3QpmjVkRgej7ya2Vnzu3jyTR/ |
|
||||
``` |
|
||||
|
|
||||
### Step 2: Get the data |
|
||||
|
|
||||
```bash |
|
||||
curl -sL "${BUCKET_URL%%/}/Message/3e866af471d0-4072-beba-06ad1e7ad4bd" |
|
||||
``` |
|
||||
|
|
||||
```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. |
|
Loading…
Reference in new issue