<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Alex Kernel</title>
    <description>The latest articles on Forem by Alex Kernel (@bitwiserokos).</description>
    <link>https://forem.com/bitwiserokos</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3671226%2Fadc178bd-d32a-4bea-8fea-ef1b3c9980aa.png</url>
      <title>Forem: Alex Kernel</title>
      <link>https://forem.com/bitwiserokos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/bitwiserokos"/>
    <language>en</language>
    <item>
      <title>Add authentication to your Nuxt 3 and Vue 3 applications (Logto)</title>
      <dc:creator>Alex Kernel</dc:creator>
      <pubDate>Mon, 22 Dec 2025 11:45:51 +0000</pubDate>
      <link>https://forem.com/bitwiserokos/add-authentication-to-your-nuxt-3-and-vue-3-applications-logto-4oeh</link>
      <guid>https://forem.com/bitwiserokos/add-authentication-to-your-nuxt-3-and-vue-3-applications-logto-4oeh</guid>
      <description>&lt;p&gt;Logto provides official SDKs for multiple frameworks, but the &lt;strong&gt;integration approach depends on your app type&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SSR / full-stack frameworks (Nuxt, Next.js, etc.)&lt;/strong&gt; typically need a server-aware SDK that can safely store secrets and manage redirect-based flows on the server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SPA frameworks (Vue, React, etc.)&lt;/strong&gt; typically use a client-side SDK and rely on redirect-based login without server secrets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this guide, you’ll implement Logto authentication &lt;strong&gt;twice&lt;/strong&gt;:&lt;br&gt;
1) &lt;strong&gt;Nuxt 3 (SSR)&lt;/strong&gt; using &lt;code&gt;@logto/nuxt&lt;/code&gt;&lt;br&gt;&lt;br&gt;
2) &lt;strong&gt;Vue 3 (SPA)&lt;/strong&gt; using Logto’s Vue SDK&lt;/p&gt;

&lt;p&gt;No fluff — just a production-ready, copy-paste-friendly walkthrough.&lt;/p&gt;


&lt;h2&gt;
  
  
  Before you start: create a Logto app in the Console
&lt;/h2&gt;

&lt;p&gt;In Logto Console, create an application that matches your framework:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For &lt;strong&gt;Nuxt 3&lt;/strong&gt;, create a &lt;strong&gt;Traditional Web&lt;/strong&gt; application.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;Vue 3&lt;/strong&gt;, create a &lt;strong&gt;Single Page Application (SPA)&lt;/strong&gt; application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll need these values for both setups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Endpoint&lt;/strong&gt; (your &lt;a href="https://logto.io/" rel="noopener noreferrer"&gt;https://logto.io/&lt;/a&gt; tenant URL)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;App ID&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;(Nuxt only) &lt;strong&gt;App Secret&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h1&gt;
  
  
  Part 1 — Nuxt 3 (SSR): add authentication with &lt;code&gt;@logto/nuxt&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Nuxt is often deployed with SSR (or hybrid rendering). Logto’s Nuxt SDK is designed for SSR and uses secure cookies + server-side handling to complete authentication.&lt;/p&gt;
&lt;h2&gt;
  
  
  1) Install the Nuxt SDK
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @logto/nuxt
&lt;span class="c"&gt;# or&lt;/span&gt;
pnpm add @logto/nuxt
&lt;span class="c"&gt;# or&lt;/span&gt;
yarn add @logto/nuxt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  2) Add environment variables
&lt;/h2&gt;

&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;NUXT_LOGTO_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://your-tenant.logto.app"&lt;/span&gt;
&lt;span class="nv"&gt;NUXT_LOGTO_APP_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-app-id"&lt;/span&gt;
&lt;span class="nv"&gt;NUXT_LOGTO_APP_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-app-secret"&lt;/span&gt;
&lt;span class="nv"&gt;NUXT_LOGTO_COOKIE_ENCRYPTION_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"a-strong-random-string"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; &lt;code&gt;NUXT_LOGTO_COOKIE_ENCRYPTION_KEY&lt;/code&gt; must be a strong random string because it protects encrypted auth cookies.&lt;/p&gt;

&lt;h2&gt;
  
  
  3) Configure &lt;code&gt;nuxt.config.ts&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// nuxt.config.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineNuxtConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@logto/nuxt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

  &lt;span class="na"&gt;runtimeConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;logto&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NUXT_LOGTO_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NUXT_LOGTO_APP_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;appSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NUXT_LOGTO_APP_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;cookieEncryptionKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NUXT_LOGTO_COOKIE_ENCRYPTION_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4) Configure redirect URIs in Logto Console
&lt;/h2&gt;

&lt;p&gt;Assuming your Nuxt app runs on &lt;code&gt;http://localhost:3000&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redirect URI&lt;/strong&gt;: &lt;code&gt;http://localhost:3000/callback&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post sign-out redirect URI&lt;/strong&gt;: &lt;code&gt;http://localhost:3000/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These must match the callback and return URLs used by the SDK.&lt;/p&gt;

&lt;h2&gt;
  
  
  5) Understand the built-in routes
&lt;/h2&gt;

&lt;p&gt;The Nuxt module provides built-in routes for the auth flow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/sign-in&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/sign-out&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/callback&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can customize them if you want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// nuxt.config.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineNuxtConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;logto&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;pathnames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;signIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;signOut&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/logout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/auth/callback&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you change the callback path, update the redirect URI in Logto Console accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  6) Implement a simple Sign in / Sign out button
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- pages/index.vue --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useLogtoUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;NuxtLink&lt;/span&gt; &lt;span class="na"&gt;:to=&lt;/span&gt;&lt;span class="s"&gt;"`/sign-$&lt;/span&gt;{user ? 'out' : 'in'}`"&amp;gt;
    Sign &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;out&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;in&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/NuxtLink&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;pre&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"user"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"margin-top: 16px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;useLogtoUser()&lt;/code&gt; is reactive — it updates when the session changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  7) Request additional user claims (scopes)
&lt;/h2&gt;

&lt;p&gt;By default, you get basic OIDC claims. If you need more (e.g., email/phone), add scopes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// nuxt.config.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UserScope&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@logto/nuxt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineNuxtConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;logto&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;scopes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;UserScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UserScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Phone&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  8) Use the Logto client (server-only)
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;useLogtoClient()&lt;/code&gt; is server-only. On the client it returns &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Example: fetch user info on the server once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// composables/useServerUserInfo.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useLogtoClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callOnce&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#imports&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useServerUserInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useLogtoClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;logto-user-info&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;callOnce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isAuthenticated&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="nx"&gt;userInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchUserInfo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;userInfo&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Part 2 — Vue 3 (SPA): add authentication with the Vue SDK
&lt;/h1&gt;

&lt;p&gt;Vue SPAs don’t have a server runtime to securely store secrets, so the recommended setup uses the Logto Vue SDK and SPA application settings in Logto Console.&lt;/p&gt;

&lt;h2&gt;
  
  
  1) Install the Vue SDK
&lt;/h2&gt;

