Operator

Serve Static Files

Static files are served directly.

For example, you can drop a photo into your content directory and it'll be accessible:

The photo.

Render Handlebars Templates

Handlebars templates are rendered at request time.

Templates can embed content from other files—even static files and executables—via {{get "/file"}}. For example, this content comes from a template, and here's a random number generated by an executable: 85

Templates are provided render data with information about the content directory, the request, the server, etc. For example, here's the data that was provided to this template:

  • /
    • _current-operator-release/
      • hash-linux-x86-64: "/_current-operator-release/hash-linux-x86-64"
      • hash-macos-x86-64: "/_current-operator-release/hash-macos-x86-64"
      • version: "/_current-operator-release/version"
    • _features/
      • 1-serve-static-files: "/_features/1-serve-static-files"
      • 2-handlebars-templates: "/_features/2-handlebars-templates"
      • 3-run-executables: "/_features/3-run-executables"
      • 4-content-negotiation: "/_features/4-content-negotiation"
      • 5-streaming: "/_features/5-streaming"
      • 6-hidden-files: "/_features/6-hidden-files"
      • 7-error-handlers: "/_features/7-error-handlers"
    • _footer: "/_footer"
    • _header: "/_header"
    • _install-commands: "/_install-commands"
    • _page: "/_page"
    • error-handler: "/error-handler"
    • examples/
      • negotiation/
        • image: "/examples/negotiation/image"
      • now: "/examples/now"
      • operator: "/examples/operator"
      • polyglot/
        • bar: "/examples/polyglot/bar"
        • baz: "/examples/polyglot/baz"
        • foo: "/examples/polyglot/foo"
      • random: "/examples/random"
      • render-data: "/examples/render-data"
      • streaming: "/examples/streaming"
      • wat: "/examples/wat"
    • favicon: "/favicon"
    • features: "/features"
    • home: "/home"
    • run-this-site: "/run-this-site"
    • stylesheets/
      • decoration: "/stylesheets/decoration"
      • layout: "/stylesheets/layout"
      • normalize: "/stylesheets/normalize"
    • usage: "/usage"
  • error-code: null
  • request
    • query-parameters
      • request-headers
        • accept: "*/*"
        • accept-encoding: "gzip, br, zstd, deflate"
        • connection: "close"
        • host: "operator.mattkantor.com"
        • user-agent: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)"
        • x-forwarded-for: "216.73.216.156"
        • x-forwarded-proto: "https"
        • x-real-ip: "216.73.216.156"
      • route: "/features"
    • server-info
      • operator-path: "/opt/operator"
      • socket-address: "0.0.0.0:80"
      • version: "0.6.5"
    • target-media-type: "text/html"

    Run Executables

    Executables in the content directory are run at request time, with standard output piped out as the response body.

    Any program that you can run from the command line will work (scripts, compiled binaries, etc). For example, this timestamp is the output of a shell script:

    now.html.sh: 2025-07-19T01:50:56Z

    Since executables are run as normal programs by your operating system, you can freely mix and match whatever languages you like:

    foo.html.js: hello from node, my pid is 1119
    
    bar.html.py: hi from python 3.11.2 running on linux
    
    baz.html.awk: happy Saturday the 19th from awk (in UTC)

    Executables are provided the same render data as templates, encoded as JSON in an environment variable named OPERATOR_RENDER_DATA.

    Content Negotiation

    Content negotiation is performed based on the Accept header.

    Multiple representations of the same content can be provided by creating files whose names differ only by extension. For example, if you have image.png and image.webp, the server will be able to satisfy GET /image as either image/png or image/webp. "Negotiation" means that Operator uses the representation which best fits the client's preferences. For example, your browser prefers Image in PNG or WebP format, depending on your browser..

    Request URLs with file extensions override the Accept header. For example, you can GET /image.png or GET /image.webp to retrieve a specific type no matter which your browser prefers.

    Streaming

    Operator sends chunks of the response body as soon as possible. It doesn't wait for the entire response to be generated.

    For example, with a script that gradually emits its response body the client can begin processing the response while the script is still running. In this case the <style> elements are applied one at a time, creating a colorful effect.

    Hidden Files

    File/directory names which start with _ are hidden from routing, but are still accessible by other files in the content directory. This can be useful for partials, scripts, or other things which don't make sense out of context.

    For example, this site has a file named _header.html.hbs in the root of its content directory, but requests for GET /_header will receive 404 responses instead of the HTML for the page header.

    Additionally, file/directory names which start with . are always completely ignored by Operator. You can safely use them for whatever you want.

    Error Handlers

    You can specify a custom error handler for Operator.

    For example, this site uses a custom error handler which presents a friendly message. You can see it in action by visiting a bogus URL.

    Error handlers are configured via the --error-handler-route command-line argument. They receive an error-code variable in their input.