Client
The client is the only sitehoster code that runs on your machine: one command you run on a timer, and
the modules behind it. It is stateless — every run rebuilds what it needs from stat(), so
nothing stays resident. The source is in three layers: client/, this hoster's own modules;
common/, the shared toolkit every hoster's client is built from (the orchestrator, the log
browsers, and the plumbing for pulling data and rendering pages); and shared/, the wire
contract it holds jointly with the server. This page is the
file map — every file, followed by a section listing its exports, or, for the command you run, its
switches. The synchronisation protocol and the
tools have their own pages for the how and why.
sitehoster-client.js the one command you run (entry point)
client/ this hoster's own modules
sync.js mirror the sites to your server
automate.js rewrite every copy from its master
index-store.js the change-skipping file index
browse/
automation.js build the Automation Logs browser
common/ the shared toolkit, common to every hoster
net.js config, TLS and API-version plumbing
client/
run.js the orchestrator every hoster's client runs
browse/
web.js build the Web Logs browser
server.js build the Server Logs browser
pull-logs.js mirror the access logs down
pull-stats.js mirror the server stats down
shell.js the page frame: strip, tabs, status, stylesheet
ui.js the UI kit: cards, tables, charts, pagination
format.js shared formatters (esc, bytes, dates)
io.js small render/IO helpers
shared/ shared with the server (one wire contract)
manifest.js the Merkle tree + path-safety rules
directives.js the sitehoster: directive scanner
version.js the API/protocol version
Files
| File | What it does |
|---|---|
| Entry point | |
sitehoster-client.js | The single command you run. Declares the tool set and the sync hook, then hands off to the shared orchestrator. |
client/ — this hoster's modules | |
client/sync.js | Compares the content root to the server by Merkle descent and pushes only the differences. |
client/automate.js | Reads the on-page master/copy markers and rewrites every copy to match, before anything is pushed. |
client/index-store.js | A persisted per-file index that lets a run skip work on files that have not changed. |
client/browse/automation.js | Reads the site sources and renders the browsable Automation Logs map of every collection and shared block. |
common/ — the shared toolkit | |
common/net.js | Reads the config, skips cert verification for a bare-IP endpoint, and discovers the server's API version. |
common/client/run.js | The orchestrator core every hoster's client runs: arg parsing, config, the tool loop, and the tools-site frame. |
common/browse/web.js | Renders the static Web Logs browser from the mirrored Caddy access logs. |
common/browse/server.js | Renders the static Server Logs browser from the mirrored server statistics, drilling month → minute. |
common/browse/pull-logs.js | Mirrors the server's access logs down incrementally (rotated files once, the active log by Range). |
common/browse/pull-stats.js | Mirrors the server's statistics day-files down incrementally, the stats counterpart of pull-logs. |
common/browse/shell.js | The shared page frame: the top strip, tool tabs, per-tool status banner, and stylesheet. |
common/browse/ui.js | The shared UI kit a browser composes: stat cards, tables, chart/table toggles, and pagination. |
common/browse/format.js | The canonical formatters: escaping, numbers, bytes, durations, and month/day labels. |
common/browse/io.js | Small render/IO helpers, chiefly the footer-stripping used by the write-if-changed comparison. |
shared/ — the wire contract (shared with the server) | |
shared/manifest.js | The Merkle-tree builder and path-safety rules both sides use, so they can never disagree. |
shared/directives.js | The one scanner for sitehoster: directive comments, shared by automation and sync. |
shared/version.js | The API/protocol version that forms the /api/vN path prefix; a mismatch fails fast. |
sitehoster-client.js — entry point
The single thing you run, and the only file with no exports. It declares sitehoster's tools — the two
universal browsers (Web Logs, Server Logs) plus the project's own Automation Logs — and its content
(sync) hook, then hands off to the shared orchestrator in common/client/run.js. Automation
is not its own switch: --sync always runs the on-page automation first and then pushes, so
un-automated content is never published.
| Switch | What it does |
|---|---|
--config <path>, -c | Path to the client config JSON. Defaults to ./config.json. |
--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. |
client/sync.js
The sync client. 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. It also mirrors the access logs down (--pull-logs). Importable (the exports
below, used by the unified client) and still runnable on its own (node client/sync.js).
| Export | What it is |
|---|---|
once(opts) | Run a single sync cycle in-process, so the unified client need not spawn a second Node process. Takes { config, dryRun, verbose, pullLogs }. |
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. |
client/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 sync is never disturbed by a no-op pass.
Also runnable on its own (node client/automate.js --config <path>).
| Export | What it is |
|---|---|
once(opts) | Run one in-process automation pass over the content root, rewriting every copy from its master. Takes { config }; the unified client calls this before syncing. |
client/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 sync'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 | The dotfile name of the index, .sitehoster-index.json. |
VERSION | The index schema version; a mismatch forces a clean full rebuild. |
client/browse/automation.js
The Automation Logs browser generator, and 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 composes the shared shell, UI kit and formatters, so it feels like one tool with the log browsers.
| Export | What it is |
|---|---|
build(options) | Scan the site sources and render the automation browser. The client passes explicit { sitesParent, outDir }; run standalone it falls back to __dirname. |
common/net.js
The shared API-client plumbing every hoster's downloaders and sync sit on: read the config, skip cert verification for a bare-IP (self-signed) endpoint, 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 client config JSON into the shape the tools expect. |
isIpHttps(apiBase) | Whether the endpoint is HTTPS to a bare IP literal (or [ipv6]) — hence self-signed. |
disableTlsVerification() | Quiet the one-time Node warning and disable TLS verification process-wide (only for an IP endpoint). |
resolvePrefix(...) | Discover the server's API version and build the /api/vN path prefix the requests use. |
common/client/run.js
The hoster-tools orchestrator core — the shared engine every hoster's client runs. A project calls
run({ tools, sync, brand }); this owns arg parsing, config and TLS, the isolated phase loop,
and the whole tools-site frame (overview cards, per-tool pages, clash-safe "not run" status, persisted
last-run state). The two universal tools come ready-made from webTool()/serverTool(),
so a sibling hoster's entry is a short declaration.
| Export | What it is |
|---|---|
run(opts) | The entry a project calls with { tools, sync, brand }: parse args, run the sync hook and every enabled tool, and write the tools site. |
webTool() | The ready-made Web Logs tool descriptor (pull the access logs, then build the browser). |
serverTool() | The ready-made Server Logs tool descriptor (pull the stats, then build the browser). |
common/browse/web.js
The Web Logs browser generator. It reads the Caddy JSON access logs mirrored down by
pull-logs.js and renders a set of static, self-contained HTML pages so the traffic can be
browsed offline. Composes the shared shell, UI kit and formatters; also runnable standalone.
| Export | What it is |
|---|---|
build(options) | Render the Web Logs browser. The client passes { logsDir, outDir }; run standalone it reads the log dir beside the script. |
common/browse/server.js
The Server Logs browser generator — the reader half of the shared stats facility. It reads the per-type
dated day-files mirrored down by pull-stats.js and writes a drill-down site (all time → month
→ day → hour → minute), each page charting every metric with a chart/table toggle.
| Export | What it is |
|---|---|
build(options) | Render the Server Logs browser. The client passes { dataDir, outDir, serverIp }; run standalone it reads the data dir beside the script. |
common/browse/pull-logs.js
Mirror the server's Caddy access logs into a data dir — the log counterpart of pull-stats.js.
Rotated files are immutable (fetched once, whole); the active log grows, so only its new tail is appended
via a Range request; a newly-appeared rotated file means the active log restarted, so it resets and
re-fetches. TLS is expected to be already configured by the caller.
| Export | What it is |
|---|---|
pull(opts) | Mirror the access logs down incrementally. Takes { logsDir, apiBase, apiToken } and reports how many lines were mirrored. |
common/browse/pull-stats.js
Mirror the server's statistics day-files into a data dir — the stats counterpart of
pull-logs.js. The server samples CPU, memory, disk and IO into per-type dated files; a frozen
past day is fetched once and today's growing file has only its tail appended by Range. TLS is expected to
be already configured by the caller.
| Export | What it is |
|---|---|
pull(opts) | Mirror the stats day-files down incrementally. Takes { dataDir, apiBase, apiToken } and reports what was fetched and appended. |
common/browse/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 same strip
goes on every page; the tab set and brand are configured per project by run.js 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. |
toolStrip(...) | The fixed top strip a tool browser injects at the top of its own pages, so the tabs are identical everywhere. |
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 (called by run.js); TOOLS, brandName() expose them. |
esc, fmtNum, fmtBytes, stripFooter | The formatters and footer-stripper re-exported for convenience, plus STYLESHEET and TOOLSTRIP_CSS. |
common/browse/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. |
viewToggle(...) | A chart ⇄ table switch for a metric. |
SCRIPT, CSS | The shared client JS (table toggle + chart switch) and the common visual furniture. |
common/browse/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. |
common/browse/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. |