webby-deploy 0.3.0

Drop a static HTML app into a local, tailnet, temporary public, or durable public URL.
webby-deploy-0.3.0 is not a library.

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

cargo install webby-deploy

Start Fast

No config required:

webby add ./clock.html
webby serve

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 add <path> [--name N] [--tmp] [--title T] [--description D] [--property K=V] [-b BAG]
webby pub <path> [--name N] [--tmp] [--title T] [--description D] [--property K=V]
webby deploy -b BAG
webby preview [APP] -b BAG [--force]
webby serve [-b BAG] [--port N]
webby ls [-b BAG]
webby rm <name> [-b BAG]
webby open <name> [-b BAG]
webby domain <hostname> -b BAG
webby where
webby init

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.

<script type="application/webby+json">
{
  "title": "Network Audit",
  "description": "Internal network and DNS audit notes.",
  "properties": {
    "category": "Documents",
    "kind": "report"
  }
}
</script>

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:

webby add ./network-audit.html -b internal \
  --title "Network Audit" \
  --description "Internal network and DNS audit notes." \
  --property category=Documents \
  --property kind=report

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:

webby add ./dashboard -b tailnet
webby deploy -b tailnet

Temporary public Funnel:

webby add ./demo -b funnel
webby deploy -b funnel

Durable public Cloudflare Pages:

export CLOUDFLARE_ACCOUNT_ID=...
export CLOUDFLARE_API_TOKEN=...
webby pub ./lissajous --name lissajous

Configuration

Run:

webby init

This writes ~/.config/webby/config.json. Override the config path with WEBBY_CONFIG, and the default bag storage root with WEBBY_DATA_DIR.

Example:

{
  "defaultBag": "local",
  "bags": {
    "local": {
      "dir": "~/.local/share/webby/local",
      "host": { "type": "local", "port": 8765 }
    },
    "tailnet": {
      "dir": "~/.local/share/webby/tailnet",
      "host": { "type": "tailscale-serve", "path": "/", "background": true }
    },
    "funnel": {
      "dir": "~/.local/share/webby/funnel",
      "host": { "type": "tailscale-funnel", "path": "/", "background": true }
    },
    "public": {
      "dir": "~/.local/share/webby/public",
      "host": {
        "type": "cloudflare-pages",
        "project": "webby",
        "tokenEnv": "CLOUDFLARE_API_TOKEN"
      }
    },
    "homepage-tools": {
      "dir": "~/.local/share/webby/tools",
      "noIndex": true,
      "host": { "type": "caddy", "url": "https://example.test/webby/" }
    }
  }
}

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:

{
  "bags": {
    "public": {
      "dir": "~/.local/share/webby/public",
      "indexChromeDir": "~/site-chrome/dist",
      "host": { "type": "cloudflare-pages", "project": "webby" }
    }
  }
}

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:

wrangler pages deploy <dir> --project-name <project> --branch <branch> --commit-dirty=true

command providers run a shell command template. {dir}, {label}, and {url} are expanded before execution.