{"version": "https://jsonfeed.org/version/1.1",
"title": "dAppling dev blog",
"home_page_url": "https://blog.dappling.network/",
"feed_url": "https://blog.dappling.network/full.json",
"description": "Nurturing the Future of Decentralized App Development",
"user_comment": "dAppling dev blog JSON feed (full articles)",
"authors": [
  {
    "name": "dAppling",
    "url": "https://dappling.network"
  }
],
"language": "en",
"icon" : "https://blog.dappling.network/icon-192.png",
"favicon" : "https://blog.dappling.network/icon-512.png","items": [{"id": "https://blog.dappling.network/a-github-app-would-like-to-act-on-your-behalf/",
"url": "https://blog.dappling.network/a-github-app-would-like-to-act-on-your-behalf/",
"title": "A GitHub App Would Like to Act on Your Behalf",
"date_published": "2024-02-08T00:00:00Z",
"language": "","content_html": "&lt;p&gt;An inaccurate warning when authorizing GitHub was a concern for some of &lt;a href=&quot;https://dappling.network/&quot;&gt;dAppling’s&lt;/a&gt; users. We got around this by using a dual-auth approach: OAuth for log in and App authentication for repo access.&lt;/p&gt;
&lt;h2 id=&quot;the-github-app-warning&quot;&gt;The GitHub App Warning&lt;/h2&gt;
&lt;p&gt;When authorizing a GitHub App, the user is presented with a warning stating our app would like to “act on your behalf”. This warning is accompanied by a link &lt;a href=&quot;https://docs.github.com/en/apps/using-github-apps/authorizing-github-apps#about-github-apps-acting-on-your-behalf&quot;&gt;about GitHub Apps acting on your behalf&lt;/a&gt;. While this is true for some actions GitHub apps can perform, this warning shows up when requesting only read access to the user’s profile. This has been understandably concerning for some of our users.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/github-app/github-app-warning.png&quot; alt=&quot;The GitHub App Warning&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;github-authentication&quot;&gt;GitHub Authentication&lt;/h3&gt;
&lt;p&gt;Ultimately we want to do two things: allow the user to log to dAppling with their GitHub account and build their specified repositories into a decentralized application. For logging in, GitHub provides OAuth as well as GitHub App authentication. For repo access, GitHub App authentication is the only reasonable option, although there are other more &lt;a href=&quot;https://docs.github.com/en/rest/authentication/authenticating-to-the-rest-api&quot;&gt;inconvenient options&lt;/a&gt;. As you might have guessed, using GitHub App authentication for both log in and repo access would work. It would also trigger the warning. Therefore, we decided to use GitHub’s OAuth for the log in and GitHub’s App authentication for repo access.&lt;/p&gt;
&lt;h2 id=&quot;flow-overview&quot;&gt;Flow Overview&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;User Clicks “Log In” and are routed through the Next Auth sign in flow via &lt;strong&gt;GitHub OAuth&lt;/strong&gt;.
&lt;ul&gt;
&lt;li&gt;If they are a new user, they are prompted to provide authorization.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;They receive a token and are redirected to the &lt;strong&gt;Auth Check&lt;/strong&gt; page.&lt;/li&gt;
&lt;li&gt;The path diverges based on whether or not they have the app authorized.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If they do not have the app installed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Since they are most likely new, they are sent to /new&lt;/li&gt;
&lt;li&gt;The user will be shown a button to connect to the &lt;strong&gt;GitHub App Authentication&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;A popup, which, upon completion, will redirect to /auth/connection&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;middleware&lt;/strong&gt; will intercept the call to /auth/connection, and using the code passed back, a GitHub App token is stored in the session&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Auth Connection Page&lt;/strong&gt; pop-up is closed and the session is refreshed with &lt;code&gt;update&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If they have the app installed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They are directly attempted to &lt;strong&gt;App Re-Authentication&lt;/strong&gt; via GitHub’s app auth flow&lt;/li&gt;
&lt;li&gt;Upon completion, they are redirected to /auth/connection&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;middleware&lt;/strong&gt; will intercept the call to /auth/connection, and using the code passed back, a GitHub App token is created and stored in the session&lt;/li&gt;
&lt;li&gt;Hitting the &lt;strong&gt;Auth Connection Page&lt;/strong&gt;, the user is redirected to their /projects page&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Celebrate! The user now has both the &lt;code&gt;githubAppAccessToken&lt;/code&gt; and &lt;code&gt;githubOauthAccessToken&lt;/code&gt; until logging out&lt;/p&gt;
&lt;h3 id=&quot;github-oauth&quot;&gt;GitHub OAuth&lt;/h3&gt;
&lt;p&gt;Using &lt;a href=&quot;https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app&quot;&gt;GitHub’s OAuth app&lt;/a&gt; is surprisingly straight-forward since we are using &lt;a href=&quot;https://next-auth.js.org/&quot;&gt;Next Auth&lt;/a&gt;, but this should be similar for other platforms. Using the Client ID and Secret, allow the user to log into the OAuth provider and GitHub will return an access token. If you notice, the access token is named specifically to separate the OAuth access token from the GitHub App access token. The following is a simplified version of our config.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NextAuth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  providers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;GithubProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      clientId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      clientSecret&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  callbacks&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;jwt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; token&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; profile&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; account &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      token&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;githubOauthAccessToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; account&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;access_token&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; token&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; session&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; token &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      session&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;githubOauthAccessToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; token&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;githubOauthAccessToken&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; session&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, when the user logs in, they are presented with a nicer looking prompt. You can see the scary warning is not present, and if you don’t need the profile, request only “email” by changing the &lt;a href=&quot;https://github.com/nextauthjs/next-auth/blob/v4/packages/next-auth/src/providers/github.ts#L68&quot;&gt;authorization scope&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/github-app/github-oauth-warning.png&quot; alt=&quot;GitHub OAuth Warning&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;auth-check-page&quot;&gt;Auth Check Page&lt;/h3&gt;
&lt;p&gt;Once the user has an OAuth token, we can check if they have the app token and redirect them accordingly. This is done in a Next.js page. Since it is not possible to see if a user has given the app &lt;a href=&quot;https://github.com/settings/apps/authorizations&quot;&gt;authorization&lt;/a&gt;, we separate our users into two categories: most likely new and most likely returning. The signal we use is if the user has any projects. If they do, assume they are returning and immediately try to run &lt;strong&gt;App Re-Authentication&lt;/strong&gt;. If not, we redirect them to the new project page and allow them to go through regular &lt;strong&gt;App Authentication&lt;/strong&gt;. The following is a simplified version of our AuthCheck function.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;AuthCheck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; session &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getServerSession&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;session&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;githubAppAccessToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/*
      This should only happen if the user has acquired the githubAppAccessToken
      from the JWT token. This should be the case only if they hit this page directly,
      as the preview user, who has the token added directly, or if they had the accessToken
      from the previous JWT.
    */&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/projects&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hasProjects&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;session&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/*
      Redirect to GitHub&#39;s auth flow to get a new token for our app. Then,
      GitHub will redirect the user to the /auth/connection page, which will upgrade
      the token and redirect to the projects page.
    */&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;GITHUB_AUTHORIZATION_URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; RedirectType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;replace&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/new&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;github-app-authentication&quot;&gt;GitHub App Authentication&lt;/h3&gt;
&lt;p&gt;So the user has logged in to our app and we can query the GitHub API, but we need more permissions since we need to build code and report changes off of the website. That is where the GitHub App authentication comes in. Head over to &lt;a href=&quot;https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app&quot;&gt;create a GitHub app&lt;/a&gt;. There is one very important option to check: “Request user authorization (OAuth) during installation”.&lt;/p&gt;
&lt;p&gt;At this point, we have a &lt;code&gt;gho&lt;/code&gt; token for the user. From GitHub’s blog post &lt;a href=&quot;https://github.blog/2021-04-05-behind-githubs-new-authentication-token-formats/#identifiable-prefixes&quot;&gt;on token formats&lt;/a&gt;, you can see this comes from the OAuth flow. After the user goes through the App flow, we will have a &lt;code&gt;ghu&lt;/code&gt; token. This is the GitHub App token which will allow our app to access the repos granted by the user.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/github-app/github-app-installation.png&quot; alt=&quot;GitHub App Installation&quot; /&gt;&lt;/p&gt;
&lt;p&gt;What’s nice is you can now see the permissions our app is requesting as well as limiting which repos the app can access. Funnily enough, the warning about acting on the user’s behalf is missing. After this step, we will now have an app token which we can also store in the cookie. Our new callbacks will look like the code below. Note, the &lt;code&gt;githubAppAccessToken&lt;/code&gt; is set in the middleware. More on that later.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token function&quot;&gt;jwt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; token&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; profile&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; account &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  token&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;githubOauthAccessToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; account&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;access_token&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; token&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; session&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; token &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  session&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;githubOauthAccessToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; token&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;githubOauthAccessToken&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  session&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;githubAppAccessToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; token&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;githubAppAccessToken&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; session&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;app-re-authentication&quot;&gt;App Re-Authentication&lt;/h3&gt;
&lt;p&gt;Great, so we have the app token, but what happens when a user logs out? Both tokens are thrown into the bin and lost to the void. On the next login, a new OAuth token is generated, but the App token must also be retrieved. How we got the token the first time was through the installation process. Since we would rather not show the installation window every time the user logs in, we can use the convenient &lt;a href=&quot;https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps&quot;&gt;GitHub Authorization URL&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;https://github.com/login/oauth/authorize&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;?client_id=${clientId}&amp;amp;state=${state}&lt;/code&gt;&lt;br /&gt;
additionally, we use the &lt;code&gt;client_id&lt;/code&gt; and &lt;code&gt;state&lt;/code&gt; parameters to identify our app and which flow the user is in. More on the state later.&lt;/p&gt;
&lt;p&gt;What’s cool about using the authorize URL is if the app is already authorized, which you can see over at your &lt;a href=&quot;https://github.com/settings/apps/authorizations&quot;&gt;GitHub Authorizations&lt;/a&gt;, the app token can be generated and stored securely without presenting extra pages.&lt;/p&gt;
&lt;h4 id=&quot;middleware&quot;&gt;Middleware&lt;/h4&gt;
&lt;p&gt;To do this, we use Next.Js Middleware. I’ve created a gist of our full &lt;a href=&quot;https://gist.github.com/Namaskar-1F64F/b185f1e25aa8d8e2c124053018c37084&quot;&gt;middleware.ts&lt;/a&gt;, but a simplified version of the relevant parts is below. This was taken mostly from a &lt;a href=&quot;https://github.com/nextauthjs/next-auth/discussions/9715&quot;&gt;GitHub discussion&lt;/a&gt; on the issue of setting a session cookie.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;updateCookie&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;token&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cookies&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NEXT_AUTH_COOKIE_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; token&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;upgradeOauthTokenWithCode&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;code&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; appOctokit&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;oauth-user&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    code&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;addAppTokenToCookie&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; code &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchParams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;code&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; githubAppToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;upgradeOauthTokenWithCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;code&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; token &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    githubAppAccessToken&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; githubAppToken&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;updateCookie&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;token&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;middleware&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pathname &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/auth/connection&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;addAppTokenToCookie&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The basic idea is to trigger this middleware on the callback of the GitHub App authorization. The code is passed back from GitHub and we use it to generate the app token. This token is then stored in the cookie. Since this happens in the middleware, the cookie will be set by the time the user hits our following react components to ultimately render their projects.&lt;/p&gt;
&lt;h3 id=&quot;auth-connection-page&quot;&gt;Auth Connection Page&lt;/h3&gt;
&lt;p&gt;Alright so we’re almost there. The user has logged in and we have both of the tokens, but what does the component look like when the user is re-directed from the GitHub App? You might have noticed the &lt;code&gt;/auth/connection&lt;/code&gt; path, which for our Next.js project is a page. This page is server side rendered and takes the &lt;code&gt;state&lt;/code&gt; I mentioned earlier to either continue completely server side and redirect the user, in the case of a login, or to update the user’s session and close the pop-up in the case of an install. Again, a simplified version of the relevant parts is below.&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;AuthPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; searchParams&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; code&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; session &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getServerSession&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;session&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;githubAppAccessToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/*
      If the githubAccessToken is set, there is no need to continue. The
      user is in a correct state.
    */&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; AuthState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;LOGIN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;/*
        The LOGIN state comes from the /login page which will not be a pop-up
        so the page should be redirected.
      */&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/projects&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; AuthState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;INSTALL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;/*
        This is the other case where the state is from the install pop-up.
        The AppConnection page will update all windows with the new token and close.
      */&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AppConnection&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AppConnection&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, in the case of the pop-up, the simplified AppConnection component is rendered. Here we want to simply show the user that they are connected and up-to-date. If there is an error, we show the error. If there is no error, we close the window after a short delay.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;AppConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; error &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Image src&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;logo&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;error &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        error
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Space&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Title level&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Connected &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; Up&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;to&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;date&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Title&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Title level&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Closing the window now&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Title&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Space&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/github-app/pop-up.png&quot; alt=&quot;GitHub Installation Pop-Up&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;While this does work, is it worth the effort? For our security conscious users at &lt;a href=&quot;https://dappling.network/&quot;&gt;dAppling&lt;/a&gt;, being explicit about permissions authorized apps have is important.&lt;/p&gt;
&lt;p&gt;We have found that using the dual-auth approach of OAuth for log in and App authentication for repo access is a good compromise. It allows us to use the GitHub App token for the necessary permissions while avoiding the inaccurate “act on your behalf” warning.&lt;/p&gt;
&lt;p&gt;If you have any questions or need help setting this up yourself, feel free to reach out to me on &lt;a href=&quot;https://twitter.com/0xbookland&quot;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
"
    },{"id": "https://blog.dappling.network/detect-web3-frontend-attacks-with-dappling-dns-monitor/",
"url": "https://blog.dappling.network/detect-web3-frontend-attacks-with-dappling-dns-monitor/",
"title": "Detect Web3 Frontend Attacks with dAppling DNS Monitor",
"date_published": "2024-01-25T00:00:00Z",
"language": "","content_html": "&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; We built &lt;a href=&quot;https://dappling.network/monitor&quot;&gt;dappling.network/monitor&lt;/a&gt; to help prevent web3 DNS hijacking attacks. This tool alerts you to potential DNS hijacking. By monitoring over 3,000 Web3 domains (everything on &lt;a href=&quot;https://defillama.com/&quot;&gt;DeFiLlama&lt;/a&gt;) for nameserver changes, it helps you be aware when domains are hijacked. &lt;a href=&quot;https://dappling.network/monitor&quot;&gt;Try the tool now&lt;/a&gt; and be part of a safer DeFi community.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/dns-monitor/monitor-demo.png&quot; alt=&quot;The DNS Monitor Tool&quot; /&gt;&lt;/p&gt;
&lt;h1 id=&quot;why-is-this-needed&quot;&gt;Why is this needed?&lt;/h1&gt;
&lt;p&gt;In the last few months there have been several DNS hijacking attacks targeting web3 protocols. These included &lt;a href=&quot;https://x.com/fraxfinance/status/1719497560543658073?s=20&quot;&gt;Frax&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/Balancer/status/1704281611326357567&quot;&gt;Balancer&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/galxe/status/1710305141016944654&quot;&gt;Galxe&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/VelodromeFi/status/1730040745736683679&quot;&gt;Velodrome&lt;/a&gt;, and &lt;a href=&quot;https://x.com/aerodromefi/status/1736780326070870072?s=20&quot;&gt;Aerodrome&lt;/a&gt; to name a few.&lt;/p&gt;
&lt;p&gt;They all happened the exact same way. The DNS registrar they were using was social engineered, and the hacker was able to change the nameservers and take control over the domain.&lt;/p&gt;
&lt;p&gt;These nameserver changes is exactly what the tool looks for and would have detected all 5 of those attacks.&lt;/p&gt;
&lt;h1 id=&quot;how-it-works&quot;&gt;How it works?&lt;/h1&gt;
&lt;p&gt;We used &lt;a href=&quot;https://defillama.com/&quot;&gt;DeFiLlama&lt;/a&gt; to build our original lists of sites to monitor. We then use cloudflare’s &lt;a href=&quot;https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/&quot;&gt;DNS over HTTPS api&lt;/a&gt; to check each of the nameservers for changes every 5 minutes. If we detect a change, we send out a notification to everyone who subscribed to nameserver changes for that domain + update the monitoring table.&lt;/p&gt;
&lt;p&gt;We also automatically add all dappling.network production domains to the monitor list + they get extra features to help them protect their users against these types of attacks.&lt;/p&gt;
&lt;h1 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dappling.network/monitor&quot;&gt;Check out the tool&lt;/a&gt; + signup for notifications for your most used site&lt;/li&gt;
&lt;li&gt;Deploy your site on &lt;a href=&quot;https://dappling.network/&quot;&gt;dAppling&lt;/a&gt; to get this + other security features with 0 config.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://twitter.com/0xbookland&quot;&gt;DM me on twitter&lt;/a&gt; if you have questions or want a site that’s missing added.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;video-demo&quot;&gt;Video Demo&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/0xBookland/status/1757493370962739438?s=20&quot;&gt;Video&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;thank-you&quot;&gt;Thank you&lt;/h1&gt;
&lt;p&gt;Thank you to the &lt;a href=&quot;https://dappling.network/&quot;&gt;dAppling team&lt;/a&gt; for building the tool. CerealSabre from &lt;a href=&quot;https://twitter.com/eth_limo&quot;&gt;eth.limo&lt;/a&gt; for helping me brainstorm this idea and &lt;a href=&quot;https://twitter.com/pcfreak30&quot;&gt;Derrick&lt;/a&gt; from &lt;a href=&quot;https://twitter.com/LumeWeb3&quot;&gt;Lume&lt;/a&gt; for helping me understand DNS at a deeper level. Also huge shout out to the &lt;a href=&quot;https://3dns.box/&quot;&gt;3DNS team&lt;/a&gt; who’s helping reduce the DNS hijacking problem with their hybrid domains.&lt;/p&gt;
"
    },{"id": "https://blog.dappling.network/simple-site-building-and-ipfs-deployment-with-dappling/",
"url": "https://blog.dappling.network/simple-site-building-and-ipfs-deployment-with-dappling/",
"title": "Simple Site Building and IPFS Deployment with dAppling",
"date_published": "2023-10-02T00:00:00Z",
"language": "","content_html": "&lt;p&gt;In this walkthrough, we will explore how to deploy your site using dAppling and try out the features that make it a tool you’ll want to use. At the end of this walkthrough, you will have a project on dAppling that will:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;automatically update on &lt;strong&gt;GitHub&lt;/strong&gt; code changes&lt;/li&gt;
&lt;li&gt;be hosted on the &lt;strong&gt;InterPlanetary File System&lt;/strong&gt; (IPFS)&lt;/li&gt;
&lt;li&gt;be live on a &lt;strong&gt;dappling.network&lt;/strong&gt; subdomain&lt;/li&gt;
&lt;li&gt;be live on a &lt;strong&gt;dappling.eth&lt;/strong&gt; subdomain&lt;/li&gt;
&lt;li&gt;receive an automatically updating &lt;strong&gt;IPNS&lt;/strong&gt; key&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you run into &lt;strong&gt;any&lt;/strong&gt; problems, want to connect, or just say hi, my DMs are open on &lt;a href=&quot;https://x.com/0xBookland&quot;&gt;𝕏&lt;/a&gt;. I would love to hear your feedback and help you get your project deployed.&lt;/p&gt;
&lt;h2 id=&quot;what-dappling-does&quot;&gt;What dAppling does&lt;/h2&gt;
&lt;p&gt;Literally, the word is a portmanteau of “dApp”, a term short for decentralized application, and “sapling,” because nature is wonderful 🌱. However, we support all kinds of web projects, not just &lt;a href=&quot;https://app.gogopool.com.dappling.eth.limo/&quot;&gt;dApps&lt;/a&gt;: &lt;a href=&quot;https://arbor-landing.dappling.eth.limo/&quot;&gt;landing pages&lt;/a&gt;, &lt;a href=&quot;https://blog.dappling.network/&quot;&gt;blogs&lt;/a&gt;, or even a simple page of content arguing against the &lt;a href=&quot;https://nomoreacronyms-xczmz4.dappling.org/&quot;&gt;usage of acronyms&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Basically, we take your code, build it, and host the resulting files on IPFS. But what makes us special are the features we provide to make your experience easier. Even if you have an existing site, you can use dAppling to create a resilient “alternative frontend” that is hosted on IPFS.&lt;/p&gt;
&lt;h1 id=&quot;the-walkthrough&quot;&gt;The Walkthrough&lt;/h1&gt;
&lt;p&gt;I will guide you step by step through the process of deploying a project on dAppling. I will be using a blog template, but you can use any code that you want to deploy.&lt;/p&gt;
&lt;p&gt;The final project, &lt;a href=&quot;https://dappling.network/projects/7ebe4f4f-70f0-4705-828b-c610fb1d9ddc&quot;&gt;on the dAppling platform&lt;/a&gt;, will be accessible at a URL like &lt;a href=&quot;https://hello-ipfs-blog.dappling.network/&quot;&gt;hello-ipfs-blog.dappling.network&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let’s start exploring!&lt;/p&gt;
&lt;h2 id=&quot;find-a-project&quot;&gt;Find a Project&lt;/h2&gt;
&lt;p&gt;To begin, find a project that you would like to deploy to the decentralized web. This project should be able to be statically exported, and luckily the major frameworks like Next.js, React, and Svelte are supported. However, we cannot build projects with a backend, such as a database, server, or serverless functions. This means you can still use dAppling for your project, but you’ll need to host your backend separately.&lt;/p&gt;
&lt;p&gt;If you can’t think of a project, I would recommend forking &lt;a href=&quot;https://github.com/lwojcik/eleventy-template-bliss&quot;&gt;a nice blog template&lt;/a&gt; to get started. Click on the “Use this template” button, then “Create a new repository”. This will bring up another screen confirming the fork.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/blog-template.png&quot; alt=&quot;The bliss blog template repository on GitHub.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Name your new blog something. I chose thoughts because one of my favorite quotes is “Thoughts become things”. I also decided to make it public, but you can make it private if you’d like. Then click “Create repository”. This will take you to the repository page, but we should head back to &lt;a href=&quot;https://dappling.network/&quot;&gt;dAppling.network&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/create-repository.png&quot; alt=&quot;A GitHub page allowing you to fork the bliss blog template&#39;s repository.&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;create-your-first-dappling-project&quot;&gt;Create your First dAppling Project&lt;/h2&gt;
&lt;p&gt;Now that we have the code to use, head over to &lt;a href=&quot;https://dappling.network/&quot;&gt;dAppling.network&lt;/a&gt; and click on “Deploy Frontend”. This will take you to the page to log in to GitHub.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/homepage.png&quot; alt=&quot;The dAppling landing page shows a hero: &amp;quot;100% uptime with alternative frontends&amp;quot;.&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;connect-your-github-account&quot;&gt;Connect your GitHub Account&lt;/h3&gt;
&lt;p&gt;Press this “Connect GitHub” button, and you will be redirected to GitHub to authorize dAppling to access your account.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/login.png&quot; alt=&quot;A mostly black dAppling login page with the GitHub logo.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;What happens when you press “Authorize dApplingNetwork” is that we collect the email associated with your account and associate it with your dAppling account. We do not store your GitHub password or any other information. This page should redirect you to your projects page on dAppling.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/authorize-github.png&quot; alt=&quot;GitHub pops up, asking the user to authorize dAppling&#39;s app.&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;deployment-steps&quot;&gt;Deployment Steps&lt;/h3&gt;
&lt;p&gt;Back on the projects page, click “New Project”. This will take you to our “Select Repository” step.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/new-project.png&quot; alt=&quot;An empty dAppling projects page with a new project button.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;To allow access to the project, click on “Add GitHub Account”. This will bring up the authorization screen again. This time, we are allowing dAppling to access the repository; this is necessary so we can build your code.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/add-github-account.png&quot; alt=&quot;The repository selector is empty, with an action to add a GitHub account.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You may first see a screen that asks, “Where do you want to install dApplingNetwork?”. If you just forked the blog template, choose the account you used. Otherwise, pick where your code is located. You can always go back and authorize more or remove authorization to revoke access.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/select-install-location.png&quot; alt=&quot;A GitHub pop-up asking the user to select an installation location.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After you select the installation location, or if you only have a personal account, you will see another step. Decide to either provide access to all repositories, so you do not have to repeat this process, or select the specific repositories you want to deploy. Then click “Install”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/authorize-repository.png&quot; alt=&quot;The next page in authorizing a list or single repository to the dAppling GitHub app.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Awesome! Now that we have repository access, you can select it from the list to deploy. Clicking on the “Select” button to the right of the repository you would like to build will move on to the configuration step.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/select-repository.png&quot; alt=&quot;Back to the dAppling select repository step, now with a single repository.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The “Configure Build” step will try to detect the configuration automatically for your app. If you are following along with the blog template, the settings will be correct. Using your code may prove more tricky. If there is no framework detected, you will need to select what type of project it is. Then, continue with each setting. Hover over the “?” to get additional information about each setting.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/review-configuration.png&quot; alt=&quot;The second step shows inputs for configuration settings.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After the configuration looks good, click “Next”. You will be taken to the “Finishing Touches” step where we do last-minute checks.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/confirm-configuration.png&quot; alt=&quot;More settings and a button at the bottom of the page to confirm configuration.&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;deploy&quot;&gt;Deploy!&lt;/h3&gt;
&lt;p&gt;If everything looks good, click “Deploy”. You will be taken to your new project page.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/deploy.png&quot; alt=&quot;A summary page showing no issues and the configured settings, with a deploy button.&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;explore-the-project-page&quot;&gt;Explore the Project Page&lt;/h3&gt;
&lt;p&gt;You will now see the project page with plenty of loading indicators! The build process may take a few minutes, so let’s explore the other parts of your project while we wait. Click on the second tab, “Deployments”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/deploying.png&quot; alt=&quot;The dAppling project page shows important information like the repository and configured domains.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Here you can see your deploying build and also where your historical deployments would be. You can also create a new deployment from this page by clicking on “Deploy Preview” to create a new version that isn’t live, or push a new “Production Deployment” to make your changes live on your domains. Speaking of domains, click on the “Domains” tab.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/deployments.png&quot; alt=&quot;The deployments page shows the deployment in progress and related information.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Here you can see the pre-configured dappling domain, which can be edited to anything you desire, as well as where you could add your ENS name or link to a live domain. Additionally, when adding the ENS domain, you will get a unique IPNS key that will have the latest CID on IPFS. Now let’s look at the “Analytics” tab.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/domains.png&quot; alt=&quot;The domains page can add different domains as well as an already set dAppling domain.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We have an analytics package that will give you unique user and page view counts. We are working on adding more features to this, but for now, it is a great way to see how many people are visiting your site. Finally, let’s look at the “Settings” tab.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/analytics.png&quot; alt=&quot;An analytics page says to integrate the dAppling Analytics package to view analytics.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You can see the same information you configured earlier. If your deployment failed, this is where you might need to change a setting. You can also add build variables, change the branch to deploy, and delete the project.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/settings.png&quot; alt=&quot;The settings page has inputs for configuration.&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;trigger-a-deployment-from-github&quot;&gt;Trigger a Deployment from GitHub&lt;/h3&gt;
&lt;p&gt;By now, the deployment should be done, which you will be able to see on the “Deployments” tab. Now we will try deploying from GitHub. Head over to your repository on GitHub, choose a file, and make a change to it. Here, I decided to change the content of the latest blog post &lt;code&gt;/content/posts/2023/12-12-sample-post-34-formatting.md&lt;/code&gt;. After committing the change, the deployment should already be started in dAppling.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/make-a-change.png&quot; alt=&quot;Editing a file on GitHub; about to push the changes.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You can see the second deployment. I introduced a bug in my change, however, so this build will fail. A perfect time to see what information you can see upon a failure!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/second-deployment.png&quot; alt=&quot;The dAppling deployments page has two deployments now.&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;ai-generated-debugging-insights&quot;&gt;AI-Generated Debugging Insights&lt;/h3&gt;
&lt;p&gt;Within the deployment page, you can see the logs, environment variables, and other information about the deployment. There is also a button that will generate debugging insights. It uses AI to analyze the logs and give you a summary of what went wrong. After clicking it, the insights panel will appear.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/failed-deployment.png&quot; alt=&quot;The deployment page has information about the deployment. This one has failed.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If your deployment did fail, you can see this; if not, you can check out the insights of &lt;a href=&quot;https://dappling.network/projects/7ebe4f4f-70f0-4705-828b-c610fb1d9ddc/BuildImage74257FD8-Ag37vnjYXUuu:2f2bd735-92d2-47b8-89d6-6bdfc60f876f&quot;&gt;my deployment&lt;/a&gt;. You can see the reason, potential solution, and files affected. If applicable, a code sample will be provided. I will resolve the problem and push another commit to trigger a new deployment.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/ai-insights.png&quot; alt=&quot;The deployment page now has a panel to the right providing information in cards to resolve the issue.&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;final-deployment&quot;&gt;Final Deployment&lt;/h3&gt;
&lt;p&gt;With those changes, the final deployment succeeded. You made it! Your project page will have a preview of the website, and the domains will be updated with your content.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/full-deployment/final-deployment.png&quot; alt=&quot;The dAppling project&#39;s main page, with a website preview of a blog.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Thank you for following along, reading, or just looking at the pictures. You can check out my deployed &lt;a href=&quot;https://dappling.network/projects/7ebe4f4f-70f0-4705-828b-c610fb1d9ddc&quot;&gt;project on dAppling&lt;/a&gt;. If there were issues that you ran into, please contact me on &lt;a href=&quot;https://x.com/0xBookland&quot;&gt;𝕏&lt;/a&gt;. For other features and guides, visit &lt;a href=&quot;https://docs.dappling.network/&quot;&gt;our documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If all went well, your project was also deployed successfully. That means you now have a live domain, an ENS subdomain, automatic deployments, and an IPNS key that will always point to the latest version of your site. Well done! Welcome to the decentralized future of IPFS. Post this acomplishment and tag us &lt;a href=&quot;https://x.com/dApplingNetwork&quot;&gt;@dApplingNetwork&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;🙏&lt;/p&gt;
"
    },{"id": "https://blog.dappling.network/adding-automatic-deployment-debugging-with-ai/",
"url": "https://blog.dappling.network/adding-automatic-deployment-debugging-with-ai/",
"title": "Adding Automatic Deployment Debugging with AI",
"date_published": "2023-09-18T00:00:00Z",
"language": "","content_html": "&lt;p&gt;When I press “Commit &amp;amp; Push” to send brand-new code on its dramatic debut, instead of confetti and fanfare, I’m given a little bop on my head as the scary ❌ icon tells me the deployment failed. There will be no code to show my friends; the link I had copied, intending to share, was overwritten by an error from the deployment logs.&lt;/p&gt;
&lt;p&gt;But wait. Why should I be reading logs and debugging a failure when we’ve got AI APIs now?&lt;/p&gt;
&lt;p&gt;Well, in this post, I share how I implemented a feature adding an “AI Insights” section for failed deployments while working on &lt;a href=&quot;https://dappling.network/&quot;&gt;dAppling.network&lt;/a&gt;, a platform that builds and deploys code to IPFS. These insights should help the user resolve the issue or tell them it’s our fault, and we will fix it. Join me as I prompt-engineer in a React + TypeScript app to make seeing the ❌ a little less painful.&lt;/p&gt;
&lt;h2 id=&quot;the-ins-and-the-outs&quot;&gt;The Ins and the Outs&lt;/h2&gt;
&lt;p&gt;I decided to use OpenAI’s API because of my familiarity with previous usage, and I somewhat know how the model “behaves” from daily use of ChatGPT. So my first question was, “What &lt;strong&gt;inputs&lt;/strong&gt; should I include to provide the LLM with useful context?” I settled on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deployment logs: &lt;em&gt;where the build actually fails&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;User editable settings: &lt;em&gt;commands, variables, GitHub branch&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Commands used to deploy: &lt;em&gt;our build script&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Instructions: &lt;em&gt;the LLM needs a direction&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then, ultimately, the &lt;strong&gt;outputs&lt;/strong&gt; should be usable information to guide the user on how to resolve the problem. I chose:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What type of failure: &lt;em&gt;our fault or the users&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Reason: &lt;em&gt;why did this fail?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Solution: &lt;em&gt;how can this be fixed?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Code and files: &lt;em&gt;suggested changes if necessary&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;getting-to-a-string&quot;&gt;Getting to a String&lt;/h3&gt;
&lt;p&gt;The model only takes a prompt, but I wanted this to be clean. A big template literal with many variables interspersed seemed not-so-maintainable to me, and that’s when I found &lt;a href=&quot;https://js.langchain.com/docs/get_started/introduction&quot;&gt;LangChain&lt;/a&gt;. It helps to put together these variables into a &lt;a href=&quot;https://js.langchain.com/docs/modules/model_io/prompts/prompt_templates/&quot;&gt;&lt;code&gt;PromptTemplate&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; getPrompt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  model&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  logs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  buildStep&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  totalBuildTimeSeconds&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  buildConfiguration&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  forChatGpt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  model&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;gpt-3.5&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;gpt-4&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  logs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  buildStep&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  totalBuildTimeSeconds&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  buildConfiguration&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  forChatGpt&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;LOG_SIZE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; model &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;gpt-4&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8_000&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16_000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; trimmedLogs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; logs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;LOG_SIZE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; delimiter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#92;n&quot;&quot;&quot;&#92;n&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PromptTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    inputVariables&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&#39;buildStep&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&#39;buildConfiguration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&#39;logs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&#39;instructions&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&#39;buildSpec&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&#39;totalBuildTimeSeconds&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    partialVariables&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      outputInstructions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; forChatGpt &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; parser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFormatInstructions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    template&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;{instructions}&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;delimiter&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;error log:&#92;n{logs}&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;delimiter&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;build step:&#92;n{buildStep}&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;delimiter&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;total build seconds:&#92;n{totalBuildTimeSeconds}&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;delimiter&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;build configuration:&#92;n{buildConfiguration}&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;delimiter&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;build specification:&#92;n{buildSpec}&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;delimiter&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;{outputInstructions}&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    buildStep&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    logs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; trimmedLogs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    buildConfiguration&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    instructions&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    buildSpec&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    totalBuildTimeSeconds&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You may be wondering&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Aren’t you just using a convoluted template literal?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And to that, I say yes. Because I am injecting the variables at the same time as making the prompt, it looks silly. I thought I should follow the conventions that LangChain has, and I ended up not using any of the advanced prompt features as they did not seem useful for this use case.&lt;/p&gt;
&lt;h4 id=&quot;tokens&quot;&gt;Tokens!&lt;/h4&gt;
&lt;p&gt;In this prompt, there is only one variable that has, well, a variable size. The logs. Basically, the size of the prompt and the output should add up to the context size. For &lt;code&gt;gpt-4&lt;/code&gt;, that context is 8k tokens, and for &lt;code&gt;gpt-3.5-turbo-16k&lt;/code&gt;, it’s unsurprisingly 16k. Using the &lt;a href=&quot;https://platform.openai.com/tokenizer&quot;&gt;Tokenizer&lt;/a&gt; app, I calculated &lt;code&gt;LOG_SIZE&lt;/code&gt; to be 8,000 characters (not tokens) and 16,000 characters, respectively. Also, you can see I &lt;code&gt;slice&lt;/code&gt; with a negative number to get the important part: the end of the logs.&lt;/p&gt;
&lt;h4 id=&quot;delimiters&quot;&gt;Delimiters&lt;/h4&gt;
&lt;p&gt;After reading &lt;a href=&quot;https://github.com/dabit3/prompt-engineering-for-javascript-developers&quot;&gt;Nader Dabit’s Prompt Engineering Guide&lt;/a&gt;, I decided to use &lt;code&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/code&gt; as a delimiter. I used this to separate the different parts. It is unclear if this is helpful.&lt;/p&gt;
&lt;h3 id=&quot;specifying-the-format&quot;&gt;Specifying the Format&lt;/h3&gt;
&lt;p&gt;The prompt includes instructions for the LLM to coax the output using an &lt;a href=&quot;https://js.langchain.com/docs/modules/model_io/output_parsers/&quot;&gt;&lt;code&gt;Output Parser&lt;/code&gt;&lt;/a&gt;. What I’m using specifically is the &lt;a href=&quot;https://js.langchain.com/docs/modules/model_io/output_parsers/structured#structured-output-parser-with-zod-schema&quot;&gt;&lt;code&gt;StructuredOutputParser&lt;/code&gt; with Zod&lt;/a&gt; Why I find this totally rad is because &lt;a href=&quot;https://github.com/colinhacks/zod&quot;&gt;Zod&lt;/a&gt; is being used in other parts of the codebase, it auto-completes well with &lt;a href=&quot;https://github.com/features/copilot&quot;&gt;GitHub Copilot&lt;/a&gt;, and it creates very readable code.&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; schema &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; z&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  errorCategory&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; z
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;enum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&#39;systemError&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&#39;configurationError&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&#39;applicationError&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&#39;unknownError&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;the error category the error falls into&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  reason&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; z
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&#39;A short description answering the question &quot;why did the build fail?&quot;&#39;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  solution&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; z
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&#39;A longer description answering the question, &quot;how can this be fixed?&quot; describing possible steps to take to solve the error.&#39;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  codeSample&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; z
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;optional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;an optional code sample if helpful to the user.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  files&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; z
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;optional&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&#39;an optional list of files that were affected. if none were affected, this will be empty.&#39;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; parser &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; StructuredOutputParser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromZodSchema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;schema&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;output-instructions&quot;&gt;Output instructions&lt;/h4&gt;
&lt;p&gt;The resulting &lt;code&gt;parser&lt;/code&gt; generates instructions for the LLM with &lt;code&gt;parser.getFormatInstructions()&lt;/code&gt;. It first teaches JSON, though that seems unnecessary, and then gives an example. After that, the Zod schema is included and sent along with the prompt. And yes, the whole message, example and all, is included at the bottom of every prompt. The example and even the &lt;code&gt;$schema&lt;/code&gt; property. I can’t complain about the extra tokens, though, because the instructions work surprisingly well.&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;You must format your output as a JSON value that adheres to a given &quot;JSON Schema&quot; instance.

&quot;JSON Schema&quot; is a declarative language that allows you to annotate and validate JSON documents.

For example, the example &quot;JSON Schema&quot; instance

‍```
{
  &quot;properties&quot;: {
    &quot;foo&quot;: {
      &quot;description&quot;: &quot;a list of test words&quot;,
      &quot;type&quot;: &quot;array&quot;,
      &quot;items&quot;: {
        &quot;type&quot;: &quot;string&quot;
      }
    }
  },
  &quot;required&quot;: [&quot;foo&quot;]
}
‍```

would match an object with one required property, &quot;foo&quot;. The &quot;type&quot; property specifies &quot;foo&quot; must be an &quot;array&quot;, and the &quot;description&quot; property semantically describes it as &quot;a list of test words&quot;. The items within &quot;foo&quot; must be strings.
Thus, the object

‍```
{
  &quot;foo&quot;: [&quot;bar&quot;, &quot;baz&quot;]
}
‍```

is a well-formatted instance of this example &quot;JSON Schema&quot;. The object

‍```
{
  &quot;properties&quot;: {
    &quot;foo&quot;: [&quot;bar&quot;, &quot;baz&quot;]
  }
}
‍```

is not well-formatted.

Your output will be parsed and type-checked according to the provided schema instance, so make sure all fields in your output match the schema exactly and there are no trailing commas!

Here is the JSON Schema instance your output must adhere to. Include the enclosing markdown codeblock:

‍```json
{
  &quot;type&quot;: &quot;object&quot;,
  &quot;properties&quot;: {
    &quot;errorCategory&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;enum&quot;: [
        &quot;systemError&quot;,
        &quot;configurationError&quot;,
        &quot;applicationError&quot;,
        &quot;unknownError&quot;
      ],
      &quot;description&quot;: &quot;the error category the error falls into&quot;
    },
    &quot;reason&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;description&quot;: &quot;A short description answering the question &#39;why did the build fail?&#39;&quot;
    },
    &quot;solution&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;description&quot;: &quot;A longer description answering the question, &#39;how can this be fixed?&#39; describing possible steps to take to solve the error.&quot;
    },
    &quot;codeSample&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;description&quot;: &quot;an optional code sample if helpful to the user.&quot;
    },
    &quot;files&quot;: {
      &quot;type&quot;: &quot;array&quot;,
      &quot;items&quot;: {
        &quot;type&quot;: &quot;string&quot;
      },
      &quot;description&quot;: &quot;an optional list of files that were affected. if none were affected, this will be empty.&quot;
    }
  },
  &quot;required&quot;: [&quot;errorCategory&quot;, &quot;reason&quot;, &quot;solution&quot;],
  &quot;additionalProperties&quot;: false,
  &quot;$schema&quot;: &quot;http://json-schema.org/draft-07/schema#&quot;
}
‍```&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;send-it-off-and-hope&quot;&gt;Send it off and Hope&lt;/h3&gt;
&lt;p&gt;The prompt is sent to the OpenAI API, and the response is hopefully good. And hopefully adheres to the schema!
Now for the magic part of the parser.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; parser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;completion&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This line is remarkable. Because the parser has the Zod schema, the response from the AI is automatically parsed into variables that can be used. For example, a recent deployment failure is neatly returned to my code with the following object:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;errorCategory&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;configurationError&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;reason&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The &#39;hardhat&#39; package is incompatible with the installed Node.js version.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;solution&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;To fix this error, you can try one of the following solutions:&#92;n&#92;n1. Update the &#39;hardhat&#39; package to a version that is compatible with Node.js 18.16.0.&#92;n2. Downgrade the Node.js version to a version that is compatible with the &#39;hardhat&#39; package (e.g., 12.x.x, 14.x.x, or 16.x.x).&#92;n3. Check if there are any other dependencies in your project that have specific Node.js version requirements and make sure they are compatible with the installed Node.js version.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;codeSample&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;files&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That means when trying to display it on the frontend, I can update an &lt;code&gt;output&lt;/code&gt; variable with the results, pass this object into my &lt;code&gt;InsightsOutput&lt;/code&gt; component, and de-structure the expected properties. All well typed.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;output&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setOutput&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Schema&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;setOutput&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;InsightsOutput insights&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;output&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;InsightsOutput&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  insights&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  insights&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Schema
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; errorCategory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reason&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; solution&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; codeSample&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; files &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; insights &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;results&quot;&gt;Results&lt;/h2&gt;
&lt;p&gt;So… Does it provide useful results?&lt;/p&gt;
&lt;p&gt;Of course, yes and no. It seems helpful for most of my testing. There have been times when it has been wrong. Impressively, the output parser has always worked. The good thing about the system of prompts and output schema is that trial and error is pretty quick. I think getting it perfect on the first try would be impossible. Anyway, here’s an explanation in the form of a gif.&lt;/p&gt;
&lt;video muted=&quot;&quot; autoplay=&quot;&quot; controls=&quot;&quot; style=&quot;width: 100%; max-width: 100%;&quot;&gt;
  &lt;source src=&quot;https://blog.dappling.network/video/09-18-using-langchain/demo.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;
&lt;p&gt;And a closer look at the output shows that the result actually gives good advice. Well, true advice at least, as we cannot export &lt;code&gt;i18n&lt;/code&gt; projects.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;use a different deployment method&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Telling our users to leave the platform is a funny result. For all I did for the AI, a petty act!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/adding-langchain/demo.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;things-i-learned&quot;&gt;Things I learned&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The temperature should be 0 to ensure the response adheres to the output format.&lt;/li&gt;
&lt;li&gt;The instructions should be at the beginning, and the context should come after.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;go-forth-and-fail&quot;&gt;Go Forth and Fail&lt;/h3&gt;
&lt;p&gt;Now is the best time to create some failing builds on &lt;a href=&quot;https://dappling.network/&quot;&gt;dAppling.network&lt;/a&gt;. We are trying to create the best experience possible for first-time deployments on dAppling and learning from every failure. Using LangChain seems useful, and having Zod schemas makes TypeScript support easy. Providing all the information while balancing the total token amount seemed to produce promising results. If this was interesting to you, I would recommend playing around with prompt engineering. I hope, if you see a ❌ on our platform, you have success working alongside our AI companions.&lt;/p&gt;
"
    },{"id": "https://blog.dappling.network/boost-your-website-insight-with-dappling-analytics-a-new-way-to-track-your-success/",
"url": "https://blog.dappling.network/boost-your-website-insight-with-dappling-analytics-a-new-way-to-track-your-success/",
"title": "Boost Your Website Insight with dappling-analytics! - A New Way to Track Your Success",
"date_published": "2023-08-29T00:00:00Z",
"language": "","content_html": "&lt;p&gt;Hey, fellow developers! 👋 Here at dAppling, we’ve been harnessing the power of &lt;code&gt;@vercel/analytics&lt;/code&gt; to keep our finger on the pulse of our site’s performance. Every Monday, we’re either celebrating a ‘good’ week or strategizing to turn a ‘bad’ week around. But we know that these insights aren’t just valuable to us. That’s why we’ve crafted something special for you: the &lt;strong&gt;dappling-analytics package.&lt;/strong&gt; 🚀&lt;/p&gt;
&lt;h4 id=&quot;why-dappling-analytics&quot;&gt;Why dappling-analytics?&lt;/h4&gt;
&lt;p&gt;Because we know that analytics are crucial to understanding what’s happening on your site, we wanted to offer something more than a one-size-fits-all solution. With dappling-analytics, you’ll get:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Per-project statistics&lt;/strong&gt;: Track unique views and explore what specific pages people are visiting.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Easy integration&lt;/strong&gt;: Installation and configuration are a breeze, especially with React and NextJS projects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Privacy preservation&lt;/strong&gt;: We care about user privacy as much as you do!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And all this comes with an easy-to-use interface right in the dappling app!&lt;/p&gt;
&lt;h4 id=&quot;how-to-get-started&quot;&gt;How to Get Started&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Installation via Your Favorite Package Manager&lt;/strong&gt;: Just like any other NPM package, installing dappling-analytics is simple: &lt;code&gt;npm install dappling-analytics&lt;/code&gt; or &lt;code&gt;yarn install dappling-analytics&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Integration with React and NextJS&lt;/strong&gt;: Our &lt;code&gt;&amp;lt;Analytics /&amp;gt;&lt;/code&gt; component, built with React, makes integration seamless. Simply import it from our /react folder to access the component. If you’re using other frameworks, a bit more configuration might be needed (but we’re here to help!).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;import { Analytics } from &#39;dappling-analytics/react&#39;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tracking Beyond Page Views&lt;/strong&gt;: Although we’re starting with tracking page views and unique users, we’re already planning to add more useful datapoints, such as customizable events.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Backend Stuff&lt;/strong&gt;: Our API endpoint and database storage are designed to make your analytics data easily accessible and interpretable.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/bxoPyW1.png&quot; alt=&quot;dappling-analytics flow&quot; /&gt;&lt;/p&gt;
&lt;h4 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;Here at dAppling, we believe that insight shouldn’t be complicated. The dappling-analytics package is our way of sharing a piece of our garden with you. 🌱 Whether you’re looking to understand your performance better or just want a quick and easy way to tell if you had a ‘good’ or ‘bad’ week, dappling-analytics has got you covered.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ready to watch your garden grow? 🌼🌻 Try dappling-analytics today!&lt;/strong&gt; Check out our &lt;a href=&quot;https://docs.dappling.network/guides/site-analytics&quot;&gt;docs&lt;/a&gt; or contact our friendly team if you need any help. We’re just an email away!&lt;/p&gt;
"
    },{"id": "https://blog.dappling.network/adding-ens-support-to-dappling/",
"url": "https://blog.dappling.network/adding-ens-support-to-dappling/",
"title": "Adding ENS Support to dAppling",
"date_published": "2023-08-15T00:00:00Z",
"language": "","content_html": "&lt;p&gt;While working on &lt;a href=&quot;https://dappling.network/&quot;&gt;dAppling&lt;/a&gt; (helping web3 developers by making decentralized websites easy), we wanted to allow our users to be able to access their site through their ENS name. The ENS community is excited about using and sharing their cool “.eth” names. Here’s how we integrated ENS name support into our platform.&lt;/p&gt;
&lt;h2 id=&quot;resolving-ens-names&quot;&gt;Resolving ENS Names&lt;/h2&gt;
&lt;p&gt;I bought the ENS name bookland.eth. I set a record that will “point” my name to a website. On the &lt;a href=&quot;https://brave.com/&quot;&gt;Brave&lt;/a&gt; browser, navigating to &lt;a href=&quot;https://bookland.eth/&quot;&gt;bookland.eth&lt;/a&gt; will automatically look up that record and load the content. This is done by going through the “dweb.link” gateway.&lt;/p&gt;
&lt;p&gt;If you’re not on brave, a popular option is &lt;a href=&quot;https://eth.limo/&quot;&gt;eth.limo&lt;/a&gt;. You can use this service simply by prepending your ENS name: &lt;a href=&quot;https://bookland.eth.limo/&quot;&gt;bookland.eth.limo&lt;/a&gt;. Your request is served by first looking at the subdomain (for this example, bookland) and doing a record lookup on ENS. That record is then decoded into the “pointer” I set. Finally, the content is retrieved from IPFS and returned.&lt;/p&gt;
&lt;h2 id=&quot;how-this-technically-works&quot;&gt;How this Technically Works&lt;/h2&gt;
&lt;p&gt;Ok - so let’s go though how this feature works. We introduced three different services with this feature: a name service, an ENS records service, and key storage service.&lt;/p&gt;
&lt;h3 id=&quot;name-service&quot;&gt;Name Service&lt;/h3&gt;
&lt;p&gt;These “pointers” I’m referring to are actually InterPlanetary Name System (&lt;a href=&quot;https://docs.ipfs.tech/concepts/ipns/&quot;&gt;IPNS&lt;/a&gt;) records. These are cool because we can actually change the mapping of these keys to the content. This is perfect for our use-case of updating a website on behalf of a user.&lt;/p&gt;
&lt;p&gt;We considered 3 different name services&lt;/p&gt;
&lt;h4 id=&quot;dwebservicesxyz&quot;&gt;dwebservices.xyz&lt;/h4&gt;
&lt;p&gt;https://dwebservices.xyz/ would have been the simplest option. They provide and API where you can create an IPNS name, and use the API to control where it points and update revisions. This is the simplest option by far if you are OK not having custody of your keys.
Unfortunately, for our use case, we needed to have custody over the IPNS keys. This is because we need to ensure that we have the ability to update where the IPNS names are pointing indefinitely into the future, and we need to be able to ensure the IPNS records are always resolvable.&lt;/p&gt;
&lt;h4 id=&quot;running-the-ipns-infra-ourselves&quot;&gt;Running the IPNS infra ourselves&lt;/h4&gt;
&lt;p&gt;The second thing we looked into was what it would look like to run the IPNS infrastructure ourselves. Unfortunately the documentation to do this in a way with high uptime was REALLY bad. I learned that js-ipfs was recently depreciated and is being replaced by Helia. But lacked good documentation around how to do &lt;a href=&quot;https://github.com/ipfs/helia-ipns&quot;&gt;IPNS on Helia&lt;/a&gt;. There are still several questions I have around how to do this in a good way, but decided to move on when I discovered w3name&lt;/p&gt;
&lt;h4 id=&quot;w3name&quot;&gt;w3name&lt;/h4&gt;
&lt;p&gt;W3name helps us by making the creation, updating, and deletion easier. Additionally, w3name enables the lookup of the values of these pointers, ensuring that they are always available. We would have control over the keys, and the service has a simple and documented &lt;a href=&quot;https://web3-storage.github.io/w3name/&quot;&gt;API&lt;/a&gt;. Feel free to &lt;a href=&quot;https://web3.storage/products/w3name/&quot;&gt;read more about w3name&lt;/a&gt; on their site.&lt;/p&gt;
&lt;h3 id=&quot;ens-service&quot;&gt;ENS Service&lt;/h3&gt;
&lt;p&gt;For the interactions with ENS names, we do contract interactions with their &lt;a href=&quot;https://docs.ens.domains/contract-api-reference/publicresolver&quot;&gt;PublicResolver&lt;/a&gt; using wagmi. Actually, the contract interactions were better than I expected. using the &lt;a href=&quot;https://wagmi.sh/cli/getting-started&quot;&gt;wagmi CLI&lt;/a&gt;, React hooks for each call are created, keeping our code nice and type-safe. We also used some utils from &lt;a href=&quot;https://www.npmjs.com/package/@ensdomains/content-hash&quot;&gt;@ensdomains/content-hash&lt;/a&gt; and the awesome, also type-safe, &lt;a href=&quot;https://viem.sh/&quot;&gt;viem&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One complaint with the ENS development is there is no nice way to resolve the content-hash. There are great ways to &lt;a href=&quot;https://docs.ens.domains/dapp-developer-guide/resolving-names&quot;&gt;resolve names&lt;/a&gt; and such, though.&lt;/p&gt;
&lt;h3 id=&quot;key-storage-service&quot;&gt;Key Storage Service&lt;/h3&gt;
&lt;p&gt;For key storage, our thoughts were pretty straightforward. How do we keep our users’ keys safe while being accessible. Our infrastructure is on AWS so we considered using our Dynamo database, but that did not seem secure. What we ended up with was using storing them encrypted through &lt;a href=&quot;https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html&quot;&gt;AWS ParameterStore&lt;/a&gt;. When the keys need looked up, we can retrieve them through our services while keeping them secure for the user.&lt;/p&gt;
&lt;h2 id=&quot;put-it-together-what-do-you-get&quot;&gt;Put it Together, What do you Get?&lt;/h2&gt;
&lt;p&gt;From the user’s perspective, it should be simple.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Show intent of adding their ENS domain. In the background we: get the latest IPFS CID, create an IPNS record, send it to w3name, update the project.&lt;/li&gt;
&lt;li&gt;The user connects their wallet and signs a transaction to update the content hash. Again, in the background we are: verifying the user owns the ENS domain or anything else that would prevent the transaction from succeeding, and creating that transaction with the newly created IPNS key.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Then, immediately after the content record is updated, the user can visit their newly hosted website via Brave, eth.limo, or hopefully soon, anywhere! Even better, every time the project has an update, the content is automatically updated. Let’s go!&lt;/p&gt;
&lt;p&gt;Go forth and update your ENS records on your dAppling projects!&lt;/p&gt;
&lt;p&gt;📚&lt;/p&gt;
"
    },{"id": "https://blog.dappling.network/poetry-in-imagery-meta-image-generation-in-nextjs/",
"url": "https://blog.dappling.network/poetry-in-imagery-meta-image-generation-in-nextjs/",
"title": "Poetry in Imagery • Meta Image Generation in Next.js",
"date_published": "2023-08-08T00:00:00Z",
"language": "","content_html": "&lt;p&gt;When I stroll through the web without intention, every link, title, and image vies for my attention.  With a goal in mind, those same links, titles, and images seem to lack dimension. I struggle to find the information I seek; behind each possible link lacking context, I’m left blind.&lt;/p&gt;
&lt;p&gt;Meta tags of a website are a helpful guide. Used to share information about potential content behind a link, these small tags can hold enormous amounts of useful data. As long as they are used correctly, that is. I hope their use is expanded and can help and inspire creators to put more energy into their own site’s images.&lt;/p&gt;
&lt;h2 id=&quot;essence-of-representation&quot;&gt;Essence of Representation&lt;/h2&gt;
&lt;p&gt;Meta tags provide glimpses into the content without requiring users to visit the page itself. These tags can articulate essential details like the title, description, and intended size of the site. Open Graph tags (og), introduced by Facebook, define a way to share a summary of the content, location data, media information, and &lt;a href=&quot;https://ogp.me/&quot;&gt;more&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The tag for an arbitrary image, &lt;code&gt;og:image&lt;/code&gt;, I think has the most potential. These images are not mere visual aids; they are the distilled essence of the web page they represent. A visual haiku. For our app &lt;a href=&quot;https://www.dappling.network/&quot;&gt;dAppling&lt;/a&gt;, I wanted to follow GitHub’s lead with a desire to give the user as much information as possible.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.blog/wp-content/uploads/2021/06/framework-open-graph-images_fig-1-GitHub-twitter-card.png?resize=2400%2C1260&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;By way of example, GItHub’s meta images. Explained more in their &lt;a href=&quot;https://github.blog/2021-06-22-framework-building-open-graph-images/&quot;&gt;A framework for building Open Graph images&lt;/a&gt; article, the image succinctly and, in my opinion, beautifully explains what you will see before you see it. I love these images. From this little representation, I have been able to quickly gather information and confidently ignore or navigate into the site.&lt;/p&gt;
&lt;h2 id=&quot;the-alchemists-wand&quot;&gt;The Alchemist’s Wand&lt;/h2&gt;
&lt;p&gt;Creating these visual haiku is a craft, a magical art. With tools like &lt;a href=&quot;https://vercel.com/blog/introducing-vercel-og-image-generation-fast-dynamic-social-card-images&quot;&gt;@vercel/og&lt;/a&gt; and &lt;a href=&quot;https://github.com/vercel/next.js&quot;&gt;Next.js&lt;/a&gt;, I started my journey.&lt;/p&gt;
&lt;p&gt;A flick of the wrist and a small creation appeared &lt;img src=&quot;https://www.dappling.network/api/og?id=blog&quot; alt=&quot;&quot; /&gt;&lt;img src=&quot;https://www.dappling.network/api/og?id=dog&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://www.dappling.network/api/og?id=bog&quot; alt=&quot;&quot; /&gt;
Each project, tied to a unique gradient, becomes recognizable at a glance. To look under the leaves, &lt;a href=&quot;https://gist.github.com/Namaskar-1F64F/c851789c4df7d528c6c08ec60a9c90f8&quot;&gt;here’s a gist of the generator&lt;/a&gt;. The examples above use different IDs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;top &lt;code&gt;https://www.dappling.network/api/og?id=blog&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;middle &lt;code&gt;https://www.dappling.network/api/og?id=dog&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;bottom &lt;code&gt;https://www.dappling.network/api/og?id=bog&lt;/code&gt;,&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another gesture of the wand; a motion with emotion. Two new cards materialize. You can see the pretty gradient for each of them is consistent (The ID for the following two are &lt;code&gt;meow&lt;/code&gt;).
&lt;img src=&quot;https://blog.dappling.network/images/dynamic-meta-images/project-card.png&quot; alt=&quot;&quot; /&gt;
Our beautiful project card contains a preview at a glance. The deployment card adds two more elements.
&lt;img src=&quot;https://blog.dappling.network/images/dynamic-meta-images/deployment-card.png&quot; alt=&quot;&quot; /&gt;
You can see, without navigating to the site, if your deployment passed or failed. You can get the “verified” badge after someone signs the deployment, attesting its legitimacy.&lt;/p&gt;
&lt;h3 id=&quot;the-poetry-and-the-pitfalls-nextjs&quot;&gt;The Poetry and the Pitfalls • Next.js&lt;/h3&gt;
&lt;p&gt;Now we come to the unfortunate story. I would love to say these things all worked perfectly, but the real-time creation of these images posed performance challenges. The metaphysical had to meet the physical world of server responses and loading times. You see, when apps like telegram, slack, discord, and the like try to get these images, the data must be created on the fly. This causes a few problems like, how do we get the information for the page on load?&lt;/p&gt;
&lt;p&gt;This is solved by &lt;a href=&quot;https://nextjs.org/docs/pages/building-your-application/data-fetching/get-server-side-props&quot;&gt;&lt;code&gt;getServerSideProps&lt;/code&gt;&lt;/a&gt;, but also causes the performance hit. In the new app router, the &lt;a href=&quot;https://nextjs.org/docs/app/api-reference/functions/generate-image-metadata&quot;&gt;&lt;code&gt;generateImageMetadata&lt;/code&gt;&lt;/a&gt; function should work nicely. However, when browsing the site normally, one must wait for the metadata to load, even though they are not concerned with the meta image, after all, they’re already on the site.&lt;/p&gt;
&lt;p&gt;We scaled back, yet the essence remains. The images still capture the spirit, albeit in a more simplistic form.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.dappling.network/api/og?type=project&amp;amp;id=meow&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;https://www.dappling.network/api/og?id=meow&amp;amp;type=deployment&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;learnings-and-such&quot;&gt;Learnings and Such&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Meta tags are first come first used. That means, if you have multiple meta tags on the page, it’ll probably use the first one encountered. This confused me because instead of being able to define site-wide metadata, I had to use Next.js’s &lt;a href=&quot;https://nextjs.org/docs/pages/api-reference/components/head&quot;&gt;&lt;code&gt;&amp;lt;Head&amp;gt;&lt;/code&gt;&lt;/a&gt; element on every page.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Having an &lt;code&gt;svg&lt;/code&gt; as a meta image will silently fail and ruin your day. Make them &lt;code&gt;png&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Using metadata website checkers do not accurately reflect exactly what will happen on each individual social media platform. They all have their own way to display the images.
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.rakko.tools/tools/9/&quot;&gt;open graph debugger&lt;/a&gt; - see if the images work&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chrome.google.com/webstore/detail/meta-debugger/jfpdemgdamgplelnlmaecbonkfgfgomp#:~:text=Meta%20Debugger&amp;amp;text=Debug%20the%20head%20section%20of,icons%20and%20many%20more%20things.&quot;&gt;meta debugger&lt;/a&gt; - view tags in chrome dev tools useful for duplicates&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Using local metadata debugging does not accurately reflect the first load of your page. There is a chance the meta tags change as hooks return data. Keep this in mind when using extensions like &lt;a href=&quot;https://chrome.google.com/webstore/detail/localhost-open-graph-debu/kckjjmiilgndeaohcljonedmledlnkij&quot;&gt;localhost open graph debugger&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;twitter:&lt;/code&gt; tags are used by more than just twitter.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The full list of image related tags used:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:url&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{url}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;itemProp&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{url}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;itemProp&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{imageUrl}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{imageUrl}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{imageUrl}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:image:alt&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;dAppling&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:image:type&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image/png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:image:width&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:image:height&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;630&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;twitter:image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{imageUrl}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;twitter:image:src&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{imageUrl}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;twitter:card&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;summary_large_image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;twitter:site&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@dApplingNetwork&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;twitter:creator&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@dApplingNetwork&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;twitter:url&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://www.dappling.network&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;twitter:title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;...&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;twitter:description&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;...&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;If I were to start over, the process simply is:&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;start ideating, sketching, and playing with &lt;a href=&quot;https://og-playground.vercel.app/&quot;&gt;the og playground&lt;/a&gt; until you have struck inspiration and want to start migrating to your actual code.&lt;/li&gt;
&lt;li&gt;create either a new Next.js project or add the og endpoint to your existing project.&lt;/li&gt;
&lt;li&gt;modify the meta tags of your site to include the dynamic url. e.g. &lt;code&gt;https://www.dappling.network/api/og?type=project&amp;amp;id=${projectId}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;test with the tools above&lt;/li&gt;
&lt;li&gt;cross your fingers&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you’re interested in our exact implementation, the full code can be found in &lt;a href=&quot;https://gist.github.com/Namaskar-1F64F/f499c85c286eaae6b2e6a7854539c140&quot;&gt;this gist&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;the-art-of-the-web&quot;&gt;The Art of the Web&lt;/h2&gt;
&lt;p&gt;Meta image generation is both a technical task and an art form. How one can translate the complex into the simple while maintaining beauty. Like a haiku captures a moment, a well-crafted meta image reminds that even in the technical world of web development, there’s room for poetry, beauty, and a touch of the metaphysical.&lt;/p&gt;
&lt;p&gt;Thank you for your time. If you need help, please reach out to me &lt;a href=&quot;https://t.me/folded_hands&quot;&gt;on telegram&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;🙏❤️🌱 What a great day to be alive!&lt;/p&gt;
"
    },{"id": "https://blog.dappling.network/content-addressable-storage-vs-location-based-storage-for-websites/",
"url": "https://blog.dappling.network/content-addressable-storage-vs-location-based-storage-for-websites/",
"title": "Content-addressable Storage vs Location-based Storage for Websites",
"date_published": "2023-08-04T00:00:00Z",
"language": "","content_html": "&lt;p&gt;Content-based storage allows the lookup of a website based on its contents instead of its name or location.&lt;/p&gt;
&lt;p&gt;It works by passing the content through a hash function, which generates a content address.
This content address can then be used to request the content, and anyone who is hosting the content that hashes to that content address can serve the content.&lt;/p&gt;
&lt;h3 id=&quot;location-based-storage&quot;&gt;Location-Based Storage&lt;/h3&gt;
&lt;p&gt;Location-based storage is the traditional and widely used approach for accessing web content. It involves accessing data through known URLs that point to specific locations where the content is stored. Here are the key traits of location-based storage:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Familiarity:&lt;/strong&gt; Most people are familiar with location-based storage, as it is the standard method for accessing websites and online content.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Direct Retrieval:&lt;/strong&gt; Users access data by navigating to known URLs, which directly point to the server hosting the content.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hierarchical Structure:&lt;/strong&gt; Location-Based Storage organizes data in hierarchical structures, making it easy to manage and categorize content.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Link Rot Susceptibility:&lt;/strong&gt; Over time, URLs might become invalid due to changes in the underlying infrastructure, leading to link rot.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No built-in data verification:&lt;/strong&gt; location-based storage does not inherently verify the integrity of the data being accessed.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;content-addressable-storage&quot;&gt;Content-Addressable Storage&lt;/h3&gt;
&lt;p&gt;Content-addressable storage is a newer approach that ensures data integrity and immutability through unique content hashes. Instead of relying on URLs with specific locations, content is accessed based on its cryptographic hash. Let’s explore the traits of content-addressable storage:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Data Integrity and Immutability:&lt;/strong&gt; Each piece of content is identified by a unique content hash generated through a hash function. Any modification to the content results in a different hash, ensuring data integrity and immutability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Efficient Deduplication:&lt;/strong&gt; Since identical content generates the same content hash, content-addressable storage can efficiently deduplicate data, reducing storage overhead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resilience to Link Rot:&lt;/strong&gt; Content is permanently accessible through its content hash, eliminating the issue of link rot experienced in location-based storage.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Complexity vs. Direct Interaction:&lt;/strong&gt; While content hashes provide strong integrity, they may be less user-friendly for direct interaction compared to traditional URLs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implementation Complexity:&lt;/strong&gt; Content-Addressable Storage may require slightly more complexity in implementation compared to straightforward location-based storage.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&quot;example-location-based-storage-vs-content-addressable-storage&quot;&gt;Example: Location-Based Storage vs. Content-Addressable Storage&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Location-Based Storage Example:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;URL: &lt;a href=&quot;https://blog.dappling.network/&quot;&gt;https://blog.dappling.network/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this blog, the URL “&lt;a href=&quot;https://blog.dappling.network/&quot;&gt;https://blog.dappling.network/&lt;/a&gt;” is a classic example of location-based storage. The content you’re accessing is stored at the specific location, “blog.dappling.network.” When you navigate to this URL, your web browser sends a request to the web server hosting the blog, and the server responds by delivering the content associated with that particular location. This is the conventional and familiar way of accessing web content, widely used for traditional websites and blogs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Content-Addressable Storage Example:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;URL: &lt;a href=&quot;https://bafybeigvbtiinlrfoewlccg4xgbxgsd4aa62gzjls6v2sg5aji4nu76n5q.ipfs.dweb.link/&quot;&gt;https://bafybeigvbtiinlrfoewlccg4xgbxgsd4aa62gzjls6v2sg5aji4nu76n5q.ipfs.dweb.link/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now, let’s consider a content-addressable storage example using IPFS (InterPlanetary File System) for this blog post. The URL “&lt;a href=&quot;https://bafybeigvbtiinlrfoewlccg4xgbxgsd4aa62gzjls6v2sg5aji4nu76n5q.ipfs.dweb.link/&quot;&gt;https://bafybeigvbtiinlrfoewlccg4xgbxgsd4aa62gzjls6v2sg5aji4nu76n5q.ipfs.dweb.link/&lt;/a&gt;” points to the content stored on the IPFS network. The string “bafybeigvbtiinlrfoewlccg4xgbxgsd4aa62gzjls6v2sg5aji4nu76n5q” is a unique content hash generated based on the blog post’s data. When you access this URL, the IPFS network retrieves the content associated with that specific content hash, ensuring data integrity and immutability.&lt;/p&gt;
&lt;p&gt;By using content-addressable storage, this blog post becomes permanently accessible via its unique content hash, even if the underlying IPFS network changes or moves.&lt;/p&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Both location-based storage and content-addressable storage present compelling use cases for hosting websites. Location-based storage offers a familiar and straightforward approach, enabling content retrieval through traditional URLs and hierarchical data organization.&lt;/p&gt;
&lt;p&gt;On the other hand, content-addressable storage ensures strong data integrity and immutability by generating unique content hashes. This method promotes efficient deduplication, reduces storage overhead, and ensures resilience to link rot.&lt;/p&gt;
&lt;p&gt;At &lt;a href=&quot;https://dappling.network/landing&quot;&gt;dAppling&lt;/a&gt;, we are developing a platform that combines the strengths of both approaches to create a secure and performant hosting solution. By leveraging content addressing, we enhance data permanence and integrity while maintaining fast and efficient content retrieval for end-users. Our platform aims to empower developers with a user-centric and developer-friendly solution for building performant and resilient websites.&lt;/p&gt;
"
    },{"id": "https://blog.dappling.network/i11g-updating-an-immutable-blog/",
"url": "https://blog.dappling.network/i11g-updating-an-immutable-blog/",
"title": "i11g - Updating an Immutable Blog",
"date_published": "2023-07-19T00:00:00Z",
"language": "","content_html": "&lt;p&gt;We needed a blog for &lt;a href=&quot;https://dappling.network/&quot; title=&quot;dAppling site&quot;&gt;dAppling&lt;/a&gt;. One of our important marketing efforts revolves around writing engaging and helpful content. The aim of this is to show two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We know things.&lt;/li&gt;
&lt;li&gt;We share things.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Not only that, but we like to help people in ways that we can. Sharing knowledge, for me, gives great joy.&lt;/p&gt;
&lt;h2 id=&quot;high-level-goals&quot;&gt;High-level Goals&lt;/h2&gt;
&lt;p&gt;So the question is, how can we use the things we know to host and share those things in a way that is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Search Engine Optimization (SEO)&lt;/li&gt;
&lt;li&gt;Pretty&lt;/li&gt;
&lt;li&gt;Easy to update&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;search-engine-optimization-seo&quot;&gt;Search Engine Optimization (SEO)&lt;/h3&gt;
&lt;p&gt;We have posted articles on &lt;a href=&quot;https://dev.to/dappling&quot;&gt;dev.to&lt;/a&gt;, but wanted stronger search on our own domain. The ideation started a few days ago because dev.to does not allow us to use a custom domain. After reading an article advocating to &lt;a href=&quot;https://indieweb.org/POSSE&quot;&gt;Publish (on your) Own Site, Syndicate Elsewhere&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;the practice of posting content on your own site first, then publishing copies or sharing links to third parties (like &lt;a href=&quot;https://indieweb.org/social_media&quot; title=&quot;social media&quot;&gt;social media&lt;/a&gt; silos) with &lt;a href=&quot;https://indieweb.org/original_post_link&quot; title=&quot;original post link&quot;&gt;original post links&lt;/a&gt; to provide viewers a path to directly interacting with your content.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So the site is hosted on our domain, that’s wonderful. Further, the content is statically generated, which means easier to crawl.&lt;/p&gt;
&lt;h3 id=&quot;pretty&quot;&gt;Pretty&lt;/h3&gt;
&lt;p&gt;I went through the templates on the 11ty &lt;a href=&quot;https://www.11ty.dev/docs/starter/&quot;&gt;starter projects&lt;/a&gt; page and found this awesome &lt;a href=&quot;https://github.com/lwojcik/eleventy-template-bliss&quot;&gt;“Bliss” template&lt;/a&gt; by &lt;a href=&quot;https://github.com/lwojcik&quot; title=&quot;Lukasz Wojcik&quot;&gt;Łukasz Wójcik&lt;/a&gt; on GitHub. I think it’s pretty. Thanks, Łukasz!&lt;/p&gt;
&lt;h3 id=&quot;easy-to-update&quot;&gt;Easy to Update&lt;/h3&gt;
&lt;p&gt;This was a consideration because if other people on our team want to update the blog, it needs to be straightforward enough to not get in anyone’s way. Since this is all Markdown, along with this &lt;a href=&quot;https://github.com/alwaysbegrowing/blog/commit/7c4562bee9b4c2962cf47f4c747d8c5751ee82c1&quot;&gt;blog post commit&lt;/a&gt;, it should be pretty self-explanatory to duplicate an article.&lt;/p&gt;
&lt;h3 id=&quot;dont-get-me-wrong&quot;&gt;Don’t Get me wrong!&lt;/h3&gt;
&lt;p&gt;I know there are many options to write blogs that are more optimized than what I’m suggesting here. There’s Medium, Substack, the aforementioned dev.to, Ghost, the list really goes on, but there’s a desire of mine to make something that’ll be compatible with our current infrastructure at dAppling, and I’m having fun. This is the best I have come up with, but do note, I’ve not exhausted all the web’s offerings!&lt;/p&gt;
&lt;h2 id=&quot;implementing-this-yourself&quot;&gt;Implementing this yourself&lt;/h2&gt;
&lt;p&gt;I’m using this blog post as a way to explain-by-doing. Here’s my workflow, from concept to creation.&lt;/p&gt;
&lt;h3 id=&quot;create-your-content&quot;&gt;Create your Content&lt;/h3&gt;
&lt;p&gt;One can obsess over the best way to display information, and that display can be in many different formats, but what most people have seemed to converge on is &lt;a href=&quot;https://www.markdownguide.org/getting-started/&quot;&gt;Markdown&lt;/a&gt;. Because of its portability, writing the content in markdown first and finding where to host it later is a great strategy. To that end, I am writing this in my favorite editor, &lt;a href=&quot;https://obsidian.md/&quot;&gt;Obsidian&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/11ty-on-ipfs/obsidian-editor.png&quot; alt=&quot;Obsidian editor with the above content written&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;manage-your-files&quot;&gt;Manage your Files&lt;/h3&gt;
&lt;p&gt;First, clone the &lt;a href=&quot;https://github.com/lwojcik/eleventy-template-bliss&quot;&gt;“Bliss” template&lt;/a&gt; and open it up locally in your favorite file editor. When managing the files while writing the blog post, it will be helpful to run the website in development mode.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; i &lt;span class=&quot;token comment&quot;&gt;# first install dependencies&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run dev &lt;span class=&quot;token comment&quot;&gt;# start a local server&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To manage the files, you have images and content.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For images, I created a folder for the specific blog post and the related images go into this dedicated folder. The image above is then referenced as: &lt;code&gt;![Obsidian editor with the above content written](/images/11ty-on-ipfs/obsidian-editor.png)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Content goes into the posts’ folder with the markdown, saved as &lt;code&gt;07-19-11ty-on-ipfs.md&lt;/code&gt;, the date and title.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/11ty-on-ipfs/file-system.png&quot; alt=&quot;vscode showing the file structure of the blog&quot; /&gt;&lt;/p&gt;
&lt;p&gt;And that’s it! After running the server, the content with images should now be viewable at &lt;code&gt;http://localhost:8080/&amp;lt;post title&amp;gt;/&lt;/code&gt; because the template we are using, thanks to 11ty, creates that route for us.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/11ty-on-ipfs/rendered-website.png&quot; alt=&quot;rendered website&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;create-your-dappling-project&quot;&gt;Create your dAppling Project&lt;/h2&gt;
&lt;p&gt;Now this should be the easy part. Head over to &lt;a href=&quot;https://dappling.network/&quot;&gt;dAppling Network&lt;/a&gt;, sign in, and create a new project. The steps for this are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Sign-up and authorize the GitHub App.&lt;/li&gt;
&lt;li&gt;Create a new project and select your blog repo.&lt;/li&gt;
&lt;li&gt;Accept the build settings, they should be automatically detected.&lt;/li&gt;
&lt;li&gt;Hit that big deploy button, and wait!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/11ty-on-ipfs/deploy-project.png&quot; alt=&quot;deployment steps on dAppling&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You’ll wait a bit while the code is deployed, and you should get a link to the site deployed on IPFS. We have a friendly URL like &lt;a href=&quot;https://blog-xn6nf4.dappling.network/&quot;&gt;blog-xn6nf4.dappling.network&lt;/a&gt; and the template should show up!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/11ty-on-ipfs/live-on-dappling.png&quot; alt=&quot;live site on dAppling&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;update-your-dappling-project&quot;&gt;Update your dAppling Project&lt;/h2&gt;
&lt;p&gt;Now, to update a new blog post, you can either update your “main” branch, by either pushing directly or by creating a PR and merging it. I will be pushing directly to the branch and let dAppling deploy the site. After that, the site should be live!&lt;/p&gt;
&lt;h3 id=&quot;artifacts&quot;&gt;Artifacts&lt;/h3&gt;
&lt;p&gt;I pushed it after writing that paragraph, and it created a &lt;a href=&quot;https://gardener-ivy-jfecm0i.dappling.network/ipfs-11ty-dappling-i11g/&quot;&gt;preview URL&lt;/a&gt; for me. You can view the &lt;a href=&quot;https://www.dappling.network/projects/4f6bf966-989f-434c-b1e1-739967f3de4d/BuildImage74257FD8-Ag37vnjYXUuu:b9627f25-d7d1-46a9-8892-40fc1327458b&quot;&gt;deployment on dAppling&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/11ty-on-ipfs/deployment-page.png&quot; alt=&quot;deployment page on dAppling&quot; /&gt;&lt;/p&gt;
&lt;p&gt;And the &lt;a href=&quot;https://github.com/alwaysbegrowing/blog/commit/7c4562bee9b4c2962cf47f4c747d8c5751ee82c1&quot;&gt;Commit on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.dappling.network/images/11ty-on-ipfs/commit.png&quot; alt=&quot;commit on github with changes&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-did-i-learn&quot;&gt;What Did I learn?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The creation of this blog post was slower than using an editor online. This is mainly from images. I have to take screenshots and then save them locally. I prefer this over hosting them on a website because having them hosted alongside the website guarantees they will always be available, whereas an online service might be down.&lt;/li&gt;
&lt;li&gt;Introducing changes to the blog layout is harder than, say Notion, as there aren’t many controls over the typed markdown. Luckily, I like the way the site looks out-of-the-box.&lt;/li&gt;
&lt;li&gt;I dare conclude that this blog post took me about 4 hours. That is long! I hope it was because of the do-as-I-go approach.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclude&quot;&gt;Conclude&lt;/h2&gt;
&lt;p&gt;I think this set-up with writing and updating in a single place and having deployment handled by dAppling will be convenient enough to use right now. There are some changes I still need to make, like branding on the site. Of course, we also miss out on features like comments and engagement. I guess we will learn more when I make the next post trying to figure out how the heck I syndicate this everywhere.&lt;/p&gt;
&lt;p&gt;Thank you for your time, and if you have any questions, do reach out.&lt;/p&gt;
"
    },{"id": "https://blog.dappling.network/how-dappling-uses-filebase/",
"url": "https://blog.dappling.network/how-dappling-uses-filebase/",
"title": "How dAppling Uses Filebase",
"date_published": "2023-06-23T00:00:00Z",
"language": "","content_html": "&lt;p&gt;At the core of &lt;a href=&quot;https://dappling.network/&quot;&gt;dAppling&lt;/a&gt;, we both build code and host the completed app, ensuring a convenient experience for our users and one akin to centralized hosting for their users.&lt;/p&gt;
&lt;p&gt;We interact with Filebase in two ways: putting things into the bucket and taking things out of the bucket.&lt;/p&gt;
&lt;h2 id=&quot;into-the-bucket&quot;&gt;Into the Bucket&lt;/h2&gt;
&lt;p&gt;In a way, we use ‘storage’ in the sense popularized by Amazon because Filebase supports s3 APIs to store content. When a build is complete, we generate build artifacts and upload them to Filebase via the API. Once this process completes, we receive a ‘receipt’ containing the content identifier (CID) or hash of the uploaded content. This CID is stored with the project for use later when content needs to be accessed.&lt;/p&gt;
&lt;p&gt;This process is outlined in the following diagram in which a Uniswap developer wants to use dAppling.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/amptlikiaxawtx9qoaqo.png&quot; alt=&quot;Request diagram of a dappling code build - how do you build a decentralized app&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;out-of-the-bucket&quot;&gt;Out of the Bucket&lt;/h2&gt;
&lt;p&gt;When it comes to content access, there are two main methods. Users can either directly access the content via the CID using their local IPFS node or a gateway, or they can use an auto-updating URL created by dAppling. This URL could be an auto-generated link such as &lt;code&gt;garden-daisy.dappling.network&lt;/code&gt;, or a custom domain configured by the user, like &lt;code&gt;ipfs.uniswap.org&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;an-aside-of-configuring-a-custom-domain&quot;&gt;An aside of configuring a custom domain&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c4gpb37i6p843pliyino.png&quot; alt=&quot;custom domain request diagram using dappling&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In the first case of accessing the CID directly, the content may or may not be downloaded via Filebase. This is because some other IPFS nodes may have cached the content. However, if the content does not exist, the “pinned” files will be ultimately retrieved at Filebase’s nodes.&lt;/p&gt;
&lt;p&gt;In the second case, we use Filebase’s gateway directly to retrieve the content.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kegbos6y42kf7s10yaih.png&quot; alt=&quot;request diagram of accessing Firebase content via dappling&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;how-could-this-be-improved&quot;&gt;How could this be improved?&lt;/h2&gt;
&lt;h3 id=&quot;replace-the-record-handling&quot;&gt;Replace the Record Handling&lt;/h3&gt;
&lt;p&gt;Currently, we use a CDN in front of Filebase’s gateway, which also functions as a CDN. This process appears to involve one extra step. But how else could we allow users to use a custom domain that maps to an updatable CID?&lt;/p&gt;
&lt;p&gt;I think it might look something like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Filebase handles DNS records somehow. I know it involves certificate provisioning, which is a bummer. It would be nice to request a domain &lt;code&gt;ipfs.uniswap.org&lt;/code&gt; and have it return the records for a user to update.&lt;/li&gt;
&lt;li&gt;Filebase allows updating the CID associated with the domain programmatically. So when the Uniswap code is updated, we can update the domain.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;subdomain-gateways&quot;&gt;Subdomain gateways&lt;/h3&gt;
&lt;p&gt;As I detailed in my article about &lt;a href=&quot;https://dev.to/namaskar-dappling/hosting-static-sites-on-ipfs-46f&quot;&gt;IPFS and resource paths&lt;/a&gt;, it’s crucial to stay as close to the user’s development environment as possible. Therefore, we stay away from anything hosted at a “path”.&lt;/p&gt;
&lt;p&gt;From &lt;a href=&quot;https://consensys.net/diligence/blog/2021/06/ipfs-gateway-security/&quot;&gt;consensys IPFS security article&lt;/a&gt;,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;TL;DR: Path-based IPFS gateways have a critical flaw: They effectively disable one of the essential security features of modern browsers: the same-origin policy.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We therefore prefer having the user bring their domain to host, like using &lt;code&gt;ipfs.uniswap.org&lt;/code&gt;, or our preview domain like &lt;code&gt;garden-daisy.dappling.network&lt;/code&gt;. Because of this, we have sites that will not work on path gateways such as &lt;code&gt;gateway.dappling.network/&amp;lt;CID hash&amp;gt;&lt;/code&gt;. We would prefer the gateway support the subdomain version a la &lt;code&gt;&amp;lt;CID hash&amp;gt;.gateway.dappling.network&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It would be ideal if Filebase supported these subdomains as the access is much faster than any other gateway, but meanwhile we will direct our users towards a subdomain version like &lt;code&gt;&amp;lt;CID hash&amp;gt;.ipfs.cf-ipfs.com&lt;/code&gt;&lt;/p&gt;
"
    }]
}