{"slug": "tablinum-local-first-database-for-the-browser", "title": "Tablinum – Local-first database for the browser", "summary": "Tablinum, a local-first database for the browser, has been released as an npm package, offering encrypted sync via NIP-59 and built-in collaboration with zero backend. The database stores data in IndexedDB, syncs to Nostr relays with end-to-end encryption, and provides TypeScript inference and reactive bindings for Svelte.", "body_md": "Encrypted sync. Built-in collaboration. Zero backend.\n\n`$ npm install tablinum`\n\nEvery record is gift-wrapped with NIP-59 encryption. The relay stores and forwards encrypted blobs — it never sees your data.\n\nA complete toolkit for local-first applications\n\nYour app works offline. All data lives on your device in IndexedDB. No server required, no loading spinners.\n\nData syncs to Nostr relays, encrypted end-to-end with NIP-59 gift wrapping. Relay operators cannot read your data.\n\nShare databases with invite links. Key rotation on member removal — like changing locks when a roommate moves out.\n\nDefine schemas with collection() and field.*() builders. Full TypeScript inference from schema to query results.\n\nFirst-class reactive bindings using async runes. Queries inside $derived(await ...) auto-update when data changes.\n\nEffect-based structured logging with timing spans. From full debug output to complete silence in one config line.\n\nUse the Effect API for full control, or the Svelte API for reactive simplicity\n\n``` js\nimport { Tablinum, collection, field } from \"tablinum/svelte\";\n\nconst schema = {\n  todos: collection(\"todos\", {\n    title: field.string(),\n    done: field.boolean(),\n  }, { indices: [\"done\"] }),\n};\n\nexport const db = new Tablinum({\n  schema,\n  relays: [\"wss://relay.example.com\"],\n});\n\nexport const todos = db.collection(\"todos\");\njs\n<script lang=\"ts\">\n  import { todos } from \"$lib/db\";\n\n  let title = $state(\"\");\n  let pending = $derived(await todos.where(\"done\").equals(false).get());\n\n  async function addTodo() {\n    await todos.add({ title, done: false });\n    title = \"\";\n  }\n</script>\n\n<form onsubmit={addTodo}>\n  <input bind:value={title} placeholder=\"Add a todo...\" />\n  <button type=\"submit\">Add</button>\n</form>\n\n{#each pending as todo (todo.id)}\n  <div>{todo.title}</div>\n{/each}\n```\n\nData lives in IndexedDB on your device. Reads are instant, writes never block.\n\nEvery record is wrapped in NIP-59 gift wrapping. Relays see encrypted blobs, nothing more.\n\nNIP-77 negentropy figures out what's missing and only transfers the difference.\n\nAvailable everywhere\n\nYour data arrives on every device and every collaborator, fully decrypted and ready to use.", "url": "https://wpnews.pro/news/tablinum-local-first-database-for-the-browser", "canonical_source": "https://tablinum.dev/", "published_at": "2026-08-25 17:49:51+00:00", "updated_at": "2026-08-25 18:15:57.414264+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": ["Tablinum", "Nostr", "IndexedDB", "Svelte", "NIP-59", "NIP-77"], "alternates": {"html": "https://wpnews.pro/news/tablinum-local-first-database-for-the-browser", "markdown": "https://wpnews.pro/news/tablinum-local-first-database-for-the-browser.md", "text": "https://wpnews.pro/news/tablinum-local-first-database-for-the-browser.txt", "jsonld": "https://wpnews.pro/news/tablinum-local-first-database-for-the-browser.jsonld"}}