- Go 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| cmd/lofidb | ||
| examples/01_seeded_inmemory | ||
| .gitignore | ||
| catalog.go | ||
| CHANGELOG.md | ||
| go.mod | ||
| go.sum | ||
| index.md | ||
| LICENSE | ||
| lofidb.go | ||
| lofidb_test.go | ||
| README.md | ||
| ROADMAP.md | ||
| task-plus.yml | ||
| Taskfile.yml | ||
| wasm_soak_test.go | ||
lofidb
A small, embeddable database browser for Go. Drop a *sql.DB into it and you get a Bulma-styled web UI that lists tables, shows their schema (columns, primary keys, foreign keys, indexes), and lets you page and sort through rows.
Works against:
- PostgreSQL natively, via
github.com/jackc/pgx/v5 - pglike (
codeberg.org/hum3/go-postgres) — a SQLite-backed driver that speaks PG SQL, so the same UI runs on a single in-memory*sql.DBwith no external service - WASM: pglike runs under
GOOS=wasip1 GOARCH=wasm, so lofidb can serve a fully in-browser DB browser via lofigui
A single set of PG-style information_schema queries powers everything — pglike installs PG-compatible catalog views per connection so the same SQL runs on either backend.
Install
go install codeberg.org/hum3/lofidb/cmd/lofidb@latest
Or import the library:
go get codeberg.org/hum3/lofidb
Quick start (CLI)
lofidb # in-memory pglike scratchpad
lofidb file:/tmp/data.db # browse a pglike on-disk file
lofidb postgres://localhost/myapp # browse PostgreSQL
lofidb --addr :9000 file:/tmp/data.db # custom port
Open the printed URL — by default http://localhost:8080/explorer.
Quick start (library)
package main
import (
"database/sql"
"log"
"net/http"
"codeberg.org/hum3/lofidb"
_ "codeberg.org/hum3/go-postgres" // or _ "github.com/jackc/pgx/v5/stdlib"
)
func main() {
db, _ := sql.Open("pglike", ":memory:")
defer db.Close()
// ... create tables, load data ...
http.Handle("/explorer", lofidb.HTTPHandler(db, lofidb.Options{}))
http.Handle("/explorer/", lofidb.HTTPHandler(db, lofidb.Options{}))
log.Fatal(http.ListenAndServe(":8080", nil))
}
HTTPHandler mounts the index at the prefix and table-detail pages at <prefix>/<table>.
WASM mode
In WASM (e.g. via lofigui), call the HTML builders directly and pipe the strings into your renderer:
import "codeberg.org/hum3/lofigui"
lofigui.Reset()
lofigui.HTML(lofidb.IndexHTML(db, lofidb.Options{}))
// or, for a specific table:
lofigui.HTML(lofidb.TableHTML(db, "users", page, sort, dir, lofidb.Options{}))
The builders are pure functions: they take a *sql.DB and return an HTML fragment. The sample example under examples/ runs an entire pglike DB plus this UI in the browser.
Options
type Options struct {
Schema string // default "public"
URLPrefix string // default "/explorer"
PageSize int // default 50
HideTables []string // tables to hide in addition to internal ones
}
Schema is "public" for both PostgreSQL (default) and pglike (which reports 'public' from its catalog views and current_schema()). For multi-schema PostgreSQL setups, set this to the schema you want to browse.
How it works
The HTML pages render from a small set of catalog queries:
| Information | Query target |
|---|---|
| Table list | information_schema.tables |
| Column info | information_schema.columns |
| Primary keys | information_schema.table_constraints ⋈ information_schema.key_column_usage |
| Foreign keys | information_schema.referential_constraints ⋈ key_column_usage ⋈ constraint_column_usage |
| Indexes | pg_indexes (+ pglike-specific pg_index_columns for column lists) |
These views exist natively in PostgreSQL. pglike installs TEMP VIEW shims with the same names and shapes per connection, so the queries are unchanged across backends.
For index columns, lofidb prefers a pg_index_columns helper view that pglike exposes. Real PostgreSQL doesn't expose this view by default — the index list still shows up, but column lists may be empty until you create an equivalent helper view. (See examples/02_postgres_index_columns_view.sql.)
Data-size testing
wasm_soak_test.go measures how much data fits in a pglike :memory: database under WASM. It runs natively or under wasip1+wazero and emits JSON-line metrics. Run:
# native (sanity check)
WASM_SOAK_ROWS=100000 task test
# wasm (real measurement)
task test:wasm:soak WASM_SOAK_ROWS=200000
# capture metrics
task test:wasm:soak WASM_SOAK_ROWS=500000 WASM_SOAK_OUTPUT=wasm-soak.jsonl
Each line is one of:
{"stage":"start", "target_rows":N,"batch_size":B,"heap_alloc_mb":...}
{"stage":"progress","inserted_rows":N,"wall_secs":...,"heap_alloc_mb":...,"batch_latency_secs":...}
{"stage":"done", "inserted_rows":N,"wall_secs":...,"heap_alloc_mb":...}
A single representative row is ~120 bytes (id + 80-byte payload + timestamp + 10-byte key); WASM heap is dominated by SQLite's page cache and Go's GC. Initial measurements on wazero put the practical ceiling between 200k and 1M rows per :memory: DB before the WASM module hits its memory limit; results vary by browser engine and module config. The harness records the last successful batch on OOM so you can chart the curve rather than just "it crashed".
Development
task check # fmt + vet + test
task build # build cmd/lofidb
task test:wasm # run tests under wasip1/wasm
Links
| Documentation | https://h3-lofidb.statichost.page/ |
| Source (Codeberg) | https://codeberg.org/hum3/lofidb |
| Mirror (GitHub) | https://github.com/drummonds/lofidb |
License
MIT