Verbiage is a wiki/knowledge base engine with one simple twist: everything is written purely from scratch, no dependencies other than the Node.js standard library. This monorepo contains the application itself, as well as all the custom-made libraries it depends on for various utilities.
Why? While I wish I could say it's some kind of commentary on the nature of dependencies in modern programming, it's really primarily for recreational purposes, and to have something neat to host on my personal site in the future.
How? By writing everything myself. We'll be doing things sort of the old fashioned way - simple server-side rendering, because the only thing I dislike more than using frontend frameworks would be trying to write one. The most modern thing frontend-wise here will probably be the CSS features.
When? On and off, whenever I feel motivated.
Should I use it? I can't stop you. It's probably not going to be production-ready for quite a while, and while I'll generally try to stick to best practices and keep things reasonably optimized, I'm not gonna make any promises about scaling.
You may notice that there's nothing one could call an "app" here right now. That's because to write a wiki engine from scratch, I must first invent the universe. Please excuse the dust while I write all the prerequisite libraries needed to get this working. For those interested, the gist of the plan is:
- Get a router working
- Write an HTML templating engine, enabling ✨ interactivity ✨ using forms
- Write a caching/driver layer on top of the file system, enabling ✨ permanent storage ✨ ( <-- you are here now )
- Once routing, interactivity, and permanent storage are all available to me, I can then start working on the application code.
In the meantime, feel free to take a look at and play around with what's already here!
Verbiage is an ambitious project. Here's the full run-down on what you can come to expect:
- Waiter
-
- Basic routing
-
- Generic parameters
-
- Streamed responses
-
- Cookies
-
- Middleware (defined in route, cascades to child routes, i.e.
requiresAuthon/secretsapplies to/secrets/shhand/secrets/shush)
- Middleware (defined in route, cascades to child routes, i.e.
-
- Multipart form data parser (todo: document!)
- Lavender
-
- Basic HTML parsing/substitution (be able to insert a string into the page)
-
- Object traversal -
{object.property}
- Object traversal -
-
- If/else statement -
{if some_prop}<p>True</p>{else}<p>Not true</p>{end}
- If/else statement -
-
- For statement -
{for item of list}{end}
- For statement -
-
- Components: HTML templates, hydration using JS
-
- Fallback components
-
- Use a layout to wrap around content
-
- HTML Sanitization
-
- Cleanup templates (remove whitespace between html tags)
-
- Better error reporting (give approximate position of a faulty expression)
- Markdawn
-
- Paragraph
-
- Bold
-
- Italics
-
- Underline
-
- Strikethrough
-
- Highlight
-
- Header
-
- Unordered list
-
- Ordered list
-
- Inline code block
-
- Code block
-
- Table
-
- Footnote
-
- Links
-
- Masked links
-
- Embedded image
-
- Extract facets from markdown (eg. TOC, document title, excerpt...)
-
-
- Table of contents
-
- Cabinet
-
- Cache file tree
-
- Cache file contents
-
- Watch for new files and add to tree
-
- File ops
-
-
- Check if exists
-
-
-
- Stat file
-
-
-
- Write file
-
-
-
- Read file
-
-
-
- Delete file
-
-
-
- isDirectory
-
-
-
- Upsert file
-
-
-
- Append file
-
-
-
- Move/rename file
-
-
-
- Test permissions
-
-
-
- Open read stream
-
-
-
- Open write stream
-
-
-
- Check if directory has a child filename (from directory object)
-
-
-
- Stat file (from file object)
-
-
-
- Get ancestry (return array of /home, /home/foo, /home/foo/bar.bin)
-
-
-
- Get entire file tree
-
-
- Bag - queryable map of objects serialized to JSON lines
-
-
- Deserialize
-
-
-
- Serialize
-
-
-
- Append/Upsert
-
-
-
- Update
-
-
-
- Delete
-
-
-
- Limit result count
-
-
-
- Order results by given property
-
-
-
- Query based on greater-than/less-than
-
-
-
- Query based on property equality
-
-
-
- Query based on arbitrary function
-
-
-
- Join queries
-
- Verbiage
-
- CRUD wiki pages
-
- Arbitrary file upload
-
- File listing view
-
- Media gallery view
-
- Search
-
- Custom sidebar
-
- Custom CSS/favicon
-
- Multi-user
-
-
- Login/auth
-
-
-
- Permission system
-
-
- Multi-wiki
-
- Audit logs / activity tab
-
- Discussions
-
- Custom markdown features
-
-
- Emotes
-
-
-
- Wiki links - [[(?namespace:)(path|$alias)]]
-
-
- Aliases - set up permanent links to files, for example
my_blog_post -> /blog/2026/post.md
- Aliases - set up permanent links to files, for example
- Clone the repo.
- Navigate to the cloned directory, run
node index.js.
./lib/waiter
Waiter is an HTTP server and routing library. It is responsible for routing requests to their respective methods and provides some utilities to make handling requests easier and more graceful.
More documentation for Waiter is available at ./doc/Waiter.md
./lib/lavender
Waiter is a component-based server-side rendering framework and templating engine. It is responsible for crafting the HTML responses to send to clients.
More documentation for Lavender is available at ./doc/Lavender.md
./lib/markdawn
Renders markdown to HTML.
./lib/cabinet
Cabinet implements utilities for file storage, writing, and retrieval.
The first component is the StorageManager class, which can be mounted to any given directory and provides a cached view of the file tree as well as optionally the contents of certain files, with some helper functions.
The second component is the Bag class, which provides a mechanism for serializing, deserializing, and querying JSONL files.