&lt;p&gt;Install Logto’s Vue SDK (Vue 3 only):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @logto/vue
&lt;span class="c"&gt;# or&lt;/span&gt;
pnpm add @logto/vue
&lt;span class="c"&gt;# or&lt;/span&gt;
yarn add @logto/vue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2) Configure redirect URIs in Logto Console
&lt;/h2&gt;

&lt;p&gt;Assuming your Vue app runs on &lt;code&gt;http://localhost:5173&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redirect URI&lt;/strong&gt;: &lt;code&gt;http://localhost:5173/callback&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post sign-out redirect URI&lt;/strong&gt;: &lt;code&gt;http://localhost:5173/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Use your actual dev server origin if it’s different.)&lt;/p&gt;

&lt;h2&gt;
  
  
  3) Initialize Logto in &lt;code&gt;main.ts&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/main.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createLogto&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@logto/vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;createLogto&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VITE_LOGTO_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VITE_LOGTO_APP_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add &lt;code&gt;.env&lt;/code&gt; variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .env&lt;/span&gt;
&lt;span class="nv"&gt;VITE_LOGTO_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://your-tenant.logto.app"&lt;/span&gt;
&lt;span class="nv"&gt;VITE_LOGTO_APP_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-app-id"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4) Add a callback route
&lt;/h2&gt;

&lt;p&gt;In SPAs, you must handle the redirect callback route.&lt;/p&gt;

&lt;p&gt;Example with Vue Router:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/router/index.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createWebHistory&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue-router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../pages/Home.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Callback&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../pages/Callback.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;createRouter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;history&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;createWebHistory&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/callback&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Callback&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Callback page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- src/pages/Callback.vue --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;onMounted&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useLogto&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@logto/vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRouter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue-router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handleSignInCallback&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useLogto&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;onMounted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;handleSignInCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Signing you in…&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5) Sign in / Sign out buttons
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- src/pages/Home.vue --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useLogto&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@logto/vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isAuthenticated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signIn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signOut&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getIdTokenClaims&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useLogto&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleSignIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;signIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VITE_LOGTO_REDIRECT_URI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleSignOut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;signOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VITE_LOGTO_POST_SIGN_OUT_REDIRECT_URI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"!isAuthenticated"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"handleSignIn"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Sign in
  &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;v-else&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"handleSignOut"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Sign out
  &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add redirect-related env vars:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;VITE_LOGTO_REDIRECT_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:5173/callback"&lt;/span&gt;
&lt;span class="nv"&gt;VITE_LOGTO_POST_SIGN_OUT_REDIRECT_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:5173/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to display the current user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useLogto&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@logto/vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isAuthenticated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getIdTokenClaims&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useLogto&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;claims&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loadClaims&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getIdTokenClaims&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"isAuthenticated"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"loadClaims"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Load user claims&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;pre&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"claims"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;claims&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Choosing between Nuxt and Vue integrations
&lt;/h2&gt;

&lt;p&gt;If you are building a Nuxt app with SSR (or hybrid rendering), prefer &lt;strong&gt;&lt;code&gt;@logto/nuxt&lt;/code&gt;&lt;/strong&gt; because it’s designed for server-side flows and secure cookie handling.&lt;/p&gt;

&lt;p&gt;If you are building a Vue SPA (no server runtime), use &lt;strong&gt;&lt;code&gt;@logto/vue&lt;/code&gt;&lt;/strong&gt; and the SPA application type in Logto Console.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick sanity check
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Clicking &lt;strong&gt;Sign in&lt;/strong&gt; should redirect you to Logto’s hosted sign-in screen.&lt;/li&gt;
&lt;li&gt;After signing in, you should land on your callback route and end up back in the app.&lt;/li&gt;
&lt;li&gt;Clicking &lt;strong&gt;Sign out&lt;/strong&gt; should clear the shared session and return you to your post sign-out URL.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final notes
&lt;/h2&gt;

&lt;p&gt;This document intentionally keeps the examples minimal and production-oriented. Once authentication works, the next steps are typically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Protecting routes (Nuxt middleware / Vue router guards)&lt;/li&gt;
&lt;li&gt;Calling your APIs with access tokens&lt;/li&gt;
&lt;li&gt;Adding organization/roles/permissions if your product needs it&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Algolia Search in Nuxt 3: Production-Ready Integration Guide</title>
      <dc:creator>Alex Kernel</dc:creator>
      <pubDate>Mon, 22 Dec 2025 10:33:23 +0000</pubDate>
      <link>https://forem.com/bitwiserokos/algolia-search-in-nuxt-3-production-ready-integration-guide-18fg</link>
      <guid>https://forem.com/bitwiserokos/algolia-search-in-nuxt-3-production-ready-integration-guide-18fg</guid>
      <description>&lt;p&gt;Algolia is a hosted search engine designed for speed, relevance, and scalability. When combined with Nuxt 3, it allows you to build fast, SEO-friendly search experiences with minimal effort and full SSR support.&lt;/p&gt;

&lt;p&gt;This guide walks through a &lt;strong&gt;practical, production-ready integration of Algolia in a Nuxt 3 application&lt;/strong&gt; using the official &lt;code&gt;@nuxtjs/algolia&lt;/code&gt; module. The focus is on real usage patterns, not abstractions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Use Algolia with Nuxt 3
&lt;/h2&gt;

&lt;p&gt;Nuxt 3 applications often require advanced search capabilities for blogs, documentation, dashboards, and e-commerce platforms. Algolia fits naturally into this ecosystem because it provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extremely fast full-text search&lt;/li&gt;
&lt;li&gt;Built-in typo tolerance and ranking&lt;/li&gt;
&lt;li&gt;Seamless SSR compatibility&lt;/li&gt;
&lt;li&gt;Auto-imported composables in Nuxt&lt;/li&gt;
&lt;li&gt;Type-safe APIs with first-class TypeScript support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The official Nuxt Algolia module removes most of the boilerplate and exposes clean, composable APIs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;Before starting, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Nuxt 3 project&lt;/li&gt;
&lt;li&gt;An Algolia account&lt;/li&gt;
&lt;li&gt;At least one Algolia index with data&lt;/li&gt;
&lt;li&gt;Algolia Application ID and Search API Key&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Installing the Algolia Module
&lt;/h2&gt;

&lt;p&gt;Install the official Nuxt module using the Nuxt CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nuxi@latest module add algolia
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or with pnpm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add @nuxtjs/algolia
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The module will be registered automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Environment Variables
&lt;/h2&gt;

&lt;p&gt;Store Algolia credentials in environment variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ALGOLIA_APPLICATION_ID=YOUR_APP_ID
ALGOLIA_API_KEY=YOUR_SEARCH_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Never commit API keys to version control.&lt;/p&gt;




&lt;h2&gt;
  
  
  Nuxt Configuration
&lt;/h2&gt;

&lt;p&gt;Enable the module in &lt;code&gt;nuxt.config.ts&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineNuxtConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nuxtjs/algolia&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

  &lt;span class="na"&gt;algolia&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// optional configuration&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The module automatically reads credentials from environment variables.&lt;/p&gt;




