wirldocs
The wirl npm package is not published yet, so the npx wirl … commands on these pages will not run today. Until it ships, connect your agent to the hosted endpoint: the first command in the Quickstart.

The contract

A wirl app is a static folder, or a static folder plus one backend module. A static folder needs nothing else: point wirl deploy at it and it is served as it is.

A server-side app adds one JavaScript module exporting fetch(request, env), named by server in wirl.json. Every request the app receives, and every response it sends back, passes through this one function. The page is HTML the module returns; the API is routes handled in the same file.

Data: env.DB#

env.DB is a SQLite database that belongs to this one app, in D1's prepared-statement shape. It is created on the app's first deploy and stays with the app, not with any one deployment — a redeploy and a wirl rollback both see the same rows, because rolling back code never rolls back data.

Who is asking#

Three headers arrive on every request, set by wirl after it verified the visitor: x-wirl-user-id, x-wirl-role, and x-wirl-org. The browser cannot forge them. A rule that actually matters — "you only see your own notes" — has to be a line of SQL against these headers, because a check written into the page is only a suggestion to whoever is holding the browser:

export default {
  async fetch(request, env) {
    const userId = request.headers.get("x-wirl-user-id") || "";
    const { results } = await env.DB
      .prepare("select id, body from notes where user_id = ?")
      .bind(userId)
      .all();
    return new Response(JSON.stringify(results), {
      headers: { "content-type": "application/json" },
    });
  },
};

The same prepared-statement shape gives you .run() for an insert or an update, bound the same way.

Calling out#

A third-party API is fetch("https://api.hubspot.com/..."), once the connection is declared in wirl.json. wirl intercepts the call on its way out, attaches the key, and forwards it — the key never arrives in the app.

Running it locally#

wirl dev runs the same bundle a deploy would, under Node, with a stub identity in the headers so the first run is already signed in; pass --user and --role to see what someone else would see. It reads keys for outgoing calls from .wirl/keys.json, which git ignores, so what you see on your machine is what ships.

What wirl keeps#

Delete an app and its data — bundles, its server module, its database — stays for 30 days before it is removed. Inside that window, ask and it comes back.

A live app keeps the bundle and server module of its newest 10 versions. Older ones are pruned: the files are gone and a wirl rollback can no longer return to them. The version currently live, and the one a rollback would return to, are always kept regardless of age.

Audit rows are kept for a year.

Every app and version the job removes is recorded in the org's activity, so a pruned version or a reaped app is something you can find, not something that quietly stopped being there.