BEARER Docs
Capy pages, Bearer runtime

Make dynamic websites like it is 2006.

Bearer is a FastCGI web runtime for the Capy language. A file is a page. Markup lives next to the code that renders it. nginx serves the static files. Bearer compiles each unit to WebAssembly on its first request and keeps the module for the requests after it.

Files are routes The Capy file you edit is the path nginx serves.
Compile on demand The first request compiles a unit to WebAssembly. Later requests reuse the module.
WebSockets included A page file can also hold the socket handler for its own path.
Why BEARER

The old web had the right instincts.

Put the code where the page lives. Let state end with the request. Keep deployment boring.

templating

Inline markup

An HTML literal is a string. Print it, return it, or pass it to a caller. Each interpolation escapes for the context it lands in.

composition

Components and units

A component is a handler in a Capy unit. Props, request keys, and call results stay shared inside one request. Bearer copies a value only when it leaves the process.

transport

FastCGI and sockets

nginx forwards page requests over FastCGI. A WebSocket upgrade on the same path reaches the built-in listener, which calls the WS handler of that file.

operations

nginx and systemd

One service unit runs the runtime. One settings file configures sockets, caches, and limits. Debian and RPM packages install the runtime only. The application stays in its own webroot.

Examples

Small programs first.

Each sample is a complete file. Save it with the .capy suffix in the webroot and request its path.

A page

RENDER answers an HTTP request. The query string is already parsed.

function RENDER() {
    var name := string(request.query.name, fallback: "guest")
    print(<><h1>Hello, <?= name ?>.</h1></>)
}

GET /hello.capy?name=Ada prints an h1 with the escaped name.

A component

A named component handler renders into the current response.

function COMPONENT:CARD(props : any) {
    print(<><article><h3><?= props.title ?></h3></article></>)
}

function RENDER() {
    component_render(":CARD", title: "News")
}

The caller passes props by name. The component receives them as its props parameter, and request is in scope without being declared. A target without a file part names a component in the same unit.

A WebSocket page

The same file serves the page and handles messages on its path.

function RENDER() {
    print(<><p>Open a WebSocket to this path.</p></>)
}

function WS() {
    ws_send("echo: " + string(request.body), false)
}

nginx sends the upgrade to the Bearer listener. Every message runs WS in a fresh workspace.

An exported function

#exports makes a function callable from other units and modules.

#exports greeting

function greeting(input : any) any {
    -> {text: "Hi " + string(input.name)}
}

function RENDER() {
    print(string(greeting({name: "Ada"}).text))
}

unit_call and module.call reach the same export from another unit.

Downloads

Install the tools, then open a file.

The VS Code extension includes highlighting, the language client, and native compilers for Linux x86-64 and Apple Silicon macOS. Install the VSIX, then open a trusted .capy workspace. For Remote SSH, connect first and install it in the SSH extension host. No npm setup, compiler path, or Bearer service is required.

Tooling

One compiler, four jobs.

capyc compiles units for Bearer. The same binary checks files, formats them, and serves editors. A compile error in development renders in the browser with the file, line, and column.

Check without a build

The checker parses and type-checks a file, a list of files, or a directory tree.

$ capyc --check site/hello.capy
site/hello.capy:4:11: unknown local 'cuont'
$ echo $?
1

The deploy receiver runs the same check over the application before it installs a new runtime.

Format

The formatter rewrites a file to the canonical layout. With --check it only reports files that would change.

$ capyc --format --check site/hello.capy
site/hello.capy
$ capyc --format site/hello.capy
$ cat site/hello.capy
function RENDER(request : any) {
	var name := string(request.query.name, fallback: "guest")
	print(<><h1>Hello, <?= name ?>.</h1></>)
}

Formatting never changes the compiled module. The test suite proves this for every Capy file in the site.

Editors

capyc --lsp speaks the Language Server Protocol over stdio: diagnostics, hover, completion, signatures, definitions, and semantic tokens.

vim.lsp.config.capy = {
  cmd = { "capyc", "--lsp" },
  filetypes = { "capy" },
  root_markers = { ".git" },
}
vim.lsp.enable("capy")

Download the ready VSIX above, or use capyc from the offline tools bundle. The editor README covers Neovim, Helix, Zed, and Emacs.

Run a unit from the shell

A CLI handler runs through the local Bearer socket. The web server never reaches it.

$ cat site/tasks.capy
function CLI(request : any) {
    print("task: ", string(request.query.task), "\n")
}
$ bearer-cli --get /tasks.capy task=rebuild
task: rebuild

Use it for migrations, imports, and cron jobs that share code with the site.

Deploy

Keep the edge boring.

nginx serves the static files and forwards Capy units to Bearer. A deployment needs one server block, one settings file, and one service unit. The install guide has the full procedure.

nginx

  • location / serves static files with try_files.
  • location ~ \.capy$ forwards to unix:/run/bearer/fastcgi.sock.
  • A request with Upgrade: websocket on a .capy path goes to the built-in listener instead.
  • root points at the application webroot, never at the runtime tree.

Runtime

  • bearer.service runs the runtime as www-data and restarts it on failure.
  • /etc/bearer/settings.cfg sets the sockets, cache paths, memory limit, and request timeout.
  • The first request compiles a unit. Proactive compilation rebuilds changed units in the background.
  • Debian and RPM packages install the runtime under /usr/lib/bearer and create no webroot.
Explore

Read the guide, then the reference.