&lt;h2&gt;
  
  
  Basic Search Implementation
&lt;/h2&gt;

&lt;p&gt;The most common way to query Algolia is via &lt;code&gt;useAlgoliaSearch&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;search&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAlgoliaSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Laptop&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"item in result.hits"&lt;/span&gt; &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"item.objectID"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This performs a client-side search against the specified index.&lt;/p&gt;




&lt;h2&gt;
  
  
  Server-Side Search with SSR
&lt;/h2&gt;

&lt;p&gt;For SEO-critical pages, use &lt;code&gt;useAsyncAlgoliaSearch&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pending&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;useAsyncAlgoliaSearch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;indexName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Phone&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"pending"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Loading…&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;pre&lt;/span&gt; &lt;span class="na"&gt;v-else&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This executes during server rendering and works seamlessly with Nuxt SSR.&lt;/p&gt;




&lt;h2&gt;
  
  
  Advanced Search Features
&lt;/h2&gt;

&lt;p&gt;The module exposes additional composables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;useAlgoliaFacetedSearch&lt;/code&gt; for filters and facets&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useAlgoliaInitIndex&lt;/code&gt; for index initialization&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useAlgoliaRecommend&lt;/code&gt; for recommendations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useAlgoliaRef&lt;/code&gt; for direct client access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These cover most advanced search scenarios without manual client setup.&lt;/p&gt;




&lt;h2&gt;
  
  
  Edge and Cloudflare Support
&lt;/h2&gt;

&lt;p&gt;When deploying to edge runtimes like Cloudflare Workers, enable fetch-based transport.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;algolia&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;useFetch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures compatibility with restricted runtime environments.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Problems
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;No search results&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check index name&lt;/li&gt;
&lt;li&gt;Verify index contains data&lt;/li&gt;
&lt;li&gt;Confirm API key permissions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SSR errors&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;useAsyncAlgoliaSearch&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Enable &lt;code&gt;useFetch&lt;/code&gt; for edge runtimes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;TypeScript issues&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update to the latest module version&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Production Best Practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use SSR search only where SEO matters&lt;/li&gt;
&lt;li&gt;Cache popular queries&lt;/li&gt;
&lt;li&gt;Never expose Admin API keys&lt;/li&gt;
&lt;li&gt;Index only searchable fields&lt;/li&gt;
&lt;li&gt;Separate read and write operations&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Notes
&lt;/h2&gt;

&lt;p&gt;The official Algolia module for Nuxt 3 provides a clean and scalable way to add search to modern applications. With minimal configuration, you get fast search, SSR compatibility, and a composable API that fits naturally into the Nuxt ecosystem.&lt;/p&gt;

&lt;p&gt;This setup is suitable for both small projects and large production systems and can be extended with InstantSearch or recommendation features when needed.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>ai</category>
    </item>
    <item>
      <title>Digler — Open-Source Disk Forensics and File Recovery Tool</title>
      <dc:creator>Alex Kernel</dc:creator>
      <pubDate>Sun, 21 Dec 2025 17:13:30 +0000</pubDate>
      <link>https://forem.com/bitwiserokos/digler-open-source-disk-forensics-and-file-recovery-tool-3nb</link>
      <guid>https://forem.com/bitwiserokos/digler-open-source-disk-forensics-and-file-recovery-tool-3nb</guid>
      <description>&lt;h1&gt;
  
  
  File Browser — Open-Source Web File Manager You Can Self-Host
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Digler — Open-Source Disk Forensics and File Recovery Tool
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Digler&lt;/strong&gt; is an open-source &lt;strong&gt;disk forensics and file recovery utility&lt;/strong&gt; designed for analyzing raw disks and disk images. It is written in &lt;strong&gt;Go&lt;/strong&gt;, distributed as a CLI tool, and focuses on &lt;strong&gt;recovering deleted files and extracting forensic metadata&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq48xda5rjxcijeclhxl4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq48xda5rjxcijeclhxl4.png" alt=" " width="800" height="609"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub repository:&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/ostafen/digler" rel="noopener noreferrer"&gt;https://github.com/ostafen/digler&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  What Problem Does Digler Solve?
&lt;/h2&gt;

&lt;p&gt;When files are deleted, the file system metadata may be lost, but the raw data often remains on disk. Digler helps by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scanning raw disks or disk images&lt;/li&gt;
&lt;li&gt;carving files without filesystem metadata&lt;/li&gt;
&lt;li&gt;extracting forensic evidence&lt;/li&gt;
&lt;li&gt;producing machine-readable forensic reports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes it useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;digital forensics&lt;/li&gt;
&lt;li&gt;incident response&lt;/li&gt;
&lt;li&gt;data recovery&lt;/li&gt;
&lt;li&gt;security research&lt;/li&gt;
&lt;li&gt;educational labs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SEO keywords covered:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;disk forensics tool, file recovery CLI, digital forensics Go, raw disk analysis, DFXML&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🔍 Scan raw disks and disk images&lt;/li&gt;
&lt;li&gt;🧩 File carving without filesystem metadata&lt;/li&gt;
&lt;li&gt;📄 DFXML (Digital Forensics XML) report generation&lt;/li&gt;
&lt;li&gt;🧱 Modular plugin-based architecture&lt;/li&gt;
&lt;li&gt;⚡ Cross-platform (Linux, macOS, Windows)&lt;/li&gt;
&lt;li&gt;🔓 Open-source&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  High-Level Architecture
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Disk / Image
   ↓
Block Scanner
   ↓
Signature Detection
   ↓
File Carving Engine
   ↓
Recovered Files + DFXML Report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The plugin system allows Digler to recognize multiple file formats.&lt;/p&gt;


&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language:&lt;/strong&gt; Go&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interface:&lt;/strong&gt; CLI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; Files + DFXML XML&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture:&lt;/strong&gt; Modular / plugin-based&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbk5eurobf9uq6xlie0se.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbk5eurobf9uq6xlie0se.gif" alt=" " width="760" height="760"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4hwgf5u09mqo3l8ou8o7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4hwgf5u09mqo3l8ou8o7.gif" alt=" " width="760" height="760"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Build from Source&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/ostafen/digler.git
&lt;span class="nb"&gt;cd &lt;/span&gt;digler
make build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiled binary will be available in the &lt;code&gt;bin/&lt;/code&gt; directory.&lt;/p&gt;




&lt;h2&gt;
  
  
  Basic Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scan a Disk Image
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;digler scan disk.img
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Recover Files
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;digler recover disk.img &lt;span class="nt"&gt;--output&lt;/span&gt; recovered_files/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Generate Forensic Report
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;digler scan disk.img &lt;span class="nt"&gt;--dfxml&lt;/span&gt; report.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The DFXML file can be used in forensic pipelines and automated analysis.&lt;/p&gt;




&lt;h2&gt;
  
  
  Plugin System
&lt;/h2&gt;

&lt;p&gt;Digler uses plugins to identify and recover file types.&lt;/p&gt;

