From c8b32dee95d6aa0363e37e950ccb40c4370e16b1 Mon Sep 17 00:00:00 2001 From: Mark Hendrickson Date: Tue, 5 Jan 2021 15:15:11 +0100 Subject: [PATCH] feat: restructure "Build apps" section - 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 Connect --- src/pages/build-apps.md | 91 -------- .../collections/overview.md} | 4 +- .../collections/types.md} | 3 +- src/pages/build-apps/guides/authentication.md | 63 ++++++ src/pages/build-apps/guides/data-storage.md | 209 ++++++++++++++++++ .../guides/transaction-signing.md} | 54 +++-- .../indexing/collaboration.md} | 4 +- .../indexing/models.md} | 4 +- .../indexing/overview.md} | 4 +- .../indexing/server.md} | 4 +- src/pages/build-apps/overview.md | 35 +++ .../references/bns.md} | 9 +- .../references/gaia.md} | 24 +- .../references/stacks-connect.md} | 34 +-- .../tutorials/angular.md} | 2 +- .../tutorials/indexing.md} | 4 +- .../tutorials/public-registry.md} | 2 +- .../tutorials/todos.md} | 6 +- src/pages/data-storage/authentication.md | 41 ---- src/pages/data-storage/storage-guide.md | 121 ---------- src/pages/data-storage/storage-write-read.md | 94 -------- src/pages/ecosystem/contributing.md | 4 +- src/pages/write-smart-contracts/overview.md | 2 +- 23 files changed, 398 insertions(+), 420 deletions(-) delete mode 100644 src/pages/build-apps.md rename src/pages/{data-storage/collections.md => build-apps/collections/overview.md} (98%) rename src/pages/{data-storage/collection-type.md => build-apps/collections/types.md} (99%) create mode 100644 src/pages/build-apps/guides/authentication.md create mode 100644 src/pages/build-apps/guides/data-storage.md rename src/pages/{write-smart-contracts/signing-transactions.md => build-apps/guides/transaction-signing.md} (84%) rename src/pages/{data-storage/indexing-collaborate.md => build-apps/indexing/collaboration.md} (97%) rename src/pages/{data-storage/indexing-models.md => build-apps/indexing/models.md} (99%) rename src/pages/{data-storage/indexing-overview.md => build-apps/indexing/overview.md} (97%) rename src/pages/{data-storage/indexing-server-extras.md => build-apps/indexing/server.md} (98%) create mode 100644 src/pages/build-apps/overview.md rename src/pages/{technology/naming-system.md => build-apps/references/bns.md} (99%) rename src/pages/{data-storage/overview.md => build-apps/references/gaia.md} (90%) rename src/pages/{authentication/overview.md => build-apps/references/stacks-connect.md} (80%) rename src/pages/{authentication/building-with-angular.md => build-apps/tutorials/angular.md} (99%) rename src/pages/{data-storage/integrate-indexing.md => build-apps/tutorials/indexing.md} (99%) rename src/pages/{write-smart-contracts/public-registry-tutorial.md => build-apps/tutorials/public-registry.md} (99%) rename src/pages/{authentication/building-todo-app.md => build-apps/tutorials/todos.md} (98%) delete mode 100644 src/pages/data-storage/authentication.md delete mode 100644 src/pages/data-storage/storage-guide.md delete mode 100644 src/pages/data-storage/storage-write-read.md diff --git a/src/pages/build-apps.md b/src/pages/build-apps.md deleted file mode 100644 index 877d066d..00000000 --- a/src/pages/build-apps.md +++ /dev/null @@ -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. diff --git a/src/pages/data-storage/collections.md b/src/pages/build-apps/collections/overview.md similarity index 98% rename from src/pages/data-storage/collections.md rename to src/pages/build-apps/collections/overview.md index 8521a49c..3f2d2eb5 100644 --- a/src/pages/data-storage/collections.md +++ b/src/pages/build-apps/collections/overview.md @@ -1,6 +1,6 @@ --- -title: Collections (Preview) -description: Learn about the beta release of Collections and how you can start using it. +title: Overview +description: Store data in standardized formats --- ## Introduction diff --git a/src/pages/data-storage/collection-type.md b/src/pages/build-apps/collections/types.md similarity index 99% rename from src/pages/data-storage/collection-type.md rename to src/pages/build-apps/collections/types.md index 06fb39c5..f3616235 100644 --- a/src/pages/data-storage/collection-type.md +++ b/src/pages/build-apps/collections/types.md @@ -1,5 +1,6 @@ --- -title: Create a Collection type +title: Types +description: Create new collection types --- ## Introduction diff --git a/src/pages/build-apps/guides/authentication.md b/src/pages/build-apps/guides/authentication.md new file mode 100644 index 00000000..8ce6e188 --- /dev/null +++ b/src/pages/build-apps/guides/authentication.md @@ -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() }); + } +} +``` diff --git a/src/pages/build-apps/guides/data-storage.md b/src/pages/build-apps/guides/data-storage.md new file mode 100644 index 00000000..08ea1cf7 --- /dev/null +++ b/src/pages/build-apps/guides/data-storage.md @@ -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/
/` 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. diff --git a/src/pages/write-smart-contracts/signing-transactions.md b/src/pages/build-apps/guides/transaction-signing.md similarity index 84% rename from src/pages/write-smart-contracts/signing-transactions.md rename to src/pages/build-apps/guides/transaction-signing.md index b8c71802..ef5f2383 100644 --- a/src/pages/write-smart-contracts/signing-transactions.md +++ b/src/pages/build-apps/guides/transaction-signing.md @@ -1,8 +1,7 @@ --- -title: Signing transactions -description: Learn how to sign transactions using Stacks Connect. -experience: advanced -duration: 30 minutes +title: Transaction signing +description: Prompt users to sign and broadcast transactions to the Stacks blockchain +experience: beginners tags: - tutorial images: @@ -12,15 +11,19 @@ images: ## Introduction -With Stacks Connect, you can interact with the Stacks 2.0 blockchain, empowering your users to sign transactions and interact with smart contracts. +This guide explains how to prompt users to sign transactions and broadcast them to the Stacks blockchain by implementing the `connect` package of [Stacks.js](https://blockstack.github.io/stacks.js/). -This functionality currently operates on [the Stacks 2.0 Testnet](http://testnet.blockstack.org/). The user interface has been designed with developers in mind and prominently displays debug information. STX testnet tokens for paying transaction fees can be obtained for free with [the testnet faucet](https://testnet.blockstack.org/faucet). We will update this functionality and experience for mainnet upon its release. +Transaction signing provides a way for users execute [smart contracts written in Clarity](/write-smart-contracts/overview) that are relevant to your app then handle the result immediately. + +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. ## How it works For your app's users to be able to execute a smart contract function, they need to sign and broadcast a transaction. -It's important that users remain in control of the private keys that sign these transactions. Connect provides an -easy-to-use workflow that allows your users to securely sign transactions. + +It's important that users remain in control of the private keys that sign these transactions. Connect provides an easy-to-use workflow that allows your users to securely sign transactions. Connect allows you to open the authenticator with parameters indicating the details of the transaction - like the smart contract address, function name, and specific arguments. Your users get the chance to see these details, and then sign @@ -35,9 +38,8 @@ with it. To initiate a contract call transaction, use the `openContractCall` function. ```tsx -import { openContractCall } from '@blockstack/connect'; +import { openContractCall } from '@stacks/connect'; -// Here's an example of options: const myStatus = 'hey there'; const options = { contractAddress: 'ST22T6ZS7HVWEMZHHFK77H4GTNDTWNPQAX8WZAKHJ', @@ -118,14 +120,14 @@ const functionArguments = [ ]; ``` -If you're using TypeScript, these Clarity types can be imported as `ContractCallArgumentType` from `@blockstack/connect`. +If you're using TypeScript, these Clarity types can be imported as `ContractCallArgumentType` from `@stacks/connect`. ## Stacks (STX) Token Transfers STX token transfers can be initiated with the `openSTXTransfer` function. ```tsx -import { openSTXTransfer } from '@blockstack/connect'; +import { openSTXTransfer } from '@stacks/connect'; openSTXTransfer({ recipient: 'ST2EB9WEQNR9P0K28D2DC352TM75YG3K0GT7V13CV', @@ -158,18 +160,18 @@ interface STXTransferOptions { } ``` -| parameter | type | optional | description | -| ---------- | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| recipient | string | false | The STX Address for the recipient of this STX transfer | -| amount | string | false | The amount of microstacks (µSTX) to be transferred. This argument is a string to prevent floating point errors. There are 1,000,000 µSTX per STX. | -| memo | string | true | An optional memo to include in the transaction. | -| appDetails | object | false | A dictionary that includes `name` and `icon` | -| finished | function | false | A callback that is fired when the transaction is signed and broadcasted. Your callback will receive an object back with a `txId` and a `txRaw`, both of which are strings. | -| authOrigin | string | true | The location of the authenticator. This is only necessary when developing the authenticator locally, or when using beta features. Defaults to `"https://app.blockstack.org"`. | +| parameter | type | optional | description | +| ---------- | -------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| recipient | string | false | The STX Address for the recipient of this STX transfer | +| amount | string | false | The amount of microstacks (µSTX) to be transferred. This argument is a string to prevent floating point errors. There are 1,000,000 µSTX per STX. | +| memo | string | true | An optional memo to include in the transaction. | +| appDetails | object | false | A dictionary that includes `name` and `icon` | +| finished | function | false | A callback that is fired when the transaction is signed and broadcasted. Your callback will receive an object back with a `txId` and a `txRaw`, both of which are strings. | +| authOrigin | string | true | Location of the authenticator to use for signing. Defaults `"https://app.blockstack.org"`. | -## Deploying Clarity Contracts +## Deploy smart contract -To allow your app's users to deploy arbitrary Clarity contracts, use the `openContractDeploy` method. +To allow your app's users to deploy Clarity smart contracts, use the `openContractDeploy` method. ```tsx import { openContractDeploy } from '@blockstack/connect'; @@ -221,7 +223,7 @@ Each transaction signing method is exposed through the `useConnect` hook, but th `open`, to remain consistent with our React action naming standards. ```tsx -import { useConnect } from '@blockstack/connect'; +import { useConnect } from '@stacks/connect'; const MyComponent = () => { const { doContractCall } = useConnect(); @@ -236,3 +238,9 @@ const MyComponent = () => { return Call my contract; }; ``` + +## Network settings + +TBD: instructions on toggling network + +You may find it useful to request testnet STX from [the faucet](https://testnet.blockstack.org/faucet) while developing and testing your app with the Stacks testnet. diff --git a/src/pages/data-storage/indexing-collaborate.md b/src/pages/build-apps/indexing/collaboration.md similarity index 97% rename from src/pages/data-storage/indexing-collaborate.md rename to src/pages/build-apps/indexing/collaboration.md index a3f338be..4f25d9a5 100644 --- a/src/pages/data-storage/indexing-collaborate.md +++ b/src/pages/build-apps/indexing/collaboration.md @@ -1,6 +1,6 @@ --- -title: Collaborate with groups -description: A key feature of Radiks is support for private collaboration between multiple users. +title: Collaboration +description: Support private collaboration between multiple users with Radiks --- ## Introduction diff --git a/src/pages/data-storage/indexing-models.md b/src/pages/build-apps/indexing/models.md similarity index 99% rename from src/pages/data-storage/indexing-models.md rename to src/pages/build-apps/indexing/models.md index 6953eb68..93be2a59 100644 --- a/src/pages/data-storage/indexing-models.md +++ b/src/pages/build-apps/indexing/models.md @@ -1,6 +1,6 @@ --- -title: Create and use models -description: Model and query application data with Radiks. +title: Models +description: Model and query application data with Radiks --- ## Introduction diff --git a/src/pages/data-storage/indexing-overview.md b/src/pages/build-apps/indexing/overview.md similarity index 97% rename from src/pages/data-storage/indexing-overview.md rename to src/pages/build-apps/indexing/overview.md index 2c09129f..4bf26b7b 100644 --- a/src/pages/data-storage/indexing-overview.md +++ b/src/pages/build-apps/indexing/overview.md @@ -1,6 +1,6 @@ --- -title: Indexing overview -description: Using Radiks you can build multi-player apps that index, store, and query user data. +title: Overview +description: Build multi-player apps that index, store, and query user data with Radiks images: large: /images/pages/radiks.svg sm: /images/pages/radiks-sm.svg diff --git a/src/pages/data-storage/indexing-server-extras.md b/src/pages/build-apps/indexing/server.md similarity index 98% rename from src/pages/data-storage/indexing-server-extras.md rename to src/pages/build-apps/indexing/server.md index 27db339c..349e2ff9 100644 --- a/src/pages/data-storage/indexing-server-extras.md +++ b/src/pages/build-apps/indexing/server.md @@ -1,6 +1,6 @@ --- -title: Server tips and tricks -description: Some tips and tricks for working with a Radiks server. +title: Server +description: Tips and tricks for working with Radiks server --- ## Access the MongoDB collection diff --git a/src/pages/build-apps/overview.md b/src/pages/build-apps/overview.md new file mode 100644 index 00000000..3225b070 --- /dev/null +++ b/src/pages/build-apps/overview.md @@ -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 diff --git a/src/pages/technology/naming-system.md b/src/pages/build-apps/references/bns.md similarity index 99% rename from src/pages/technology/naming-system.md rename to src/pages/build-apps/references/bns.md index f9c4215d..2866d858 100644 --- a/src/pages/technology/naming-system.md +++ b/src/pages/build-apps/references/bns.md @@ -1,12 +1,11 @@ --- -title: Overview -description: 'Blockchain naming system (BNS)' +title: Blockchain Naming System +description: Binds Stacks usernames to off-chain state --- -# Blockchain Naming System (BNS) - -BNS is a network system that binds names +Blockchain Naming System (BNS) is a network system that binds Stacks usernames to off-chain state without relying on any central points of control. + The Stacks V1 blockchain implemented BNS through first-order name operations. In Stacks V2, BNS is instead implemented through a smart-contract loaded during the genesis block. diff --git a/src/pages/data-storage/overview.md b/src/pages/build-apps/references/gaia.md similarity index 90% rename from src/pages/data-storage/overview.md rename to src/pages/build-apps/references/gaia.md index 33bf26ec..ace31550 100644 --- a/src/pages/data-storage/overview.md +++ b/src/pages/build-apps/references/gaia.md @@ -1,6 +1,6 @@ --- -title: A decentralized storage architecture -description: Storing user data with Stacks +title: Gaia +description: Decentralized storage architecture for off-chain data images: large: /images/pages/data-storage.svg sm: /images/pages/data-storage-sm.svg @@ -8,11 +8,13 @@ images: ## Introduction -The Stacks Network stores application data using a storage system called -Gaia. 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 +Apps built with the Stacks blockchain store off-chain data using a storage system called +Gaia. + +Whereas public transactional metadata is best stored on the Stacks blockchain, user +application data can often be stored more efficiently and privately 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. ## Understand Gaia in the Stacks architecture @@ -63,7 +65,13 @@ Applications writing directly on behalf of `alice.id` do not need to perform a l A Gaia hub stores the written data _exactly_ as given. It offers minimal guarantees about the data. It does not ensure that data is validly formatted, contains valid signatures, or is encrypted. Rather, the design philosophy is that these concerns are client-side concerns. -Client libraries (such as `Stacks.js`) are capable of providing these guarantees. Blockstack used a liberal definition of the [end-to-end principle](https://en.wikipedia.org/wiki/End-to-end_principle) to guide this design decision. +Client libraries (such as `Stacks.js`) are capable of providing these guarantees. A liberal definition of the [end-to-end principle](https://en.wikipedia.org/wiki/End-to-end_principle) guides this design decision. + +When an application writes to a Gaia hub, an authentication token, key, and the data are passed to the Gaia hub. + +![Gaia writes](/images/gaia-writes.png) + +The token ensures the app has the authorization to write to the hub on the user's behalf. ## Gaia versus other storage systems diff --git a/src/pages/authentication/overview.md b/src/pages/build-apps/references/stacks-connect.md similarity index 80% rename from src/pages/authentication/overview.md rename to src/pages/build-apps/references/stacks-connect.md index 842b5de5..fe7ee248 100644 --- a/src/pages/authentication/overview.md +++ b/src/pages/build-apps/references/stacks-connect.md @@ -1,26 +1,28 @@ --- -title: Authentication -description: Stacks Auth provides single sign on and authentication without third parties or remote servers. +title: Stacks Connect +description: Open protocol for connecting apps built with Stacks images: large: /images/pages/authentication.svg sm: /images/pages/authentication-sm.svg --- +Stacks Connect is an open protocol for connecting apps built with the Stacks blockchain, such as consumer apps with authenticators and wallets. + ## Authentication flow -For an application developer, the application flow is similar to the typical client-server flow used by centralized sign in services (for example, OAuth). However, with Stacks auth, the authentication flow happens entirely client-side. +For an application developer, the application flow is similar to the typical client-server flow used by centralized sign in services (for example, OAuth). However, with Stacks Connect, the authentication flow happens entirely client-side. -A decentralized application and [the Blockstack App](https://github.com/blockstack/ux/tree/master/packages/app) communicate during the authentication flow by passing back and forth two tokens. The requesting application sends the Blockstack App an `authRequest` token. Once a user approves a sign-in, the Blockstack App responds to the application with an `authResponse` token. These tokens are JSON Web Tokens, and they are passed via URL query strings. +An app and authenticator, such as [the Stacks Wallet](https://blockstack.org/wallet), communicate during the authentication flow by passing back and forth two tokens. The requesting application sends the authenticator an `authRequest` token. Once a user approves a sign-in, the authenticator responds to the application with an `authResponse` token. These tokens are JSON Web Tokens, and they are passed via URL query strings. ![](/images/app-sign-in.png) -When a user chooses to authenticate a decentralized application, it calls the `doOpenAuth()` method which sends an `authRequest` to the Blockstack App. Stacks auth passes the token in via a URL query string in the `authRequest` parameter: +When a user chooses to authenticate a decentralized application, it calls the `doOpenAuth()` method which sends an `authRequest` to the authenticator. Stacks auth passes the token in via a URL query string in the `authRequest` parameter: `https://app.blockstack.org/#/sign-up?authRequest=j902120cn829n1jnvoa...` -When the Blockstack App receives the request, it generates an (`authResponse`) token to the application using an _ephemeral transit key_ . The ephemeral transit key is just used for the particular instance of the application, in this case, to sign the `authRequest`. The application stores the ephemeral transit key during the request generation. The public portion of the transit key is passed in the `authRequest` token. The Blockstack App uses the public portion of the key to encrypt an _app-private key_ which is returned via the `authResponse`. +When the authenticator receives the request, it generates an (`authResponse`) token to the application using an _ephemeral transit key_ . The ephemeral transit key is just used for the particular instance of the application, in this case, to sign the `authRequest`. The application stores the ephemeral transit key during the request generation. The public portion of the transit key is passed in the `authRequest` token. The authenticator uses the public portion of the key to encrypt an _app-private key_ which is returned via the `authResponse`. -During sign in, the Blockstack App generates the app-private key from the user's _identity-address private_ key and the application's `appDomain`. The app private key serves three functions: +During sign in, the authenticator generates the app-private key from the user's _identity-address private_ key and the application's `appDomain`. The app private key serves three functions: - It is used to create the credentials that give an app access to the Gaia storage bucket for that specific app. - It is used in the end-to-end encryption of files stored for the app in the user's Gaia storage. @@ -62,7 +64,7 @@ The following is an example manifest file. } ``` -The Blockstack App retrieves the manifest file from the app during the authentication process and displays the +The Stacks Wallet retrieves the manifest file from the app during the authentication process and displays the information in it such as the app `name` and to the user during sign in. The location of the app manifest file is specific in the authentication request token and **must** be on the same origin as the app requesting authentication. @@ -88,13 +90,13 @@ following sections describe the three public-private key pairs used in the authe ### Transit private key The transit private is an ephemeral key that is used to encrypt secrets that -need to be passed from the Blockstack App to the decentralized app during the +need to be passed from the authenticator to the decentralized app during the authentication process. It is randomly generated by the app at the beginning of the authentication response. The public key that corresponds to the transit private key is stored in a single element array in the `public_keys` key of the authentication request token. The -Blockstack App encrypts secret data such as the app private key using this +authenticator encrypts secret data such as the app private key using this public key and sends it back to the app when the user signs in to the app. The transit private key signs the app authentication request. @@ -103,7 +105,7 @@ transit private key signs the app authentication request. The identity address private key is derived from the user's keychain phrase and is the private key of the Stacks username that the user chooses to use to sign in to the app. It is a secret owned by the user and never leaves the user's -instance of the Blockstack App. +instance of the authenticator. This private key signs the authentication response token for an app to indicate that the user approves sign in to that app. @@ -114,7 +116,7 @@ user's identity address private key using the `domain_name` as input. It is deterministic in that for a given Stacks username and `domain_name`, the same private key is generated each time. -The app private key is securely shared with the app on each authentication, encrypted by the Blockstack App with the transit public key. +The app private key is securely shared with the app on each authentication, encrypted by the authenticator with the transit public key. ## JSON Web Token signatures @@ -144,9 +146,9 @@ const requestPayload = { public_keys, // single entry array with public key of transit key domain_name, // app origin manifest_uri, // url to manifest file - must be hosted on app origin - redirect_uri, // url to which the Blockstack App redirects user on auth approval - must be hosted on app origin + redirect_uri, // url to which the authenticator redirects user on auth approval - must be hosted on app origin version, // version tuple - do_not_include_profile, // a boolean flag asking Blockstack App to send profile url instead of profile object + do_not_include_profile, // a boolean flag asking authenticator to send profile url instead of profile object supports_hub_url, // a boolean flag indicating gaia hub support scopes, // an array of string values indicating scopes requested by the app }; @@ -277,3 +279,7 @@ const recoveredProfile = Person.fromToken(tokenFile, publicKey); ```jsx const validationResults = Person.validateSchema(recoveredProfile); ``` + +### Transaction signing + +TBD: info on transaction signing protocol diff --git a/src/pages/authentication/building-with-angular.md b/src/pages/build-apps/tutorials/angular.md similarity index 99% rename from src/pages/authentication/building-with-angular.md rename to src/pages/build-apps/tutorials/angular.md index d3e64535..cd9a3225 100644 --- a/src/pages/authentication/building-with-angular.md +++ b/src/pages/build-apps/tutorials/angular.md @@ -1,5 +1,5 @@ --- -title: Stacks Auth with Angular +title: Angular description: How to integrate authentication within an Angular application experience: beginners duration: 30 minutes diff --git a/src/pages/data-storage/integrate-indexing.md b/src/pages/build-apps/tutorials/indexing.md similarity index 99% rename from src/pages/data-storage/integrate-indexing.md rename to src/pages/build-apps/tutorials/indexing.md index 2d8b71e5..e5278995 100644 --- a/src/pages/data-storage/integrate-indexing.md +++ b/src/pages/build-apps/tutorials/indexing.md @@ -1,6 +1,6 @@ --- -title: Integrate Radiks -description: Learn how to setup Radiks with your application +title: Radiks +description: Learn how to setup Radiks with your app icon: BlockstackIcon duration: 1 hour experience: intermediate diff --git a/src/pages/write-smart-contracts/public-registry-tutorial.md b/src/pages/build-apps/tutorials/public-registry.md similarity index 99% rename from src/pages/write-smart-contracts/public-registry-tutorial.md rename to src/pages/build-apps/tutorials/public-registry.md index 2d9c3921..ac96daa9 100644 --- a/src/pages/write-smart-contracts/public-registry-tutorial.md +++ b/src/pages/build-apps/tutorials/public-registry.md @@ -1,5 +1,5 @@ --- -title: Building a public registry +title: Public registry description: Learn how to read state from the Stacks blockchain. duration: 60 minutes experience: intermediate diff --git a/src/pages/authentication/building-todo-app.md b/src/pages/build-apps/tutorials/todos.md similarity index 98% rename from src/pages/authentication/building-todo-app.md rename to src/pages/build-apps/tutorials/todos.md index 996b982f..b156c4d9 100644 --- a/src/pages/authentication/building-todo-app.md +++ b/src/pages/build-apps/tutorials/todos.md @@ -1,6 +1,6 @@ --- -title: Building a Todo app -description: Learn how to integrate authentication and data storage with React. +title: Todos app +description: Review authentication and data storage integration experience: beginners duration: 30 minutes tags: @@ -10,8 +10,6 @@ images: sm: /images/pages/todo-app-sm.svg --- -# Learn from a todos app built with Stacks - ![What you'll be studying in this tutorial](/images/todos/home.png) ## Introduction diff --git a/src/pages/data-storage/authentication.md b/src/pages/data-storage/authentication.md deleted file mode 100644 index 17382e06..00000000 --- a/src/pages/data-storage/authentication.md +++ /dev/null @@ -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 JSON Web Tokens, 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. diff --git a/src/pages/data-storage/storage-guide.md b/src/pages/data-storage/storage-guide.md deleted file mode 100644 index 18c82d60..00000000 --- a/src/pages/data-storage/storage-guide.md +++ /dev/null @@ -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) diff --git a/src/pages/data-storage/storage-write-read.md b/src/pages/data-storage/storage-write-read.md deleted file mode 100644 index fde93cf7..00000000 --- a/src/pages/data-storage/storage-write-read.md +++ /dev/null @@ -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/
/` 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. diff --git a/src/pages/ecosystem/contributing.md b/src/pages/ecosystem/contributing.md index 31edcd5a..20243691 100644 --- a/src/pages/ecosystem/contributing.md +++ b/src/pages/ecosystem/contributing.md @@ -148,11 +148,9 @@ sections: - path: /hello-world-tutorial - path: /counter-tutorial - path: /testing-contracts - - path: /public-registry-tutorial - - path: /signing-transactions - path: /mining # is an overview page - - title: Technology + - title: Technology ## NEEDS UPDATE ONCE STABLE pages: - path: /authentication pages: diff --git a/src/pages/write-smart-contracts/overview.md b/src/pages/write-smart-contracts/overview.md index 2adb4f88..0d1bea19 100644 --- a/src/pages/write-smart-contracts/overview.md +++ b/src/pages/write-smart-contracts/overview.md @@ -67,7 +67,7 @@ Note some of the key Clarity language rules and limitations. ## Try a tutorial [@page-reference | grid] -| /write-smart-contracts/hello-world-tutorial, /write-smart-contracts/counter-tutorial, /write-smart-contracts/signing-transactions, /write-smart-contracts/public-registry-tutorial +| /write-smart-contracts/hello-world-tutorial, /write-smart-contracts/counter-tutorial, /build-apps/guides/transaction-signing, /build-apps/tutorials/public-registry ## Explore more