diff --git a/apps/academy/public/assets/tracks/arweave-101/4/figure-1.png b/apps/academy/public/assets/tracks/arweave-101/4/figure-1.png new file mode 100644 index 00000000..ab904ca0 Binary files /dev/null and b/apps/academy/public/assets/tracks/arweave-101/4/figure-1.png differ diff --git a/apps/academy/public/assets/tracks/arweave-101/4/figure-2.png b/apps/academy/public/assets/tracks/arweave-101/4/figure-2.png new file mode 100644 index 00000000..6cb4a335 Binary files /dev/null and b/apps/academy/public/assets/tracks/arweave-101/4/figure-2.png differ diff --git a/apps/academy/public/meta-images/arweave-101/building-a-static-website-on-arweave.png b/apps/academy/public/meta-images/arweave-101/building-a-static-website-on-arweave.png new file mode 100644 index 00000000..5e6c3b3e Binary files /dev/null and b/apps/academy/public/meta-images/arweave-101/building-a-static-website-on-arweave.png differ diff --git a/apps/academy/src/components/LessonLayout.tsx b/apps/academy/src/components/LessonLayout.tsx index 81954207..e0ab19c1 100644 --- a/apps/academy/src/components/LessonLayout.tsx +++ b/apps/academy/src/components/LessonLayout.tsx @@ -1,9 +1,9 @@ // import AboutCourse from "@/components/AboutCourse"; -import { useRouter } from "next/router"; +// import { useRouter } from "next/router"; import CreatedBy from "@/components/CreatedBy"; import PageSeoLayout from "@/components/PageSeoLayout"; -import { api } from "@/utils/api"; +// import { api } from "@/utils/api"; interface LessonLayoutProps { children: React.ReactNode; @@ -26,14 +26,12 @@ export default function LessonLayout({ authorImage, authorTwitter, }: LessonLayoutProps) { - const router = useRouter(); + // const router = useRouter(); - console.log(router.pathname); + // const { data: lessonData } = api.lessons.getLessonsByLessonPath.useQuery({ + // lessonPath: router.pathname, + // }); - const { data: lessonData } = api.lessons.getLessonsByLessonPath.useQuery({ - lessonPath: router.pathname, - }); - console.log("poop", { lessonData }); return ( (
diff --git a/apps/academy/src/components/mdx/QuizStatusChecker.tsx b/apps/academy/src/components/mdx/QuizStatusChecker.tsx index 6085fa6c..2ce202d8 100644 --- a/apps/academy/src/components/mdx/QuizStatusChecker.tsx +++ b/apps/academy/src/components/mdx/QuizStatusChecker.tsx @@ -10,7 +10,7 @@ import Quiz from "./Quiz"; export interface QuizStatusCheckerType { quiz: string; successMessage: { message: string }[]; - successTitle: string; + successTitle?: string; actionButton: any; } diff --git a/apps/academy/src/data/questions/arweave-101/4/quiz-1/Q1.json b/apps/academy/src/data/questions/arweave-101/4/quiz-1/Q1.json new file mode 100644 index 00000000..87f541d9 --- /dev/null +++ b/apps/academy/src/data/questions/arweave-101/4/quiz-1/Q1.json @@ -0,0 +1,15 @@ +{ + "question": "What is the goal of Arweave?", + "options": [ + { + "answer": "To provide the world with permanent, immutable storage.", + "correct": true + }, + { + "answer": "To dethrone Bitcoin as the most valuable cryptocurrency." + }, + { + "answer": "To save you from paying taxes." + } + ] +} diff --git a/apps/academy/src/data/questions/arweave-101/4/quiz-1/Q2.json b/apps/academy/src/data/questions/arweave-101/4/quiz-1/Q2.json new file mode 100644 index 00000000..9f9f04c5 --- /dev/null +++ b/apps/academy/src/data/questions/arweave-101/4/quiz-1/Q2.json @@ -0,0 +1,15 @@ +{ + "question": "Does Arweave treat websites different from other data?", + "options": [ + { + "answer": "Yes, if you add the correct tags, gateways will deliver the data as website to a browser.", + "correct": true + }, + { + "answer": "No, Arweave forces all browsers to download them as files." + }, + { + "answer": "Yes, Arweave uses React to render websites on the gateway." + } + ] +} diff --git a/apps/academy/src/data/questions/arweave-101/4/quiz-1/Q3.json b/apps/academy/src/data/questions/arweave-101/4/quiz-1/Q3.json new file mode 100644 index 00000000..3385d091 --- /dev/null +++ b/apps/academy/src/data/questions/arweave-101/4/quiz-1/Q3.json @@ -0,0 +1,15 @@ +{ + "question": "Why is a static website generator the perfect tool to create websites for Arweave?", + "options": [ + { + "answer": "Arweave gateways can’t render HTML and build-time rendering solves this issue.", + "correct": true + }, + { + "answer": "It isn’t well suited for Arweave, as the files are getting too big." + }, + { + "answer": "Static websites revive the spirit of the early web." + } + ] +} diff --git a/apps/academy/src/data/questions/arweave-101/4/quiz-2/Q1.json b/apps/academy/src/data/questions/arweave-101/4/quiz-2/Q1.json new file mode 100644 index 00000000..498c8b4a --- /dev/null +++ b/apps/academy/src/data/questions/arweave-101/4/quiz-2/Q1.json @@ -0,0 +1,15 @@ +{ + "question": "Does Eleventy need any special configuration to work with Arweave?", + "options": [ + { + "answer": "Yes, it needs a filter to rewrite relative URLs, so they work with the TXID Arweave websites start with.", + "correct": true + }, + { + "answer": "No, Eleventy works with Arweave out of the box." + }, + { + "answer": "Yes, Eleventy needs advanced compression to fit the page on Arweave." + } + ] +} diff --git a/apps/academy/src/data/questions/arweave-101/4/quiz-2/Q2.json b/apps/academy/src/data/questions/arweave-101/4/quiz-2/Q2.json new file mode 100644 index 00000000..28704a8e --- /dev/null +++ b/apps/academy/src/data/questions/arweave-101/4/quiz-2/Q2.json @@ -0,0 +1,15 @@ +{ + "question": "How do you use the rel filter in a Eleventy template?", + "options": [ + { + "answer": "{{ post.url | rel }}", + "correct": true + }, + { + "answer": "rel(post.url)" + }, + { + "answer": "{{ rel(post.url) }}" + } + ] +} diff --git a/apps/academy/src/data/questions/arweave-101/4/quiz-2/Q3.json b/apps/academy/src/data/questions/arweave-101/4/quiz-2/Q3.json new file mode 100644 index 00000000..fffe94ca --- /dev/null +++ b/apps/academy/src/data/questions/arweave-101/4/quiz-2/Q3.json @@ -0,0 +1,15 @@ +{ + "question": "How can using relative URLs in your website lead to permanent links?", + "options": [ + { + "answer": "While the URLs are relative to the TXID they belong to, this ID never changes.", + "correct": true + }, + { + "answer": "It can’t. The moment the TXID changes, the relative links of your website are all broken." + }, + { + "answer": "Eleventy automatically applies a element to all of your links." + } + ] +} diff --git a/apps/academy/src/data/questions/arweave-101/4/quiz-3/Q1.json b/apps/academy/src/data/questions/arweave-101/4/quiz-3/Q1.json new file mode 100644 index 00000000..2e03948b --- /dev/null +++ b/apps/academy/src/data/questions/arweave-101/4/quiz-3/Q1.json @@ -0,0 +1,15 @@ +{ + "question": "Why can you deploy a website with a randomly generated wallet?", + "options": [ + { + "answer": "Arweave bundling services subsidize uploads under 100 KiB.", + "correct": true + }, + { + "answer": "They can’t, all uploads are in transactions that cost money to settle onchain." + }, + { + "answer": "The first 10 files are free to upload." + } + ] +} diff --git a/apps/academy/src/data/questions/arweave-101/4/quiz-3/Q2.json b/apps/academy/src/data/questions/arweave-101/4/quiz-3/Q2.json new file mode 100644 index 00000000..c4d90c6b --- /dev/null +++ b/apps/academy/src/data/questions/arweave-101/4/quiz-3/Q2.json @@ -0,0 +1,15 @@ +{ + "question": "How does Arweave change the content of a TXID after you uploaded a new version of a file?", + "options": [ + { + "answer": "It doesn’t, the new upload will get a new TXID.", + "correct": true + }, + { + "answer": "It apppends the new version to the old file." + }, + { + "answer": "You can’t upload multiple versions of one file." + } + ] +} diff --git a/apps/academy/src/data/questions/arweave-101/4/quiz-3/Q3.json b/apps/academy/src/data/questions/arweave-101/4/quiz-3/Q3.json new file mode 100644 index 00000000..64a860e5 --- /dev/null +++ b/apps/academy/src/data/questions/arweave-101/4/quiz-3/Q3.json @@ -0,0 +1,15 @@ +{ + "question": "What enables users to access multiple files with one TXID?", + "options": [ + { + "answer": "A JSON path manifest that maps paths to TXIDs.", + "correct": true + }, + { + "answer": "ZIP archive uploads." + }, + { + "answer": "An XML manifest that links TXIDs to URLs." + } + ] +} diff --git a/apps/academy/src/data/questions/arweave-101/4/quiz-4/Q1.json b/apps/academy/src/data/questions/arweave-101/4/quiz-4/Q1.json new file mode 100644 index 00000000..469b2634 --- /dev/null +++ b/apps/academy/src/data/questions/arweave-101/4/quiz-4/Q1.json @@ -0,0 +1,15 @@ +{ + "question": "Are ArNS names immutable?", + "options": [ + { + "answer": "No, you can change their target TXIDs.", + "correct": true + }, + { + "answer": "Yes, once you set a TXID, they will always point to that." + }, + { + "answer": "No, they can change 5 times after registration." + } + ] +} diff --git a/apps/academy/src/data/questions/arweave-101/4/quiz-4/Q2.json b/apps/academy/src/data/questions/arweave-101/4/quiz-4/Q2.json new file mode 100644 index 00000000..f6e31634 --- /dev/null +++ b/apps/academy/src/data/questions/arweave-101/4/quiz-4/Q2.json @@ -0,0 +1,15 @@ +{ + "question": "Are ArNS names linked to a gateway?", + "options": [ + { + "answer": "No, you can access every ArNS name from every gateway.", + "correct": true + }, + { + "answer": "Yes, you have to choose a gateway at registration." + }, + { + "answer": "Yes, but only for the first month." + } + ] +} diff --git a/apps/academy/src/data/questions/arweave-101/4/quiz-4/Q3.json b/apps/academy/src/data/questions/arweave-101/4/quiz-4/Q3.json new file mode 100644 index 00000000..41ad0584 --- /dev/null +++ b/apps/academy/src/data/questions/arweave-101/4/quiz-4/Q3.json @@ -0,0 +1,15 @@ +{ + "question": "Where can you find the immutable TXID of an ArNS page?", + "options": [ + { + "answer": "In the X-Arns-Resolved-Id header field.", + "correct": true + }, + { + "answer": "In the Arweave-Txid header field." + }, + { + "answer": "You can’t, the TXID is hidden from the client." + } + ] +} diff --git a/apps/academy/src/data/quizzes/arweave-101/4.json b/apps/academy/src/data/quizzes/arweave-101/4.json new file mode 100644 index 00000000..d2740b80 --- /dev/null +++ b/apps/academy/src/data/quizzes/arweave-101/4.json @@ -0,0 +1,196 @@ +{ + "title": "Final Quiz: Building a Static Site on Arweave", + "questions": [ + { + "question": "Why was Arweave created?", + "options": [ + { + "answer": "To provide the world with permanent, immutable storage.", + "correct": true + }, + { + "answer": "To increase the TPS of Ethereum and Solana." + }, + { + "answer": "To bridge Bitcoin to Ethereum." + } + ] + }, + + { + "question": "Does websites get special treatment from Arweave gateways?", + "options": [ + { + "answer": "Yes, if you add the correct tags, gateways will deliver the data as website to a browser.", + "correct": true + }, + { + "answer": "No, you must download all files with BitTorrent." + }, + { + "answer": "Yes, Arweave uses Vite to render websites on the gateway." + } + ] + }, + + { + "question": "Why would you use a static site generator to build website on Arweave?", + "options": [ + { + "answer": "Arweave gateways can’t render HTML and build-time rendering solves this issue.", + "correct": true + }, + { + "answer": "You wouldn’t, as the rendering inhibits token allocations." + }, + { + "answer": "You woulnd’t, dynamic websites would be better suited, as they’re more flexible.." + } + ] + }, + + { + "question": "Does Eleventy create Arweave compliant websites out of the box?", + "options": [ + { + "answer": "No, it needs a filter to rewrite relative URLs, so they work with the TXID Arweave websites start with.", + "correct": true + }, + { + "answer": "Yes, no special configuration required." + }, + { + "answer": "No, Eleventy needs advanced compression to fit the page on Arweave." + } + ] + }, + + { + "question": "How do you make Eleventy’s relative URLs work on Arweave?", + "options": [ + { + "answer": "{{ post.url | rel }}", + "correct": true + }, + { + "answer": "rel(post.url)" + }, + { + "answer": "{{ rel(post.url) }}" + } + ] + }, + + { + "question": "Do the relative links created by Eleventy break on an update?", + "options": [ + { + "answer": "No, updates always get new TXIDs. Existing versions stay the same.", + "correct": true + }, + { + "answer": "Yes, when the TXID changes, the links don’t work anymore." + }, + { + "answer": "No, gateways always update TXIDs retroactively." + } + ] + }, + + { + "question": "Do you need to purchase AR tokens to deploy a website?", + "options": [ + { + "answer": "No, Arweave bundling services subsidize uploads under 100 KiB.", + "correct": true + }, + { + "answer": "Yes, all uploads are in transactions that cost money to settle onchain." + }, + { + "answer": "No, new wallets can submit 5 free transactions." + } + ] + }, + + { + "question": "Does Arweave change the content of a TXID after you upload a new version of a file?", + "options": [ + { + "answer": "No, each new upload will get a new TXID.", + "correct": true + }, + { + "answer": "Yes, it apppends the new version to the old file." + }, + { + "answer": "Np, You can’t upload multiple versions of one file." + } + ] + }, + + { + "question": "How can you access multiple files with one TXID?", + "options": [ + { + "answer": "A JSON path manifest that maps paths to TXIDs.", + "correct": true + }, + { + "answer": "Impossible,you need to keep track of the TXIDs of all pages to link them with each other." + }, + { + "answer": "An XML manifest that links TXIDs to URLs." + } + ] + }, + + { + "question": "Can you change the TXID of an ArNS name?", + "options": [ + { + "answer": "Yes, you can change their target TXIDs.", + "correct": true + }, + { + "answer": "No, once you set a TXID, they will always point to that." + }, + { + "answer": "Yes, they can change 5 times after registration." + } + ] + }, + + { + "question": "How many gateways can deliver a website behind an ArNS name?", + "options": [ + { + "answer": "You can access every ArNS name from every gateway.", + "correct": true + }, + { + "answer": "Only the first 5 gateways that access the ArNS name will deliver the website." + }, + { + "answer": "10 gateways will deliver the website" + } + ] + }, + + { + "question": "Is there a way to access the immutable TXID of a website?", + "options": [ + { + "answer": "Yes, it’s stored in the X-Arns-Resolved-Id header field.", + "correct": true + }, + { + "answer": "Yes, it’s stored in the Arweave-Txid header field." + }, + { + "answer": "No, the TXID is hidden from the client." + } + ] + } + ] +} diff --git a/apps/academy/src/pages/tracks/arweave-101/1.mdx b/apps/academy/src/pages/tracks/arweave-101/1.mdx index 9d57fc50..2500814b 100644 --- a/apps/academy/src/pages/tracks/arweave-101/1.mdx +++ b/apps/academy/src/pages/tracks/arweave-101/1.mdx @@ -1,6 +1,6 @@ --- title: Arweave 101: Lesson 0 -description: TODO +description: This lesson covers the technical and theoretical essentials of the Arweave blockchain. It’s not required to get started, but it helps you understand its features and limitations. icons: ["arweave", "fundamentals"] --- diff --git a/apps/academy/src/pages/tracks/arweave-101/2.mdx b/apps/academy/src/pages/tracks/arweave-101/2.mdx index b3714605..4407f31c 100644 --- a/apps/academy/src/pages/tracks/arweave-101/2.mdx +++ b/apps/academy/src/pages/tracks/arweave-101/2.mdx @@ -1,6 +1,6 @@ --- title: Arweave 101: Lesson 1 -description: TODO +description: This lesson covers data access on Arweave via AR.IO gateways. It's the first practical lesson of the course, but it doesn’t require knowledge of wallets or Arweave. icons: ["arweave", "fundamentals"] --- diff --git a/apps/academy/src/pages/tracks/arweave-101/3.mdx b/apps/academy/src/pages/tracks/arweave-101/3.mdx index 57379557..a5a875d3 100644 --- a/apps/academy/src/pages/tracks/arweave-101/3.mdx +++ b/apps/academy/src/pages/tracks/arweave-101/3.mdx @@ -1,6 +1,6 @@ --- title: Arweave 101: Lesson 2 -description: TODO +description: This lesson covers data upload to Arweave via bundling services. It's the first practical lesson of the course that requires an Arweave wallet. icons: ["arweave", "fundamentals"] --- diff --git a/apps/academy/src/pages/tracks/arweave-101/4.mdx b/apps/academy/src/pages/tracks/arweave-101/4.mdx new file mode 100644 index 00000000..58c5b508 --- /dev/null +++ b/apps/academy/src/pages/tracks/arweave-101/4.mdx @@ -0,0 +1,542 @@ +--- +title: Arweave 101: Lesson 3 +description: Deploy a static website to Arweave. Start with a static site generator and learn to deploy and update the site. +icons: ["arweave", "fundamentals"] +--- + +import LessonLayout from "../../../components/LessonLayout"; +import Callout from "../../../components/mdx/Callout"; +import QuizStatusChecker from "../../../components/mdx/QuizStatusChecker"; +import Question from "../../../components/mdx/Question"; +import LessonQuestionsModal from "../../../components/mdx/LessonQuestionsModal"; +import LessonInformationalModal from "../../../components/mdx/LessonInformationalModal"; + + + +## About this lesson + +Greetings! I welcome you to the first project lesson of the Arweave 101 track. In this lesson, you +will test your knowledge of the previous lessons by hosting your first website on Arweave. + +You will start by **creating a website** with [Eleventy](https://www.11ty.dev/), a popular static +site generator. Then, implement an upload script that leverages +[the Turbo SDK](https://github.com/ardriveapp/turbo-sdk) and +[Arweave-js](https://github.com/ArweaveTeam/arweave-js) for deployment and choose an +easy-to-remember ArNS name. + +## Prerequisites + +**Understanding the topics of [lessons 1](/tracks/arweave-101/2) and +[lesson 2](/tracks/arweave-101/3) is a must to start this lesson.** + +You need a basic understanding of web technologies like **HTTPS, HTML, JavaScript, and Node.js.** + +A **basic understanding of blockchains** is helpful, too. At least you should understand what +wallets and transactions are. + +A **browser, Node.js, and an ArConnect wallet** to try the examples. + + + Note: The ArNS section of this lesson requires AR.IO test tokens (tIO). You can complete the whole + lesson without the ArNS part, but the UX of your site might suffer from a missing ArNS name. You + can get tIO through an ecosystem DEX or by completing an EXPerience quest. I will explain that + process later in the ArNS section of this lesson. + + +## Why a Static Website? + +Arweave’s primary goal is to **provide the world with permanent, immutable storage**. Hosting static +websites and single-page applications (SPAs) are great realizations of that mission. While Arweave +allows the upload of arbitrary data, websites have a special place: they form the Permaweb—a +decentralized, censorship-resistant version of the Web, without dead links. If you add the correct +tags when uploading a website, gateway nodes will deliver them with `text/html` Content-Type and +make them accessible via a browser. + +The spirit of this course is to **start from the simple concepts and then move to the more complex +ones**, and since a static website is easier to build than a SPA, you will begin with a static +website. After mastering the basics, you’ll learn about SPAs in the next lesson. + + + + + + + +## Creating Your Website + +Your first step is to create the website; in this case, you will create a blog with Eleventy. To do +this, you must create a new Node.js project, install Eleventy, and add files that conform to +Eleventy’s templating language. + +### Setting Up the Project + +To create the website, you have to create a new Node.js project and install Eleventy with the +following commands: + + mkdir permablog && cd permablog + npm init -y + npm i -D arweave @11ty/eleventy @ardrive/turbo-sdk @ar.io/sdk + +Create a `.eleventy.js` file to define a filter that drops the first slash. Eleventy will start all +permalinks with a slash, but websites on Arweave are nested under their transaction ID (TXID), so +going to `/your-post/` doesn’t work. Arweave TXIDs are permanent, so that’s no issue on the +Permaweb, but Eleventy doesn’t know this. + +Add the following code to the config file: + +```javascript +module.exports = function (eleventyConfig) { + eleventyConfig.addFilter("rel", (url) => url.replace(/^\//, "")); +}; +``` + +This enables you to use `{{ yourUrl | rel }}` in the templates when you want to convert an absolute +URL generated by Eleventy to a relative URL that works on the Permaweb. + +By default, an Eleventy permalink defined as `/your-post/` would point to this (non-existent) URL: + + https://arweave.developerdao.com/your-post/ + +With the filter, it will point to this URL instead: + + https://arweave.developerdao.com/some-transaction-id-9999/your-post/ + +### Creating a Layout + +Eleventy's sophisticated templating system allows you to reuse HTML code from layout files. So, your +first step is to create a layout file containing the scaffold HTML for your blog, which Eleventy +will wrap around all your content files. + +Create a new file at `src/_includes/layout.html`. Eleventy processes HTML files with liquid +templates, which enables you to render dynamic content and display elements conditionally. Eleventy +will render the template you wrapped with this layout at the `content`. If the wrapped template +contains a `title` in its data, Eleventy will render a link back to your home page. + +Add the following code to the new file: + +```html + + + +{{ title | default: "Permablog" }} +