&lt;p&gt;Each plugin defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file signature&lt;/li&gt;
&lt;li&gt;block structure&lt;/li&gt;
&lt;li&gt;extraction logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows easy extension for new formats.&lt;/p&gt;




&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Digital forensics investigations&lt;/li&gt;
&lt;li&gt;Malware and incident response&lt;/li&gt;
&lt;li&gt;Recovering accidentally deleted files&lt;/li&gt;
&lt;li&gt;Security training and labs&lt;/li&gt;
&lt;li&gt;Research on file systems&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Safety Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Always work on disk images, not live disks&lt;/li&gt;
&lt;li&gt;Use read-only access when possible&lt;/li&gt;
&lt;li&gt;Ensure you have legal authorization to analyze data&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Digler Is Worth Using
&lt;/h2&gt;

&lt;p&gt;Digler is valuable because it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;focuses on raw forensic workflows&lt;/li&gt;
&lt;li&gt;avoids unnecessary UI complexity&lt;/li&gt;
&lt;li&gt;produces structured forensic output&lt;/li&gt;
&lt;li&gt;is easy to extend and audit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s a solid open-source alternative for forensic tooling.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;If you work with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;disk images&lt;/li&gt;
&lt;li&gt;forensic analysis&lt;/li&gt;
&lt;li&gt;data recovery&lt;/li&gt;
&lt;li&gt;security tooling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Digler is worth exploring and learning from.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>web</category>
      <category>opensource</category>
      <category>cli</category>
    </item>
    <item>
      <title>File Browser — Open-Source Web File Manager You Can Self-Host</title>
      <dc:creator>Alex Kernel</dc:creator>
      <pubDate>Sun, 21 Dec 2025 17:09:14 +0000</pubDate>
      <link>https://forem.com/bitwiserokos/file-browser-open-source-web-file-manager-you-can-self-host-5chp</link>
      <guid>https://forem.com/bitwiserokos/file-browser-open-source-web-file-manager-you-can-self-host-5chp</guid>
      <description>&lt;h1&gt;
  
  
  File Browser — Open-Source Web File Manager You Can Self-Host
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;File Browser&lt;/strong&gt; is a fast, lightweight, open-source &lt;strong&gt;web-based file manager&lt;/strong&gt; that lets you manage files on a server through a clean browser UI.&lt;/p&gt;

&lt;p&gt;It’s written in &lt;strong&gt;Go&lt;/strong&gt;, distributed as a single binary, and designed to be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;easy to deploy&lt;/li&gt;
&lt;li&gt;easy to secure&lt;/li&gt;
&lt;li&gt;easy to self-host&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6p5awu5asn9ebuk8lkt9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6p5awu5asn9ebuk8lkt9.png" alt=" " width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub repository:&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/gtsteffaniak/filebrowser" rel="noopener noreferrer"&gt;https://github.com/gtsteffaniak/filebrowser&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Why File Browser Is So Popular
&lt;/h2&gt;

&lt;p&gt;File Browser solves a very practical problem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I want to manage files on my server without SSH, FTP, or heavy admin panels.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead of complex setups, File Browser gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a web UI&lt;/li&gt;
&lt;li&gt;authentication&lt;/li&gt;
&lt;li&gt;file operations&lt;/li&gt;
&lt;li&gt;user permissions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All in one small binary.&lt;/p&gt;

&lt;p&gt;SEO keywords naturally included:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;open-source file manager, web file browser, self-hosted file manager, Go web application, server file management&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📁 Browse files and directories from your browser&lt;/li&gt;
&lt;li&gt;⬆️ Upload / download files&lt;/li&gt;
&lt;li&gt;✏️ Rename, move, copy, delete&lt;/li&gt;
&lt;li&gt;👥 User and permission management&lt;/li&gt;
&lt;li&gt;🔐 Authentication support&lt;/li&gt;
&lt;li&gt;⚙️ Configurable root directory&lt;/li&gt;
&lt;li&gt;🖥️ Runs as a single Go binary&lt;/li&gt;
&lt;li&gt;🐧 Cross-platform (Linux, macOS, Windows)&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  High-Level Architecture
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser UI
   ↓
HTTP API
   ↓
File system access
   ↓
Config / auth layer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;There is no heavy database dependency by default, which keeps the system simple and fast.&lt;/p&gt;


&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language:&lt;/strong&gt; Go&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; Embedded web UI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Go HTTP server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage:&lt;/strong&gt; Local filesystem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment:&lt;/strong&gt; Single binary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes File Browser ideal for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VPS servers&lt;/li&gt;
&lt;li&gt;NAS devices&lt;/li&gt;
&lt;li&gt;homelabs&lt;/li&gt;
&lt;li&gt;internal tools&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Installation Guide
&lt;/h2&gt;

&lt;p&gt;File Browser is extremely easy to install.&lt;/p&gt;


&lt;h3&gt;
  
  
  Option 1: Download Prebuilt Binary (Recommended)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to the releases page
&lt;/li&gt;
&lt;li&gt;Download the binary for your OS
&lt;/li&gt;
&lt;li&gt;Make it executable&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example (Linux / macOS):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x filebrowser
./filebrowser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Option 2: Install via Package Managers
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Homebrew (macOS / Linux)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;filebrowser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h4&gt;
  
  
  Docker
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run   &lt;span class="nt"&gt;-v&lt;/span&gt; /path/to/your/files:/srv   &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80   filebrowser/filebrowser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Option 3: Build from Source
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/gtsteffaniak/filebrowser.git
&lt;span class="nb"&gt;cd &lt;/span&gt;filebrowser
go build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  First Run &amp;amp; Login
&lt;/h2&gt;

&lt;p&gt;On first launch, File Browser will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create a configuration file&lt;/li&gt;
&lt;li&gt;start a web server (default port: 80 or 8080)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open in browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Default credentials:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; admin&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; admin&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⚠️ Change credentials immediately after login.&lt;/p&gt;




&lt;h2&gt;
  
  
  Basic Configuration
&lt;/h2&gt;

&lt;p&gt;Common flags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;filebrowser   &lt;span class="nt"&gt;--root&lt;/span&gt; /path/to/files   &lt;span class="nt"&gt;--address&lt;/span&gt; 0.0.0.0   &lt;span class="nt"&gt;--port&lt;/span&gt; 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets you control:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which directory is exposed&lt;/li&gt;
&lt;li&gt;network interface&lt;/li&gt;
&lt;li&gt;port&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;p&gt;File Browser is widely used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;managing files on VPS servers&lt;/li&gt;
&lt;li&gt;NAS and home servers&lt;/li&gt;
&lt;li&gt;DevOps workflows&lt;/li&gt;
&lt;li&gt;temporary file sharing&lt;/li&gt;
&lt;li&gt;internal admin panels&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s especially useful when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSH access is limited&lt;/li&gt;
&lt;li&gt;non-technical users need access&lt;/li&gt;
&lt;li&gt;you want a lightweight solution&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Security Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Always change default credentials&lt;/li&gt;
&lt;li&gt;Use HTTPS (reverse proxy recommended)&lt;/li&gt;
&lt;li&gt;Limit root directory access&lt;/li&gt;
&lt;li&gt;Use firewall rules where possible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;File Browser is simple, but security is still your responsibility.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Project Is Worth Using
&lt;/h2&gt;

