K Kivu CGI, reconsidered

Kivu documentation

Server-rendered by default. Enhanced where it matters.

Kivu is designed for traditional CGI hosting: every request boots, routes, renders and exits. The architecture keeps that lifecycle inexpensive while preserving the ergonomics of a modern web app.

01 · Philosophy

Built with the constraints, not around them

Kivu does not emulate a persistent application server. It treats a short-lived process as the platform contract and moves avoidable work into local build commands.

Small cold starts

Services load lazily. Config, routes and templates can be cached before deployment.

Explicit flow

Routes point to controller actions; context owns request-scoped state and rendering.

Secure defaults

Escaped templates, CSRF middleware, signed sessions and request-size limits are built in.

Shared-host portable

The deployable app is files and Perl modules — no daemon or container is required.

02 · Lifecycle

One request, one clean exit

  1. 01
    Apache invokes public/index.cgi

    The front controller captures CGI input and starts the application.

  2. 02
    Kivu builds the request context

    Configuration, request limits, sessions and application services are wired.

  3. 03
    The router invokes a controller

    A named route resolves to a focused controller#action.

  4. 04
    The response is finalized

    Cookies and headers are emitted, the body is written, and the process exits.

03 · Frontend

Tailwind CSS and HTMX, progressively applied

Text::Xslate renders complete, accessible HTML. Tailwind supplies responsive utility styling; HTMX boosts navigation and swaps focused fragments. Forms and links still work without JavaScript.

templates/example.tx
<a class="rounded-xl bg-gold px-4 py-2 text-navy"
   href="/docs">Read docs</a>

<form method="post"
      hx-post="/login"
      hx-target="this"
      hx-swap="outerHTML">
  ...
</form>

The browser Tailwind build keeps this starter free of a Node build requirement. Production applications can replace it with compiled Tailwind CSS without changing template classes.

04 · Start here

Prepare a local application

terminal
cpanm --installdeps .
perl script/kivu migrate
perl script/kivu seed
perl script/smoke_test.pl

05 · CLI reference

The script/kivu commands

config:cache

Write the merged configuration cache when it does not already exist.

routes:cache

Compile route declarations for fast production loading.

views:cache

Precompile Text::Xslate templates.

cache:clear

Remove config, route and compiled-view caches.

clear:all

Remove every generated cache and log file.

migrate

Apply pending migrations from database/migrations/.

migrate:status

Show applied and pending database migrations.

rollback [n]

Revert the latest migration, or the latest n migrations.

seed [user] [pass] [email]

Create the starter user; defaults to demo / demo1234 / demo@example.test.

user:superadmin <user> <email> <pass>

Create a superadmin account, or promote an existing username and reset its email/password.

session:cleanup

Delete expired records for the file-session driver.

secret

Generate a session secret and rotate the current value into session.previous_secret.

deploy <path>

Replace a local target with the deployable project tree, then apply destination .deploy adaptations.

06 · Local workflow

Deploy to a local virtual host

The deploy command removes the destination contents before copying, so stale files cannot survive from an earlier build. It excludes .git, cpanfile and docs/, and preserves a destination .deploy JSON file so host-specific adaptations survive every redeploy.

terminal
perl script/kivu deploy ~/Sites/kivu
~/Sites/kivu/.deploy
{
  "shebang": "#!/usr/bin/perl",
  "chmod": {
    "script/kivu": "0755",
    "public/index.cgi": "0755"
  },
  "files": {
    "public/index.cgi": {
      "shebang": "#!/usr/local/bin/perl"
    }
  },
  "substitutions": [
    {
      "path": "kivu.json",
      "find": "\"name\" : \"MyApp\"",
      "replace": "\"name\" : \"LocalKivu\""
    }
  ]
}
The target is replaced, not merged. Kivu refuses dangerous targets such as /, your home directory, or the source tree. Top-level shebang rewrites public/index.cgi; per-file files.*.shebang and literal/regex substitutions cover other host tweaks. Deploy always sets script/kivu and public/index.cgi to mode 0755 (overridable via chmod or files.*.chmod).