docs: added unlisted page and encrypted page
This commit is contained in:
@@ -24,6 +24,13 @@ This plugin provides support for [Obsidian Bases](https://obsidian.md/changelog/
|
||||
- **Property configuration**: Custom display names for properties.
|
||||
- **Link rendering**: Wikilinks and Markdown links within cell values are rendered as clickable links.
|
||||
|
||||
## Interaction with `unlisted` pages
|
||||
|
||||
`BasesPage` respects the `file.data.unlisted` convention written by [[UnlistedPages]] and [[EncryptedPages]]. Pages marked `unlisted: true` (or encrypted pages with `stealth: true`) are excluded from every rendered base view — table, list, board, cards, gallery, and any custom view — regardless of whether the base's filter expression would match them. Unlisted pages also cannot be dereferenced from formulas on visible pages via `.asFile()`.
|
||||
|
||||
> [!note]
|
||||
> Base views are **server-side rendered** HTML baked at build time. They do not update client-side after a visitor decrypts an encrypted page. Graph, explorer, and search all re-hydrate from the patched in-memory content index and show newly-unlocked pages for the rest of the browser session — base views do not, because they were materialized at build time with unlisted pages already excluded. A visitor who successfully decrypts a revealable encrypted page will see it appear in graph, explorer, and search, but **not** in any base view, until the site is rebuilt with that page listed. This is the same structural limitation that applies to backlinks, recent notes, folder listings, and tag listings.
|
||||
|
||||
## Configuration
|
||||
|
||||
This plugin accepts the following configuration options:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: Encrypted Pages Demo
|
||||
password: quartz
|
||||
unlisted: true
|
||||
tags:
|
||||
- plugin/transformer
|
||||
image:
|
||||
@@ -8,18 +9,19 @@ image:
|
||||
|
||||
Congratulations! You've successfully decrypted this page. 🎉
|
||||
|
||||
This is a live demo of the [[EncryptedPages]] plugin. The content you're reading was encrypted at build time using AES-256-GCM and decrypted in your browser using the Web Crypto API.
|
||||
This is a live demo of the [[EncryptedPages]] plugin. The content you're reading was encrypted at build time using AES-256-GCM and decrypted in your browser using the Web Crypto API. This page is also `unlisted: true`, which means it was hidden from every discovery surface on the site until you entered the password.
|
||||
|
||||
## What just happened?
|
||||
|
||||
1. At build time, the plugin read the `password` field from this page's frontmatter and encrypted all content below the title.
|
||||
2. When you visited this page, you were shown a password prompt instead of the page content.
|
||||
3. After entering the correct password, the plugin derived an encryption key using PBKDF2 and decrypted the content client-side.
|
||||
4. A `render` event was dispatched so other components (graph, explorer, etc.) could re-initialize with the decrypted content.
|
||||
2. Because this page is `unlisted: true`, the plugin emitted its metadata (slug, title, links, tags) to a separate `static/encryptedContentIndex.json` file, encrypted with this page's own password.
|
||||
3. When you visited this page, you were shown a password prompt instead of the content. The page was absent from the sidebar graph, explorer, search, RSS, sitemap, backlinks, tag listings, and bases views.
|
||||
4. After entering the correct password, the plugin derived an encryption key using PBKDF2 and decrypted the content client-side.
|
||||
5. The plugin then used the cached password to unlock this page's entry in the shadow content index and patched the in-memory content index in place. A `content-index-updated` event was dispatched, so graph, explorer, and search re-initialized with the newly unlocked entry — if you navigate back to any other page now, you will see this page in the sidebar, the graph, and search results. Server-side rendered listings (backlinks, recent notes, tag pages, folder listings, and [[BasesPage|bases views]]) were baked into HTML at build time and will not update within this session; they will only reflect decrypted pages on a fresh build of the site.
|
||||
|
||||
## Password caching
|
||||
|
||||
Your password has been cached in session storage. If there were other encrypted pages on this site, the plugin would automatically try this password before showing the prompt — so you'd only need to enter it once per session for pages that share the same password.
|
||||
Your password has been cached in session storage. If there were other encrypted pages on this site with the same password, the plugin would automatically try this password on each one — unlocking its content as well as its entry in the shadow content index — so you'd only need to enter it once per session.
|
||||
|
||||
## Try it yourself
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
title: EncryptedPages
|
||||
tags:
|
||||
- plugin/transformer
|
||||
- plugin/filter
|
||||
- plugin/emitter
|
||||
image: "#FF1493"
|
||||
---
|
||||
|
||||
Password-protected encrypted pages. Encrypts page content at build time using AES-256-GCM and decrypts client-side with the Web Crypto API. Passwords are set per-page via frontmatter.
|
||||
Password-protected encrypted pages. Encrypts page content at build time using AES-256-GCM and decrypts client-side with the Web Crypto API. Passwords are set per-page via frontmatter. A companion emitter writes an encrypted shadow content index so unlisted encrypted pages can be dynamically revealed in graph, explorer, and search after a successful decryption — without ever leaking their metadata to visitors who do not hold the password.
|
||||
|
||||
> [!example] Live demo
|
||||
> Try it yourself: [[EncryptedPages Demo]]. The password is `quartz`.
|
||||
@@ -29,19 +29,65 @@ The page content will be encrypted at build time. Visitors must enter the correc
|
||||
|
||||
Successful passwords are cached in the browser's session storage and automatically tried on other encrypted pages for convenience.
|
||||
|
||||
### Hiding encrypted pages from discovery surfaces
|
||||
|
||||
By default, encrypted pages still appear in the graph, explorer, search, RSS, sitemap, and backlinks — visitors can see the page exists and its title, but cannot read the content without the password.
|
||||
|
||||
To hide an encrypted page entirely until a visitor successfully decrypts it, set `unlisted: true` in its frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: My Secret Page
|
||||
password: mysecretpassword
|
||||
unlisted: true
|
||||
---
|
||||
```
|
||||
|
||||
An unlisted page:
|
||||
|
||||
- Is **absent** from `contentIndex.json`, `sitemap.xml`, the RSS feed, backlinks, recent notes, folder listings, tag listings, bases views, graph, explorer, and search.
|
||||
- Is still emitted as HTML, so it remains accessible by direct URL.
|
||||
- Has its metadata (slug, title, links, tags) written to a separate `static/encryptedContentIndex.json` file, encrypted with the page's own password.
|
||||
- Is dynamically re-added to the in-memory content index when a visitor successfully decrypts it, so graph, explorer, and search reflect it for the rest of the browser session. Server-rendered listings — backlinks, recent notes, tag pages, folder listings, and [[BasesPage|bases views]] — remain statically hidden even after decryption because they are baked as HTML at build time.
|
||||
|
||||
To make this the default for every encrypted page on your site, set `unlistWhenEncrypted: true` in the plugin options. Individual pages can then opt back in with `unlisted: false`.
|
||||
|
||||
> [!note]
|
||||
> The `unlisted: true` frontmatter field above only takes effect for encrypted pages when this plugin is installed. If you also want `unlisted: true` to work on **non-encrypted** pages across your site, install [[UnlistedPages]] alongside this one. The two plugins compose cleanly — when both are enabled, `unlisted: true` hides any page, encrypted or not, from every discovery surface that respects the `file.data.unlisted` convention.
|
||||
|
||||
### Permanently hiding encrypted pages (`stealth`)
|
||||
|
||||
By default, an `unlisted: true` encrypted page is _revealed_ in graph, explorer, and search after a visitor successfully decrypts it. This is usually what you want: the user just proved they know the password, so showing them the page in the sidebar makes sense for the rest of their session.
|
||||
|
||||
If you instead want a page that stays permanently invisible — accessible only by direct URL, even to users who have successfully decrypted other pages on the same site — set `stealth: true` in its frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Deep Secret
|
||||
password: mysecretpassword
|
||||
stealth: true
|
||||
---
|
||||
```
|
||||
|
||||
A stealth page:
|
||||
|
||||
- Is **absent** from every discovery surface, same as any `unlisted` page.
|
||||
- Has **no entry** in the shadow content index (`encryptedContentIndex.json`). The plugin deliberately skips stealth pages when building the shadow index.
|
||||
- Stays hidden even after the visitor enters the correct password. Since there is no shadow-index entry to decrypt, there is nothing to patch into the in-memory content index — graph, explorer, and search never learn the page exists. Only the decrypted HTML is visible to the user on the page itself.
|
||||
- The password is still cached in session storage, so re-visiting the same stealth page will auto-unlock it.
|
||||
|
||||
`stealth: true` implies `unlisted: true` — you do not need to set both, and if you write `stealth: true, unlisted: false` the stealth flag wins. On non-encrypted pages `stealth: true` has no effect (there is no shadow index to skip).
|
||||
|
||||
Use stealth pages for "secret door" content that should only reach users who already know the exact URL: private notes linked from an external wiki, personal pages you send to specific people, or anything you never want to show up in a site-internal search even to authenticated readers.
|
||||
|
||||
## Configuration
|
||||
|
||||
This plugin provides a transformer, a filter, and a component.
|
||||
This plugin provides a transformer, an emitter, and a component. All options are set on a single config entry and shared between the transformer and the emitter — Quartz instantiates both automatically.
|
||||
|
||||
### Transformer options
|
||||
|
||||
- `visibility`: How encrypted pages appear in graph, explorer, and backlinks. `"visible"` shows the page normally, `"icon"` adds a lock indicator, `"hidden"` hides the page completely. Defaults to `"icon"`.
|
||||
- `iterations`: PBKDF2 iteration count for key derivation. Higher values are more secure but slower to unlock. Defaults to `600000`.
|
||||
- `passwordField`: Frontmatter field name that holds the page password. Defaults to `"password"`.
|
||||
|
||||
### Filter options
|
||||
|
||||
- `visibility`: Controls whether encrypted pages appear in search, RSS, and sitemap. When set to `"hidden"`, encrypted pages are excluded from content indices entirely. Defaults to `"icon"`.
|
||||
- `passwordField`: Frontmatter field name that holds the page password. Shared by the transformer and the emitter. Defaults to `"password"`.
|
||||
- `unlistWhenEncrypted`: If `true`, every encrypted page is marked `unlisted` unless its frontmatter explicitly overrides it. Defaults to `false`.
|
||||
- `outputPath`: Output path for the shadow content index, relative to Quartz's output directory. Defaults to `"static/encryptedContentIndex.json"`.
|
||||
|
||||
### Component options
|
||||
|
||||
@@ -53,21 +99,26 @@ This plugin provides a transformer, a filter, and a component.
|
||||
- source: github:quartz-community/encrypted-pages
|
||||
enabled: true
|
||||
options:
|
||||
visibility: icon
|
||||
iterations: 600000
|
||||
passwordField: password
|
||||
unlistWhenEncrypted: false
|
||||
outputPath: static/encryptedContentIndex.json
|
||||
```
|
||||
|
||||
> [!warning]
|
||||
> The `EncryptedPages` transformer replaces the entire HAST tree of an encrypted page with an opaque ciphertext container. Any transformer that needs to read the real HTML — in particular [[CrawlLinks]], which populates the links used by backlinks and the shadow content index — must run **before** `EncryptedPages`. Use the `order` field in `quartz.config.yaml` to control this.
|
||||
|
||||
## Security
|
||||
|
||||
- Content is encrypted with AES-256-GCM using PBKDF2 SHA-256 key derivation.
|
||||
- Plaintext is stripped from search indices and RSS feeds regardless of visibility setting.
|
||||
- Plaintext is stripped from search indices, RSS feeds, and the shadow content index regardless of visibility setting.
|
||||
- The shadow content index is a flat array of opaque encrypted blobs. An attacker who downloads it learns only the number of unlisted encrypted pages and the PBKDF2 iteration count — no slugs, titles, or link relationships leak.
|
||||
- Passwords are set per-page in frontmatter. Avoid committing passwords to public repositories.
|
||||
- This is client-side encryption of a static site. It protects against casual browsing but not against determined attackers with access to the page source.
|
||||
|
||||
## API
|
||||
|
||||
- Category: Transformer, Filter
|
||||
- Function name: `ExternalPlugin.EncryptedPages()`, `ExternalPlugin.EncryptedPageFilter()`.
|
||||
- Category: Transformer, Emitter
|
||||
- Function name: `ExternalPlugin.EncryptedPages()`, `ExternalPlugin.EncryptedContentIndex()`.
|
||||
- Source: [`quartz-community/encrypted-pages`](https://github.com/quartz-community/encrypted-pages)
|
||||
- Install: `npx quartz plugin add github:quartz-community/encrypted-pages`
|
||||
|
||||
63
docs/plugins/UnlistedPages.md
Normal file
63
docs/plugins/UnlistedPages.md
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
title: UnlistedPages
|
||||
tags:
|
||||
- plugin/transformer
|
||||
image:
|
||||
---
|
||||
|
||||
Zero-config transformer that makes `unlisted: true` in a page's frontmatter a first-class way to opt out of every listing surface on your site. The page is still emitted as HTML and remains accessible by direct URL, but is absent from `contentIndex.json`, RSS, sitemap, graph, explorer, search, backlinks, recent notes, folder listings, and tag listings.
|
||||
|
||||
> [!note]
|
||||
> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page.
|
||||
|
||||
## Usage
|
||||
|
||||
Add an `unlisted` field to any page's frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: My Draft
|
||||
unlisted: true
|
||||
---
|
||||
```
|
||||
|
||||
That's it. Every Quartz v5 plugin that respects the `file.data.unlisted` convention will then hide the page.
|
||||
|
||||
## What each plugin does
|
||||
|
||||
| Plugin | Behavior when `unlisted: true` |
|
||||
| -------------- | -------------------------------------------------------------------------- |
|
||||
| `ContentIndex` | Page absent from `contentIndex.json`, `sitemap.xml`, and the RSS feed. |
|
||||
| `Search` | Page absent from search results (derived from `contentIndex.json`). |
|
||||
| `Graph` | Page absent from graph nodes and edges (derived from `contentIndex.json`). |
|
||||
| `Explorer` | Page absent from the sidebar file tree (derived from `contentIndex.json`). |
|
||||
| `Backlinks` | Page never appears as a backlink source on other pages. |
|
||||
| `RecentNotes` | Page absent from the recent notes list. |
|
||||
| `FolderPage` | Page absent from folder listings and folder discovery. |
|
||||
| `TagPage` | Page absent from tag discovery and tag listings. |
|
||||
|
||||
In every case, the page's HTML is still emitted and accessible by direct URL.
|
||||
|
||||
## Configuration
|
||||
|
||||
Zero options. Just enable it.
|
||||
|
||||
```yaml title="quartz.config.yaml"
|
||||
- source: github:quartz-community/unlisted-pages
|
||||
enabled: true
|
||||
```
|
||||
|
||||
## Interaction with [[EncryptedPages]]
|
||||
|
||||
The [[EncryptedPages]] plugin also sets `file.data.unlisted` when its `unlistWhenEncrypted: true` option is set or when a page has `unlisted: true` in frontmatter. The two plugins compose cleanly:
|
||||
|
||||
- If you install only `UnlistedPages`: any page with `unlisted: true` in frontmatter is hidden from listing surfaces. Encryption is independent.
|
||||
- If you install only `EncryptedPages`: `unlisted: true` only takes effect on pages that are also encrypted (have a password). Non-encrypted pages with `unlisted: true` are silently ignored.
|
||||
- If you install both: `unlisted: true` works for every page, encrypted or not. This is the recommended setup for sites that use encrypted pages.
|
||||
|
||||
## API
|
||||
|
||||
- Category: Transformer
|
||||
- Function name: `ExternalPlugin.UnlistedPages()`.
|
||||
- Source: [`quartz-community/unlisted-pages`](https://github.com/quartz-community/unlisted-pages)
|
||||
- Install: `npx quartz plugin add github:quartz-community/unlisted-pages`
|
||||
Reference in New Issue
Block a user