&lt;p&gt;File Browser stands out because it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;does one thing very well&lt;/li&gt;
&lt;li&gt;avoids unnecessary complexity&lt;/li&gt;
&lt;li&gt;is easy to audit&lt;/li&gt;
&lt;li&gt;is easy to deploy anywhere&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you like &lt;strong&gt;small, focused open-source tools&lt;/strong&gt;, this is a must-have.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;File Browser is a great example of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pragmatic Go development&lt;/li&gt;
&lt;li&gt;clean self-hosted tooling&lt;/li&gt;
&lt;li&gt;real-world open-source utility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⭐ Star the repository if you use it or learn from it.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>web</category>
      <category>opensource</category>
      <category>security</category>
    </item>
    <item>
      <title>PearPass Desktop — Open-Source Peer-to-Peer Password Manager Built on Pear Runtime</title>
      <dc:creator>Alex Kernel</dc:creator>
      <pubDate>Sun, 21 Dec 2025 17:04:16 +0000</pubDate>
      <link>https://forem.com/bitwiserokos/pearpass-desktop-open-source-peer-to-peer-password-manager-built-on-pear-runtime-1kop</link>
      <guid>https://forem.com/bitwiserokos/pearpass-desktop-open-source-peer-to-peer-password-manager-built-on-pear-runtime-1kop</guid>
      <description>&lt;h1&gt;
  
  
  PearPass Desktop — Open-Source Peer-to-Peer Password Manager Built on Pear Runtime
&lt;/h1&gt;

&lt;p&gt;If you’re tired of “password managers” that still depend on centralized cloud servers, &lt;strong&gt;PearPass Desktop&lt;/strong&gt; is a refreshing direction: a &lt;strong&gt;distributed password manager&lt;/strong&gt; built on &lt;strong&gt;Pear Runtime&lt;/strong&gt;, designed to keep your vault data &lt;strong&gt;on your devices&lt;/strong&gt; and sync it across your own endpoints.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg960sj0wky9r0d9vaigr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg960sj0wky9r0d9vaigr.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repo:&lt;br&gt;
&lt;a href="https://github.com/tetherto/pearpass-app-desktop" rel="noopener noreferrer"&gt;https://github.com/tetherto/pearpass-app-desktop&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Why This Project Is Cool (and Why Devs Should Care)
&lt;/h2&gt;

&lt;p&gt;PearPass Desktop is interesting for more than just end-user security:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Peer-to-peer / distributed sync mindset&lt;/strong&gt; (no classic “one cloud to breach” architecture)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open-source by default&lt;/strong&gt; (easier to audit and extend)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern desktop stack&lt;/strong&gt; (Pear Runtime + React ecosystem)&lt;/li&gt;
&lt;li&gt;Great real-world reference for:

&lt;ul&gt;
&lt;li&gt;crypto + security UX&lt;/li&gt;
&lt;li&gt;local-first apps&lt;/li&gt;
&lt;li&gt;end-to-end encryption product design&lt;/li&gt;
&lt;li&gt;multi-device sync without centralized infra&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SEO keywords naturally covered:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;open-source password manager, peer-to-peer password vault, local-first security app, end-to-end encrypted vault, Pear Runtime desktop app&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Features (What You Get)
&lt;/h2&gt;

&lt;p&gt;PearPass Desktop focuses on secure storage and usability:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secure storage for &lt;strong&gt;passwords, identities, credit cards, notes, and custom fields&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-device and cross-platform synchronization&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline access&lt;/strong&gt; (local-first usage)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encryption&lt;/strong&gt; for vault security&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Password strength analysis&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Random password generator&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Simple, clean UI&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  High-Level Architecture
&lt;/h2&gt;

&lt;p&gt;PearPass Desktop follows a local-first model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UI (React)
  ↓
Vault / state management
  ↓
Local encrypted storage
  ↓
Peer-to-peer distribution (Pear Runtime)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means your “source of truth” is your devices, not a centralized web account.&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting Started (Installation &amp;amp; Dev Setup)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;This repo is primarily a &lt;strong&gt;developer setup&lt;/strong&gt; flow (clone → install deps → run via Pear).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  0) Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js&lt;/strong&gt; (match the version in &lt;code&gt;.nvmrc&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;npm&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pear Runtime&lt;/strong&gt; installed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check Node version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  1) Clone the Repo
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/tetherto/pearpass-app-desktop.git
&lt;span class="nb"&gt;cd &lt;/span&gt;pearpass-app-desktop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  2) Update Submodules
&lt;/h3&gt;

&lt;p&gt;PearPass uses submodules. Update them with the provided script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run update-submodules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need a specific remote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run update-submodules &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;remote-name]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3) Install Dependencies
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4) Generate i18n (Translations)
&lt;/h3&gt;

&lt;p&gt;PearPass uses Lingui. Generate and compile message catalogs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run lingui:extract
npm run lingui:compile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5) Run the Desktop App (Dev Mode)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pear run &lt;span class="nt"&gt;--dev&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything is set correctly, the app should launch.&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;PearPass uses Jest for unit testing.&lt;/p&gt;

&lt;p&gt;Run tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Usage: What to Try First
&lt;/h2&gt;

&lt;p&gt;Once the app runs, a good “first session” checklist:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a vault and set a strong master password&lt;/li&gt;
&lt;li&gt;Add sample entries:

&lt;ul&gt;
&lt;li&gt;login&lt;/li&gt;
&lt;li&gt;note&lt;/li&gt;
&lt;li&gt;identity&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Try password generator + strength checks&lt;/li&gt;
&lt;li&gt;Explore sync / distribution options (if you have multiple devices)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Pear Runtime&lt;/li&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;Styled Components&lt;/li&gt;
&lt;li&gt;Redux&lt;/li&gt;
&lt;li&gt;Lingui (i18n)&lt;/li&gt;
&lt;li&gt;Jest (tests)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a great repo to learn how a security-focused desktop app structures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;state&lt;/li&gt;
&lt;li&gt;encryption boundaries&lt;/li&gt;
&lt;li&gt;UX flows for sensitive data&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Who Should Fork This?
&lt;/h2&gt;

&lt;p&gt;This repo is perfect if you want to build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a local-first password manager fork&lt;/li&gt;
&lt;li&gt;a secure “vault” module for another app&lt;/li&gt;
&lt;li&gt;P2P sync experiments&lt;/li&gt;
&lt;li&gt;privacy-first productivity tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add hardware key / OS keychain integrations&lt;/li&gt;
&lt;li&gt;add vault export formats&lt;/li&gt;
&lt;li&gt;add threat-model docs + security tooling&lt;/li&gt;
&lt;li&gt;build a plugin system for record types&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Related Projects in the Ecosystem
&lt;/h2&gt;

&lt;p&gt;PearPass also has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;browser extension&lt;/li&gt;
&lt;li&gt;mobile app&lt;/li&gt;
&lt;li&gt;vault core libraries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want full-stack parity (desktop + browser autofill), look at the extension repo too.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Notes
&lt;/h2&gt;

