Write elegant, narrative-driven documentation that treats codebases and systems as exhibits worth exploring...
Documentation as hospitality. Code as curated collection.
The art of transforming technical systems into welcoming guided tours. Museum documentation positions the writer as a guide walking alongside readers through "why" questions before diving into implementation specifics.
This is Grove's elegant documentation styleโmeant for Wanderers of any experience level who want to understand the technologies, patterns, and decisions that make Grove what it is.
Not for:
Documentation should function as hospitality.
When readers enter a codebase or system, they're visitors unfamiliar with its architecture and design decisions. Museum-style documentation positions the writer as a guide rather than a reference compiler, walking alongside readers through "why" questions before diving into implementation.
๐ฒ Welcome ๐ฒ
โญโโโโโโโโโโโโโโโโโโโโโโฎ
โ โ
โ โญโโโโโโโโโโโโโโฎ โ
โ โ EXHIBIT A โ โ
โ โ โ โ
โ โ How Login โ โ
โ โ Works โ โ
โ โ โ โ
โ โฐโโโโโโโโโโโโโโฏ โ
โ โ โ
โ โโโโโโชโโโโโ โ
โ โ โ
โ โญโโโโโโโโโโโโโโฎ โ
โ โ EXHIBIT B โ โ
โ โฐโโโโโโโโโโโโโโฏ โ
โ โ
โฐโโโโโโโโโโโโโโโโโโโโโโฏ
Walk through. Take your time.
Every exhibit tells a story.
A museum doesn't hand you a catalog and wish you luck. It guides you through a curated experience, placing context before complexity, stories before specifications.
Start exhibits by establishing context. Before showing code or architecture, tell readers:
Instead of:
The TokenRefreshMap stores active refresh operations keyed by user ID.
Write:
When someone's login expires, we need to refresh their credentials without logging them out. This map coordinates that processโpreventing duplicate refresh attempts when multiple tabs are open.
Code shows what. Documentation explains why.
Rather than stating that a Map stores token refresh operations, describe the coordination problem it solves. Connect the abstraction to the experience it creates.
Connect technical concepts to familiar experiences:
| Technical Concept | Museum Metaphor |
|---|---|
| Database | A filing cabinet with organized drawers |
| Cache | A quick-lookup shelf by the door |
| Middleware | A security checkpoint you pass through |
| Queue | A line where requests wait their turn |
| Worker | A helpful assistant handling tasks in the background |
| Webhook | A doorbell that rings when something happens |
Present actual code snippets followed by clear explanation of their significance:
const pending = this.refreshInProgress.get(userId);
if (pending) return pending;
If a refresh is already happening for this person, we wait for that one instead of starting another. One kitchen, one cook.
Museum exhibits follow consistent anatomy:
# The Authentication Exhibit
> Where login happens. Where trust begins.
Orient the reader immediately:
## What You're Looking At
This is where login happens. When someone proves they own an email
address, this code decides what they can access and how long that
access lasts.
You'll see OAuth flows, session management, and token refresh logic.
Nothing scaryโjust careful choreography.
Use galleries, parts, or sections to organize the journey:
## The Tour
### Gallery 1: The Front Door
How visitors arrive and prove who they are.
### Gallery 2: The Memory Room
How we remember who's logged in.
### Gallery 3: The Renewal Office
How sessions stay fresh without interrupting work.
Highlight transferable lessons:
## Patterns Worth Stealing
**The "already in progress" check.** Before starting an expensive
operation, check if it's already running. Simple, but easy to forget.
**Centralized error handling.** All auth failures flow through one
function. One place to fix, one place to log.
Offer honest reflection:
## Lessons Learned
We tried stateless JWTs first. Simpler, they said. Scalable, they
promised. But logout was impossibleโtokens couldn't be revoked.
Session-based auth is older, but it works. Sometimes boring is right.
Link to related exhibits:
## Continue Your Tour
- **[The Database Exhibit](./database.md)** โ Where sessions are stored
- **[The API Exhibit](./api.md)** โ How protected routes check access
- **[The Security Exhibit](./security.md)** โ Rate limiting and protection
---
_โ Autumn, January 2026_
Or a poetic one-liner:
---
_Trust is built one verified request at a time._
Show processes and relationships:
Visitor Grove Google
โ โ โ
โ "Log me in" โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ>โ โ
โ โ "Who is this?" โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโ>โ
โ โ โ
โ โ "It's autumn@..." โ
โ โ <โโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ โ
โ "Welcome back" โ โ
โ <โโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
| File | What It Does | Why It Matters |
| --------------- | -------------------- | -------------- |
| `auth.ts` | Handles OAuth flow | The front door |
| `session.ts` | Manages login state | The memory |
| `middleware.ts` | Checks every request | The bouncer |
Never show code in isolation. Always explain before or after:
// Every request passes through this checkpoint
export async function authGuard(request: Request): Promise<Response | null> {
const session = await getSession(request);
if (!session) {
return redirect("/login");
}
return null; // Continue to the page
}
If you're not logged in, you go to login. If you are, you continue. Simple checkpoint logic.
Refer to owl-archive/references/anti-patterns.md for the full list. Key ones for museum writing:
Generic (avoid):
This module provides robust functionality for handling authentication flows in a seamless manner, leveraging industry-standard OAuth protocols.
Museum style (use):
This is where login happens. When someone proves they own an email address, this code decides what they can access.
For documenting an entire codebase, create a central MUSEUM.md that serves as the entrance:
# Welcome to the Grove Museum
> A guided tour of how this forest grows.
## The Wings
- **[The Architecture Wing](./docs/exhibits/architecture.md)** โ The big picture
- **[The Authentication Exhibit](./docs/exhibits/auth.md)** โ Trust and identity
- **[The Database Galleries](./docs/exhibits/database.md)** โ Where data lives
For a single complex feature or directory:
# The Editor Exhibit
> Where words become posts.
This directory contains everything that powers the writing experience...
For multi-part systems, use galleries:
## Gallery 1: Data Layer
How posts are stored and retrieved.
## Gallery 2: API Endpoints
The doors visitors knock on.
## Gallery 3: The Editor Component
Where the magic happens in the browser.
## Gallery 4: Security Considerations
Keeping things safe.
Before finalizing any museum documentation:
GroveTerm, GroveSwap, or GroveText from @autumnsgrove/lattice/ui instead of hardcoding terms. New visitors see standard terms by default; Grove Mode users see the nature-themed vocabulary. Use [[term]] syntax in markdown content for auto-transformation via the rehype-groveterm plugin.# The Session Exhibit
> How Grove remembers who you are.
## What You're Looking At
When you log in, Grove needs to remember you. Not foreverโjust long
enough for your visit. This exhibit shows how that memory works.
You'll see cookies, database tables, and the careful dance of
"who are you?" that happens with every page load.
---
## Gallery 1: The Cookie Jar
A session starts with a cookie. Not the chocolate chip kindโa small
piece of text your browser holds onto.
```typescript
const SESSION_COOKIE = "grove_session";
const SESSION_DURATION = 7 * 24 * 60 * 60 * 1000; // 7 days
```
Seven days. Long enough to be convenient, short enough to stay secure.
When you log in, we generate a random ID and store it in this cookie. The ID itself means nothingโit's just a key to look you up.
The actual session data lives in the database:
| Column | What It Holds |
|---|---|
id |
The random key from the cookie |
user_id |
Who this session belongs to |
created_at |
When you logged in |
expires_at |
When this session ends |
Every time you load a page, we look up your cookie's ID in this table. If we find it (and it hasn't expired), you're still logged in.
Sessions expire. But logging in every week is annoying.
So we do a quiet refresh: when your session is more than halfway through its life, we extend it. You never notice. You just stay logged in while you're active.
if (session.expiresAt < Date.now() + SESSION_DURATION / 2) {
await extendSession(session.id);
}
Active visitors stay. Abandoned sessions expire.
The "halfway refresh" pattern. Don't wait until expiration to extend sessions. Extend them while the visitor is active.
Random IDs over sequential. Session IDs should be unpredictable. Never use auto-incrementing numbers for security tokens.
We used to store session data in the cookie itself (JWTs). Simpler, we thought. But then we couldn't revoke sessionsโif someone's token leaked, we had no way to invalidate it.
Database sessions are older technology. Sometimes older is wiser.
โ Autumn, January 2026
---
## The Underlying Purpose
Museum documentation respects readers' time and intelligence while acknowledging that code is knowledge deserving careful stewardship.
Wanderers who read these exhibits should leave understanding not just *what* Grove does, but *why* it does it that way. They should feel like they've been welcomed into the workshop, not handed a manual.
*Every codebase has stories. Museum documentation tells them.*