chord Full-stack Lua apps, deployed in seconds

Chord is a Lua-based full-stack platform. Write a few lines of code, push with one command, and end up with a full app on a subdomain. Auth, DB, AI, email and templates included!

☝️🤓 This site and the dashboard are both Chord apps.

Install

$ curl -sSL chord.run/install | sh

Works on macOS and Linux! Run chord login after install to pair the CLI with your account.

If you just prefer to host it yourself, take a look at the repo.

Hello world

Three files, ~20 lines. The whole app:

## entities/post.lua
entity("Post", {
  title = text,
  body  = text,
})
## routes/posts.lua
route("GET", "/posts", function(req)
  respond(200, Post.all())
end)

route("POST", "/posts", function(req)
  Post.create(req.body)
  respond(201)
end, { auth = true })
## frontend/index.html
<ul>
  <% for _, p in ipairs(Post.all()) do %>
    <li><%= p.title %></li>
  <% end %>
</ul>
$ chord dev    # run it locally
$ chord deploy # ship to chord.run
  → live at https://my-app.chord.run

The runtime

Db
Relational database with no migrations. Declare entities and their relations.
local post = Post.create({
  title = "Hello",
  body  = "World",
})
Auth
Authentication and role-based authorization, enabled when entity("User") is defined.
local user, err = Auth.login(email, password)
Ai
LLM completions and chat.
local answer = Ai.complete("Summarize: " .. text)
Templates
ERB-style HTML in .htmlua files: <%= %>, auto-escape, partials via render(...). Pairs cleanly with htmx if you don't use a JS framework.
<%# frontend/post.htmlua %>
<article>
  <h1><%= post.title %></h1>
  <%- render("partials/byline", { user = post.author }) %>
</article>
Email
Transactional sends, same template engine as above.
Mail.send_template("welcome.htmlua", {
  to  = user.email,
  ctx = { name = user.display_name },
})
<%# emails/welcome.htmlua %>
<% mail.subject = "Welcome, " .. ctx.name .. "!" %>
<% mail.from    = "noreply@chord.run" %>
<p>Hi <%= ctx.name %>,</p>
<p>Thanks for signing up.</p>
Hx
Helpers for HTMX response headers (trigger events, redirect, push URL) from your route handlers.
Hx.trigger("post-created", { id = p.id })
Hx.push_url("/posts/" .. p.id)
Kv
Generic key-value store.
Kv.set("user:" .. user.id .. ":theme", "dark")
Http
Outbound HTTPS calls.
local res = Http.get("https://api.example.com/posts")
Env
Set via CLI or dashboard.
local key = Env.get("STRIPE_KEY")

Frontend

Pick your approach: server-render with the built-in template engine, or ship a JS framework (React, Vue, Svelte) as a SPA. Both first-class.

For server-rendered pages, use .htmlua: ERB-style HTML with <%= %>, auto-escape, partials. Each template doubles as a fragment via render(name, ctx), which pairs cleanly with htmx.

Anything else (.html, .css, .js, bundled output from Vite or similar) is served raw. For an SPA with a client-side router, flip a flag and unknown paths fall through to index.html:

# chord.toml
[frontend]
spa = true

When you use templates, anything you interpolate with <%= %> is escaped before it reaches the page. The engine looks at where the interpolation lands and uses the right escape function for that context: if the position is inherently unsafe, the template fails to compile and the server won't start.

Open source

The runtime is AGPL v3. Clone the repo, run cargo build --release, and drop the binary on your VPS with Caddy or nginx in front: your code doesn't know whether it's on chord.run or your own server.

The multi-tenant platform that runs chord.run (router, dashboard, billing) is closed source. That's where we make money. But your Lua sits on the open side, so when our prices or our policies stop working for you, you take it and walk.

Pricing

Pricing details available soon. We're still setting up the infrastructure and finalizing the launch plan.

See also

Reference and quickstart will land here as we publish the OSS runtime and finish setting up the infrastructure.