&lt;p&gt;PearPass Desktop is one of those repos that’s both:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;immediately useful&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;and a &lt;strong&gt;very teachable architecture&lt;/strong&gt; for local-first security apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re exploring modern, open-source security software — this is absolutely worth starring and reading.&lt;/p&gt;

</description>
      <category>security</category>
      <category>privacy</category>
      <category>javascript</category>
      <category>passwordmanager</category>
    </item>
    <item>
      <title>BookHunter — Open-Source CLI Tool for Downloading and Managing eBooks</title>
      <dc:creator>Alex Kernel</dc:creator>
      <pubDate>Sun, 21 Dec 2025 17:01:29 +0000</pubDate>
      <link>https://forem.com/bitwiserokos/bookhunter-open-source-cli-tool-for-downloading-and-managing-ebooks-502h</link>
      <guid>https://forem.com/bitwiserokos/bookhunter-open-source-cli-tool-for-downloading-and-managing-ebooks-502h</guid>
      <description>&lt;h1&gt;
  
  
  BookHunter — Open-Source CLI Tool for Downloading and Managing eBooks
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;BookHunter&lt;/strong&gt; is an open-source command-line tool written in &lt;strong&gt;Go&lt;/strong&gt; that allows users to download and manage eBooks from multiple online sources using a single unified CLI interface.&lt;/p&gt;

&lt;p&gt;The project focuses on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;automation&lt;/li&gt;
&lt;li&gt;multi-source support&lt;/li&gt;
&lt;li&gt;cross-platform binaries&lt;/li&gt;
&lt;li&gt;fast and simple CLI usage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub repository:&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/bookstairs/bookhunter/" rel="noopener noreferrer"&gt;https://github.com/bookstairs/bookhunter/&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  What Problem Does BookHunter Solve?
&lt;/h2&gt;

&lt;p&gt;Downloading large collections of eBooks manually is time-consuming and repetitive.&lt;br&gt;&lt;br&gt;
BookHunter automates this process by providing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a single CLI tool instead of multiple scripts&lt;/li&gt;
&lt;li&gt;built-in parsers for different sources&lt;/li&gt;
&lt;li&gt;multi-threaded downloads&lt;/li&gt;
&lt;li&gt;consistent output formats&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes it useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;personal libraries&lt;/li&gt;
&lt;li&gt;research collections&lt;/li&gt;
&lt;li&gt;automation workflows&lt;/li&gt;
&lt;li&gt;CLI tool practice and learning&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📚 Download eBooks from multiple sources&lt;/li&gt;
&lt;li&gt;⚡ Fast, multi-threaded downloads&lt;/li&gt;
&lt;li&gt;🖥️ Cross-platform (Windows, macOS, Linux)&lt;/li&gt;
&lt;li&gt;🧰 Simple and consistent CLI interface&lt;/li&gt;
&lt;li&gt;📦 Supports common formats (PDF, EPUB, AZW3)&lt;/li&gt;
&lt;li&gt;🔓 Open-source (MIT License)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SEO keywords covered naturally:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ebook downloader CLI, Go command line tool, open-source ebook downloader, automation with Go&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Supported Sources
&lt;/h2&gt;

&lt;p&gt;Depending on configuration and version, BookHunter supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SoBooks&lt;/li&gt;
&lt;li&gt;TaleBook&lt;/li&gt;
&lt;li&gt;Telegram channels&lt;/li&gt;
&lt;li&gt;Other community-maintained sources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each source is implemented as a separate command module.&lt;/p&gt;


&lt;h2&gt;
  
  
  High-Level Architecture
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bookhunter (CLI)
├── source parsers
├── download manager
├── concurrency control
├── format handling
└── output directory management
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The modular design makes it easy to extend with new sources.&lt;/p&gt;


&lt;h2&gt;
  
  
  Installation Guide
&lt;/h2&gt;

&lt;p&gt;BookHunter provides prebuilt binaries for all major platforms.&lt;/p&gt;


&lt;h3&gt;
  
  
  macOS / Linux (Homebrew)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew tap bookstairs/tap
brew &lt;span class="nb"&gt;install &lt;/span&gt;bookhunter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Windows (Scoop)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;scoop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bookstairs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https://github.com/bookstairs/scoop-bucket&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;scoop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bookstairs/bookhunter&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Manual Installation (All Platforms)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open the GitHub releases page
&lt;/li&gt;
&lt;li&gt;Download the archive for your OS
&lt;/li&gt;
&lt;li&gt;Extract the binary
&lt;/li&gt;
&lt;li&gt;Move it to a directory in your PATH
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example (Linux / macOS):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x bookhunter
&lt;span class="nb"&gt;mv &lt;/span&gt;bookhunter /usr/local/bin/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Verify Installation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bookhunter &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If installed correctly, you should see the available commands and options.&lt;/p&gt;




&lt;h2&gt;
  
  
  Basic Usage Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Download from SoBooks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bookhunter sobooks   &lt;span class="nt"&gt;--download&lt;/span&gt; ./ebooks   &lt;span class="nt"&gt;--thread&lt;/span&gt; 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Download from Telegram Channel
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bookhunter telegram   &lt;span class="nt"&gt;--appID&lt;/span&gt; YOUR_APP_ID   &lt;span class="nt"&gt;--appHash&lt;/span&gt; YOUR_APP_HASH   &lt;span class="nt"&gt;--channelID&lt;/span&gt; https://t.me/example_channel   &lt;span class="nt"&gt;--download&lt;/span&gt; ./tgbooks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will need Telegram API credentials for this command.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Options
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--download&lt;/code&gt; — output directory&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--thread&lt;/code&gt; — number of concurrent downloads&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--proxy&lt;/code&gt; — use HTTP/SOCKS proxy&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--verbose&lt;/code&gt; — detailed logs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Building a personal digital library&lt;/li&gt;
&lt;li&gt;Automating downloads in scripts&lt;/li&gt;
&lt;li&gt;Learning Go by reading real-world CLI code&lt;/li&gt;
&lt;li&gt;Teaching CLI tools and concurrency concepts&lt;/li&gt;
&lt;li&gt;Research and offline reading workflows&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Limitations and Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Availability of sources may change&lt;/li&gt;
&lt;li&gt;Download speed depends on source limits&lt;/li&gt;
&lt;li&gt;Users are responsible for complying with local laws and content licenses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;BookHunter is a &lt;strong&gt;tool&lt;/strong&gt;, not a content provider.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Project Is Interesting
&lt;/h2&gt;

&lt;p&gt;BookHunter is a good example of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clean Go CLI design&lt;/li&gt;
&lt;li&gt;modular architecture&lt;/li&gt;
&lt;li&gt;real-world automation use cases&lt;/li&gt;
&lt;li&gt;cross-platform distribution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s useful both as a tool and as a learning resource.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Notes
&lt;/h2&gt;

&lt;p&gt;If you are interested in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CLI tools&lt;/li&gt;
&lt;li&gt;Go automation&lt;/li&gt;
&lt;li&gt;managing large collections of files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;BookHunter is worth checking out.&lt;/p&gt;

