obol
Owner: IIIlllIIIllI URL: git@git.0x00nyx.xyz:seb/obolserver.git
README.md
Obol - small static site server in Nim
=====================================
Obol serves a directory of JSON content and HTML templates with a single
self-contained binary. No dependencies outside Nim stdlib.
Features
--------
- declarative routing from config.json
- JSON content files, no schema enforcement
- minimal template engine (includes, blocks, if/range)
- static files from /static
- param routes with {param} data paths
- hot reload in DEV_MODE
Installation
------------
Build with Nim:
nim c -d:release obol.nim
Usage
-----
1. Prepare a site directory (see layout below or `example-site/`).
2. Run the server:
SITE_DIR=/path/to/site ./obol
or build the server binary and run it from there.
3. Optional env:
- PORT (default 8080)
- SITE_DIR (default .)
- DEV_MODE=1 (reload config/global on mtime change)
Layout
------
Each site directory follows this structure:
- config.json
- content/global.json
- content/pages/*.json
- templates/*.html
- static/
config.json
-----------
Example:
{
"site": {...},
"navigation": [...],
"pages": [
{
"path": "/docs",
"template": "docs.html",
"data": "docs.json"
}
],
"notFoundTemplate": "404.html"
}
Rules:
- paths must be unique
- template names must exist in templates/
- data is optional; missing file -> empty object
Render Data
-----------
Every template gets a single JSON object:
{
"config": config.json tree,
"global": content/global.json tree,
"page": current page data,
"pageConfig": the page entry from config.json,
"request": {"path", "method", "params"}
}
Template Approach
-----------------
Templates are plain HTML with a tiny, data-only template language. There is
no custom context and no helpers: everything comes from the render data above.
Includes and blocks are resolved at runtime and cached in production.
Template Syntax
---------------
- Variable: {{name}} (supports dotted paths, e.g. {{page.title}})
- Conditionals:
{{if page.published}}
...
{{else}}
...
{{end}}
- Ranges (arrays/objects):
{{range page.items}}
{{name}}
{{end}}
- Include another template file:
{{template "header"}} -> templates/header.html
- Define a named block:
{{define "layout"}}
...
{{end}}
Notes:
- include depth is capped at 10; circular chains error
- include data is always the merged render object (no custom context)
Routing
-------
- routes are defined only in config.json
- /static/* is served automatically when static/ exists
- /health is provided unless you define it
- :param segments are supported; /blog/:slug exposes request.params.slug
- exact routes win over parameterized, then fewer params, then declaration order
Data Paths
----------
- data accepts {param} placeholders: posts/{slug}.json
- missing params or files -> 404 (with log message)
- resolved paths are clamped to content/ (no traversal)
Error Handling
--------------
- missing route -> render notFoundTemplate or plain 404
- JSON parse failure -> 500, logged to stdout
- template failure -> 500, logged to stdout
- framework never crashes on user content
Philosophy
----------
What Obol does: map URL paths to JSON files and render them into HTML.