Local
The local package is the sitehoster code that runs on your own machine. You download one versioned directory,
sitehoster-local-<version>, and run the one command inside it,
sitehoster-local.js. That command parses the command
line, loads your config, and hands off to one script per job: sync (run
the on-page automation, then push the changed files) and the local
browser tools. The package is in four parts:
localhoster-common/, the shared toolkit every hoster carries; localhoster-tools/, the two
universal browser tools; sitehoster-tools/, this hoster's own Automation Logs; and
sitehoster-sync/, the sync command with the on-page automation
compiler it drives. The directory you see here is exactly the directory in the zip. This page is the file map —
every file, then a section giving its exports, or, for the command you run, its switches.
sitehoster-local-<version>/ unzips from sitehoster-local-<version>.zip
sitehoster-local.js the one command you run: parse args, load config, dispatch
config.example.json the config template (copy to config.json)
localhoster-common/ the shared toolkit, identical across every hoster
index.js the single require surface (loadConfig, toolsSite, the UI kit)
net.js config load, bare-IP TLS, API-version
run.js the tools-site frame + per-tool run state
shell.js the page frame: strip, tabs, status, stylesheet
ui.js the UI kit: cards, tables, charts, pagination
format.js formatters: esc, numbers, bytes, dates
io.js small render/IO helpers
localhoster-tools/ the universal tools, shared across every hoster
web-logs/
index.js run(config): pull the access logs, then build the browser
lib/
pull.js mirror the access logs down
render.js build the Web Logs pages
server-logs/
index.js run(config): pull the stats, then build the browser
lib/
pull.js mirror the server stats down
render.js build the Server Logs pages
sitehoster-tools/ this hoster's own tools
automation-logs/
index.js run(config): read the site sources, then build the browser
lib/
render.js render the per-site Automation Logs pages
sitehoster-sync/ the sync command and the automation compiler it drives
index.js run(config): automate, then diff vs the server and push
lib/
automate.js rewrite every on-page copy from its master
sync.js diff the sites by Merkle descent and push the differences
index-store.js the change-skipping file index
directives.js the sitehoster: directive scanner
manifest.js the Merkle tree + path-safety rules
version.js the API/protocol version
Files
| File | What it does |
|---|---|
| the entry | |
sitehoster-local.js | The one command you run. Parses the command line, loads the config, and dispatches to each command's own script. Holds no logic itself. |
config.example.json | The config template: your server address, its token, your sites folder, and where the tools pages are built. Copy it to config.json. |
localhoster-common/ — the shared toolkit | |
localhoster-common/index.js | The single require surface the entry and every tool use: loadConfig, the toolsSite frame builder, recordRun, and the UI kit. |
localhoster-common/net.js | Reads and validates the config into a frozen object, skips cert verification for a bare-IP endpoint, and discovers the server's API version. |
localhoster-common/run.js | The tools-site frame: toolsSite(config, brand) builds the overview + tabs + clash-safe status; recordRun persists each tool's last run. |
localhoster-common/shell.js | The shared page frame: the top strip, tool tabs, per-tool status banner, and stylesheet. |
localhoster-common/ui.js | The shared UI kit a browser composes: stat cards, tables, chart/table toggles, and pagination. |
localhoster-common/format.js | The canonical formatters: escaping, numbers, bytes, durations, and month/day labels. |
localhoster-common/io.js | Small render/IO helpers, chiefly the footer-stripping used by the write-if-changed comparison. |
localhoster-tools/ — the universal tools | |
localhoster-tools/web-logs/ | Web Logs: index.js is the tool (run(config)); lib/pull.js mirrors the Caddy access logs down and lib/render.js builds the browser. |
localhoster-tools/server-logs/ | Server Logs: index.js is the tool; lib/pull.js mirrors the server statistics down and lib/render.js builds the drill-down browser. |
sitehoster-tools/ — this hoster's own tools | |
sitehoster-tools/automation-logs/ | Automation Logs: index.js is the tool; lib/render.js reads the site sources and renders the browsable per-site map of records, listings, counts and shared blocks. Touches no API. |
sitehoster-sync/ — the sync command + compiler | |
sitehoster-sync/index.js | The sync command (run(config)): automate every site from its masters, then diff the sites root against the server by Merkle descent, push the differences and prune removals. |
sitehoster-sync/lib/automate.js | The on-page automation compiler: rewrites every copy from its master (duplication, navigation, records, listings, counts). Detailed on Automation. |
sitehoster-sync/lib/sync.js | The push half: compares the content root to the server by recursive Merkle descent and uploads only the differences. Detailed on Sync. |
sitehoster-sync/lib/index-store.js | A persisted per-file index that lets a run skip work on files that have not changed. |
sitehoster-sync/lib/directives.js | The one scanner for sitehoster: directive comments, shared by the automation compiler and the Automation Logs browser. |
sitehoster-sync/lib/manifest.js | The Merkle-tree builder and path-safety rules the push half uses (the server carries its own copy of the same contract). |
sitehoster-sync/lib/version.js | The API/protocol version that forms the /api/vN path prefix; a mismatch fails fast. |
sitehoster-local.js — the entry
The single thing you run, and the only file with no exports. It parses the command line, loads the config
into a frozen object, and runs each requested command on its own line: the two universal browsers
(Web Logs, Server Logs), this hoster's own Automation Logs, and the content (sync) command. Automation is
not its own switch: --sync always runs the on-page automation first and then pushes, so
un-automated content is never published. --config is mandatory and must be a full absolute
path to a .json file.
| Switch | What it does |
|---|---|
--config <path> | The full absolute path to the config JSON. Required; validated before anything runs. |
--sync | Automate every site from its masters, then push the changes to the server. |
--web-logs | Mirror the access logs down and build the Web Logs browser. |
--server-logs | Mirror the server stats down and build the Server Logs browser. |
--automation-logs | Build the Automation Logs browser from the site sources. |
--all | Everything in one process: sync and all three browser tools. |
localhoster-common/net.js
The shared API-client plumbing every hoster's downloaders and sync sit on: read and validate the config
into a frozen { apiBase, apiToken, insecure, contentRoot, toolsRoot }, skip cert verification
for a bare-IP (self-signed) endpoint per request, and discover the server's API version so request paths
follow it. Meant to be copied across the hosters unchanged.
| Export | What it is |
|---|---|
loadConfig(path) | Read and validate the config JSON into a frozen object, rejecting a relative content root. |
isIpHttps(apiBase) | Whether the endpoint is HTTPS to a bare IP literal (or [ipv6]) — hence self-signed. |
apiGet(url, opts) | GET one API URL, skipping cert verification per request for a self-signed IP endpoint. |
resolvePrefix(...) | Discover the server's API version and build the /api/vN path prefix the requests use. |
localhoster-common/run.js
The tools-site frame. Each tool renders its own pages during its own run(config) and records
its result; this assembles the surrounding site — the overview (one card per tool) and a "not run yet"
status page for any tool that has never produced pages. It reads each tool's own state file, so running one
tool never clobbers another tool's card.
| Export | What it is |
|---|---|
toolsSite(config, brand) | A builder: register each tool's tab with .add(tool), then .render() writes the overview + tabs + status frame. |
recordRun(outDir, summary) | A tool records its last-run time and summary into its own output dir, which the overview card reads back. |
localhoster-common/shell.js
The shared shell that owns the page frame for the tools site: the fixed top strip (brand + Overview + a tab per tool + the IP), the per-tool status banner, the stylesheet, and small formatters. The tab set and brand are configured per project at startup, so this file carries no project identity and copies across the hosters unchanged.
| Export | What it is |
|---|---|
layout(opts) | Render a full framed page (top strip + body) for the Overview and any not-run tool's status page. |
statusBanner(tool) | The per-tool banner shown when a tool has not been run. |
overviewCard(tool, state) | One tool's card on the Overview, reflecting its last-run state. |
setTools(tools), setBrand(brand) | Configure the tab set and brand for this project; brandName() exposes the brand name. |
esc, fmtNum, stripFooter | The formatters and footer-stripper re-exported for convenience, plus STYLESHEET. |
localhoster-common/ui.js
The shared UI kit for the tools browsers — the reusable builders a domain tool composes instead of
hand-rolling markup. The page frame lives in shell.js and the formatters in
format.js; specialised chart bodies stay per-browser, bound to each tool's data.
| Export | What it is |
|---|---|
panel(...), table(...) | A titled panel, and a table with a built-in Show-all toggle. |
statCards(items) | A row of stat blocks (name + value, or a custom cell). |
navButtons(prev, next, up) | Prev / Next (with an optional Up) pagination, used on every dated page. |
SCRIPT, CSS | The shared client JS (table toggle + chart switch) and the common visual furniture. |
localhoster-common/format.js
The canonical formatters shared by the tools browsers — pure functions with no state, meant to be copied across the hoster projects unchanged.
| Export | What it is |
|---|---|
esc(s) | HTML-escape a value for safe interpolation. |
fmtNum(n) | A grouped decimal number (thousands separators). |
fmtBytes(b) | A human byte size (B, KB, MB, …). |
fmtDuration(ms) | A compact duration (ms, s, or m s). |
monthLabel, monthLabelShort, dayLabel, MONTH_NAMES | Month/day label helpers and the month-name table used by the dated pages. |
localhoster-common/io.js
Small shared render/IO helpers for the tools browsers. The footer carries the page's generation time,
which changes every run; stripFooter removes the whole footer before the write-if-changed
comparison so a new timestamp alone never forces a rewrite.
| Export | What it is |
|---|---|
stripFooter(html) | Remove the footer block before comparing a freshly rendered page to the one on disk. |
rmrf(p) | Recursively remove a path (for a clean full rebuild). |
GEN_DURATION_TOKEN | The placeholder a browser writes for the generation time and patches with the real value last. |
localhoster-tools/web-logs/
The Web Logs tool, universal across every hoster. index.js is the tool descriptor
({ key, label, run(config) }): it mirrors the Caddy access logs down, then builds the browser
and records its result. lib/pull.js does the incremental download and lib/render.js
renders a set of static, self-contained HTML pages so the traffic can be browsed offline.
| Export | What it is |
|---|---|
index.js — run(config) | Pull the access logs into <contentRoot>/_logs, build the browser under <toolsRoot>/web, and record the run. |
lib/pull.js — pull(opts) | Mirror the access logs down incrementally (rotated files once, the active log by Range). |
lib/render.js — build(opts) | Render the Web Logs pages from the mirrored logs. |
localhoster-tools/server-logs/
The Server Logs tool, universal across every hoster. index.js mirrors the server statistics
down, then builds the drill-down browser (all time → month → day → hour → minute). lib/pull.js
fetches the per-type dated day-files incrementally and lib/render.js charts every metric with a
chart/table toggle.
| Export | What it is |
|---|---|
index.js — run(config) | Pull the stats into <contentRoot>/_server/_data, build the browser under <toolsRoot>/server, and record the run. |
lib/pull.js — pull(opts) | Mirror the statistics day-files down incrementally, the stats counterpart of the web-logs pull. |
lib/render.js — build(opts) | Render the Server Logs drill-down pages from the mirrored stats. |
sitehoster-tools/automation-logs/
The Automation Logs tool, this hoster's own domain tool. Where the log browsers read the access logs, this one reads the site source — every HTML file under the sibling site folders — pulls out all of the automation markers, and renders a browsable map of the records, listings, counts, duplication and navigation behind each site. It touches no API and shares its directive scanner with the sync's compiler, so the read-only view reads exactly what the compiler writes.
| Export | What it is |
|---|---|
index.js — run(config) | Scan the site sources under contentRoot, build the browser under <toolsRoot>/automation, and record the run. |
lib/render.js — build(options) | Scan the site sources and render the automation browser, taking { sitesParent, outDir }. |
sitehoster-sync/index.js
The content command, in one pass. It first runs the on-page automation over every site (so no un-automated content is ever pushed), then runs the Merkle-descent push. It receives the frozen config from the entry and never mutates it.
| Export | What it is |
|---|---|
run(config) | One content pass: automate.run(config) then sync.run(config). |
sitehoster-sync/lib/automate.js
On-page automation. It reads the <!-- sitehoster:… --> comments that mark blocks as a
master (the source of truth) or a copy (generated), and rewrites the copies to match — duplication,
navigation, records, listings and counts. The whole tree is validated before a byte is written, and a
file is only rewritten when its bytes actually change, so the push is never disturbed by a no-op pass.
Also runnable on its own (node automate.js --config <path>).
| Export | What it is |
|---|---|
run(config) | Run one in-process automation pass over the content root, rewriting every copy from its master. |
sitehoster-sync/lib/sync.js
The push half. Each run it reads the content root, compares it to the server using recursive Merkle
descent over file metadata, and pushes only the differences — nothing stays resident or watches the
filesystem. Importable (the exports below) and still runnable on its own
(node sync.js --config <path>, with --dry-run to preview).
| Export | What it is |
|---|---|
run(config) | Run a single push cycle against the server, driven by the frozen config. |
buildGroupMap(contentRoot) | Scan the content root for group folders and build the map from each grouped member host to its group directory. Empty when no groups exist. |
toDiskRel(groupMap, rel) | Translate a logical (flattened) site path back to its real path on disk, restoring the group-directory prefix for a grouped host. |
sitehoster-sync/lib/index-store.js
A small persisted index at <contentRoot>/.sitehoster-index.json that lets a run skip
work on unchanged files. It is a pure optimisation: a missing, out-of-version or corrupt index just means
"recompute everything", so it can never cause wrong output, only more or less work. It is a dotfile, so
the push's own scan ignores it.
| Export | What it is |
|---|---|
load(contentRoot) | Load the index, or an empty one if it is missing, a different version, or unreadable. |
save(contentRoot, index) | Write the index back to disk under the content root. |
indexPath(contentRoot) | The absolute path to the index file for a given content root. |
stampOf(st) | A file's change-stamp from a stat(): raw byte size plus whole-second mtime. |
sameStamp(a, b) | Whether two change-stamps are equal — i.e. the file is unchanged. |
INDEX_NAME, VERSION | The dotfile name of the index, and the schema version whose mismatch forces a clean full rebuild. |
sitehoster-sync/lib/directives.js
The shared sitehoster directive helpers: one comment scanner and one "is this a directive" gate, used by the automation compiler (to parse the directive comments), by the push (to strip them from the copies it uploads), and by the Automation Logs browser (to read them), so every side agrees on what a directive is.
| Export | What it is |
|---|---|
scanComments(text) | Every HTML comment in the text, with byte offsets. |
stripDirectives(html) | Remove every sitehoster: directive comment from rendered HTML, keeping everything else. |
DIRECTIVE_RE | The single regex that recognises a directive comment's inner text. |
parseHead(...), parseFences(...), dedent(...) | Parse a marker's opening attributes and fenced template bodies, and dedent them. |
sitehoster-sync/lib/manifest.js
The Merkle-tree and path-safety helpers the push uses (the server carries its own copy of the same contract). A file's hash is derived from its metadata (size + whole-second mtime), never its contents, so building the tree reads no file data; equal directory hashes mean the same shape and per-file metadata throughout, which is what lets the push compare one root hash and descend only into subtrees that differ.
| Export | What it is |
|---|---|
buildTree(root, rel) | Build the Merkle tree node for a directory from stat() alone. |
createMerkleTree(...) | The underlying tree/hash constructor buildTree is built on. |
isAllowedRelPath(p) | Whether a relative path is safe: non-dot, URL-safe segments, no traversal or absolute paths. |
normalizeRelPath(p) | Normalise a relative path to the canonical form both sides agree on. |
resolveInRoot(root, rel) | Resolve a validated relative path to an absolute one confined within the content root. |
sitehoster-sync/lib/version.js
The API/protocol version — the /api/vN path prefix the local push and the server both use. The
push pins it to its own value, so a server on a different version answers a uniform failure instead of the
push silently misparsing a changed contract. It is bumped whenever any part of the wire contract changes,
and is distinct from the code release version in the VERSION file.
| Export | What it is |
|---|---|
API_VERSION | The current wire-contract version number (an integer). |