</description>
      <category>cli</category>
      <category>opensource</category>
      <category>books</category>
      <category>automation</category>
    </item>
    <item>
      <title>DiscovAI Search — Open‑Source AI Search Engine for Tools, Docs, and Custom Data</title>
      <dc:creator>Alex Kernel</dc:creator>
      <pubDate>Sun, 21 Dec 2025 16:53:36 +0000</pubDate>
      <link>https://forem.com/bitwiserokos/discovai-search-open-source-ai-search-engine-for-tools-docs-and-custom-data-4ko0</link>
      <guid>https://forem.com/bitwiserokos/discovai-search-open-source-ai-search-engine-for-tools-docs-and-custom-data-4ko0</guid>
      <description>&lt;h1&gt;
  
  
  DiscovAI Search — Open‑Source AI Search Engine for Tools and Knowledge Bases
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;DiscovAI Search&lt;/strong&gt; is an open‑source, AI‑powered search engine designed to index, understand, and search AI tools and custom knowledge bases using modern &lt;strong&gt;vector search + LLM reasoning&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The project combines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;semantic search (embeddings)&lt;/li&gt;
&lt;li&gt;fast caching (Redis)&lt;/li&gt;
&lt;li&gt;structured storage (Supabase / PostgreSQL)&lt;/li&gt;
&lt;li&gt;modern frontend (Next.js)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is suitable both as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;production-ready AI search layer&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;an &lt;strong&gt;educational reference project&lt;/strong&gt; for AI + web developers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F12cbv1idnha4dqgyw74c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F12cbv1idnha4dqgyw74c.png" alt=" " width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub repository:&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/DiscovAI/DiscovAI-search/" rel="noopener noreferrer"&gt;https://github.com/DiscovAI/DiscovAI-search/&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  What Problem Does DiscovAI Search Solve?
&lt;/h2&gt;

&lt;p&gt;Traditional keyword search fails when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;queries are vague or natural language&lt;/li&gt;
&lt;li&gt;data is unstructured&lt;/li&gt;
&lt;li&gt;users don’t know exact keywords&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DiscovAI Search solves this by using &lt;strong&gt;semantic vector search&lt;/strong&gt;, allowing users to search by &lt;em&gt;meaning&lt;/em&gt;, not exact words.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“tools for image generation”
&lt;/li&gt;
&lt;li&gt;“AI for document summarization”
&lt;/li&gt;
&lt;li&gt;“open-source alternatives to ChatGPT plugins”&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🔍 &lt;strong&gt;Semantic Search with Embeddings&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🧠 &lt;strong&gt;LLM‑powered answer generation&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;⚡ &lt;strong&gt;Redis caching for fast responses&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🗄️ &lt;strong&gt;Supabase (PostgreSQL + pgvector) backend&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🌐 &lt;strong&gt;Next.js frontend&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🔓 &lt;strong&gt;Fully open source&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SEO keywords naturally covered:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI search engine, vector search, semantic search, OpenAI embeddings, LLM search, Next.js AI app&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  High‑Level Architecture
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Query
   ↓
Next.js API Route
   ↓
Embedding (OpenAI)
   ↓
Vector Search (Supabase / pgvector)
   ↓
Redis Cache (optional)
   ↓
LLM‑generated response
   ↓
UI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This design keeps the system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scalable&lt;/li&gt;
&lt;li&gt;modular&lt;/li&gt;
&lt;li&gt;easy to extend with new data sources&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; Next.js (React)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Models:&lt;/strong&gt; OpenAI (embeddings + completion)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; Supabase (PostgreSQL + pgvector)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache:&lt;/strong&gt; Redis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language:&lt;/strong&gt; TypeScript&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Installation Guide (Local Setup)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 18+&lt;/li&gt;
&lt;li&gt;npm or yarn&lt;/li&gt;
&lt;li&gt;OpenAI API key&lt;/li&gt;
&lt;li&gt;Supabase account&lt;/li&gt;
&lt;li&gt;Redis instance (local or cloud)&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  1. Clone the Repository
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/DiscovAI/DiscovAI-search.git
&lt;span class="nb"&gt;cd &lt;/span&gt;DiscovAI-search
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Install Dependencies
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="c"&gt;# or&lt;/span&gt;
yarn &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Environment Variables
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;.env.local&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OPENAI_API_KEY=your_openai_key

NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key

SUPABASE_SERVICE_ROLE_KEY=your_service_role_key

REDIS_URL=redis://localhost:6379
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4. Supabase Setup
&lt;/h3&gt;

&lt;p&gt;In Supabase:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enable the &lt;strong&gt;pgvector&lt;/strong&gt; extension&lt;/li&gt;
&lt;li&gt;Create tables for:

&lt;ul&gt;
&lt;li&gt;documents&lt;/li&gt;
&lt;li&gt;embeddings&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Store vectors as &lt;code&gt;vector&lt;/code&gt; columns&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This enables fast semantic search.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Run the App
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now see the DiscovAI Search interface.&lt;/p&gt;




&lt;h2&gt;
  
  
  Indexing Data
&lt;/h2&gt;

&lt;p&gt;DiscovAI Search can index:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI tools&lt;/li&gt;
&lt;li&gt;documentation&lt;/li&gt;
&lt;li&gt;articles&lt;/li&gt;
&lt;li&gt;internal knowledge bases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Typical flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add documents to Supabase&lt;/li&gt;
&lt;li&gt;Generate embeddings&lt;/li&gt;
&lt;li&gt;Store vectors&lt;/li&gt;
&lt;li&gt;Query via UI&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Customization Ideas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add your own datasets&lt;/li&gt;
&lt;li&gt;Swap OpenAI for open‑source embedding models&lt;/li&gt;
&lt;li&gt;Connect multiple vector indexes&lt;/li&gt;
&lt;li&gt;Add authentication&lt;/li&gt;
&lt;li&gt;Deploy to Vercel&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;Recommended:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; Vercel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; Supabase&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache:&lt;/strong&gt; Upstash Redis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project is cloud‑native and deploys cleanly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Project Is Useful
&lt;/h2&gt;

&lt;p&gt;DiscovAI Search is valuable because it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;shows a real‑world AI search architecture&lt;/li&gt;
&lt;li&gt;combines LLMs with vector databases correctly&lt;/li&gt;
&lt;li&gt;is easy to fork and customize&lt;/li&gt;
&lt;li&gt;works as both product and reference implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s a solid starting point for anyone building:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI‑powered search&lt;/li&gt;
&lt;li&gt;internal knowledge assistants&lt;/li&gt;
&lt;li&gt;tool discovery platforms&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Notes
&lt;/h2&gt;

&lt;p&gt;This project demonstrates how modern AI search systems are built &lt;strong&gt;today&lt;/strong&gt;, not in theory.&lt;/p&gt;

