# Botanica
**Botanica is free forever.** No asterisks, no "free for now," no pivot to paid.
[](https://translifeline.org/) [](https://www.thetrevorproject.org/) [](https://github.com/sponsors/Michael-A-Kuykendall)
π Support Botanica: [sponsor on GitHub](https://github.com/sponsors/Michael-A-Kuykendall) β 100% of support keeps it free forever. See [SPONSORS.md](SPONSORS.md). π
## Support
This project is a safe space. Trans rights are human rights.
If you or someone you love needs support:
- [The Trevor Project](https://www.thetrevorproject.org/) β 24/7 for LGBTQ+ young people. Call 1-866-488-7386 or text START to 678-678
- [Trans Lifeline](https://translifeline.org/) β peer support run by and for trans people. US: 877-565-8860
- [988 Suicide & Crisis Lifeline](https://988lifeline.org/) β call or text 988
---
**Cultivated-plant knowledge base (Rust + DuckDB) β with a loaded public seed.**
Open-source βstrawβ for human agricultural / garden plant data: taxonomy, traits, names, provenance. The **schema for personal inventory (L3) is empty by design**; the **knowledge tables are filled** and shipped as parquet. Apps (e.g. [Budsy](https://github.com/Michael-A-Kuykendall/budsy)) write L3 locally.
| | |
|--|--|
| **Status** | **Data loaded** β cultivated KEEP product on GitHub (`data-v0.2.0`) |
| **Product species (KEEP)** | **~18,566** cultivated/germplasm-linked taxa |
| **Warehouse** | ~62.5k USDA Species rows (reference; not the public product) |
| **EN common names (KEEP)** | **~91%** of KEEP |
| **Care-rich core** | **~2.1k** with β₯3 practical Tier1 fields (soil/moisture/height/toxicity) |
| **Engine** | DuckDB locally; **shareable source of truth = parquet tables** |
| **License** | MIT OR Apache-2.0 (code); data PD / CC BY / CC0 β see MANIFEST |
| **Release** | [data-v0.2.0](https://github.com/Michael-A-Kuykendall/botanica/releases/tag/data-v0.2.0) |
Botanica stays free/open for the **knowledge + schema**. Product UX (camera, plant ID, sync) lives in Budsy.
**Open source, not open contribution.** Sole developer: Michael A. Kuykendall. Unsolicited PRs are closed by default. See [CONTRIBUTING.md](CONTRIBUTING.md) and [GOVERNANCE.md](GOVERNANCE.md). Optional support: [SPONSORS.md](SPONSORS.md).
## This is a loaded database, not an empty shell
| Layer | Content | Public seed |
|-------|---------|-------------|
| **L1** Taxonomy | families, genera, species, external IDs | **Yes β filled** |
| **L2** Knowledge | traits, cultivation requirements, vernaculars, synonyms, distribution, provenance | **Yes β filled** (depth varies) |
| **L3** Inventory | *your* plants, photos, care logs | **Schema only (0 rows)** |
### Whatβs in the public KEEP product right now
| Metric | Value |
|--------|------:|
| Species | ~18,566 |
| Families / genera | ~532 / ~3,687 |
| Vernacular name rows | ~247k |
| Trait rows | ~26k |
| Cultivation requirement rows | ~26k |
| Synonyms | ~35k |
| English vernacular coverage | ~91% of KEEP |
| Practical Tier1 (β₯3 of soil/moisture/height/toxicity) | ~11% of KEEP (~2.1k deep core) |
| Hardiness (Wikidata free pass) | ~545 species |
| Uses / cultivars | 0 (not loaded yet) |
**KEEP membership rule:** hort payload (traits / cult.req / uses) **or** GRIN / FAOSTAT allowlist hit.
Full USDA wild bulk is **not** the product β ~44k empty rows were filtered out.
Sources already in: **USDA PLANTS**, **GRIN taxonomy**, **FAOSTAT** crop labels, **POWO**, **GBIF** vernaculars, **Wikidata** hardiness (sparse).
## Get the data (GitHub columnar)
One **directory per table** under `data/silver_keep/` β each table split into small
`part-NNNNNN.parquet` files (every part β€ 40 MB, so we never need Git LFS). DuckDB reads
a whole table by globbing the parts. See `docs/DATA_PARQUET.md`.
| Table dir (examples) | ~Total |
|-----------------|------:|
| `vernacular_names/` | ~16 MB |
| `distribution_regions/` | ~57 MB |
| `species_identifiers/` | ~16 MB |
| `species/` | ~4 MB |
| `traits/` / `cultivation_requirements/` | ~1 MB each |
| **All KEEP tables together** | **~126 MB** |
```bash
# after clone
duckdb -c "SELECT count(*) FROM read_parquet('data/silver_keep/species/*.parquet');"
duckdb -c "SELECT scientific_name FROM read_parquet('data/silver_keep/species/*.parquet') WHERE scientific_name ILIKE 'Monstera%' LIMIT 10;"
```
Or download the [Release asset](https://github.com/Michael-A-Kuykendall/botanica/releases/tag/data-v0.2.0).
Rebuild KEEP after fills:
```bash
python scripts/export_keep_set.py --tag
```
## Provenance (where data comes from)
Every knowledge fact is labeled by source. See **[\docs/PROVENANCE.md\](docs/PROVENANCE.md)** for licenses, tables, and queries.
| Source | License | Role |
|--------|---------|------|
| USDA PLANTS | Public domain | Taxonomy + most care traits |
| GRIN | Free + attribution | Cultivated / germplasm membership |
| FAOSTAT | Free + attribution | Commercial crop signal |
| POWO (Kew) | CC BY 4.0 | Synonyms, distribution, lifeform/climate |
| GBIF | CC BY 4.0 | Vernacular names |
| Wikidata | CC0 | Sparse hardiness |
Row-level: \provenance\ + \source\ columns on L2 tables. Batch-level: \data/manifests/*.json\.
## Iterative updates (houseplants, crops, β¦)
See **[`docs/ITERATIVE_FILL.md`](docs/ITERATIVE_FILL.md)**.
```bash
# Whatβs missing from a priority list (starter houseplants included)?
python scripts/gap_report.py
# β data/manifests/gap-houseplants.txt then scrape only those gaps
```
Loop: **score β gap list β fail-fast scrape β merge β export KEEP β quality JSON β commit parquets**.
## What works in code today
- Family β genus β species types and CRUD
- DuckDB migrations for L1/L2 + empty L3
- Seed build / KEEP export scripts
- Optional network ingest feature (`--features ingestion`)
## Gaps / not yet
- Deep care fields on most of the 18.5k (only ~2k rich)
- Hardiness / sunlight completeness
- Uses, cultivars
- Some common houseplants still missing from KEEP (see `gap_report.py`)
## Docs
| Doc | Topic |
|-----|--------|
| [`data/README.md`](data/README.md) | Artifacts + load |
| [`docs/ITERATIVE_FILL.md`](docs/ITERATIVE_FILL.md) | Agile fill loop |
| [`docs/RELEASE_PROCESS.md`](docs/RELEASE_PROCESS.md) | Tagging / Release |
| [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) | Design |
| [`docs/WORKSTREAMS.md`](docs/WORKSTREAMS.md) | Work queue map |
## Quick start (Rust API)
```toml
[dependencies]
botanica = { version = "0.3", path = "..." } # or crates.io when published truthfully
tokio = { version = "1", features = ["full"] }
```
```rust
use botanica::{BotanicalDatabase, Family, Genus, Species};
use botanica::queries::{family, genus, species};
#[tokio::main]
async fn main() -> botanica::Result<()> {
let db = BotanicalDatabase::memory().await?;
db.migrate().await?;
let rosaceae = Family::new("Rosaceae".into(), "Juss.".into());
family::insert_family(&db, &rosaceae).await?;
let rosa = Genus::new(rosaceae.id, "Rosa".into(), "L.".into());
genus::insert_genus(&db, &rosa).await?;
let briar = Species::new(
rosa.id,
"rubiginosa".into(),
"L.".into(),
Some(1753),
Some("LC".into()),
);
species::insert_species(&db, &briar).await?;
let found = family::get_families_by_name(&db, "Rosaceae").await?;
println!("families: {}", found.len());
Ok(())
}
```
Prefer **parquet KEEP** for real species data; the snippet above only demos the in-memory API.
## Features
| Feature | Purpose |
|---------|---------|
| `ingestion` | HTTP/CSV importers + CLI bins |
| `darwin-core` | DwC types (partial; not a full GBIF stack) |
| `conservation` | IUCN types (mock client β not production) |
| `full` | Turns on optional pro flags (still incomplete) |
Default features: **none**.
## Related
- **Budsy** β app lifecycle, inventory UI, CrabCamera
- **CrabCamera** β desktop capture (plant-agnostic)
## License
MIT OR Apache-2.0. Knowledge stays open. Build cool things.