Amarra-cais

Full-stack Go framework for mini apps (Lightsail-friendly): Amarra Views + Drive, Tailwind, and SQLite — with a Rails-style CLI.
This repository is the framework + CLI only. Generate apps with amarra-cais new. The CLI binary is amarra-cais (it does not overwrite cais). Cais v0.11.x remains the Inertia + Svelte product.
Stack
| Layer |
Choice |
| Language |
Go 1.26 (net/http stdlib; see go.mod) |
| Frontend |
Amarra Views + Drive (pkg/amarra/view + /static/js/amarra.js) |
| CSS |
Tailwind CSS 3.x |
| DB |
SQLite (modernc.org/sqlite, no CGO) |
| PWA |
Manifest, service worker, offline page, icons, fullscreen |
| Meta |
Open Graph / Twitter via pkg/cais/meta |
| Core |
Router, session, CSRF, jobs, SQLite in pkg/cais/ |
The browser does not mount a SPA. Handlers call view.Write. Drive morphs #amarra-main. There is no Vite, Svelte, or Inertia in generated apps. Migrating a Cais Inertia app is a UI rewrite — see docs/migrate-inertia.md.
Quick start
export PATH="$HOME/go/bin:$PATH"
go install github.com/puppe1990/amarra-cais/cmd/amarra-cais@v0.1.0 # or: make install-cli from this repo
amarra-cais version # expect 0.1.0
amarra-cais new myapp
cd myapp && amarra-cais install && amarra-cais dev # http://localhost:8080
Developing the framework itself:
export PATH="$HOME/go/bin:$PATH"
make install-cli
make test # go test ./... -race
make js-test # pkg/cais/js + pkg/amarra/js
make ci # test + js-test + lint + format-check
# Local CLI against this checkout:
amarra-cais link . # from an app dir, or set CAIS_REPLACE; unlink before push
Demo login in a fresh scaffold (dev seed): demo@example.com / password.
CLI (Rails-style)
make install-cli
export PATH="$HOME/go/bin:$PATH"
| Command |
Description |
amarra-cais new <app> [dir] [--minimal|--blank] [--module path] |
Scaffold app (HTML + Amarra Drive by default) |
amarra-cais g [--dry-run] handler|page|resource|model|migration|auth|console|ci|job|stream|live|component |
Generators |
amarra-cais destroy [--dry-run] resource|handler|model|auth|migration|component |
Undo generators |
amarra-cais install |
npm install + go mod tidy (+ Tailwind build) |
amarra-cais dev |
air + Tailwind watch (HTML templates reload with air) |
amarra-cais css / amarra-cais build / amarra-cais server / amarra-cais test |
CSS, binary, run, tests |
amarra-cais console |
REPL (store, cfg, db + SQL) |
amarra-cais routes [--verbose] |
List routes from internal/app/routes.go |
amarra-cais db migrate|status|rollback|prune-sessions|seed |
Migrations & seeds |
amarra-cais jobs work|status|retry|discard|prune |
SQLite background jobs + /jobs dashboard |
amarra-cais doctor [--mobile] |
Verify amarra.js, #amarra-main, PWA, mobile |
amarra-cais pwa [--bump] |
Write/refresh PWA assets; --bump cache |
amarra-cais link [path] [--unlink] |
Local go.mod replace for framework dev |
amarra-cais version |
Framework version |
Field types for generators: string, text, url, bool, int, date, references (or name:belongs_to). Suffix ? for optional.
amarra-cais g resource bookmark --fields title:string,url:url,notes:text? --public --paginate
amarra-cais g handler settings # Go handler + test + web/templates/pages/settings.html
amarra-cais g component card # web/templates/components/card.html
Development experience (in generated apps)
- Port auto-pick if
:8080 is busy
- Boot banner with LAN URLs for phone testing on Wi‑Fi
- Logs — JSON request (
kind: request) + SQL (kind: sql); LOG_FORMAT=text for plain text
/logs — localhost-only log viewer in development
/jobs — localhost-only queue dashboard (counts, failed retry/discard, recurring)
- Frontend — server-rendered HTML; Drive morphs
#amarra-main (no Vite)
- PWA — SW is network-first for
/static/js/amarra.js and /static/css/; amarra-cais pwa --bump after HTML changes on phones
Structure
pkg/cais/ framework packages (router, httpx, session, jobs, pwa, …)
pkg/amarra/ Views, Drive, Frame, Stream, Live hub, amarra.js sources
internal/cli/ amarra-cais CLI + scaffold templates (split by domain)
cmd/amarra-cais/ CLI entry point
cmd/pwagen/ helper to write PWA assets into a directory
scripts/ smoke-scaffold + smoke-production (via amarra-cais new)
Scaffolded apps get cmd/server, internal/app, internal/handlers, web/templates, web/static/js/amarra.js — not this repo.
Amarra Views + Drive (generated apps)
Handlers render HTML via view.Write:
view.Write(w, r, h.views, view.Page{
Layout: "app",
Name: "login",
Data: map[string]any{
"Title": "Login",
"Site": meta.ForRequest(h.site, r),
"CSRFToken": csrf.TokenFromRequest(r),
},
}, h.cfg)
// Validation: same page, status 422, `.Errors` on inputs
// Flash on redirect: flash.Set(w, kind, msg, secure) + http.Redirect(..., 303)
Pages live in web/templates/pages/*.html and define a content block. Kit tags expand at boot:
{{ define "content" }}
<h1>{{ .Title }}</h1>
<.form action="/contact" method="post">
{{ csrfField .CSRFToken }}
<.input name="email" type="email" label="Email" value="{{ .Email }}" error="{{ fieldError .Errors "email" }}" />
<.button type="submit">Send</.button>
</.form>
{{ end }}
Drive — clicks/submits with data-amarra-drive="true" (or {{ linkTo }} / <.form>) become fetch with Amarra-Drive: true + CSRF, then morph #amarra-main. First load, curl, and crawlers get the full layout.
JSON bodies — if a client posts JSON, handlers should use:
if err := httpx.ParseFormOrJSON(r); err != nil { /* ... */ }
email := r.FormValue("email")
Framework APIs (highlights)
Router
r.Get("/blog/{slug}", cais.StringParam("slug", blog.Show))
r.Group(middleware.RequireAuth("/login"), func(g *cais.Router) {
g.Get("/dashboard", dashboard.ServeHTTP)
})
httpx — RenderOrError, WritePage, SeeOther, ParseFormOrJSON, FormTruthy, ETag helpers.
Sessions — cookie auth (7-day TTL), session.SignIn / SignOut, amarra-cais db prune-sessions.
CSRF — double-submit cookie cais_csrf + form field / X-CSRF-Token.
Jobs — SQLite queue, no Redis:
amarra-cais g job send_welcome --cron "0 3 * * *"
amarra-cais jobs work --concurrency 2
Framework commands
make test # go test ./... -race
make test-v # verbose
make js-test # pkg/cais/js + pkg/amarra/js unit tests
make lint # golangci-lint
make format # prettier --write
make ci # test + js-test + lint + format-check
make build # bin/amarra-cais
make install-cli # go install ./cmd/amarra-cais
CI runs Go tests, JS unit tests, lint, Prettier, and smoke (amarra-cais new + production boot of a scaffolded app).
Production deploy (generated apps)
amarra-cais css # Tailwind → web/static/css/styles.css
amarra-cais build --os linux --arch amd64 -o bin/server-linux
tar czf release.tar.gz bin/server-linux web/static
Ship web/static (CSS, js/amarra.js, PWA) beside the binary. There is no Vite web/static/build/ step.
- Guide:
docs/deploy/lightsail-systemd.md
- Template:
deploy/systemd/cais-app.service.example
License
See LICENSE.