&lt;p&gt;If you’re interested in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;semantic search&lt;/li&gt;
&lt;li&gt;vector databases&lt;/li&gt;
&lt;li&gt;LLM‑powered UX&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DiscovAI Search is worth exploring.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>openai</category>
    </item>
    <item>
      <title>Building a WoW Farming Bot with NitroGen</title>
      <dc:creator>Alex Kernel</dc:creator>
      <pubDate>Sun, 21 Dec 2025 16:45:01 +0000</pubDate>
      <link>https://forem.com/bitwiserokos/building-a-wow-farming-bot-with-nitrogen-dhn</link>
      <guid>https://forem.com/bitwiserokos/building-a-wow-farming-bot-with-nitrogen-dhn</guid>
      <description>&lt;p&gt;NitroGen is an open research project from the MineDojo / NVIDIA ecosystem.&lt;br&gt;&lt;br&gt;
It trains AI agents to play games &lt;strong&gt;purely from RGB screen pixels&lt;/strong&gt; by imitating real human controller actions.&lt;/p&gt;

&lt;p&gt;No game APIs.&lt;br&gt;&lt;br&gt;
No memory reading.&lt;br&gt;&lt;br&gt;
No engine hooks.  &lt;/p&gt;

&lt;p&gt;Just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;screen → neural network → controller input
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this guide, we extend NitroGen to build a &lt;strong&gt;vision-based farming bot for World of Warcraft&lt;/strong&gt; (Retail, Classic, TBC, or private servers).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: This article is for research and educational purposes only.  &lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Core Idea
&lt;/h2&gt;

&lt;p&gt;Instead of scripting rotations or reading game memory, the agent learns by &lt;strong&gt;watching gameplay videos&lt;/strong&gt; and copying human behavior.&lt;/p&gt;

&lt;p&gt;The model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sees health bars, enemies, minimap, cooldowns&lt;/li&gt;
&lt;li&gt;predicts the next controller action&lt;/li&gt;
&lt;li&gt;executes it like a human would&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes the bot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;engine-agnostic&lt;/li&gt;
&lt;li&gt;patch-resistant&lt;/li&gt;
&lt;li&gt;closer to human behavior than classic bots&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fully vision-based (no hooks, no addons)&lt;/li&gt;
&lt;li&gt;Works with pretrained NitroGen models&lt;/li&gt;
&lt;li&gt;Fine-tuning on your own WoW footage improves results&lt;/li&gt;
&lt;li&gt;Human-like imperfection (non-deterministic loops)&lt;/li&gt;
&lt;li&gt;Scales to multiple characters / accounts (research only)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What the Bot Can Do
&lt;/h2&gt;

&lt;p&gt;With sufficient training data, the agent can learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mob grinding&lt;/li&gt;
&lt;li&gt;Herbalism / mining routes&lt;/li&gt;
&lt;li&gt;Basic navigation paths&lt;/li&gt;
&lt;li&gt;Simple combat rotations&lt;/li&gt;
&lt;li&gt;Dungeon queue farming (LFD)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Performance depends entirely on dataset quality.&lt;/p&gt;




&lt;h2&gt;
  
  
  System Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Windows 11 (for WoW process capture)&lt;/li&gt;
&lt;li&gt;NVIDIA GPU (RTX 30/40 recommended, ≥8GB VRAM)&lt;/li&gt;
&lt;li&gt;Python 3.12+&lt;/li&gt;
&lt;li&gt;Xbox / PlayStation controller
(or virtual controller via ViGEmBus)&lt;/li&gt;
&lt;li&gt;World of Warcraft client installed&lt;/li&gt;
&lt;li&gt;Screen resolution fixed during data collection&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Clone NitroGen
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/MineDojo/NitroGen.git
&lt;span class="nb"&gt;cd &lt;/span&gt;NitroGen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  2. Create Virtual Environment
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
venv&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="se"&gt;\a&lt;/span&gt;ctivate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. Install Dependencies
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure PyTorch detects your GPU:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import torch; print(torch.cuda.is_available())"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Preparing World of Warcraft
&lt;/h2&gt;

&lt;p&gt;Recommended setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windowed or borderless fullscreen&lt;/li&gt;
&lt;li&gt;Fixed resolution (e.g. 1920x1080)&lt;/li&gt;
&lt;li&gt;Controller-friendly UI layout&lt;/li&gt;
&lt;li&gt;Consistent camera distance&lt;/li&gt;
&lt;li&gt;Disable UI animations where possible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NitroGen works best with &lt;strong&gt;stable visual layouts&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Collecting Training Data
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Record Gameplay
&lt;/h3&gt;

&lt;p&gt;Record:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;grinding sessions&lt;/li&gt;
&lt;li&gt;gathering routes&lt;/li&gt;
&lt;li&gt;combat encounters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the same time, record:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;controller inputs&lt;/li&gt;
&lt;li&gt;button presses&lt;/li&gt;
&lt;li&gt;joystick movements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This produces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;frame_t → action_t
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;pairs for training.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Dataset Structure
&lt;/h3&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dataset/
├── frames/
│   ├── 000001.png
│   ├── 000002.png
│   └── ...
└── actions/
    ├── 000001.json
    ├── 000002.json
    └── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Training the Model
&lt;/h2&gt;

&lt;p&gt;Fine-tune NitroGen on WoW footage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python scripts/train.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--config&lt;/span&gt; configs/wow_train.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dataset&lt;/span&gt; datasets/wow_grinding
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;frame resolution&lt;/li&gt;
&lt;li&gt;temporal window size&lt;/li&gt;
&lt;li&gt;action discretization&lt;/li&gt;
&lt;li&gt;batch size&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Training is &lt;strong&gt;much more stable than reinforcement learning&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Running the Bot
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python scripts/run_agent.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--config&lt;/span&gt; configs/wow_eval.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--checkpoint&lt;/span&gt; checkpoints/wow_finetuned.pt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;read frames from the WoW window&lt;/li&gt;
&lt;li&gt;predict controller actions&lt;/li&gt;
&lt;li&gt;execute them in real time&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No long-term quest planning&lt;/li&gt;
&lt;li&gt;Weak at PvP&lt;/li&gt;
&lt;li&gt;Sensitive to UI changes&lt;/li&gt;
&lt;li&gt;Requires significant training data&lt;/li&gt;
&lt;li&gt;Not competitive with human players&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a &lt;strong&gt;research agent&lt;/strong&gt;, not a perfect farmer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Detection and Ethics
&lt;/h2&gt;

&lt;p&gt;NitroGen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;does not inject code&lt;/li&gt;
&lt;li&gt;does not read memory&lt;/li&gt;
&lt;li&gt;does not modify the client&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;automation is still detectable on live servers&lt;/li&gt;
&lt;li&gt;Blizzard actively bans bots&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use responsibly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;World of Warcraft is one of the richest interactive environments ever built.&lt;/p&gt;

&lt;p&gt;Using it as a benchmark helps research:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;embodied AI&lt;/li&gt;
&lt;li&gt;vision-based control&lt;/li&gt;
&lt;li&gt;human-like decision making&lt;/li&gt;
&lt;li&gt;long-horizon tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The techniques here apply far beyond games.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>gamedev</category>
    </item>
  </channel>
</rss>
