{"id":51359500,"url":"https://github.com/lightninglabs/go-wasmsqlite","last_synced_at":"2026-07-02T22:05:02.426Z","repository":{"id":322784546,"uuid":"1046947481","full_name":"lightninglabs/go-wasmsqlite","owner":"lightninglabs","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-27T09:26:50.000Z","size":1886,"stargazers_count":2,"open_issues_count":1,"forks_count":3,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-27T11:12:06.224Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://lightning.community/go-wasmsqlite/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lightninglabs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-29T13:41:10.000Z","updated_at":"2026-06-27T09:26:54.000Z","dependencies_parsed_at":null,"dependency_job_id":"4b81325f-1607-49b5-9262-d8ec3dfb711a","html_url":"https://github.com/lightninglabs/go-wasmsqlite","commit_stats":null,"previous_names":["sputn1ck/go-wasmsqlite","lightninglabs/go-wasmsqlite"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/lightninglabs/go-wasmsqlite","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lightninglabs%2Fgo-wasmsqlite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lightninglabs%2Fgo-wasmsqlite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lightninglabs%2Fgo-wasmsqlite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lightninglabs%2Fgo-wasmsqlite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lightninglabs","download_url":"https://codeload.github.com/lightninglabs/go-wasmsqlite/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lightninglabs%2Fgo-wasmsqlite/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35064279,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-02T02:00:06.368Z","response_time":173,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-02T22:05:01.387Z","updated_at":"2026-07-02T22:05:02.387Z","avatar_url":"https://github.com/lightninglabs.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# go-wasmsqlite\n\nA WebAssembly SQLite driver for Go. It lets browser-based Go WASM applications use the standard `database/sql` API with SQLite running entirely client-side and persisting to OPFS when the browser supports it.\n\n## Current Architecture\n\nThe runtime uses SQLite's supported object-oriented WASM API directly. The deprecated SQLite Worker1/Promiser API is not used at runtime.\n\n```\nGo App (WASM)\n    -\u003e database/sql driver (\"wasmsqlite\")\n    -\u003e syscall/js adapter\n    -\u003e sqlite-bridge.js RPC client on the main thread\n    -\u003e sqlite-worker.js dedicated Worker\n    -\u003e sqlite3.oo1.DB / sqlite3.oo1.OpfsDb\n    -\u003e SQLite WASM + OPFS storage\n```\n\n`sqlite-worker.js` imports `sqlite3.js`, calls `sqlite3InitModule()`, opens databases with an automatic OPFS preference order by default (`opfs-wl`, `opfs-sahpool`, then `opfs`), and falls back to `:memory:` only when memory fallback is allowed. SQLite still runs in a Worker because OPFS requires worker-side file APIs.\n\n## Features\n\n- Standard `database/sql` driver named `wasmsqlite`\n- OPFS persistence in supported browsers\n- In-memory fallback when OPFS is unavailable\n- Transactions through `BEGIN IMMEDIATE`, `COMMIT`, and `ROLLBACK`\n- BLOB round trips through `[]byte`\n- Database dump/load helpers with BLOB-safe SQL literals\n- Embedded SQLite WASM and bridge assets\n- Optional `golang-migrate` driver support\n\n## Requirements\n\n- Go 1.24+ for this module\n- A modern browser with WebAssembly and OPFS support\n- HTTPS or localhost for OPFS\n- Cross-origin isolation for OPFS support:\n  - `Cross-Origin-Opener-Policy: same-origin`\n  - `Cross-Origin-Embedder-Policy: require-corp` or `credentialless`\n\nThe example must be served by a host that sets these headers on the page and runtime assets.\n\n## Installation\n\n```bash\ngo get github.com/lightninglabs/go-wasmsqlite\n```\n\n## Usage\n\nUse the driver through `database/sql`:\n\n```go\nimport (\n    \"database/sql\"\n\n    _ \"github.com/lightninglabs/go-wasmsqlite\"\n)\n\nfunc main() {\n    db, err := sql.Open(\"wasmsqlite\", \"file=/myapp.db?vfs=auto\u0026busy_timeout=5000\")\n    if err != nil {\n        panic(err)\n    }\n    defer db.Close()\n\n    // Recommended for every OPFS database handle.\n    db.SetMaxOpenConns(1)\n\n    if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)`); err != nil {\n        panic(err)\n    }\n}\n```\n\nThe convenience helper sets the OPFS connection limit for you:\n\n```go\ndb, err := wasmsqlite.Open(\u0026wasmsqlite.Options{\n    File:           \"/myapp.db\",\n    VFS:            \"auto\",\n    DisallowMemory: true,\n})\n```\n\nDirect `sql.Open(...)` callers should call `db.SetMaxOpenConns(1)` for each OPFS database. The worker rejects duplicate opens of the same OPFS filename in one worker, and multiple concurrent database handles are not useful for this browser storage model.\n\nNamed SQLite parameters are supported through `sql.Named`:\n\n```go\n_, err := db.Exec(\n    `INSERT INTO notes(id, body) VALUES (:id, $body)`,\n    sql.Named(\"id\", 1),\n    sql.Named(\"body\", \"hello\"),\n)\n```\n\nDo not mix positional and named parameters in one call.\n\nFor high-throughput inserts or updates, use the explicit batch helper. It sends\none worker request, prepares the SQL once in the SQLite worker, wraps the whole\nbatch in one transaction, and binds/steps/resets the statement for each row:\n\n```go\nresult, err := wasmsqlite.ExecBatchContext(ctx, db,\n    `INSERT INTO notes(id, body) VALUES (?, ?)`,\n    [][]any{\n        {1, \"hello\"},\n        {2, \"world\"},\n    },\n)\nif err != nil {\n    panic(err)\n}\n\nrows, _ := result.RowsAffected()\nfmt.Println(rows)\n```\n\n`ExecBatchContext` is intentionally separate from `database/sql` prepared\nstatements. Normal `Stmt.Exec` calls still execute immediately so per-call\nerrors, row counts, and read-your-writes behavior remain consistent with\n`database/sql`.\n\n`ExecContext`, `QueryContext`, `BeginTx`, `Ping`, dump, and load respect context cancellation while waiting for the JavaScript bridge. Canceling a Go context stops waiting on the Go side; it does not forcibly interrupt a SQLite operation that has already been posted to the Worker.\n\n## DSN Options\n\n- `file`: database filename, default `/app.db`\n- `vfs`: SQLite VFS, default `auto`\n- `busy_timeout`: busy timeout in milliseconds, default `5000`\n- `mode`: SQLite URI mode such as `ro`, `rw`, `rwc`, or `memory`\n- `cache`: SQLite URI cache mode such as `shared` or `private`\n- `journal_mode`: runs `PRAGMA journal_mode=\u003cvalue\u003e` after open\n- `pragma`: semicolon-separated pragmas to run after open\n- `worker_url`: optional URL for `sqlite-worker.js`\n- `sqlite_js_url`: optional URL for `sqlite3.js`, used by `sqlite-worker.js`\n- `require_persistent`: fail instead of falling back to memory when persistent storage is unavailable\n- `disallow_memory`: explicit alias for `require_persistent`; fail instead of falling back to memory\n- `parse_time`: parse SQLite timestamp strings into `time.Time` values for scans into `time.Time` or `sql.NullTime`\n\n`parse_time=true` recognizes `time.RFC3339Nano`, `time.RFC3339`, SQLite-style datetime strings with optional fractional seconds and numeric offsets, `YYYY-MM-DD HH:MM:SS`, and `YYYY-MM-DD`.\n\nSupported VFS values:\n\n- `auto`: default; tries `opfs-wl`, then `opfs-sahpool`, then `opfs`, then memory if allowed\n- `opfs`: persistent OPFS database via `sqlite3.oo1.OpfsDb`; falls back to memory if unavailable and memory is allowed\n- `opfs-wl`: OPFS database using SQLite's Web Locks VFS for fairer multi-tab lock scheduling when the browser supports `Atomics.waitAsync`\n- `opfs-sahpool`: explicit SQLite SAH pool VFS when available\n- `memory`: in-memory database\n\n`file=:memory:` also opens an in-memory database.\n\n## Assets\n\nSQLite WASM assets are embedded with `//go:embed`. The runtime needs these files to be served from the same directory in a browser app:\n\n- `sqlite-bridge.js`\n- `sqlite-worker.js`\n- `sqlite3.js`\n- `sqlite3.wasm`\n- `sqlite3-opfs-async-proxy.js`\n- Go's `wasm_exec.js`\n- your Go WASM binary\n\nThe upstream SQLite Worker1 files are intentionally not fetched or published because this project no longer loads them at runtime.\n\n### Extract Assets\n\n```go\nerr := wasmsqlite.ExtractAssets(\"./static/wasm\")\nif err != nil {\n    log.Fatal(err)\n}\n```\n\n`ExtractAssets` writes the runtime files in a flat layout:\n\n```text\n./static/wasm/sqlite-bridge.js\n./static/wasm/sqlite-worker.js\n./static/wasm/sqlite3.js\n./static/wasm/sqlite3.wasm\n./static/wasm/sqlite3-opfs-async-proxy.js\n```\n\n### Serve Assets\n\n```go\nhandler := wasmsqlite.AssetHandler()\nhttp.Handle(\"/wasm/\", http.StripPrefix(\"/wasm\", handler))\n```\n\nAvailable flat runtime paths include:\n\n```text\n/wasm/sqlite3.wasm\n/wasm/sqlite3.js\n/wasm/sqlite3-opfs-async-proxy.js\n/wasm/sqlite-bridge.js\n/wasm/sqlite-worker.js\n```\n\n`AssetHandler` also preserves the embedded `/assets/...` and `/bridge/...` paths for direct access, but the flat paths are the browser runtime layout. By default, `sqlite-worker.js` is resolved relative to the loaded `sqlite-bridge.js`, and `sqlite3.js` is resolved relative to `sqlite-worker.js`. If you serve files from unrelated directories, set both `worker_url` and `sqlite_js_url`.\n\nCross-origin isolation headers must be set on the app page, not only on SQLite assets. Wrap the whole app handler:\n\n```go\napp := wasmsqlite.WithCrossOriginIsolation(http.FileServer(http.Dir(\"./public\")))\nhttp.Handle(\"/\", app)\n```\n\n## Production Serving\n\nA production deployment should serve one flat runtime directory:\n\n```text\npublic/\n  index.html\n  main.wasm\n  wasm_exec.js\n  sqlite-bridge.js\n  sqlite-worker.js\n  sqlite3.js\n  sqlite3.wasm\n  sqlite3-opfs-async-proxy.js\n```\n\nMinimal Go static server:\n\n```go\npackage main\n\nimport (\n    \"log\"\n    \"net/http\"\n\n    \"github.com/lightninglabs/go-wasmsqlite\"\n)\n\nfunc main() {\n    app := http.FileServer(http.Dir(\"./public\"))\n    handler := wasmsqlite.WithCrossOriginIsolation(app)\n\n    log.Fatal(http.ListenAndServe(\":8080\", handler))\n}\n```\n\n`WithCrossOriginIsolation` sets COOP/COEP on every response and `application/wasm` for `.wasm` files. Static hosts must do the same for the app page. For caching, prefer hash-named immutable files; otherwise deploy `main.wasm`, `sqlite-bridge.js`, `sqlite-worker.js`, `sqlite3.js`, `sqlite3.wasm`, and `sqlite3-opfs-async-proxy.js` together. The bridge and worker include a small protocol check so mismatched runtime files fail clearly instead of producing opaque worker errors.\n\n### Access Individual Assets\n\n```go\nwasmBytes, _ := wasmsqlite.GetSQLiteWASM()\nsqliteJS, _ := wasmsqlite.GetSQLiteJS()\nbridgeJS, _ := wasmsqlite.GetBridgeJS()\nworkerJS, _ := wasmsqlite.GetWorkerJS()\n```\n\n## Example App\n\nBuild and serve the demo:\n\n```bash\nmake build-example\nmake serve\n```\n\nThen open `http://localhost:8081`.\n\nThe example uses:\n\n- `example/index.html`\n- `example/main.go` for the Go WASM app\n- `example/migrations/` with `golang-migrate`\n- generated `database/sql` query code in `example/generated/`\n\n`make build-example` copies the runtime browser files into `example/`, which is also what the GitHub Pages workflow publishes.\n\n## Database Dump/Load\n\n```go\ndump, err := wasmsqlite.DumpDatabase(db)\nif err != nil {\n    return err\n}\n\nif err := wasmsqlite.LoadDatabase(db, dump); err != nil {\n    return err\n}\n```\n\nDump output uses hex literals for BLOB values, so `[]byte` data can be restored safely.\n\n## VFS Detection\n\n```go\nstorageInfo, err := wasmsqlite.GetStorageInfo(db)\nif err != nil {\n    return err\n}\n\nswitch storageInfo.VFSType {\ncase wasmsqlite.VFSTypeOPFS:\n    // Persistent OPFS storage.\ncase wasmsqlite.VFSTypeOPFSWebLocks:\n    // Persistent OPFS storage using SQLite's Web Locks VFS.\ncase wasmsqlite.VFSTypeOPFSSAHPool:\n    // Persistent OPFS storage using SQLite's SAH pool VFS.\ncase wasmsqlite.VFSTypeMemory:\n    // In-memory storage; data will reset on refresh.\n}\n```\n\n`GetVFSType(conn)` remains available when code already has a dedicated `*sql.Conn`. Use `DisallowMemory: true`, `RequirePersistent: true`, `disallow_memory=true`, or `require_persistent=true` when falling back to memory would be incorrect.\n\n## Storage Behavior\n\n- One `*sql.DB` should own a given OPFS filename in a page. Use `SetMaxOpenConns(1)`.\n- Opening the same OPFS filename twice in the same Worker returns `ErrDuplicateOpen`.\n- Opening the same OPFS filename from two tabs uses separate Workers and browser OPFS locking. Browser behavior can differ; apps should handle either a successful second open or an actionable lock/open error.\n- Default `vfs=auto` tries `opfs-wl`, then `opfs-sahpool`, then `opfs`, then `memory` if fallback is allowed.\n- Use `vfs=opfs-wl` for multi-tab workloads where Web Locks support is available. It preserves OPFS persistence while letting the browser arbitrate lock acquisition more fairly across tabs.\n- Use `vfs=opfs-sahpool` for the explicit SQLite SAH pool VFS when single-tab performance is more important than transparent multi-tab access.\n- Private/incognito modes may expose reduced or temporary OPFS storage. Use `DisallowMemory` or `RequirePersistent` when data loss would be unacceptable.\n\n## Migrations\n\nThe package includes a `golang-migrate` database driver that runs migrations against an existing `*sql.DB`:\n\n```go\nsourceDriver, err := iofs.New(migrationFS, \"migrations\")\nif err != nil {\n    return err\n}\n\ndbDriver, err := wasmsqlite.NewMigrateDriver(db)\nif err != nil {\n    return err\n}\n\nm, err := migrate.NewWithInstance(\"iofs\", sourceDriver, \"wasmsqlite\", dbDriver)\nif err != nil {\n    return err\n}\n\nreturn m.Up()\n```\n\n## Development\n\n```bash\nmake setup          # Fetch SQLite WASM assets\nmake build          # Fetch assets, build root WASM, and build example\nmake build-example  # Build only the demo app and copy browser runtime files\nmake serve          # Serve demo at http://localhost:8081\nmake test           # Run normal Go tests\nmake browser-test   # Run headless Chrome browser E2E tests\nnpm test            # Run Playwright tests against the built example page\nmake clean          # Remove build artifacts\n```\n\n`make fetch-assets` reads the official SQLite download metadata and fetches the current `sqlite-wasm-*.zip` bundle with SHA3 verification. To pin a specific archive, pass `SQLITE_URL` and `EXPECTED_SHA`; `SQLITE_VERSION=3530100 make fetch-assets` selects that version when it is still listed on the download page.\n\nBrowser tests build a WASM test binary, serve the SQLite assets with the required headers, launch headless Chrome, and verify OPFS persistence, BLOBs, dump/load, transactions, memory mode, migrations, generated SQL shapes, cancellation, named parameters, browser-context storage behavior, and the example path. CI runs these browser tests on every push and pull request.\n\n## GitHub Pages\n\nThe Pages workflow runs `make build-example` and publishes `./example`. The published directory must contain:\n\n- `index.html`\n- `_headers`\n- `main.wasm`\n- `wasm_exec.js`\n- `sqlite-bridge.js`\n- `sqlite-worker.js`\n- `sqlite3.js`\n- `sqlite3.wasm`\n- `sqlite3-opfs-async-proxy.js`\n\nGitHub Pages does not apply `_headers`, so this workflow is only useful as a static artifact publisher. The public `lightning.community` route must add the required headers before serving these files.\n\n## Deployment\n\nThe public demo is intended to be served from `https://lightning.community/go-wasmsqlite/`.\n\nThe host in front of the built `./example` directory must set the OPFS headers\non every response:\n\n```text\nCross-Origin-Opener-Policy: same-origin\nCross-Origin-Embedder-Policy: require-corp\nCross-Origin-Resource-Policy: same-origin\n```\n\nIt must also serve `*.wasm` as `application/wasm`. The repository keeps\n`example/_headers` for static hosts that understand that file format, but\nGitHub Pages does not apply `_headers`; the `lightning.community` route should\ntherefore terminate on a header-capable edge/proxy.\n\nIf the route is proxied through Cloudflare, avoid TLS/proxy settings that make\nthe origin redirect back to the same HTTPS URL. A same-location `301` on\n`/go-wasmsqlite/` happens before the app loads, so it cannot be fixed by the\ndemo code.\n\n## Limitations\n\n- SQLite extensions cannot be loaded dynamically.\n- OPFS storage is origin-scoped.\n- OPFS requires HTTPS or localhost.\n- If OPFS is unavailable, `vfs=auto` and `vfs=opfs` fall back to in-memory storage unless memory fallback is disabled.\n- Context cancellation stops waiting on the Go side but does not interrupt already-running SQLite work in the Worker.\n- Named and positional parameters cannot be mixed in the same call.\n\n## Troubleshooting\n\n| Symptom | Likely cause | Fix |\n| --- | --- | --- |\n| `ErrBridgeNotLoaded` | `sqlite-bridge.js` was not loaded before `main.wasm` | Load `sqlite-bridge.js` before starting Go WASM. |\n| `ErrAssetUnavailable` | Worker cannot fetch `sqlite3.js`, `sqlite3.wasm`, or the OPFS proxy | Serve the flat runtime files together or set `worker_url` and `sqlite_js_url`. |\n| `ErrPersistentRequired` | `RequirePersistent` or `DisallowMemory` was set but OPFS/persistent storage is unavailable | Show a user-facing persistence error or allow memory fallback for demo mode. |\n| `ErrDuplicateOpen` | Same OPFS filename opened twice in one Worker | Use one `*sql.DB` per OPFS file and `SetMaxOpenConns(1)`. |\n| `ErrUnsupportedVFS` | Requested VFS is not available in the browser build | Use `auto`, `opfs`, `opfs-wl`, `opfs-sahpool`, or `memory`. |\n| `ErrNamedParameter` | Mixed positional/named params, missing named param, or unused named param | Use all positional or all named params and match SQL names. |\n| `ErrProtocolMismatch` | Cached runtime JS files are from different versions | Redeploy `sqlite-bridge.js`, `sqlite-worker.js`, SQLite assets, and `main.wasm` together. |\n\n## Compatibility\n\n| Component | Current support |\n| --- | --- |\n| Go | 1.24+ |\n| SQLite WASM | Official `sqlite-wasm` bundle, currently 3.53.1 / 3530100 |\n| Browser | Modern Chromium-class browsers with WebAssembly; OPFS requires secure context and cross-origin isolation |\n| VFS modes | `auto`, `opfs-wl`, `opfs-sahpool`, `opfs`, `memory` |\n| SQL API | Go `database/sql`, positional and named parameters, transactions, BLOBs, `parse_time`, dump/load |\n| Unsupported | Dynamic SQLite extensions, forced interruption of already-running Worker SQL |\n\nReleases should follow semantic versioning. Runtime JS/WASM files are part of the compatibility contract and should be deployed with the matching Go module version.\n\n## License\n\nMIT\n\n## Acknowledgments\n\n- [SQLite](https://sqlite.org/) for SQLite and the official WASM build\n- [database/sql](https://pkg.go.dev/database/sql) for the standard Go SQL API\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flightninglabs%2Fgo-wasmsqlite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flightninglabs%2Fgo-wasmsqlite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flightninglabs%2Fgo-wasmsqlite/lists"}