Dashboard¶
The dashboard is a lightweight web UI served at /. It's a
go-app progressive web app — written in Go, compiled to
WebAssembly, and embedded in the binary — so there is no Node toolchain, no JS
build, and nothing extra to deploy. It reads from the same-origin
REST API.
Source: internal/dashboard
+ cmd/kasas-wasm.
Toggle with dashboard.enabled (default on).
Pages¶
A collapsible left sidebar (the choice is remembered) navigates the pages:
| Page | What it does |
|---|---|
| Dashboard | A balance card per account, and a transactions table — account filter, sortable columns, selectable page size, pagination, inline label editing, and manual entry: add/edit/delete accounts and transactions. |
| Search | The search language over a query box with a syntax-help modal; results table with the same sorting/pagination/label editing. The last query persists across navigation. |
| Labels | Every label with its transaction count, and a delete that strips it from all of them. |
| Rules | Create, edit, enable/disable, delete rules inline, and Run one or all. |
| Events | A live feed of the event stream, polled forward, with an expandable JSON payload per row. |
| Webhooks | Register, edit, toggle, and test webhook endpoints; per-endpoint health. |
| Plugins | Plugin status/health, enable/disable, reload, uninstall (runs the plugin's cleanup hook). |
| Marketplace | Browse and install community plugins with a capability-tier warning; integrity-verified and installed disabled. |
| Sources | Every ingestion source — active and inactive — in one place: connection status, credentials (paste, add/remove per bank, watch/unwatch Bitcoin and Ethereum addresses, or browser OAuth), a per-source Sync now, and each source's editable configuration (Plaid app credentials, Teller mTLS cert paths, Etherscan key/chain, CSV folder profiles, …). Configure an inactive source here (e.g. set the Etherscan API key, or Bitcoin's API URL), restart, and it activates — then add its watched addresses and Sync now right on the card. |
| Settings | Edit how kasas works — sync schedule, plugins, events & history retention, webhooks, MCP, updates, logging — plus the dashboard token, API keys, a force-sync, a one-click Migrate to Postgres (shown on the SQLite backend), and the read-only bootstrap config (server address, database, secret store). Changes are permanent: they override the config file/env, and a banner offers the one-click restart that applies them. |
Plugins with a [ui] manifest block contribute their own sidebar entries
below the built-ins (at /ext/<plugin>): the server renders the plugin's
declarative page document and the
dashboard draws it with its own components — plugin output is text-only, never
markup. Entries appear only while the plugin is enabled and loaded.
Synced data is read-only except for labels (editable inline); extensions are shown read-only. Accounts and transactions you create yourself are fully editable — see Manual Entry.
How it's built and served¶
flowchart TB
subgraph build["Build time (make build)"]
SRC["internal/dashboard/*.go<br/>+ cmd/kasas-wasm"] -->|"GOOS=js GOARCH=wasm"| WASM[app.wasm]
WASM -->|"gzip -9"| GZ[app.wasm.gz]
GZ -->|"go:embed web"| BIN[kasas static binary]
CSS[dashboard.css · logo] -->|"go:embed web"| BIN
end
subgraph run["Runtime"]
B[Browser] -->|GET /| H[dashboard.Handler]
H -->|"index shell + app.wasm.gz + service worker"| B
B -->|instantiates WASM| APP["go-app runtime<br/>chrome + page views"]
APP -->|"same-origin REST + Bearer"| API[REST API]
APP -->|"in-browser"| SEARCH["internal/search<br/>(compiled to WASM)"]
end
BIN -.serves.-> H
The same dashboard.Routes() is registered on both the WASM client and the
server handler, so a deep link like /search is served the SPA shell instead of
404-ing.
Component structure¶
chrome— a shared struct embedded by every page. It owns the sidebar, the auth gate (prompts for a token on first visit, remembers it in LocalStorage), theapiClient, and the version badge.- Page views —
dashboardView,searchView, etc. Each embedschromeplus mix-ins likelabelEditing(the inline label editor, shared between the Dashboard and Search tables) andhistoryViewing(the history timeline modal). apiClient(client.go) wraps an HTTP client whose transport injectsAuthorization: Bearer <token>. It mirrors the server DTOs locally so the WASM build doesn't import the server package.
Search is the standout: because internal/search is pure
Go, the dashboard imports it and runs the same parser and matcher in the
browser — instant results with no per-keystroke round-trip.
Service-worker versioning
go-app's service worker caches the app shell under a key of app-<Version>.
kasas sets that version to the build version plus a hash of the WASM
content, so a stale version string can't pin an old UI — any change to the
dashboard busts the cache. A corollary when developing: because the service
worker serves the cached shell offline, a "Failed to fetch" inside a
rendered dashboard usually means the server is down, not a JS bug — check
the port.
Disabling it¶
Set dashboard.enabled=false (or KASAS_DASHBOARD_ENABLED=false) to stop serving
the UI. The WASM stays embedded in the binary; the route simply isn't mounted, and
/ falls through.