{"id":50075192,"url":"https://github.com/prisma-risk/tsoracle","last_synced_at":"2026-06-01T23:00:44.622Z","repository":{"id":359012724,"uuid":"1243869408","full_name":"prisma-risk/tsoracle","owner":"prisma-risk","description":"Distributed timestamp \u0026 sequence oracle for Rust — strictly monotonic timestamps and gapless, dense ID sequences, raft-replicated over gRPC.","archived":false,"fork":false,"pushed_at":"2026-05-30T13:01:45.000Z","size":3997,"stargazers_count":44,"open_issues_count":8,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-30T15:14:58.134Z","etag":null,"topics":["distributed","gapless","gapless-sequence","grpc","monotonic","mvcc","openraft","rust","sequence","timestamp","timestamp-oracle","tso"],"latest_commit_sha":null,"homepage":"https://tsoracle.rs","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/prisma-risk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":"GOVERNANCE.md","roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-19T18:40:22.000Z","updated_at":"2026-05-30T14:57:19.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/prisma-risk/tsoracle","commit_stats":null,"previous_names":["prisma-risk/tsoracle"],"tags_count":194,"template":false,"template_full_name":null,"purl":"pkg:github/prisma-risk/tsoracle","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prisma-risk%2Ftsoracle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prisma-risk%2Ftsoracle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prisma-risk%2Ftsoracle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prisma-risk%2Ftsoracle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/prisma-risk","download_url":"https://codeload.github.com/prisma-risk/tsoracle/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prisma-risk%2Ftsoracle/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33750478,"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-05-31T02:00:06.040Z","response_time":95,"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":["distributed","gapless","gapless-sequence","grpc","monotonic","mvcc","openraft","rust","sequence","timestamp","timestamp-oracle","tso"],"created_at":"2026-05-22T06:09:15.067Z","updated_at":"2026-06-01T23:00:44.604Z","avatar_url":"https://github.com/prisma-risk.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n    \u003cpicture\u003e\n        \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"site/static/tsoracle-dark.svg\"\u003e\n        \u003cimg alt=\"tsoracle\" src=\"site/static/tsoracle-light.svg\" width=\"360\"\u003e\n    \u003c/picture\u003e\n    \u003cbr /\u003e\u003cbr /\u003e\n\n[![Crates.io](https://img.shields.io/crates/v/tsoracle.svg)](https://crates.io/crates/tsoracle)\n[![DeepWiki](https://img.shields.io/badge/DeepWiki-tsoracle-blue.svg)](https://deepwiki.com/prisma-risk/tsoracle)\n![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)\n![Crates.io](https://img.shields.io/crates/d/tsoracle-core.svg)\n\u003cbr/\u003e\n[![CI](https://github.com/prisma-risk/tsoracle/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/prisma-risk/tsoracle/actions/workflows/ci.yml)\n[![stress](https://github.com/prisma-risk/tsoracle/actions/workflows/stress-nightly.yml/badge.svg?branch=main)](https://github.com/prisma-risk/tsoracle/actions/workflows/stress-nightly.yml)\n[![Coverage Status](https://coveralls.io/repos/github/prisma-risk/tsoracle/badge.svg?branch=main)](https://coveralls.io/github/prisma-risk/tsoracle?branch=main)\n[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/prisma-risk/tsoracle/badge)](https://scorecard.dev/viewer/?uri=github.com/prisma-risk/tsoracle)\n[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/12991/badge)](https://www.bestpractices.dev/projects/12991)\n\n\u003c/div\u003e\n\nA distributed timestamp **and sequence** oracle for Rust — highly available and fault-tolerant, issuing strictly monotonic integer timestamps and gapless, dense ID sequences over gRPC, replicated via openraft or OmniPaxos (or your own log) with pluggable consensus.\n\n## Features\n\n- 🔢 **Strictly monotonic timestamps** — every issued timestamp is strictly greater than every previously issued one; no duplicates and no regression, ever. The packed integer space is not dense (logical resets when `physical_ms` advances, so some integer values are unused), but the issued sequence is total-ordered and unique. Call `get_ts` when you need *order*.\n- 🆔 **Gapless sequences** — `get_seq(key, count)` hands out dense, contiguous ID blocks `[start, start + count)` per named key — nothing skipped, never reused across crashes or failover — for surrogate keys, invoice numbers, ledger lines, or partition offsets. The tradeoff is non-idempotency: an ambiguous failure surfaces as `SeqUncertain` for the caller to reconcile rather than being silently retried. Call `get_seq` when you need *no holes*. Implemented on the file and openraft drivers today (OmniPaxos on the roadmap).\n- 🛡️ **Crash-safe** — window state is fsync'd before any timestamp in that window is handed out, so a restart never rewinds.\n- 🔌 **Pluggable consensus, openraft + OmniPaxos included** — `tsoracle-driver-openraft` and `tsoracle-driver-paxos` ship production-ready replicated drivers; implement one trait (`ConsensusDriver`) to back tsoracle with raft-rs, etcd, or your own replicated log instead. See [`docs/consensus-integration.md`](docs/consensus-integration.md#choosing-a-driver) for picking between drivers.\n- 📦 **gRPC client included** — `tsoracle-client` handles leader discovery, request coalescing, and reconnection for you.\n- 📈 **Operational metrics** — enable the `metrics` feature on `tsoracle-server` to emit allocator, leader, and request metrics through the `metrics` facade.\n- 🧪 **Hardened** — coverage-guided fuzzing on the postcard decoders, failpoint-driven crash tests, and a stress harness covering in-process and multi-process topologies across the openraft and OmniPaxos backends.\n- 🧩 **Embeddable** — host the server inside your own binary with a few lines of Rust, or run the standalone CLI.\n\n## Quickstart\n\nInstall the standalone binary and start the server:\n\n```bash\ncargo install tsoracle\ntsoracle\n```\n\nReleases are signed with SLSA build provenance — see [Verifying release signatures](docs/release-signatures.md).\n\nBare `tsoracle` is shorthand for `tsoracle serve file`. The server listens on `127.0.0.1:50551` and persists state under `./tsoracle-data/`.\n\nThen call it from Rust:\n\n```rust\nuse tsoracle_client::Client;\n\nlet client = Client::connect(vec![\"http://127.0.0.1:50551\".into()]).await?;\n\nlet ts = client.get_ts().await?;             // a strictly increasing Timestamp\nlet batch = client.get_ts_batch(64).await?;  // amortize RPC cost across many IDs\n\nlet block = client.get_seq(\"orders\", 64).await?;  // 64 gapless IDs: [start, start + 64)\n```\n\n## Use as a library\n\nEmbed the server in your own binary instead of running the CLI:\n\n```rust\nuse tsoracle_server::Server;\nuse tsoracle_driver_file::FileDriver;\n\nlet driver = FileDriver::open_or_init(\"./tsoracle-data\")?;\nlet server = Server::builder()\n    .consensus_driver(driver)\n    .build()?;\n\nserver\n    .serve_with_shutdown(\"127.0.0.1:50551\".parse()?, async {\n        let _ = tokio::signal::ctrl_c().await;\n    })\n    .await?;\n```\n\nA complete, runnable version lives in [`examples/embedded-server`](examples/embedded-server). Want to mount tsoracle inside an existing tonic server? See [`Server::into_router`](https://docs.rs/tsoracle-server/latest/tsoracle_server/struct.Server.html#method.into_router).\n\n## What's a TSO?\n\nA *timestamp oracle* is a service that hands out strictly increasing integer IDs which order events across a distributed system. You reach for one when:\n\n- You're building a database with snapshot isolation or MVCC (Spanner, CockroachDB, FoundationDB all use a TSO internally).\n- You need to merge change-data from many shards into one globally ordered stream.\n- You want audit logs with a real \"happens-before\" relation across machines.\n- Per-host clocks aren't monotonic or accurate enough, and database sequences don't scale to your workload.\n\ntsoracle is a small, embeddable Rust implementation. The consensus layer is left as a trait, so you can run it single-node behind one fsync (the default), or wire it into a replicated log of your choice.\n\nBeyond ordering, tsoracle also issues **gapless sequences**. Where a timestamp only has to be *ordered* (and may skip values), a sequence has to be *contiguous* — `…, 41, 42, 43, …` with nothing missing. Call `get_seq(key, count)` for surrogate keys, invoice or order numbers, ledger line numbers, or partition offsets, with one durable, gapless counter per named key. The cost is non-idempotency, since a gapless advance can't be un-spent — see [Gapless sequences with GetSeq](https://tsoracle.rs/posts/gapless-sequences-with-getseq/) for the full story and [`docs/client-api-and-usage.md`](docs/client-api-and-usage.md) for the API.\n\n## Documentation\n\n- [DeepWiki](https://deepwiki.com/prisma-risk/tsoracle) — prose documentation covering the window allocator, the `ConsensusDriver` contract, and operational topics (fsync cost, leader handoff, deployment topologies).\n- [docs.rs/tsoracle-server](https://docs.rs/tsoracle-server) — generated API reference.\n\n## Project documents\n\n- [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) — Contributor Covenant v2.1.\n- [`CONTRIBUTING.md`](CONTRIBUTING.md) — how to contribute, including the DCO sign-off requirement.\n- [`GOVERNANCE.md`](GOVERNANCE.md) — roles, decision-making, and continuity-of-access plan.\n- [`ROADMAP.md`](ROADMAP.md) — near / mid / long-term direction.\n- [`SECURITY.md`](SECURITY.md) — vulnerability reporting and response commitments.\n- [`docs/assurance-case.md`](docs/assurance-case.md) — safety and security claims with supporting evidence.\n- [`docs/release-signatures.md`](docs/release-signatures.md) — verifying release provenance and signed tags.\n\n## Maintainers\n\n- **Sebastian Thiebaud** ([@sebastianthiebaud](https://github.com/sebastianthiebaud)) — lead maintainer.\n- **Charles Merill** ([@crmerrill](https://github.com/crmerrill)) — co-maintainer.\n- **Idriss Maoui** ([@idmao](https://github.com/idmao)) — co-maintainer.\n\nSee [`GOVERNANCE.md`](GOVERNANCE.md) for roles, authorities, and the continuity-of-access plan.\n\n## Examples\n\n- [examples/embedded-server](examples/embedded-server) — embed `tsoracle-server` with the file driver in your own binary, with graceful shutdown.\n- [examples/failover-demo](examples/failover-demo) — pedagogy: watch the failover fence keep timestamps strictly monotonic across simulated leadership changes, in-process, no openraft.\n- [examples/openraft-standalone](examples/openraft-standalone) — HA: three-node multi-process cluster on a dedicated openraft, wired through [`tsoracle-driver-openraft`](crates/tsoracle-driver-openraft/) with a tonic peer transport and follower-redirect via `LeaderHint`.\n- [examples/openraft-piggyback](examples/openraft-piggyback) — HA: in-process three-node demo of the envelope pattern, where your service's existing openraft carries both your `AppData` and the tsoracle `HighWaterCommand` on a single log, with one snapshot covering both halves.\n- [examples/paxos-standalone](examples/paxos-standalone) — HA: three-node multi-process cluster on a dedicated OmniPaxos, wired through [`tsoracle-driver-paxos`](crates/tsoracle-driver-paxos/) with a tonic peer transport and follower-redirect via `LeaderHint`.\n- [examples/paxos-piggyback](examples/paxos-piggyback) — HA: in-process three-node demo of the envelope pattern on OmniPaxos, where your service's existing paxos log carries both your `AppData` and the tsoracle `HighWaterCommand`, with one snapshot covering both halves.\n- [examples/paxos-embedded](examples/paxos-embedded) — HA: single-process 3-node OmniPaxos cluster (OmniPaxos rejects single-node configs, so even \"embedded\" runs all three nodes in-memory). The closest paxos equivalent to `embedded-server`.\n- [examples/metrics-prometheus](examples/metrics-prometheus) — embed `tsoracle-server` with `metrics-exporter-prometheus` installed before the server starts, exposing `/metrics` on a separate port for Prometheus to scrape; swap recorders with a one-line change.\n\nEach example is its own crate. Build with `cargo run -p example-\u003cname\u003e`.\n\n## Contributing\n\nIssues and pull requests are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for local setup, the checks CI will run on your PR, commit message conventions, and the release process.\n\n### Contributors\n\n\u003ca href=\"https://github.com/prisma-risk/tsoracle/graphs/contributors\"\u003e\n  \u003cimg src=\"https://contrib.rocks/image?repo=prisma-risk/tsoracle\"/\u003e\n\u003c/a\u003e\n\nMade with [contrib.rocks](https://contrib.rocks).\n\n## License\n\nLicensed under [Apache-2.0](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprisma-risk%2Ftsoracle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprisma-risk%2Ftsoracle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprisma-risk%2Ftsoracle/lists"}