Menu

Site layout

A release separates visitor-facing files from private server code. This page lists the directories, what is reachable from the web, the reserved names, and the import rules.

Layout

site/
  public/                  static files, served as is
    index.html
    styles.css
    404.html               optional not-found page (or set errorPages in the manifest)
    errors/                optional static error pages named in errorPages
    assets/logo.svg
  api/                     handlers; /api/<name> runs api/<name>.js
    contact.js
    _admin/                private extension handlers (never public)
  jobs/                    scheduled and background job scripts (never public; see jobs.md)
  lib/                     shared modules imported by handlers
    html.js
  private/
    migrations/            SQL migrations, 0001_name.sql ...
  microhost.json           optional manifest   (or microhost.config.js)

Only public/ is a web root. A visitor requests /styles.css, not /public/styles.css. Everything outside public/ is the private namespace: not served, importable by handlers. The folder names api, lib and private are conventions (only api/ has routing meaning through /api/<name>, and only private/migrations/ has meaning for migrations); you can place handler scripts anywhere outside public/ and route to them from the manifest.

What is reachable

Reserved names

The server owns resolved-manifest.json, resolved-manifest.meta.json and db-schema.json at the release root. An upload that contains any of them is rejected. microhost.config.js and microhost.json are manifest sources, not served.

Imports

Handlers are ES modules and use static imports:

import { escapeHtml } from "../lib/html.js";
import { tasks } from "microhost/db";

Module-level code runs on every request (each execution has a fresh runtime). Do not keep persistent state in module variables.

Browser code versus server code

public/app.js runs in the visitor's browser with the DOM and the browser's own fetch. Server handlers do not have those. A handler cannot be imported by browser code, and browser code cannot read anything outside public/.

Packaging

For a ZIP upload, the archive root must contain public/, api/, microhost.json or microhost.config.js directly, not inside a wrapping directory. The ingest validates entry names, rejects symlinks, special files, encrypted entries, duplicates and case or Unicode-normalization collisions. Default ingest limits: 200 MiB extracted, 10,000 files, depth 16, compression ratio 100, 60 seconds; the admin API accepts a ZIP upload of 32 MiB by default. See Deploying.