Skip to content

Instantly share code, notes, and snippets.

@OrigamingWasTaken
Last active November 19, 2025 17:10
Show Gist options
  • Select an option

  • Save OrigamingWasTaken/d70f62c782bc99283eb7df1caa333c26 to your computer and use it in GitHub Desktop.

Select an option

Save OrigamingWasTaken/d70f62c782bc99283eb7df1caa333c26 to your computer and use it in GitHub Desktop.

Hi 👋, my name is Ori and I'll help you setup password-protected notes on Quartz!

To do so, you will need to modify a few files.

Firstly, install @expo/spawn-async and Staticrypt with $ npm install @expo/spawn-async staticrypt

After that, paste this code into quartz/password.ts

import spawn from "@expo/spawn-async"
import fs from "fs"
import path from "path"
import { toHtml } from "hast-util-to-html"

import { BuildCtx } from "./util/ctx"
import { Root as HTMLRoot } from "hast"
import { QuartzTransformerPlugin } from "./plugins/types"
import { VFile } from "vfile"

const quartzCache = path.resolve("./quartz/.quartz-cache/password/")

export async function encryptPages() {
  const ppath = path.join(quartzCache, "cache.json")
  const passwordCache: { password: string; savePath: string }[] = fs.existsSync(ppath)
    ? JSON.parse(fs.readFileSync(ppath, "utf-8"))
    : []
  for (const file of passwordCache) {
    await spawn("npx", [
      "staticrypt",
      file.savePath,
      "-p",
      file.password,
      "--short",
      "-d",
      path.dirname(file.savePath),
    ])
  }
  // Little cute delay :3
  await setTimeout(() => {}, 100)
}

interface Options {
  /*** The description of every password-protected notes */
  lockedDescription?: string
}

// Clears the password directory
const passwordCache = path.resolve("./quartz/.quartz-cache/password/cache.json")
fs.rmSync(quartzCache, { recursive: true, force: true })

export const Staticrypt: QuartzTransformerPlugin<Options> = (opts?: Options) => {
  return {
    name: "Staticrypt",
    htmlPlugins(ctx: BuildCtx) {
      return [
        () => {
          return async (tree: HTMLRoot, file: VFile) => {
            const savePath = path.join(quartzCache, ctx.argv.output, `${file.data.slug}.html`)
            const frontmatter = file.data.frontmatter

            file.data.isProtected = false
            if (frontmatter && "password" in frontmatter && frontmatter.password) {
              if (!fs.existsSync(quartzCache)) {
                fs.mkdirSync(quartzCache, { recursive: true })
              }
              file.data.isProtected = true

              // Password cache
              let currentCache: { password: string; savePath: string }[] = []
              if (!fs.existsSync(passwordCache)) {
                fs.writeFileSync(passwordCache, "[]")
              } else {
                currentCache = JSON.parse(fs.readFileSync(passwordCache, "utf-8"))
              }

              currentCache.push({
                password: frontmatter.password as string,
                savePath: path.join(ctx.argv.output, `${file.data.slug}.html`),
              })
              fs.writeFileSync(passwordCache, JSON.stringify(currentCache))

              fs.mkdirSync(path.dirname(savePath), { recursive: true })
              fs.writeFileSync(savePath, toHtml(tree))
            }
            file.data.description = opts?.lockedDescription || "This note is password-protected."
          }
        },
      ]
    },
  }
}

declare module "vfile" {
  interface DataMap {
    isProtected: boolean
  }
}

Now, go into your quartz.config.ts file and add the Staticrypt transformer (Staticrypt is the tool used to encrypt static webpages.) like so:

import { Staticrypt } from "./quartz/password"

// ...

plugins: {
    transformers: [
      Staticrypt(),
// ... the rest of the file

Now we must add the encrypting function to the build process. To do that, open the quart/build.ts file, and add this import:

  import { encryptPages } from "./password"

Then, place this line of code

  await encryptPages()

just before

  console.log(chalk.green(`Done processing ${fps.length} files in ${perf.timeSince()}`))

You are almost finished! The last step is to add 1 line to the quartz/plugins/emitters/contentIndex.ts file. You need to paste this:

  if (file.data.isProtected) file.data.text = "";

just before the line where there is:

  const slug = file.data.slug!

(tip: press control+f to find the line)

And you're all set!

To password lock your note, simply add the password key to the frontmatter. Like so:

---
password: MyCoolPassword1234
---

Hi this is a note!

To unlock your awesome note, just enter your password into the textbox that will appear when you visit the note on your quartz site.

Warning

Due to a little bug, you'll have to refresh the page once you go on an encrypted note for the password-prompting window to appear.

Good writing 🖊️!

@nkapila6
Copy link

nkapila6 commented Aug 19, 2024

This is great, thank you very much! Is it possible to password protect an entire folder?

edit: Doesn't work once deployed, only on localhost.. Not sure what goes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment