docs llms.txt codecov pypi cran stars downloads

LaminDB: data management for traceable, multimodal AI .md

LaminDB is an open-source data management tool that makes it easy to query, trace & govern datasets across diverse storage formats and locations. It gives you context through annotations, memory through lineage, and governance through branching and versioning. It uses a scalable lakehouse architecture that integrates files, tables, arrays, ontologies, and notes. It supports biological formats and registries by the creators of Scanpy.

Why?
  1. Untraceable results cannot be trusted, especially in the age of agents.

  2. Without effective access to multimodal data, models burn tokens or fail entirely.

  3. We want to govern changes to data like we govern changes to code with git.

Especially in life sciences, teams need end-to-end traceability for GxP compliance (21 CFR Part 11 and EU Annex 11).

lamindb-schematic

How?

  • lineage → trace results across agent sessions, notebooks, scripts & workflows

  • lakehouse → query across many datasets, manage tables & arrays schema-based and ACID

  • LIMS & ELN → unified schema-based records management with support for ontologies & notes

  • FAIR datasets → validate & annotate files, DataFrame, AnnData, SpatialData, zarr, …

  • governancemanage changes via branching & by versioning data + code

Architecture?

  • zero lock-in → uses open standards (metadata in SQLite/Postgres, data in parquet, zarr, etc.)

  • scalable → hit storage & database directly through your pydata or R stack, no REST API involved

  • simplepip install lamindb or install.packages('laminr') - no Docker required, no separate backend

  • unified → federate data across storage locations (local, S3, GCP, …) in any database

  • distributed → federate data zero-copy & lineage-aware across databases

  • reproducibletrack agent traces, source code & compute environments

  • idempotentre-run logic without worries about duplications or overwrites

  • integrationsbio ontologies git, nextflow, vitessce, redun, and more

  • extensible → create custom plug-ins based on the Django ORM, the basis for LaminDB’s registries

Who?

Scientists and engineers at leading research institutions and biotech companies, including:

  • Industry → Pfizer, Altos Labs, Ensocell Therapeutics, …

  • Academia & Research → scverse, DZNE (National Research Center for Neuro-Degenerative Diseases), Helmholtz Munich (National Research Center for Environmental Health), …

  • Research Hospitals → Global Immunological Swarm Learning Network: Harvard, MIT, Stanford, ETH Zürich, Charité, U Bonn, Mount Sinai, …

From personal research projects to pharma-scale deployments managing petabytes of data across:

entities

OOMs

observations & datasets

10¹² & 10⁶

runs & transforms

10⁹ & 10⁵

proteins & genes

10⁹ & 10⁶

biosamples & species

10⁵ & 10²

UI, permissions, audit logs? LaminHub is a collaboration hub built on LaminDB similar to how GitHub is built on git.

Quickstart

To install the Python package with recommended dependencies, use:

pip install lamindb
Install with minimal dependencies.

The lamindb package adds data-science related dependencies through the [full] extra, see here.

For a minimal install of the lamindb namespace, use:

pip install lamindb-core

Agent? The lamindb skill ships with the lamindb package at .agents/skills/. Pure markdown docs ship with the package in docs/ and are available from the web via llms.txt.

Query databases & datasets

You can browse public databases at lamin.ai/explore. To access laminlabs/cellxgene, run:

import lamindb as ln

db = ln.DB("laminlabs/cellxgene")  # a database object for queries
df = db.Artifact.to_dataframe()    # a dataframe listing datasets & models
 connected lamindb: anonymous/test-readme
! truncated query result to limit=20 Artifact objects

To get a specific dataset, run:

artifact = db.Artifact.get("BnMwC3KZz0BuKftR")  # a metadata object for a dataset
artifact.describe()                             # describe the context of the dataset
Artifact: cell-census/2025-11-08/h5ads/82346769-8733-485e-ab49-f14923d2b5bc.h5ad (2025-11-08)
|   description: OPCs
├── uid: BnMwC3KZz0BuKftR0001            run: 7FgSsR6 (annotate-register-new-release.py)
kind: None                           otype: AnnData                                 
hash: hu09QNaDv3RLVyvrAlOFfg         size: 63.2 MB                                  
branch: main                         space: all                                     
created_at: 2026-02-17 13:38:30 UTC  created_by: zethson                            
n_observations: 3324.0               schema: CELLxGENE AnnData of ontology_id       
├── storage/path: s3://cellxgene-data-public/cell-census/2025-11-08/h5ads/82346769-8733-485e-ab49-f14923d2b5bc.h5ad
├── Dataset features
├── obs (11.0)                                                                                                 
│   assay_ontology_term_id         bionty.ExperimentalFactor.ontology…  EFO:0009922                            
│   cell_type_ontology_term_id     bionty.CellType.ontology_id          CL:0002453                             
│   development_stage_ontology_t…  bionty.DevelopmentalStage.ontology…  HsapDv:0000147, HsapDv:0000162, HsapDv…
│   disease_ontology_term_id       bionty.Disease.ontology_id           MONDO:0004975, MONDO:0800027, PATO:000…
│   donor_id                       str                                                                         
│   is_primary_data                ULabel                                                                      
│   self_reported_ethnicity_onto…  bionty.Ethnicity.ontology_id         HANCESTRO:0568, HANCESTRO:0590, unknown
│   sex_ontology_term_id           bionty.Phenotype.ontology_id         PATO:0000383, PATO:0000384             
│   suspension_type                ULabel                               nucleus                                
│   tissue_ontology_term_id        bionty.Tissue.ontology_id|bionty.C…  UBERON:0000451, UBERON:0016528, UBERON…
│   tissue_type                    ULabel                               tissue                                 
├── uns (1.0)                                                                                                  
│   organism_ontology_term_id      bionty.Organism.ontology_id          NCBITaxon:9606                         
└── var (2.0)                                                                                                  
    feature_is_filtered            bool                                                                        
    var_index                      bionty.Gene.ensembl_gene_id[source…                                         
└── Labels
    └── .recreating_runs               Run                                  2026-02-17 13:43:36.195894+00:00, 2026…
        .ulabels                       ULabel                               nucleus, tissue                        
        .organisms                     bionty.Organism                      human                                  
        .tissues                       bionty.Tissue                        prefrontal cortex, white matter of fro…
        .cell_types                    bionty.CellType                      oligodendrocyte precursor cell         
        .diseases                      bionty.Disease                       Alzheimer disease, leukoencephalopathy…
        .phenotypes                    bionty.Phenotype                     female, male                           
        .experimental_factors          bionty.ExperimentalFactor            10x 3' v3                              
        .developmental_stages          bionty.DevelopmentalStage            81-year-old stage, 53-year-old stage, …
        .ethnicities                   bionty.Ethnicity                     African American, unknown, European Am…
See the output.

Access the content of the dataset via:

local_path = artifact.cache()  # return a local path from a cache
adata = artifact.load()        # load object into memory
! run input wasn't tracked, call `ln.track()` and re-run
! run input wasn't tracked, call `ln.track()` and re-run

For broader queries of cellxgene, see docs.lamin.ai/cellxgene.

Save files & folders

You can create a LaminDB instance at lamin.ai and invite collaborators. To connect to an existing instance, run:

lamin login
lamin connect account/name  # tip: add flag `--here` to scope to current directory
Or init a new instance instead (no login required).
lamin init --storage ./quickstart-data --modules bionty

On the terminal and in a Python session, LaminDB will now auto-connect. For more configuration, see docs.lamin.ai/setup.

To save a file or folder via the API:

import lamindb as ln
# → connected lamindb: account/instance

open("sample.fasta", "w").write(">seq1\nACGT\n")        # create dataset
ln.Artifact("sample.fasta", key="sample.fasta").save()  # save dataset
! no run & transform got linked, call `ln.track()` & re-run
! calling anonymously, will miss private instances
Artifact(uid='DJuc2fG1vdkKomLs0000', key='sample.fasta', description=None, suffix='.fasta', kind=None, otype=None, size=11, hash='83rEPcAoBHmYiIuyBYrFKg', n_files=None, n_observations=None, extra_data=None, branch_id=1, created_on_id=1, space_id=1, storage_id=1, run_id=None, schema_id=None, created_by_id=1, created_at=2026-08-07 16:20:31 UTC, is_locked=False, version_tag=None, is_latest=True)

To save a file or folder via the CLI, run:

lamin save sample.fasta --key sample.fasta

To load an artifact via the CLI into a local cache, run:

lamin load --key sample.fasta

Read more about the CLI: docs.lamin.ai/cli.

Trace data, code & agents

The lamindb skill ships with the lamindb package at .agents/skills/. Ask your coding agent to copy it to wherever it reads skills from — .claude/skills/ for Claude Code, .agents/skills/ for GitHub Copilot — so that it automatically tracks agent sessions.

To create a dataset in a script or notebook while tracking source code, inputs, outputs, logs, and environment:

import lamindb as ln
# → connected lamindb: account/instance

ln.track()                                              # track code execution
open("sample.fasta", "w").write(">seq1\nACGT\n")        # create dataset
ln.Artifact("sample.fasta", key="sample.fasta").save()  # save dataset
ln.finish()                                             # mark run as finished
 created Transform('iI6Kty6mg6jF0000', key='README.ipynb'), started new Run('RXwpzbmf2n1Ylbdk') at 2026-08-07 16:20:31 UTC
 notebook imports: anndata==0.13.2 bionty==2.4.3 lamindb numpy==2.5.1 pandas==2.3.3
 tip: to identify the notebook across renames, pass the uid: ln.track("iI6Kty6mg6jF")
 returning artifact with same hash: Artifact(uid='DJuc2fG1vdkKomLs0000', key='sample.fasta', description=None, suffix='.fasta', kind=None, otype=None, size=11, hash='83rEPcAoBHmYiIuyBYrFKg', n_files=None, n_observations=None, extra_data=None, branch_id=1, created_on_id=1, space_id=1, storage_id=1, run_id=None, schema_id=None, created_by_id=1, created_at=2026-08-07 16:20:31 UTC, is_locked=False, version_tag=None, is_latest=True); to track this artifact as an input, use: ln.Artifact.get()
! run was not set on Artifact(uid='DJuc2fG1vdkKomLs0000', key='sample.fasta', description=None, suffix='.fasta', kind=None, otype=None, size=11, hash='83rEPcAoBHmYiIuyBYrFKg', n_files=None, n_observations=None, extra_data=None, branch_id=1, created_on_id=1, space_id=1, storage_id=1, run_id=None, schema_id=None, created_by_id=1, created_at=2026-08-07 16:20:31 UTC, is_locked=False, version_tag=None, is_latest=True), setting to current run
! cells [(4, 6), (8, 11), (22, 24)] were not run consecutively
 finished Run('RXwpzbmf2n1Ylbdk') after 2s at 2026-08-07 16:20:34 UTC

Running this snippet as a script (python create_fasta.py) produces the following data lineage:

artifact = ln.Artifact.get(key="sample.fasta")  # get artifact by key
artifact.describe()      # context of the artifact
artifact.view_lineage()  # fine-grained lineage
Artifact: sample.fasta (0000)
├── uid: DJuc2fG1vdkKomLs0000            run: RXwpzbm (README.ipynb)
hash: 83rEPcAoBHmYiIuyBYrFKg         size: 11 B                 
branch: main                         space: all                 
created_at: 2026-08-07 16:20:31 UTC  created_by: anonymous      
└── storage/path: /home/runner/work/lamindb/lamindb/test-readme/.lamindb/DJuc2fG1vdkKomLs0000.fasta
_images/23ba36a4c57a5857d3e22274f156fb246cd2eee3ff206fc352fe30d3fb029750.svg

Watch a mini video: youtu.be/yK3ODFZLL1A

Access run & transform.
run = artifact.run              # get the run object
transform = artifact.transform  # get the transform object
run.describe()                  # context of the run
Run: RXwpzbm (README.ipynb)
├── uid: RXwpzbmf2n1Ylbdk                transform: README.ipynb (0000)      
started_at: 2026-08-07 16:20:31 UTC  finished_at: 2026-08-07 16:20:34 UTC
status: completed                                                        
branch: main                         space: all                          
created_at: 2026-08-07 16:20:31 UTC  created_by: anonymous               
└── environment: dpZzKlI
    aiobotocore==3.9.0
    aiohappyeyeballs==2.7.1
    aiohttp==3.14.3
    aioitertools==0.13.0
    │ …
transform.describe()  # context of the transform
Transform: README.ipynb (0000)
|   description: LaminDB: data management for traceable, multimodal AI
├── uid: iI6Kty6mg6jF0000                                     
hash: qu4MWboDFlheftTyytXftw         type: notebook       
branch: main                         space: all           
created_at: 2026-08-07 16:20:31 UTC  created_by: anonymous
└── source_code: 
    # %% [markdown]
    # [![docs](https://img.shields.io/badge/docs-yellow)](https://docs.lamin.ai) [![ …
    #
    #
    #
    # LaminDB is an open-source data management tool that makes it easy to query, tr …
    #
    # <details>
    # <summary>Why?</summary>
    #
    # 1. Untraceable results cannot be trusted, especially in the age of agents.
    # 2. Without effective access to multimodal data, models burn tokens or [fail en …
    # 3. We want to govern changes to data like we govern changes to code with git.
    #
    # Especially in life sciences, teams need end-to-end traceability for GxP compli …
    #
    # </details>
    #
    # <img width="800px" alt="lamindb-schematic" src="https://lamin-site-assets.s3.a …
    #
    # How?
    #
    # - **lineage** → trace results across agent sessions, notebooks, scripts & work …
    # - **lakehouse** → query across many datasets, manage tables & arrays schema-ba …
    # - **LIMS & ELN** → unified schema-based records management with support for on …
    # - **FAIR datasets** → validate & annotate files, `DataFrame`, `AnnData`, `Spat …
    # - **governance** → [manage changes](https://docs.lamin.ai/manage-changes) via  …
    #
    # Architecture?
    #
    │ …
Track a project or an agent plan.

Pass a project/artifact to ln.track(), for example:

Note that you have to create a project or save the agent plan in case they don’t yet exist:

# create a project with the CLI
lamin create project "My project"

# save an agent plan with the CLI
lamin save /path/to/.cursor/plans/curate-dataset-x.plan.md
lamin save /path/to/.claude/plans/curate-dataset-x.md

Or in Python:

You can track workflows by decorating functions:

import lamindb as ln

@ln.flow()
def create_fasta(fasta_file: str = "sample.fasta"):
    open(fasta_file, "w").write(">seq1\nACGT\n")    # create dataset
    ln.Artifact(fasta_file, key=fasta_file).save()  # save dataset

if __name__ == "__main__":
    pass

Beyond what you get for scripts & notebooks, this automatically tracks function & CLI params and integrates well with established Python workflow managers: docs.lamin.ai/track. To integrate advanced bioinformatics pipeline managers like Nextflow, see docs.lamin.ai/pipelines.

A richer example.

Here is an automatically generated re-construction of the project of Schmidt et al. (Science, 2022):

A phenotypic CRISPRa screening result is integrated with scRNA-seq data. Here is the result of the screen input:

You can explore it here on LaminHub or here on GitHub.

Label artifacts

You can label an artifact by running:

my_label = ln.ULabel(name="My label").save()   # a universal label
project = ln.Project(name="My project").save() # a project label
artifact.ulabels.add(my_label)
artifact.projects.add(project)

Query for it:

ln.Artifact.filter(ulabels=my_label, projects=project).to_dataframe()
uid key description suffix kind otype size hash n_files n_observations ... is_latest is_locked created_at branch_id created_on_id space_id storage_id run_id schema_id created_by_id
id
1 DJuc2fG1vdkKomLs0000 sample.fasta None .fasta None None 11 83rEPcAoBHmYiIuyBYrFKg None None ... True False 2026-08-07 16:20:31.081000+00:00 1 1 1 1 1 None 1

1 rows × 22 columns

You can also query by the metadata that lamindb automatically collects:

ln.Artifact.filter(run=run).to_dataframe()              # by creating run
ln.Artifact.filter(transform=transform).to_dataframe()  # by creating transform
ln.Artifact.filter(size__gt=1e6).to_dataframe()         # size greater than 1MB
uid id key description suffix kind otype size hash n_files ... is_latest is_locked created_at branch_id created_on_id space_id storage_id run_id schema_id created_by_id

0 rows × 23 columns

If you want to include more information into the resulting dataframe, pass include.

ln.Artifact.to_dataframe(include=["created_by__name", "storage__root"])  # include fields from related registries
uid key created_by__name storage__root
id
1 DJuc2fG1vdkKomLs0000 sample.fasta None /home/runner/work/lamindb/lamindb/test-readme

The query syntax for DB objects and for your default database is the same.

Here is an overview that illustrates how artifacts can be labeled by other entities:

Read more: docs.lamin.ai/organize.

Manage features & records

Let’s define some features:

from datetime import date

gc_content = ln.Feature(name="gc_content", dtype=float).save()
experiment_note = ln.Feature(name="experiment_note", dtype=str).save()
experiment_date = ln.Feature(name="experiment_date", dtype=date, coerce=True).save()  # accept date strings

The most basic thing you can do with features is annotating artifacts, records, or runs with them:

artifact.features.set_values({
    gc_content: 0.55,
    experiment_note: "Looks great",
    experiment_date: "2025-10-24",
})

# query
ln.Artifact.filter(experiment_date == "2025-10-24").to_dataframe(include="features")  # query all artifacts annotated with `experiment_date`
uid key gc_content experiment_note experiment_date
id
1 DJuc2fG1vdkKomLs0000 sample.fasta 0.55 Looks great 2025-10-24

You can create records for entities underlying your experiments (samples, perturbations, instruments, etc.):

ln.Record(name="Sample 1", features={gc_content: 0.5}).save()
Record(uid='gMu7Q5a4YhGvmXJ6', is_type=False, name='Sample 1', description=None, reference=None, reference_type=None, extra_data=None, branch_id=1, created_on_id=1, space_id=1, created_by_id=1, type_id=None, schema_id=None, run_id=None, created_at=2026-08-07 16:20:34 UTC, is_locked=False)

You can dynamically create registries and relationships of entities via record types:

# create an experiments registry by defining a record type
experiments_registry = ln.Record(name="Experiments", is_type=True).save()

# create a record inside the Experiments registry
ln.Record(name="Experiment 1", type=experiments_registry).save()

# create a feature that links experiments, creating a relationship
experiment = ln.Feature(name="experiment", dtype=experiments_registry).save()

# create a sample record that links the sample to `Experiment 1` via the `experiment` feature
ln.Record(name="Sample 2", features={gc_content: 0.5, experiment: "Experiment 1"}).save()

# export a registry to a dataframe
experiments_registry.to_dataframe()
! you are trying to create a record with name='experiment' but records with similar names exist: 'experiment_note', 'experiment_date'. Did you mean to load one of them?
! you are trying to create a record with name='Sample 2' but a record with similar name exists: 'Sample 1'. Did you mean to load it?
 exporting 1 records of 'Experiments'
__lamindb_record_uid__ __lamindb_record_name__
__lamindb_record_id__
3 7rGAKLGBMm0ecqLJ Experiment 1
You can edit records like Excel sheets on LaminHub.

Lakehouse

Here is how you ingest a DataFrame:

import pandas as pd

df = pd.DataFrame({
    "sequence_str": ["ACGT", "TGCA"],
    "gc_content": [0.55, 0.54],
    "experiment_note": ["Looks great", "Ok"],
    "experiment_date": [date(2025, 10, 24), date(2025, 10, 25)],
})
ln.Artifact.from_dataframe(df, key="my_datasets/sequences.parquet").save()  # no validation
Artifact(uid='Wabmyf4yafKacMA60000', key='my_datasets/sequences.parquet', description=None, suffix='.parquet', kind='dataset', otype='DataFrame', size=3405, hash='iK-5bby-Qk0lbWYf9aL4WQ', n_files=None, n_observations=2, extra_data=None, branch_id=1, created_on_id=1, space_id=1, storage_id=1, run_id=None, schema_id=None, created_by_id=1, created_at=2026-08-07 16:20:35 UTC, is_locked=False, version_tag=None, is_latest=True)

To validate & annotate the content of the dataframe, use the built-in schema valid_features:

ln.Feature(name="sequence_str", dtype=str).save()  # define a remaining feature
artifact = ln.Artifact.from_dataframe(
    df,
    key="my_datasets/sequences.parquet",
    schema="valid_features"  # validate columns against features
).save()
artifact.describe()
 returning artifact with same hash: Artifact(uid='Wabmyf4yafKacMA60000', key='my_datasets/sequences.parquet', description=None, suffix='.parquet', kind='dataset', otype='DataFrame', size=3405, hash='iK-5bby-Qk0lbWYf9aL4WQ', n_files=None, n_observations=2, extra_data=None, branch_id=1, created_on_id=1, space_id=1, storage_id=1, run_id=None, schema_id=None, created_by_id=1, created_at=2026-08-07 16:20:35 UTC, is_locked=False, version_tag=None, is_latest=True); to track this artifact as an input, use: ln.Artifact.get()
 loading artifact into memory for validation
Artifact: my_datasets/sequences.parquet (0000)
├── uid: Wabmyf4yafKacMA60000  kind: dataset                      
otype: DataFrame           hash: iK-5bby-Qk0lbWYf9aL4WQ       
size: 3.3 KB               branch: main                       
space: all                 created_at: 2026-08-07 16:20:35 UTC
created_by: anonymous      n_observations: 2                  
schema: valid_features                                        
├── storage/path: /home/runner/work/lamindb/lamindb/test-readme/.lamindb/Wabmyf4yafKacMA60000.parquet
└── Dataset features
    └── columns (4)                                                                                                
        experiment_date                date                                                                        
        experiment_note                str                                                                         
        gc_content                     float                                                                       
        sequence_str                   str                                                                         

Watch a mini video: youtu.be/Ji6E7hTnReQ

You can filter for datasets by schema and then launch distributed queries or batch load distributed datasets. For tables, see: docs.lamin.ai/tables. For arrays, see: docs.lamin.ai/arrays.

To validate an AnnData, call:

import anndata as ad
import numpy as np
import pandas as pd

adata = ad.AnnData(
    X=np.ones((21, 10)),
    obs=pd.DataFrame({'cell_type_by_model': ['T cell', 'B cell', 'NK cell'] * 7}),
    var=pd.DataFrame(index=[f'ENSG{i:011d}' for i in range(10)])
)
artifact = ln.Artifact.from_anndata(
    adata,
    key="my_datasets/scrna.h5ad",
    schema="ensembl_gene_ids_and_valid_features_in_obs"
).save()
artifact.describe()
 loading artifact into memory for validation
/opt/hostedtoolcache/Python/3.14.6/x64/lib/python3.14/functools.py:982: ImplicitModificationWarning: Transforming to str index.
  return dispatch(args[0].__class__)(*args, **kw)
 created 1 Organism record from Bionty matching name: 'human'
! no values were validated for columns!
 added 2 records from_public with bionty.Gene for "columns": 'ENSG00000000003', 'ENSG00000000005'
 returning schema with same hash: Schema(uid='0000000000000000', is_type=False, name='valid_features', description=None, n_members=None, coerce=None, flexible=True, itype='Feature', otype=None, suffix=None, hash='kMi7B_N88uu-YnbTLDU-DA', minimal_set=True, ordered_set=False, maximal_set=False, branch_id=1, created_on_id=1, space_id=1, created_by_id=1, run_id=None, type_id=None, created_at=2026-08-07 16:20:35 UTC, is_locked=False)
Artifact: my_datasets/scrna.h5ad (0000)
├── uid: ytiGCYvblkImH8Hk0000                                   kind: dataset                      
otype: AnnData                                              hash: uBhn8P4HVyl77x1zR9ju5w       
size: 20.8 KB                                               branch: main                       
space: all                                                  created_at: 2026-08-07 16:20:37 UTC
created_by: anonymous                                       n_observations: 21                 
schema: anndata_ensembl_gene_ids_and_valid_features_in_obs                                     
├── storage/path: /home/runner/work/lamindb/lamindb/test-readme/.lamindb/ytiGCYvblkImH8Hk0000.h5ad
└── Dataset features
    ├── obs (None)                                                                                                 
    └── var.T (2 bionty.Gene.ensembl…                                                                              
        TNMD                           num                                                                         
        TSPAN6                         num                                                                         

To validate a SpatialData or any other array-like dataset, you need to construct a Schema. You can do this by composing simple pandera-style schemas: docs.lamin.ai/curate.

Branching & versioning

LaminDB co-versions code and datasets for you. If edit and run the create_fasta.py script, you’ll automatically create a new version of the transform and the sample.fasta artifact.

The edited script
# create_fasta.py
import lamindb as ln

ln.track()
open("sample.fasta", "w").write(">seq1\nTGCA\n")  # a new sequence
ln.Artifact("sample.fasta", key="sample.fasta", features={"experiment": "Experiment 1"}).save()  # annotate with the new experiment
ln.finish()
 found notebook README.ipynb, making new version -- anticipating changes
 created Transform('iI6Kty6mg6jF0001', key='README.ipynb'), started new Run('c4GuPp1RIJJYtPjl') at 2026-08-07 16:20:37 UTC
 notebook imports: anndata==0.13.2 bionty==2.4.3 lamindb numpy==2.5.1 pandas==2.3.3
 tip: to identify the notebook across renames, pass the uid: ln.track("iI6Kty6mg6jF")
 creating new artifact version for key 'sample.fasta' in storage '/home/runner/work/lamindb/lamindb/test-readme'
! cells [(4, 6), (8, 11), (22, 24)] were not run consecutively
/opt/hostedtoolcache/Python/3.14.6/x64/lib/python3.14/site-packages/nbformat/_struct.py:28: RuntimeWarning: coroutine '_wait_for_close' was never awaited
  def __init__(self, *args, **kw):
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
 returning artifact with same hash: Artifact(uid='0lkqP4AoQAnjWy3D0000', key=None, description='Report of run RXwpzbmf2n1Ylbdk', suffix='.html', kind='__lamindb_run__', otype=None, size=344568, hash='JUpgwy08L66BVW1OB-bKHg', n_files=None, n_observations=None, extra_data=None, branch_id=1, created_on_id=1, space_id=1, storage_id=1, run_id=None, schema_id=None, created_by_id=1, created_at=2026-08-07 16:20:34 UTC, is_locked=False, version_tag=None, is_latest=True); to track this artifact as an input, use: ln.Artifact.get()
! run was not set on Artifact(uid='0lkqP4AoQAnjWy3D0000', key=None, description='Report of run RXwpzbmf2n1Ylbdk', suffix='.html', kind='__lamindb_run__', otype=None, size=344568, hash='JUpgwy08L66BVW1OB-bKHg', n_files=None, n_observations=None, extra_data=None, branch_id=1, created_on_id=1, space_id=1, storage_id=1, run_id=None, schema_id=None, created_by_id=1, created_at=2026-08-07 16:20:34 UTC, is_locked=False, version_tag=None, is_latest=True), setting to current run
! updated description from Report of run RXwpzbmf2n1Ylbdk to Report of run c4GuPp1RIJJYtPjl
! returning transform  with same hash & key: Transform(uid='iI6Kty6mg6jF0000', key='README.ipynb', description='LaminDB: data management for traceable, multimodal AI', kind='notebook', hash='qu4MWboDFlheftTyytXftw', reference=None, reference_type=None, environment=None, plan=None, branch_id=1, created_on_id=1, space_id=1, run_id=None, created_by_id=1, created_at=2026-08-07 16:20:31 UTC, is_locked=False, version_tag=None, is_latest=False)
! run was not set on Transform(uid='iI6Kty6mg6jF0000', key='README.ipynb', description='LaminDB: data management for traceable, multimodal AI', kind='notebook', hash='qu4MWboDFlheftTyytXftw', reference=None, reference_type=None, environment=None, plan=None, branch_id=1, created_on_id=1, space_id=1, run_id=None, created_by_id=1, created_at=2026-08-07 16:20:31 UTC, is_locked=False, version_tag=None, is_latest=False), setting to current run
 new latest Transform version is: iI6Kty6mg6jF0000
 finished Run('c4GuPp1RIJJYtPjl') after 1s at 2026-08-07 16:20:38 UTC
artifact_latest = ln.Artifact.get(key="sample.fasta")  # pass version for a previous version: ln.Artifact.get(key="sample.fasta", version="1.0")
artifact_latest.versions.to_dataframe()                # all versions of that artifact
artifact_latest.transform.versions.to_dataframe()      # all versions of the transform that created the artifact
uid key description kind source_code hash reference reference_type version_tag is_latest is_locked created_at branch_id created_on_id space_id environment_id plan_id run_id created_by_id
id
1 iI6Kty6mg6jF0000 README.ipynb LaminDB: data management for traceable, multim... notebook # %% [markdown]\n# [![docs](https://img.shield... qu4MWboDFlheftTyytXftw None None None True False 2026-08-07 16:20:31.760000+00:00 1 1 1 None None None 1

To isolate changes, create a contribution branch and switch to it as in git:

lamin switch -c my_branch

To merge a contribution branch into main, run:

lamin switch main  # switch to the main branch
lamin merge my_branch  # merge contribution branch into main

Read more: docs.lamin.ai/manage-changes.

Watch a mini video: youtu.be/rzRwcMj6-fc

Data sharing

To share data in a lineage-aware way, transfer objects from a source database to your default database:

db = ln.DB("laminlabs/lamindata")
artifact = db.Artifact.get(key="example_datasets/mini_immuno/dataset1.h5ad")
artifact.save()
! database has module pertdb, configure it: lamin settings modules set bionty,pertdb
/opt/hostedtoolcache/Python/3.14.6/x64/lib/python3.14/site-packages/fsspec/asyn.py:108: RuntimeWarning: coroutine '_wait_for_close' was never awaited
  def sync_wrapper(func, obj=None):
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
 mapped: Schema(uid='0000000000000002')
 transferred: Artifact(uid='9K1dteZ6Qx0EXK8g0000'), Storage(uid='D9BilDV2')
Artifact(uid='9K1dteZ6Qx0EXK8g0000', key='example_datasets/mini_immuno/dataset1.h5ad', description='Flow cytometry readouts on invitro cell culture', suffix='.h5ad', kind='dataset', otype='AnnData', size=31672.0, hash='FB3CeMjmg1ivN6HDy6wsSg', n_files=None, n_observations=3.0, extra_data=None, branch_id=1, created_on_id=1, space_id=1, storage_id=2, run_id=3, schema_id=4, created_by_id=1, created_at=2025-07-29 12:27:25 UTC, is_locked=False, version_tag=None, is_latest=True)

This is zero-copy for the artifact’s data in storage. Read more: docs.lamin.ai/transfer.

Ontologies

Plugin bionty gives you >20 public ontologies as SQLRecord registries. This was used to validate the ENSG ids in the adata just before.

import bionty as bt

bt.CellType.import_source()  # import the default ontology
bt.CellType.to_dataframe()   # your extensible cell type ontology in a simple registry
 import is completed!
! truncated query result to limit=20 CellType objects
uid name ontology_id abbr synonyms description is_locked created_at branch_id created_on_id space_id created_by_id run_id source_id
id
3437 1ChUsEzDZXWW4B beam B cell, human CL:7770006 None None A Trabecular Meshwork Cell Within The Eye'S Tr... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3436 5xoxfxIf7WrLdU beam cell CL:7770005 None None A Trabecular Meshwork Cell That Is Part Of The... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3435 2j5mhhFoV2vBDV suprabasal cell CL:7770004 None None An Epithelial Cell That Resides In The Layer(S... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3434 RBCFqAmkM1oaaZ beam A cell CL:7770003 None None A Beam Cell Within The Eye'S Trabecular Meshwo... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3433 79Ow7BGPRP018I juxtacanalicular tissue cell CL:7770002 None None A Trabecular Meshwork Cell Of The Juxtacanalic... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3432 4qJMS0d5FyIXQK OB FRMD7 GABA GABAergic neuron (Primate) CL:4310148 None OB FRMD7 GABA A Gabaergic Neuron Of The Primates Brain. Thes... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3431 6iU1Q2FIIkrgND OB Dopa-GABA neuron (Primate) CL:4310147 None OB Dopa-GABA A Ob-Dopa-Gaba Of The Primates Brain. These Ce... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3430 100qUn3IH1Ksw2 AMY-SLEA-BNST GABA GABAergic interneuron (Prim... CL:4310146 None AMY-SLEA-BNST GABA A Gabaergic Interneuron Of The Primates Brain.... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3429 QJ8929f49Ncfrf AMY-SLEA-BNST D1 GABA GABAergic interneuron (P... CL:4310145 None AMY-SLEA-BNST D1 GABA A Gabaergic Interneuron Of The Primates Brain.... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3428 2RruqlADchF4D3 VTR-HTH Glut glutamatergic neuron of the basal... CL:4310144 None F M Glut|VTR-HTH Glut A Glutamatergic Neuron Of The Basal Ganglia Of... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3427 44qrwytpXARNMz ZI-HTH GABA GABAergic interneuron (Primate) CL:4310143 None ZI-HTH GABA A Gabaergic Interneuron Of The Primates Brain.... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3426 3dVBqPp88IUIib STRv D2 medium spiny neuron (Primate) CL:4310138 None D2-Shell/OT|STRv D2 MSN A Nucleus Accumbens Shell And Olfactory Tuberc... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3425 4RRtGKe1oGoVGV STRv D1-NUDAP medium spiny neuron (Primate) CL:4310137 None D1-NUDAP|STRv D1 NUDAP MSN A D1-Nudap Medium Spiny Neuron Of The Primates... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3424 1Tw4GET7C0W2Az STRv D1 medium spiny neuron (Primate) CL:4310136 None STRv D1 MSN|D1-Shell/OT A Nucleus Accumbens Shell And Olfactory Tuberc... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3423 3taUBSHOL3ffx3 STR cholinergic-GABAergic neuron (Primate) CL:4310135 None STR Cholinergic GABA A Striatal Cholinergic-Gabaergic Neuron Of The... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3422 2OzOl5s6HeWYYM striosomal D2 medium spiny neuron (Primate) CL:4310134 None D2-Striosome|STRd D2 Striosome MSN A Striosomal D2 Medium Spiny Neuron Of The Pri... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3421 3ZypczMtxf4KQu STRd D2 Striomat hybrid medium spiny neuron (P... CL:4310133 None STRd D2 StrioMat Hybrid MSN A Indirect Pathway Medium Spiny Neuron Of The ... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3420 2Mk5r9Xsi24yLc STRd D2 Matrix medium spiny neuron (Primate) CL:4310132 None D2-Matrix|STRd D2 Matrix MSN A Matrix D2 Medium Spiny Neuron Of The Primate... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3419 5e4cn19DcnuNH0 STRd D1/D2-hybrid medium spiny neuron (Primate) CL:4310131 None D1/D2 Hybrid|STR D1D2 Hybrid MSN A D1/D2-Hybrid Medium Spiny Neuron Of The Prim... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26
3418 67anmsVZv7HmGa striosomal D1 medium spiny neuron (Primate) CL:4310130 None STRd D1 Striosome MSN|D1-Striosome A Striosomal D1 Medium Spiny Neuron Of The Pri... False 2026-08-07 16:20:41.804000+00:00 1 1 1 1 None 26

You can then create objects, e.g. for labeling, analogous to ULabel, Project, or Record:

t_cell = bt.CellType.get(name="T cell")
artifact.cell_types.add(t_cell)

Read more: docs.lamin.ai/manage-ontologies.

Watch a mini video: youtu.be/3vpWjHj3Kw8

Manage notes

When in your development directory, you can save markdown files as records:

lamin save <topic>/<my-note.md>