{{ title | default: "Permablog" }}

+{% if title %} +
Articles +{% endif %} {{ content }} +``` + +### Creating the Pages + +Next, create a new `src/index.md` file. This will become your blog’s home page, listing links to all +blog posts. Like HTML, Markdown files are processed with liquid templates to allow dynamic content. + +The front matter at the top of a template contains either data that Eleventy uses, for example, to +find the right layout file, or data you can use inside the template. + +The `collection` property contains an array of tags you will use in your templates later. Creating +pages with a post tag will add them to the post collection and ensure they appear in the list +rendered on your home page. + +The `post.url` is added with the rel filter you defined above to ensure the URL doesn’t start with a +slash; instead, it keeps the TXID in which your website is nested. + +The `pkg.author` comes from `author` field of your project’s `package.json` file. So make sure you +enter a name there! + +Add this code to the new file: + +```markdown +--- +layout: layout.html +--- + +

Articles

+ +

by {{ pkg.author }}

+``` + +To prevent repetition, create a data file inside a directory that applies this front matter to all +template files in that directory. + +Create a new file at `src/posts/posts.json` to define data for all post files. + +- `layout` defines which file should wrap your post pages +- `tags` define the tags of each post page; here, using post ensures every post will be part of the + post-collection used on the home page. +- `permalink` defines the URL schema for your pages; the schema ensures the file name without the + extension is used as the post URL. + +Add the following code to the JSON file: + +```json +{ + "layout": "layout.html", + "tags": "post", + "permalink": "/{{ page.fileSlug }}/" +} +``` + +Now, only the actual posts of your blog are missing, so let’s create a new file at +`src/posts/hello-world.md`. This template will inherit the data from the `posts.json` file and add +its own data as a title. + +Add the following code to the template file: + +```markdown +--- +title: Hello, World! +--- + +I deployed my first website to the Permaweb! +``` + +You can modify the first post or the layout to suit your blogging needs and add other pages to the +`src/posts` directory. You can move to the build step when you’re happy with your work. + +### Building a Release + +Add new commands to your scripts in the `package.json` file to generate HTML from your templates. + +- The `start` script runs a web server that lets you test your blog locally. +- The `build` script generates the HTML and stores it in the `dist` directory. + +```json +"scripts": { + "start": "npx @11ty/eleventy --serve --input=src --output=dist", + "build": "npx @11ty/eleventy --input=src --output=dist" +}, +``` + +Execute the build script with this command: + + npm run build + +The output should look like this: + +```bash +> permablog@1.0.0 build +> npx @11ty/eleventy --input=src --output=dist + +[11ty] Writing dist/index.html from ./src/index.md (liquid) +[11ty] Writing dist/hello-world/index.html from ./src/posts/hello-world.md (liquid) +[11ty] Wrote 2 files in 0.10 seconds (v2.0.1) +``` + +After completing this command, you’ll find your rendered HTML files in the `dist` directory. + +### Testing the Release Locally + +If you want to check that everything with your website is working as you expect, run the following +command: + + npm start + +The `start` script builds your website and starts a development server on `http://localhost:8080/`, +where you can test your blog before deploying it. + + + + + + + +## Deploying Your Website to Arweave + +Now that you have a website, you can move to the interesting part: deploying on Arweave. + +To deploy your website, you’ll implement a script that uses Node.js, the Turbo SDK, and an Arweave +wallet. + +### Setting Up a Wallet + +If you completed [lesson 2](/tracks/arweave-101/3), you should already have a wallet created in +ArConnect and exported as a keyfile. Copy it to your project directory and ensure it’s named +`key.json`. + +You can continue this lesson with a randomly generated keyfile, as all uploads are smaller than +100 KiB and are free with Turbo; however, using a wallet that is charged with Turbo Credits +([explained in lesson 2](/tracks/arweave-101/3)) is recommended to ensure uploads are larger than +100 KiB. + +If you want to continue without creating a charged wallet, create a new file at +`scripts/create-wallet.mjs` with this code: + +```javascript +import fs from "node:fs"; +import Arweave from "arweave"; +const wallet = await new Arweave({}).wallets.generate(); +fs.writeFileSync("key.json", JSON.stringify(wallet)); +``` + +Then execute it with this command: + + node scripts/create-wallet.mjs + +When the command is complete, you’ll have a `key.json` file in your project directory. + +### Creating a Deployment Script + +As you learned in [lesson 2](/tracks/arweave-101/2), you can upload files via the browser and +Node.js. As deployments are often automated, it makes sense to implement it as a Node.js script. + +Create a new file at `scripts/deploy-website.mjs` and add the following entry to `scripts` in your +`package.json` file: + +```json +"deploy": "node scripts/deploy-website.mjs" +``` + +This script will collect all the files in the dist directory, load your `key.json` file, and upload +each file with the Turbo SDK to Arweave. After it uploads all files, it will use its paths and TXIDs +to generate a path manifest that allows users to access all files via a single TXID. + +Add the following code to the deployment script: + +```javascript +import fs from "node:fs"; +import path from "node:path"; +import mimeTypes from "mime-types"; +import * as TurboSdk from "@ardrive/turbo-sdk"; +console.log("Deploying website..."); +function getAllFiles(dir, allFiles = []) { + fs.readdirSync(dir).forEach((file) => { + const filePath = path.join(dir, file); + if (!fs.statSync(filePath).isDirectory()) return allFiles.push(filePath); + allFiles = getAllFiles(filePath, allFiles); + }); + return allFiles; +} +const turbo = TurboSdk.TurboFactory.authenticated({ + privateKey: JSON.parse(fs.readFileSync("key.json", { encoding: "utf-8" })), +}); +const uploadResults = []; +for (let filePath of getAllFiles("dist")) { + console.log(`- ${filePath}`); + const result = await turbo.uploadFile({ + fileStreamFactory: () => fs.createReadStream(filePath), + fileSizeFactory: () => fs.statSync(filePath).size, + dataItemOpts: { + tags: [{ name: "Content-Type", value: mimeTypes.lookup(filePath) }], + }, + }); + uploadResults.push({ + path: filePath.replaceAll(path.sep, "/").replace("dist/", "").replace("/index.html", "/"), + txId: result.id, + }); +} +console.log("- manifest.json"); +const pathManifest = { + manifest: "arweave/paths", + version: "0.2.0", + index: { path: "index.html" }, + paths: uploadResults.reduce((paths, file) => ({ ...paths, [file.path]: { id: file.txId } }), {}), +}; +fs.writeFileSync("manifest.json", JSON.stringify(pathManifest)); +const result = await turbo.uploadFile({ + fileStreamFactory: () => fs.createReadStream("manifest.json"), + fileSizeFactory: () => fs.statSync("manifest.json").size, + dataItemOpts: { + tags: [{ name: "Content-Type", value: "application/x.arweave-manifest+json" }], + }, +}); +fs.writeFileSync("deployment-id", result.id); +console.log(`https://arweave.developerdao.com/${result.id}`); +``` + +### Deploying the Release + +Uploading your files to Arweave is just one command away: + + npm run deploy + +The output of this command should look like this: + +```bash +> permablog@1.0.0 deploy +> node scripts/deploy-website.mjs + +Deploying website... +- dist/hello-world/index.html +- dist/index.html +- manifest.json +https://arweave.developerdao.com/ +``` + +The deployment script will write a `deployment-id` file with the latest TXID to help find your +latest deployments. + +**Congratulations!** + +You’ve deployed your first website to the Permaweb! If you stop now, you’ve already learned enough +to build and deploy basic websites. However, if you move on, you’ll learn to improve the user +experience and, in later lessons, how to build DApps! + +### Testing the Deployed Release + +Testing is quite simple; you just need to open the printed URL in your browser. In the end, the TXID +should work with all Arweave gateways. Figure 1 shows the blog in the browser. + +![Figure 1: Permablog preview](/assets/tracks/arweave-101/4/figure-1.png) +Figure 1: Permablog preview + +You can try the blog I deployed with different gateways: + +- [Developer DAO](https://arweave.developerdao.com/n__amTpTRD3eb6CUQxr9U0IFlgnqb_0xNOedKLN19FI) +- [g8way.io](https://g8way.io/n__amTpTRD3eb6CUQxr9U0IFlgnqb_0xNOedKLN19FI) +- [ar-io.dev](https://ar-io.dev/n__amTpTRD3eb6CUQxr9U0IFlgnqb_0xNOedKLN19FI) + + + + + + + +## Creating a Human-Friendly Name For Your Website + +Your blog is fully functional already, but working with the TXID can be cumbersome. They are nice +for permanent links to your pages but not for driving new users to your website. Also, when you +upload a new blog release, the pages from previous transactions won’t reflect the changes. This is +where ArNS comes into play: The Arweave Name System. + +### What is the Arweave Name System? + +ArNS is a decentralized naming system that allows you to rent or buy names for your TXIDs. In +contrast to TXIDs, they are mutable, allowing UX closer to traditional websites. + +Instead of the TXID URL that looks like this: + + https://arweave.developerdao.com/some-transaction-id-9999/path/to/some/file.html + https://ar-io.dev/some-transaction-id-9999/path/to/some/file.html + +You can share an ArNS URL that looks like this: + + https://arweave101.arweave.developerddao.com/path/to/some/file.html + https://arweave101.ar-io.dev/path/to/some/file.html + +When you upload a new release, you can simply point the ArNS name to the new TXID, and your users +will automatically see the latest version. + +ArNS supports so-called under names, which are additional names prefixed with an underline to your +ArNS domain. + + blog_arweave101.arweave.developerddao.com/path/to/file.html + test_arweave101.arweave.developerddao.com/path/to/file.html + first_test_arweave101.arweave.developerddao.com/path/to/file.html + second_test_arweave101.arweave.developerddao.com/path/to/file.html + +Arweave Name Tokens (ANTs) are NFTs that enable the management of ArNS names in a decentralized way. + +In the next step, we will walk you through registering an ArNS name. If you don't already have AR.IO +test tokens then you can claim some by completing EXPerience quests. + +### Getting tIO Tokens + +AR.IO Testnet Tokens (tIO) are required to register an ArNS name. + +Users who completed our previous course, Lesson 3 - Storing Data on Arweave, AND completed the corresponding AR.IO EXPerience quest have been airdropped sufficent tIO tokens to register a 12-character ArNS name. + +You can check for the tokens using your ARWEAVE wallets at https://arns.app/ . + +If you did not complete this prerequisite, then do not worry - there will be opportunities to acquire tokens in the future - stay tuned! + +### Registering an ArNS Name + +You can register an ArNS name on the [ArNS website](https://arns.app/). Keep in mind that ArNS is +currently in testnet. + +First, click the "Connect" button at the top right, to connect your ArConnect wallet. + +Then, search for your desired ArNS name. The tIO tokens from the quests should be enough to lease a +12 characters long name for 1 year. + +When the name is available, you click the "Register Now" button to start the registration process. + +Figure 2 shows how the registration form. Enter the TXID from the `deployment-id` file in +your project directory into the "Arweave Transaction ID" field, to point your ArNS name to your +latest release right after the registration completes. + +![Figure 2: Registering an ArNS name](/assets/tracks/arweave-101/4/figure-2.png) +Figure 2: Registering an ArNS name + +### Testing Deployed Release With the ArNS Name + +After changing the TXID and your ArNS name, you can access your blog via your new ArNS name. + +I created an example undername to illustrate the functionality. You can test them with different +Arweave gateways: + + https://demo-blog_fllstck.arweave.developerdao.com/ + https://demo-blog_fllstck.ar-io.dev/ + +And that’s it! + +You’re now ready to build and deploy any website on Arweave while ensuring that your users' URLs +don’t change with each deployment. + + + + + + + +## Summary + +In this lesson, you learned how to put your knowledge to the test by creating a static website, +uploading it to Arweave, and registering an ArNS name for it. You used Node.js scripts to interact +with ANTs to automate the update for your ArNS names after a website update. Now you can build +websites for the Permaweb to save your users from link-rot and ensure they have access to your page +as long as Arweave is online. + + + +## Conclusion + +Website hosting and registering human-friendly names works similar to the traditional web. Instead +of a hoster, you use Arweave and instead of DNS, you use ArNS. In contrast to the traditional +method, your website stays online even if you don’t pay your hosting providers subscription fee. If +an Arweave gateway gets blocked or stops serving your website, your users can still access it via +other gateways. + +**Congratulations!** + +You finished the first project lesson of the Arweave 101 track and can consider yourself now a +Permaweb developer! + +Your pages might just have basic functionality, but that’s already enough to build news sites, +blogs, and landing pages. + +In the next lesson, you will learn how to build an interactive DApp that lets users interact with +Arweave from the browser. + +