{"id":51669118,"url":"https://github.com/oz4462/aletheia","last_synced_at":"2026-07-14T23:00:56.585Z","repository":{"id":363407734,"uuid":"1263213000","full_name":"Oz4462/aletheia","owner":"Oz4462","description":"Entropy-aware containers for Roaring bitmaps: near-Shannon-H0 space in the dense-irregular regime where Roaring spends a flat 8 KiB, never larger than Roaring, with a faster select. Pure Rust, zero deps, no unsafe.","archived":false,"fork":false,"pushed_at":"2026-06-08T20:18:02.000Z","size":33,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-08T20:24:41.988Z","etag":null,"topics":["bitmap","bitmap-index","bitset","compression","data-structures","elias-fano","entropy-coding","information-retrieval","roaring","roaring-bitmaps","rust","rust-crate","succinct","succinct-data-structures","zero-dependencies"],"latest_commit_sha":null,"homepage":null,"language":"Rust","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/Oz4462.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":"2026-06-08T18:25:57.000Z","updated_at":"2026-06-08T20:19:22.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Oz4462/aletheia","commit_stats":null,"previous_names":["oz4462/aletheia"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/Oz4462/aletheia","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Oz4462%2Faletheia","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Oz4462%2Faletheia/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Oz4462%2Faletheia/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Oz4462%2Faletheia/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Oz4462","download_url":"https://codeload.github.com/Oz4462/aletheia/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Oz4462%2Faletheia/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35482263,"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-14T02:00:06.603Z","response_time":114,"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":["bitmap","bitmap-index","bitset","compression","data-structures","elias-fano","entropy-coding","information-retrieval","roaring","roaring-bitmaps","rust","rust-crate","succinct","succinct-data-structures","zero-dependencies"],"created_at":"2026-07-14T23:00:25.418Z","updated_at":"2026-07-14T23:00:56.573Z","avatar_url":"https://github.com/Oz4462.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# 🔓 ALETHEIA\n\n### Near-entropy containers for Roaring bitmaps\n\n*aletheia* (ἀλήθεια) — *\"unconcealment\"*: the 8 KiB a flat bitset hides, revealed.\n\n[![CI](https://github.com/Oz4462/aletheia/actions/workflows/ci.yml/badge.svg)](https://github.com/Oz4462/aletheia/actions/workflows/ci.yml)\n[![License: PolyForm--Noncommercial](https://img.shields.io/badge/License-PolyForm--Noncommercial-blue.svg)](LICENSE)\n[![unsafe: forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](src/lib.rs)\n[![runtime deps: 0](https://img.shields.io/badge/runtime%20deps-0-success.svg)](Cargo.toml)\n[![Rust: 1.94+](https://img.shields.io/badge/rust-1.94%2B-orange.svg)](https://www.rust-lang.org)\n\n\u003cbr/\u003e\n\n\u003e ### Up to **4× smaller** than Roaring — **never larger** — with a **14× faster `select`**.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd align=\"center\" width=\"25%\"\u003e\u003ch2\u003e≤ ¼\u003c/h2\u003e\u003csub\u003e\u003cb\u003ethe size\u003c/b\u003e\u003cbr/\u003eon its worst-case chunks\u003c/sub\u003e\u003c/td\u003e\n\u003ctd align=\"center\" width=\"25%\"\u003e\u003ch2\u003e0×\u003c/h2\u003e\u003csub\u003e\u003cb\u003eever larger\u003c/b\u003e\u003cbr/\u003emathematically guaranteed\u003c/sub\u003e\u003c/td\u003e\n\u003ctd align=\"center\" width=\"25%\"\u003e\u003ch2\u003e14×\u003c/h2\u003e\u003csub\u003e\u003cb\u003efaster \u003ccode\u003eselect\u003c/code\u003e\u003c/b\u003e\u003cbr/\u003evs a flat bitset\u003c/sub\u003e\u003c/td\u003e\n\u003ctd align=\"center\" width=\"25%\"\u003e\u003ch2\u003e0\u003c/h2\u003e\u003csub\u003e\u003cb\u003eruntime deps\u003c/b\u003e\u003cbr/\u003epure std · no \u003ccode\u003eunsafe\u003c/code\u003e\u003c/sub\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c/div\u003e\n\n---\n\n## ✨ At a glance\n\n- **The problem.** Roaring stores any 2¹⁶ chunk with cardinality \u003e 4096 as a **flat, uncompressed 8 KiB bitset** — no matter how compressible the chunk is. In the *dense-but-irregular* band (≈10 % or ≈90 % full) that's **2–4× bigger than the Shannon entropy floor**, and Roaring has no smaller option.\n- **The fix.** ALETHEIA adds two **Elias-Fano** encodings — of the set positions *and* of the complement — as extra candidates, and keeps the smallest per chunk.\n- **The guarantee.** Its candidate set is a **strict superset** of Roaring's, so the result is **never larger than Roaring** — and *strictly smaller* across the whole dense-irregular band.\n- **The proof.** Every claim is backed by reproducible, CPU-only, zero-dependency tests — including an independent cross-check against the real [`roaring`](https://crates.io/crates/roaring) crate's actual serialized bytes.\n\n```rust\nuse aletheia::Container;\n\n// a dense-but-irregular chunk Roaring would store as a flat 8 KiB bitset\nlet values: Vec\u003cu16\u003e = (0u16..65_535).step_by(7).collect();\nlet c = Container::from_sorted(\u0026values);\n\nassert!(c.size_bytes() \u003c 8192);   // ← Roaring spends a full 8192 here\n```\n\n---\n\nAn **entropy-aware container** for Roaring-style bitmaps. It closes a real,\nliterature-grounded gap: Roaring stores any 2¹⁶ chunk with cardinality \u003e 4096\nas a **flat, uncompressed 8 KiB bitset**, no matter the chunk's entropy. In the\ndense-but-irregular regime (too many runs for a run container, too dense for an\narray) that bitset can be 2–4× larger than the Shannon-H₀ floor — and Roaring\nhas no smaller option there.\n\nALETHEIA adds two Elias-Fano candidate encodings (of the set positions, and of\nthe **complement** for the symmetric high-density tail) to the container\nselector and keeps the smallest representation per chunk. Because the candidate\nset is a strict superset of Roaring's (array, run, bitset **+** EF-set,\nEF-clear), the result is **never larger than the container Roaring would\nchoose**, and strictly smaller across the dense-irregular band.\n\n## 👥 Who is this for?\n\nAnyone who stores **large sets of integer IDs** with Roaring and is **memory- or\nstorage-bound** — and whose access pattern leans on `select`/`rank`/`iterate`\nrather than point membership:\n\n| Domain | Typical use |\n|---|---|\n| 🔎 **Search engines** (Lucene, Elasticsearch, Solr) | inverted-index posting lists — \"which documents contain term *X*\" |\n| 📊 **Analytical / OLAP databases** (Druid, ClickHouse, Pinot) | bitmap indexes \u0026 fast filters over billions of rows |\n| ⚡ **Big-data engines** (Spark) | set operations over huge ID sets |\n| 🚩 **Feature flags / permissions** | \"which users are in segment *Y*\" |\n\nBest fit: **medium-cardinality columns** with many chunks in the dense-irregular\nband, where shrinking each chunk means more fits in cache/RAM and less I/O.\n**Not the goal:** membership-heavy hot loops — see the honest speed tradeoff below.\n\n## 🚀 Quickstart\n\n```toml\n[dependencies]\naletheia = { git = \"https://github.com/Oz4462/aletheia\" }\n```\n\n```rust\nuse aletheia::Container;\n\n// a dense-but-irregular chunk Roaring would store as a flat 8 KiB bitset\nlet values: Vec\u003cu16\u003e = (0u16..65_535).step_by(7).collect();\nlet c = Container::from_sorted(\u0026values);\n\nassert!(c.contains(7));\nassert_eq!(c.iter().len(), c.cardinality() as usize);\nprintln!(\"{} bytes (Roaring would use 8192)\", c.size_bytes());\n```\n\n\u003e **Tip:** `from_sorted` trusts that its input is sorted \u0026 distinct (the fast\n\u003e path). If you're not sure, use `Container::from_values`, which sorts and\n\u003e de-duplicates for you.\n\n### Full `u32` bitmap, set operations \u0026 serialization\n\n```rust\nuse aletheia::Bitmap;\n\nlet a = Bitmap::from_values(\u0026[1, 2, 3, 1_000_000, 5_000_000]);\nlet b = Bitmap::from_values(\u0026[3, 4, 1_000_000]);\n\nassert_eq!((\u0026a \u0026 \u0026b).to_vec(), vec![3, 1_000_000]);    // ∩ (operators or .and())\nassert_eq!((\u0026a | \u0026b).cardinality(), 6);                // ∪\nassert_eq!((\u0026a ^ \u0026b).to_vec(), vec![1, 2, 4, 5_000_000]);\nassert_eq!((\u0026a - \u0026b).to_vec(), vec![1, 2, 5_000_000]);\n\n// rank / select / mutation over the whole u32 universe\nassert_eq!(a.rank(1_000_000), 3);\nassert_eq!(a.select(0), Some(1));\nlet mut m = a.clone();\nm.insert(7);\nm.remove(2);\n\n// ALETHEIA's own compact format (validated: malformed bytes are refused\n// with a DecodeError naming the violated check — never a panic)\nlet bytes = a.serialize();\nassert_eq!(Bitmap::deserialize(\u0026bytes).unwrap(), a);\n\n// the official Roaring interop format — byte-identical to what\n// roaring-rs itself writes, readable by every Roaring implementation\nlet portable = a.serialize_portable();\nassert_eq!(Bitmap::deserialize_portable(\u0026portable).unwrap(), a);\n```\n\n`Bitmap` is the Roaring-shaped wrapper: one entropy-aware `Container` per 2¹⁶\nchunk, so the never-larger-than-Roaring guarantee holds chunk by chunk.\nBoth deserializers validate everything (magic/version, per-container\nstructure, cardinality cross-checks, ordering) and refuse malformed input\nwith a typed `DecodeError` — never a panic, never attacker-driven allocation.\n\n## 📐 Verified result (reproducible, CPU-only, zero dependencies)\n\n**Space** — `cargo run --release --example space_report`:\n\n| density | card  | roaring | aletheia | kind    | ratio | H₀ floor |\n|--------:|------:|--------:|---------:|---------|------:|---------:|\n| 0.065   | 4260  | 8192    | **3259** | EfSet   | 2.51× | 2843     |\n| 0.111   | 7260  | 8192    | **4783** | EfSet   | 1.71× | 4115     |\n| 0.199   | 13073 | 8192    | 7187     | EfSet   | 1.14× | 5906     |\n| 0.299   | 19575 | 8192    | 8192     | Bitset  | 1.00× | 7207     |\n| 0.496   | 32525 | 8192    | 8192     | Bitset  | 1.00× | 8192     |\n| 0.851   | 55750 | 8192    | **5931** | EfClear | 1.38× | 4982     |\n| 0.935   | 61305 | 8192    | **3244** | EfClear | 2.53× | 2829     |\n| 0.964   | 63147 | 8192    | **2062** | EfClear | 3.97× | 1850     |\n| 0.985   | 64583 | 3766¹   | **982**  | EfClear | 3.84× | 898      |\n\n¹ at this density Roaring picks a *run* container (3766 B), not a bitset —\nALETHEIA still beats it 3.84×. The container tracks the H₀ floor to within\n~10–16% across the band, and degrades to exactly Roaring's bitset (1.00×, never\nworse) in the middle.\n\n**Speed** — `cargo run --release --example speed_report` (card 6486, d≈0.10,\nmin-of-7):\n\n\u003e 💡 **The tradeoff in one line:** ALETHEIA is a *different Pareto point*, not a\n\u003e free lunch — **smaller and far faster at `select`**, at the cost of slower\n\u003e point membership.\n\n| op       | aletheia | bitset  | factor          |\n|----------|---------:|--------:|-----------------|\n| contains | ~73 ns   | ~1.6 ns | **~45× slower** |\n| select   | ~60 ns   | ~893 ns | **~15× faster** |\n| iter     | ~4.6 ns  | ~2.2 ns | ~2× slower      |\n\n(Numbers swing between runs on a clock-scaling CPU; min-of-7 methodology,\nfresh run 2026-07-04 — full tables with machine info in `docs/CLAIMS.md`.)\n`contains` started at 176 ns and a single-`select0` fast path brought it to\n`select`-parity — both are now dominated by one directory lookup. Set\noperations cost ~0.4–1.1 ms per 2¹⁶ chunk (materialize + full re-selection —\nthe documented single-code-path trade-off).\n\nThis is the honest tradeoff, not a hidden one: ALETHEIA is a **different Pareto\npoint**, not a free lunch. It is smaller *and* dramatically faster at `select`\n(Roaring's bitset `select` is an O(U) popcount scan), at the cost of ~40× slower\nmembership (a directory lookup vs a flat bit test). It fits memory-constrained,\nselect-/rank-/iterate-heavy, membership-light workloads — e.g. bitmap-index\nposting lists on medium-cardinality columns.\n\n## ✅ Correctness\n\n`cargo test` runs a deterministic splitmix64 fuzz oracle that verifies every\n`contains`/`rank`/`select`/`iter` against a brute-force `Vec\u003cbool\u003e` truth table\nacross the **full 65536-value universe** for ~110 configurations (all\ndensities, uniform + clustered + edge cases), and asserts the never-larger-than-\nRoaring guarantee on every one. `space_win.rs` additionally asserts the ≥1.5×\nwin at the band edges, so CI fails if any change erases it. A failure prints the\nexact seed to replay.\n\nAs a second, independent witness, `roaring_crosscheck.rs` checks every\ncardinality, full-universe membership, iteration, and set-operation result\nagainst the real `roaring` crate (roaring-rs — a separate codebase), and\nconfirms never-larger against its *actual serialized output* — including a\nwhole-bitmap footprint check (the measured edge-band ratios, 1.2×–6.5× on\nthe current run, are printed by the test; the CI-gated bound is\nnever-larger plus the ≥1.5× band-edge floor in `space_win.rs`). (`roaring` is a dev-only dependency; the\nlibrary itself stays pure std with zero runtime dependencies.)\n\nBeyond that: `setops_oracle.rs` proves never-larger **after every set\noperation** across an 8×8 density grid; `malformed.rs` fuzzes both\ndeserializers with random buffers, single-byte mutations of every container\nkind, and every truncation point (refusal or provable self-consistency,\nnever a panic); `portable.rs` holds the interop format **byte-identical**\nto roaring-rs's own output across eight shapes and round-trips both\ndirections. The full claim→evidence map is `docs/CLAIMS.md`.\n\n```\ncargo test                                       # correctness + win assertions\ncargo test --test roaring_crosscheck -- --nocapture  # vs real roaring-rs\ncargo clippy --all-targets -- -D warnings        # lint-clean\ncargo run --release --example space_report       # the space table above\ncargo run --release --example speed_report       # the honest cost table above\n```\n\n## 🔬 How it works\n\nA value `v` splits into `high = v \u003e\u003e l` and `l` low bits. Low bits are packed;\nhigh bits are a count-unary bitvector (`m + max_high + 1` bits) with a coarse\n512-bit-block prefix-popcount directory for near-constant-time `select1`,\n`select0` and `rank1`. With `l = floor(log2(U/m))` the structure sits within a\ncouple of bits per element of the H₀ floor. The complement variant captures the\nhigh-density tail symmetrically.\n\n## 🗺️ Scope\n\nThis started as an MVP proving the invention with exact, unfakeable numbers; it\nnow also ships the bitmap machinery built on top of it.\n\n**Done in v0.2.0:**\n* **Set operations** — `and` / `or` / `xor` / `andnot` (+ `\u0026 | ^ -` operator\n  traits) on both `Container` and `Bitmap`, via materialize-to-bitset with the\n  result re-run through the full selector (so the never-larger guarantee holds\n  on results too — proven per-op by `tests/setops_oracle.rs`).\n* **Multi-chunk `Bitmap`** — the Roaring-shaped `u32` wrapper: one container per\n  2¹⁶ chunk, with `contains` / `rank` / `select` / `min` / `max`, mutation\n  (`insert` / `remove` with automatic encoding re-selection), lazy iteration,\n  and the std traits (`FromIterator`, `Extend`, `IntoIterator`, `PartialEq`).\n* **Native serialization** — a compact, self-describing, versioned byte format\n  (magic `ALTH`) for both `Container` and `Bitmap`. Deserialization validates\n  every structural invariant and cross-checks the stored cardinality against\n  the payload; malformed input is refused with a typed `DecodeError`, never a\n  panic, and untrusted lengths never drive allocation (fuzz-proven).\n* **Roaring-portable serialization** — the official RoaringFormatSpec,\n  **byte-identical** to what roaring-rs writes (golden-tested) and readable by\n  every Roaring implementation; the validating reader re-selects each chunk\n  back into ALETHEIA's smallest encoding.\n* **`SparseVec\u003cT\u003e`** — compressed `u32` indices paired with values, for sparse\n  workloads (see below). Optional `serde` / `serde_bincode` features (default\n  build stays zero-dependency).\n* **Cross-checked** — `Bitmap` and every set operation are validated against the\n  real `roaring` crate over the full `u32` universe, not just our own oracle.\n\n**Done since v0.1.0:** a single-`select0` `contains` fast path (176 ns → ~73 ns);\na hardened `EliasFano::select` (bounds-checked rather than spinning on an\nout-of-range index).\n\n**Measured \u0026 rejected:**\n* A BMI2 `PDEP` broadword `select_in_u64` — 2.8× *slower* than the naive\n  clear-lowest-bit loop here (per-call feature dispatch plus the\n  `#[target_feature]` inlining barrier outweigh `PDEP`; within-word select is\n  only ~15 ns of a ~67 ns `select` — the directory block search is the floor).\n* An **RRR candidate** for the mid-band — the math doesn't pay off. At d≈0.3,\n  15-bit RRR blocks cost ~9.4 KB (the 4-bit-per-block class header dominates),\n  *worse* than the flat 8 KB bitset; even 63-bit blocks reach only ~8.0 KB, a\n  ~2 % win that a sampled rank/select directory erases. Where the bitset already\n  ties the H₀ floor (d≈0.5) RRR can't help at all. Not worth the complexity for a\n  break-even encoding — revisit only with a fundamentally cheaper class coding.\n  Re-measured rigorously against a pre-registered rule (63-bit blocks,\n  combinadic offsets, a directory every 32 blocks; full uniform density\n  sweep, 3 seeds/step): win peaked at 12.7% (d≈0.76), but the widest\n  contiguous ≥10%-win band was only 0.04 wide against the pre-registered\n  0.05 threshold — rejected on the numbers; full sweep in `docs/DECISIONS.md`.\n\n**Out of scope** (deliberately, with reasons in `docs/DECISIONS.md` and\n`docs/AUDIT.md` §6): the Roaring 64-bit extension format, SIMD (`unsafe` is\nforbidden), specialized non-materializing set-op kernels, in-library\nparallelism.\n\n## 🧠 Sparse indices for LLM / agent workloads\n\n`SparseVec\u003cT\u003e` pairs a compressed `Bitmap` index set with a parallel value\narray — positions compress toward the entropy floor, values stay contiguous:\n\n```rust\nuse aletheia::SparseVec;\n\n// pruned weights: (index, value) pairs in any order\nlet sv = SparseVec::from_pairs(vec![(7u32, 0.5f32), (1 \u003c\u003c 20, 0.25), (42, 0.9)]);\nassert_eq!(sv.len(), 3);\nassert_eq!(sv.get(1), Some((42, \u00260.9)));            // i-th pair, index order\nlet total: f32 = sv.iter().map(|(_, v)| v).sum();   // lazy iteration\n```\n\nTypical fits: pruned LLM weight matrices (\u003e90 % sparse), sparse KV-cache\npositions, MoE routing masks, large agent memory ID sets — build once, then\n`select`/`rank`/iterate on the compressed form.\n\n## 📦 Optional features\n\n| Feature | Adds | Default |\n|---|---|---|\n| *(none)* | the whole library — pure std, zero dependencies | ✅ |\n| `serde` | `Serialize`/`Deserialize` for `Bitmap` + `SparseVec\u003cT\u003e` (value-list representation; hostile input is validated, never trusted) | off |\n| `serde_bincode` | `SparseVec::to_bytes` / `from_bytes` (both return `Result` — no silent failure) | off |\n\n## 🔍 Audit trail\n\nWritten to be walked file-by-file by an external reviewer:\n\n| Document | Contents |\n|---|---|\n| [`docs/AUDIT.md`](docs/AUDIT.md) | the ~90-minute verification runbook, incl. a falsify-each-claim attack table |\n| [`docs/CLAIMS.md`](docs/CLAIMS.md) | every claim → reproduction command → guarding test → last measured value |\n| [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) | file-by-file walkthrough, exact byte layouts, data flow, cost model |\n| [`docs/DECISIONS.md`](docs/DECISIONS.md) | every non-obvious choice with alternatives and evidence — incl. the full RRR rejection experiment |\n\n## 📄 License\n\n[PolyForm Noncommercial 1.0.0](LICENSE), © Ozan Kuesmez. In short: the\nsource is open; use, modify, and share freely for **any noncommercial\npurpose** (personal projects, research, education, evaluation — charities\nand public institutions included). **Commercial use requires a separate\nlicense — just ask** (GitHub issue or profile contact). Copyright and\nownership remain with the author; required notices must be preserved.\nDual licensing: one codebase, free for the community, licensed for\nbusiness.\n\n\u003cdiv align=\"center\"\u003e\u003csub\u003eBuilt in Rust 🦀 · zero runtime dependencies · \u003ccode\u003e#![forbid(unsafe_code)]\u003c/code\u003e\u003c/sub\u003e\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foz4462%2Faletheia","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foz4462%2Faletheia","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foz4462%2Faletheia/lists"}