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
The sitehoster-local package: the entry, the shared toolkit, the universal and sitehoster-specific tools, and the sync command with its automation compiler.

Files

FileWhat it does
the entry
sitehoster-local.jsThe 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.jsonThe 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.jsThe single require surface the entry and every tool use: loadConfig, the toolsSite frame builder, recordRun, and the UI kit.
localhoster-common/net.jsReads 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.jsThe tools-site frame: toolsSite(config, brand) builds the overview + tabs + clash-safe status; recordRun persists each tool's last run.
localhoster-common/shell.jsThe shared page frame: the top strip, tool tabs, per-tool status banner, and stylesheet.
localhoster-common/ui.jsThe shared UI kit a browser composes: stat cards, tables, chart/table toggles, and pagination.
localhoster-common/format.jsThe canonical formatters: escaping, numbers, bytes, durations, and month/day labels.
localhoster-common/io.jsSmall 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.jsThe 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.jsThe on-page automation compiler: rewrites every copy from its master (duplication, navigation, records, listings, counts). Detailed on Automation.
sitehoster-sync/lib/sync.jsThe 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.jsA persisted per-file index that lets a run skip work on files that have not changed.
sitehoster-sync/lib/directives.jsThe one scanner for sitehoster: directive comments, shared by the automation compiler and the Automation Logs browser.
sitehoster-sync/lib/manifest.jsThe 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.jsThe 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.

SwitchWhat it does
--config <path>The full absolute path to the config JSON. Required; validated before anything runs.
--syncAutomate every site from its masters, then push the changes to the server.
--web-logsMirror the access logs down and build the Web Logs browser.
--server-logsMirror the server stats down and build the Server Logs browser.
--automation-logsBuild the Automation Logs browser from the site sources.
--allEverything 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.

ExportWhat 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.

ExportWhat 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.

ExportWhat 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, stripFooterThe 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.

ExportWhat 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, CSSThe 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.

ExportWhat 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_NAMESMonth/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.

ExportWhat 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_TOKENThe 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.

ExportWhat it is
index.jsrun(config)Pull the access logs into <contentRoot>/_logs, build the browser under <toolsRoot>/web, and record the run.
lib/pull.jspull(opts)Mirror the access logs down incrementally (rotated files once, the active log by Range).
lib/render.jsbuild(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.

ExportWhat it is
index.jsrun(config)Pull the stats into <contentRoot>/_server/_data, build the browser under <toolsRoot>/server, and record the run.
lib/pull.jspull(opts)Mirror the statistics day-files down incrementally, the stats counterpart of the web-logs pull.
lib/render.jsbuild(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.

ExportWhat it is
index.jsrun(config)Scan the site sources under contentRoot, build the browser under <toolsRoot>/automation, and record the run.
lib/render.jsbuild(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.

ExportWhat 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>).

ExportWhat 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).

ExportWhat 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.

ExportWhat 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, VERSIONThe 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.

ExportWhat 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_REThe 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.

ExportWhat 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.

ExportWhat it is
API_VERSIONThe current wire-contract version number (an integer).