{"id":13796756,"url":"https://github.com/elh/bitempura","last_synced_at":"2026-03-10T08:33:14.907Z","repository":{"id":43733392,"uuid":"450940997","full_name":"elh/bitempura","owner":"elh","description":"Toy bitemporal key-value database and visualizer","archived":false,"fork":false,"pushed_at":"2024-07-17T22:30:27.000Z","size":119,"stargazers_count":10,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-19T04:57:56.481Z","etag":null,"topics":["bitemporal","bitempura","database","go","key-value","wasm"],"latest_commit_sha":null,"homepage":"https://elh.github.io/bitempura-viz/#/tests/TestRobinhoodExample","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2022-01-22T21:33:31.000Z","updated_at":"2024-09-17T04:40:04.000Z","dependencies_parsed_at":"2024-06-19T20:15:13.577Z","dependency_job_id":null,"html_url":"https://github.com/elh/bitempura","commit_stats":null,"previous_names":["elh/bitemporal"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/elh/bitempura","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elh%2Fbitempura","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elh%2Fbitempura/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elh%2Fbitempura/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elh%2Fbitempura/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elh","download_url":"https://codeload.github.com/elh/bitempura/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elh%2Fbitempura/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30328251,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T05:25:20.737Z","status":"ssl_error","status_checked_at":"2026-03-10T05:25:17.430Z","response_time":106,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["bitemporal","bitempura","database","go","key-value","wasm"],"created_at":"2024-08-03T23:01:14.729Z","updated_at":"2026-03-10T08:33:13.996Z","avatar_url":"https://github.com/elh.png","language":"Go","funding_links":[],"categories":["Bi-temporality Tools + Modeling"],"sub_categories":[],"readme":"# bitempura ⌛ → ⏳!\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/elh/bitempura.svg)](https://pkg.go.dev/github.com/elh/bitempura)\n[![Build Status](https://github.com/elh/bitempura/actions/workflows/go.yml/badge.svg?branch=main)](https://github.com/elh/bitempura/actions/workflows/go.yml?query=branch%3Amain)\n[![Go Report Card](https://goreportcard.com/badge/github.com/elh/bitempura)](https://goreportcard.com/report/github.com/elh/bitempura)\n\u003c!-- This is often broken :/ --\u003e\n\u003c!-- [![Go Non-Test Lines Of Code](https://tokei.rs/b1/github/elh/bitempura?category=code)](https://github.com/elh/bitempura/blob/main/.tokeignore) \u003csup\u003e\u003csup\u003e*non-test\u003c/sup\u003e\u003c/sup\u003e --\u003e\n\n**Bitempura.DB is a simple, [bitemporal](https://en.wikipedia.org/wiki/Bitemporal_Modeling) key-value database.**\n\nBitempura provides an in-memory, concurrency-safe [reference implementation](https://github.com/elh/bitempura/blob/main/memory/db.go).\n* Use a developer-friendly API that is as simple or complex as your use case. See \"Design\" section.\n* Visualize and debug the 2D valid time and transaction time history with [bitempura-viz 🔮](https://github.com/elh/bitempura-viz).\n* Run on the web with compilation to [WebAssembly 🧩](https://github.com/elh/bitempura/blob/main/memory/wasm).\n\nAn experimental, [SQL-querying implementation](https://github.com/elh/bitempura/blob/main/sql/db.go) was promptly abandoned. Check out XTDB :)\n\n\u003cp align=\"center\"\u003e\n\t\u003ca href=\"https://github.com/elh/bitempura-viz\"\u003e\n\t\t\u003cimg width=\"90%\" alt=\"bitempura\" src=\"https://user-images.githubusercontent.com/1035393/154894531-396b2ec4-0a4c-474f-bb11-73b5cc456eb2.gif\"\u003e\n\t\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cbr /\u003e\n\n## Bitemporality\n\nTemporal databases model time as a core aspect of storing and querying data. A bitemporal database is one that supports these orthogonal axes.\n* **Valid time**: When the fact was *true* in the real world. This is the *application domain's* notion of time.\n* **Transaction time**: When the fact was *recorded* in the database. This is the *system's* notion of time.\n\nBecause every fact in a bitemporal database has these two dimensions, it enables use cases like this:\n```go\n// We initialize a DB and start using it like an ordinary key-value store.\ndb, err := memory.NewDB()\nerr := db.Set(\"Bob/balance\", 100)\nval, err := db.Get(\"Bob/balance\")\nerr := db.Delete(\"Alice/balance\")\n// and so on...\n\n// We later learn that Bob had a temporary pending charge we missed from Dec 30 to Jan 3. (VT start = Dec 30, VT end = Jan 3)\n// Retroactively record it! This does not change his balance today nor does it destroy any history we had about that period.\nerr := db.Set(\"Bob/balance\", 90, WithValidTime(dec30), WithEndValidTime(jan3))\n\n// We can at any point seamlessly ask questions about the real world past AND database record past!\n// \"What was Bob's balance on Jan 1 as best we knew on Jan 8?\" (VT = Jan 1, TT = Jan 8)\nval, err := db.Get(\"Bob/balance\", AsOfValidTime(jan1), AsOfTransactionTime(jan8))\n\n// More time passes and more corrections are made... When trying to make sense of what happened last month, we can ask again:\n// \"But what was it on Jan 1 as best we now know?\" (VT = Jan 1, TT = now)\nval, err := db.Get(\"Bob/balance\", AsOfValidTime(jan1))\n\n// And while we are at it, let's double check all of our transactions and known states for Bob's balance.\nversions, err := db.History(\"Bob/balance\")\n```\n*See [full examples](https://github.com/elh/bitempura/blob/main/memory/db_examples_test.go)\n\nUsing a bitemporal database allows you to offload management of temporal application data (valid time) and data versions (transaction time) from your code and onto infrastructure. This provides a universal \"time travel\" capability across models in the database. Adopting these capabilities proactively is valuable because by the time you realize you need to update (or have already updated) data, it may be too late. Context may already be lost or painful to reconstruct manually.\n\n\u003cbr /\u003e\n\n## Design\n\n```go\n// DB for bitemporal data.\n//\n// Temporal control options.\n// ReadOpt's: AsOfValidTime, AsOfTransactionTime.\n// WriteOpt's: WithValidTime, WithEndValidTime.\ntype DB interface {\n\t// Get data by key (as of optional valid and transaction times).\n\tGet(key string, opts ...ReadOpt) (*VersionedKV, error)\n\t// List all data (as of optional valid and transaction times).\n\tList(opts ...ReadOpt) ([]*VersionedKV, error)\n\t// Set stores value (with optional start and end valid time).\n\tSet(key string, value Value, opts ...WriteOpt) error\n\t// Delete removes value (with optional start and end valid time).\n\tDelete(key string, opts ...WriteOpt) error\n\n\t// History returns all versioned key-values for key by descending end transaction time, descending end valid time.\n\tHistory(key string) ([]*VersionedKV, error)\n}\n\n// VersionedKV is a transaction time and valid time versioned key-value. Transaction and valid time starts are inclusive\n// and ends are exclusive. No two VersionedKVs for the same key can overlap both transaction time and valid time.\ntype VersionedKV struct {\n\tKey   string\n\tValue Value\n\n\tTxTimeStart    time.Time  // inclusive\n\tTxTimeEnd      *time.Time // exclusive\n\tValidTimeStart time.Time  // inclusive\n\tValidTimeEnd   *time.Time // exclusive\n}\n\n// Value is the user-controlled data associated with a key (and valid and transaction time information) in the database.\ntype Value interface{}\n```\n\n* DB interface is inspired by XTDB (and Datomic).\n* Storage model is inspired by Snodgrass' SQL implementations.\n\n\u003cbr /\u003e\n\n## Author\n\nI was learning about [bitemporal databases](https://en.wikipedia.org/wiki/Bitemporal_Modeling) and thought the best way to build intuition about their internal design was by building a simple one for myself. My goals are:\n* Sharing a viable, standalone key-value store lib\n* Creating artifacts for explaining bitemporality\n* Expanding scope in new tools for gracefully extending existing SQL databases with bitemporality\n\nBitempura was the name of my time traveling shrimp. RIP 2049-2022. 🦐\n\nSee [TODO](https://github.com/elh/bitempura/blob/main/TODO.md) for more.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felh%2Fbitempura","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felh%2Fbitempura","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felh%2Fbitempura/lists"}