webby
Drop a static HTML app into a bag and get a URL.
webby is for tiny tools, one-off dashboards, agent-made prototypes, and standalone HTML files. It works with no config on localhost, and can also activate tailnet, temporary public, durable public, or custom hosting.
Install
Start Fast
No config required:
That stages clock.html in the local bag and serves the bag at
http://localhost:8765.
Commands
A bag is a named directory plus a hosting provider. Apps are copied into a bag, then the provider decides how that directory gets a URL.
webby ls lists all bags by default. Use -b / --bag to select one bag.
An app is a folder with index.html or a standalone .html file. Names that
start with tmp are shown under the Temp section in generated indexes.
webby preview -b BAG captures static JPEG card previews into the bag's
webby-previews/ directory. It shells out to uvx shot-scraper, skips
existing previews unless --force is passed, and keeps generated indexes fast
by serving images instead of live iframes. Pass an app name, for example
webby preview jobsearch-docs -b internal --force, to refresh one preview.
Every deploy writes webby-cards.json next to the generated assets. That JSON
contains the same card data used by Webby's index component, so another page can
embed a Webby bag without scraping a directory listing.
App Metadata
Apps can carry their own card metadata. For a standalone app, put it in the
.html file. For a folder app, put it in index.html.
{
"title": "Network Audit",
"description": "Internal network and DNS audit notes.",
"properties": {
"category": "Documents",
"kind": "report"
}
}
title and description customize the generated card. If they are omitted,
Webby falls back to the page's <title> and <meta name="description"> when
available.
properties is an app-defined key/value object. Webby copies it into
webby-cards.json without assigning meaning to the keys. Host pages can use
those properties however they want, for example grouping by
properties.category.
For compatibility with older card consumers, a string properties.category
also appears as the generated card's top-level category field.
You can also set metadata while staging an app. Webby writes the metadata into the staged HTML app and then regenerates card data from that self-contained artifact:
Provider Examples
Built-in bags:
| bag | provider | reach |
|---|---|---|
local |
local |
localhost preview |
tailnet |
tailscale-serve |
private Tailscale HTTPS |
funnel |
tailscale-funnel |
temporary public HTTPS |
public |
cloudflare-pages |
durable public HTTPS |
If INTERNAL_URL or INTERNAL_DIR is set, webby also adds an internal Caddy
compatibility bag.
Tailnet:
Temporary public Funnel:
Durable public Cloudflare Pages:
Configuration
Run:
This writes ~/.config/webby/config.json. Override the config path with
WEBBY_CONFIG, and the default bag storage root with WEBBY_DATA_DIR.
Example:
webby can also load a KEY=VALUE env file from WEBBY_ENV; when running from a
checkout, a local .env.secret file is loaded if present.
Provider Notes
local generates an index and can be served with webby serve.
Webby always writes webby-cards.json and webby-card-grid.js. By default it
also writes a standalone index.html for the bag root.
Set noIndex: true on a bag, or pass --no-index to add, pub, deploy,
or serve, when a larger site consumes the card data and the bag should not
publish its own root index page.
Set indexChromeDir on a bag to inline optional head.html and body.html
fragments into the generated root index. This is intended for host-site chrome
such as a shared header while keeping webby itself generic:
Use WEBBY_INDEX_CHROME_DIR to apply the same chrome directory to every bag.
Caddy-hosted bags do not get a special listing mode. They can either expose the
normal generated Webby index, or set noIndex when another homepage owns the
root experience.
tailscale-serve and tailscale-funnel call the tailscale CLI with the bag
directory as the target.
cloudflare-pages calls:
command providers run a shell command template. {dir}, {label}, and
{url} are expanded before execution.