This is Koala, the official Web client for Kowabunga: a React single-page application generated from the Kowabunga OpenAPI specification.
| Project | Release Badge |
|---|---|
| Kowabunga | |
| Kowabunga OpenAPI | |
| Kowabunga Koala |
koala application must be served by a proper Web server. It can be installed by either:
- extracting release content in a directory of your choice (e.g. /var/www/kowabunga)
- using dpkg or apt command on Debian/Ubuntu, e.g.:
$ apt-get install kowabunga-koala- running its container image (amd64 and arm64), see Container Image:
$ docker run -p 8080:8080 ghcr.io/kowabunga-cloud/koala:latestCheck out the list of released versions.
Koala can be exposed through any Web server.
Here's a few to do so through Nginx, provided Kowabunga Kahuna engine is running on 127.0.0.1:8080 and Koala content being hosted in /var/www/kowabunga directory.
server {
server_name kowabunga.acme.com;
server_tokens off;
listen 443 ssl http2;
ssl_certificate /etc/nginx/ssl/kowabunga.acme.com/cert.pem;
ssl_certificate_key /etc/nginx/ssl/kowabunga.acme.com/key.pem;
ssl_session_cache shared:le_nginx_SSL:10m;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;
# HSTS
add_header Strict-Transport-Security "max-age=63072000" always;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
# Timeouts
keepalive_timeout 1h;
send_timeout 1h;
client_body_timeout 1h;
client_header_timeout 1h;
etag on;
# Error Pages
error_page 404 /404.html;
location = /404.html {
root /var/www/html;
internal;
}
# Koala (single-page app: unknown paths fall back to index.html,
# revalidated on each load so browsers pick up new releases)
root /var/www/kowabunga;
location / {
add_header Cache-Control "no-cache";
try_files $uri /index.html;
}
# Koala build assets have content-hashed names and never change
location /assets/ {
add_header Cache-Control "public, max-age=31536000, immutable";
try_files $uri =404;
}
# Kowabunga Orchestrator
location ~ (/api|/confirm|/confirmForgotPassword|/latest) {
proxy_pass http://127.0.0.1:8080$request_uri;
proxy_set_header Host $host;
proxy_connect_timeout 1h;
proxy_read_timeout 1h;
proxy_send_timeout 1h;
}
location ~ (/ws) {
proxy_pass http://127.0.0.1:8080$request_uri;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
}alternatively you can do the same in a much more elegant way with Caddy:
kowabunga.acme.com {
encode
@api path /api/* /confirm /confirmForgotPassword /latest/meta-data
@ws {
path /ws
header Connection *Upgrade*
header Upgrade websocket
}
# Kahuna Orchestrator
handle @api {
reverse_proxy 127.0.0.1:8080
}
handle @ws {
reverse_proxy @ws 127.0.0.1:8080
}
# Koala WebUI: build assets have content-hashed names and never change (missing ones are 404s)
handle /assets/* {
root * /var/www/kowabunga
@exists file
header @exists Cache-Control "public, max-age=31536000, immutable"
file_server
}
# Koala WebUI: single-page app, any other path (/kompute, /login, ...) is served by index.html,
# revalidated on each load so browsers pick up new releases
handle {
root * /var/www/kowabunga
try_files {path} /index.html
header Cache-Control "no-cache"
file_server
}
}
The ghcr.io/kowabunga-cloud/koala image (minimal scratch image, running as non-root) serves Koala on port
8080 with static-web-server: single-page app fallback to index.html,
compression, security headers, and the same caching policy as above. GET /health can be used for liveness and
readiness probes.
Koala calls the Kahuna API on the same origin: route /api, /confirm, /confirmForgotPassword, /latest and
/ws to Kahuna in front of it (e.g. Kubernetes Ingress, reverse proxy). Server settings can be overridden through
SERVER_* environment variables.
Images are signed (Sigstore keyless signing) and carry SBOM and provenance attestations:
$ cosign verify ghcr.io/kowabunga-cloud/koala:<version> \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp '^https://github.com/kowabunga-cloud/'
$ gh attestation verify oci://ghcr.io/kowabunga-cloud/koala:<version> --owner kowabunga-cloud| Concern | Choice |
|---|---|
| Build | Vite + TypeScript |
| API client | Orval, generated from the Kowabunga OpenAPI spec |
| Server state | TanStack Query |
| Routing | TanStack Router (file-based, src/routes/) |
| UI | shadcn/ui (Tailwind + Radix), components live in src/components/ui/ |
| Forms | react-hook-form + Zod schemas generated from the spec |
| Mocks | MSW handlers generated from the spec |
$ make help # list all targets
$ make dev # /api proxied to https://labs.kowabunga.cloud
$ make dev KAHUNA_URL=http://127.0.0.1:8080 # or any other Kahuna
$ make dev-mock # no backend: fake data generated from the spec
$ make check # lint + type-check
$ make build # production bundle in dist/In mock mode any email/password signs you in as superAdmin.
kowabunga-cloud/openapi ──(api:fetch, pinned tag)──▶ openapi/openapi.yaml
│
orval (api:generate)
▼
src/api/generated/
model/ TypeScript types (Kaktus, Subnet, …)
endpoints/<tag>/<tag>.ts fetch functions + useXxx() query/mutation hooks
endpoints/<tag>/<tag>.msw.ts MSW mock handlers
zod/<tag>/<tag>.zod.ts Zod schemas for request/response bodies
- The spec version is pinned in
package.json→config.openapiVersion. To move to a new API version runmake api-update OPENAPI_VERSION=x.y.z: it fetches the spec, regenerates the client and type-checks the app, so breaking changes show up as type errors. - To try an unreleased spec:
make api-fetch api-generate OPENAPI_SPEC=../openapi/openapi.yaml. - CI runs
make api-check, which fails if the committed client doesn't match the committed spec. - Generated code is committed so spec bumps are reviewable as diffs. Never edit it by hand.
- Every request goes through
src/api/client.ts(base URL/api/v1,Authorization: Bearer <jwt>, sign-out on 401, errors thrown asApiError). GEToperations becomeuseXxx()queries, everything elseuseXxx()mutations:
const { data: kaktus } = useReadKaktus(id)
const enable = useEnableKaktusMaintenance({
mutation: { onSuccess: () => queryClient.invalidateQueries({ queryKey: getReadKaktusQueryKey(id) }) },
})
enable.mutate({ kaktusId: id })- Create
src/routes/_app/<name>.tsx(the route tree is regenerated automatically). - Add it to
NAVinsrc/components/app-sidebar.tsx. - Use
useScope()for the current project/region/zone and the generated hooks for data.
src/routes/_app/kaktus.tsx is the reference implementation.
Pushing a tag (e.g. 0.3.0) runs .github/workflows/release.yml: check, build, then make release
(kowabunga-koala-<version>.tgz) and make deb (kowabunga-koala_<version>_all.deb, installs into
/var/www/kowabunga), published as a GitHub release and pushed to the APT repository. The container image
is built and scanned on every change, and published to ghcr.io/kowabunga-cloud/koala on release.
Licensed under Apache License, Version 2.0, see LICENSE.