Developer-first model inventory and governance framework for SR 11-7, EU AI Act, and NIST AI RMF compliance
Project description
model-ledger
Know what models you have deployed, where they run, what they depend on, and what changed.
model-ledger is a model inventory for any company with deployed models. It discovers models across your platforms, maps the dependency graph, and tracks every change as an immutable event. Unlike model registries tied to a single platform (MLflow, SageMaker, W&B), model-ledger discovers across all of them — as one connected graph.
Quick Start
Talk to your inventory — point Claude (or any MCP-compatible agent) at it:
pip install model-ledger[mcp]
claude mcp add model-ledger -- model-ledger mcp --demo
You: "what models are in my inventory?"
Claude: "7 models across 5 platforms. fraud_scoring was retrained
and deployed this week. Want me to dig into anything?"
You: "if we deprecate customer_features, what breaks?"
Claude: "3 models consume it directly, 2 more transitively."
Or use the Python SDK:
from model_ledger import Ledger, DataNode
ledger = Ledger.from_sqlite("./inventory.db")
ledger.add([
DataNode("segmentation", platform="etl", outputs=["customer_segments"]),
DataNode("fraud_scorer", platform="ml", inputs=["customer_segments"], outputs=["risk_scores"]),
DataNode("fraud_alerts", platform="alerting", inputs=["risk_scores"]),
])
ledger.connect()
ledger.trace("fraud_alerts")
# ['segmentation', 'fraud_scorer', 'fraud_alerts']
graph LR
A["segmentation<br/><small>ETL pipeline</small>"] -->|customer_segments| B["fraud_scorer<br/><small>ML model</small>"]
B -->|risk_scores| C["fraud_alerts<br/><small>Alert queue</small>"]
style A fill:#607D8B,color:#fff,stroke:#455A64
style B fill:#4CAF50,color:#fff,stroke:#388E3C
style C fill:#FF9800,color:#fff,stroke:#F57C00
Install
pip install model-ledger # Core — SDK + tools + CLI
pip install model-ledger[mcp] # + MCP server (for Claude Code / AI agents)
pip install model-ledger[rest-api] # + REST API (for frontends / dashboards)
pip install model-ledger[snowflake] # + Snowflake backend
pip install model-ledger[mcp,rest-api,snowflake] # Everything
How It Works
graph TB
subgraph consumers ["Consumers"]
direction LR
AGENT["Claude / AI Agents<br/><small>MCP</small>"]
FRONT["Frontends<br/><small>REST API</small>"]
SCRIPT["Scripts / Notebooks<br/><small>Python SDK</small>"]
CLI_C["CLI<br/><small>model-ledger</small>"]
end
subgraph tools ["Agent Protocol — 6 Consolidated Tools"]
direction LR
DISC["discover"] ~~~ REC["record"] ~~~ INV["investigate"]
QRY["query"] ~~~ TRC["trace"] ~~~ CHG["changelog"]
end
subgraph sdk ["Ledger SDK"]
direction LR
REG["register()"] ~~~ RECD["record()"] ~~~ GET["get() / list()"]
HIST["history()"] ~~~ TRAC["trace()"] ~~~ CONN["connect()"]
end
subgraph discover ["Discovery Sources"]
direction LR
DB["SQL databases"] --> F["sql_connector()"]
API["REST APIs"] --> G["rest_connector()"]
GH["GitHub repos"] --> H["github_connector()"]
CUSTOM["Your platform"] --> I["SourceConnector protocol"]
end
subgraph backends ["Storage — Pluggable Backends"]
direction LR
JSON["JSON files<br/><small>default</small>"]
SQLITE["SQLite"]
SNOW["Snowflake"]
PLUG["Plugin<br/><small>Postgres, GitHub, ...</small>"]
end
consumers --> tools
tools --> sdk
sdk --> discover
sdk --> backends
style consumers fill:#F3E5F5,stroke:#7B1FA2,color:#4A148C
style tools fill:#E3F2FD,stroke:#1565C0,color:#0D47A1
style sdk fill:#E1F5FE,stroke:#0277BD,color:#01579B
style discover fill:#E8F5E9,stroke:#2E7D32,color:#1B5E20
style backends fill:#FFF3E0,stroke:#E65100,color:#BF360C
Every model is a DataNode with typed input and output ports. When an output port name matches an input port name, connect() creates the dependency edge automatically. Every mutation is recorded as an immutable Snapshot — an append-only event log that gives you full history and point-in-time reconstruction.
Agent Protocol
Six consolidated tools designed for AI agents (Anthropic's tool design guidance). Each is a plain Python function with Pydantic I/O — usable via MCP, REST, CLI, or direct import.
| Tool | What it does | Scale |
|---|---|---|
| discover | Add models from any source — scan platforms, import files, inline data | Bulk |
| record | Register a model or record an event with arbitrary metadata | Single |
| investigate | Deep dive — identity, merged metadata, recent events, dependencies | Single |
| query | Search and filter the inventory with pagination | Multi |
| trace | Dependency graph — upstream, downstream, impact analysis | Graph |
| changelog | What changed across the inventory in a time range | Multi |
Using the tools directly
from model_ledger import Ledger, record, investigate, query
from model_ledger.tools.schemas import RecordInput, InvestigateInput, QueryInput
from model_ledger.graph.models import DataNode
ledger = Ledger.from_sqlite("./inventory.db")
# Register a model
record(RecordInput(
model_name="fraud_scoring", event="registered",
owner="risk-team", model_type="ml_model",
purpose="Real-time fraud detection",
), ledger)
# Record an event with schema-free payload
record(RecordInput(
model_name="fraud_scoring", event="retrained",
payload={"accuracy": 0.94, "features_added": ["velocity_24h"]},
actor="ml-pipeline",
), ledger)
# Deep dive
result = investigate(InvestigateInput(model_name="fraud_scoring"), ledger)
result.metadata # {"accuracy": 0.94, "features_added": ["velocity_24h"]}
result.total_events # 2
# Search
models = query(QueryInput(text="fraud", model_type="ml_model"), ledger)
models.total # 1
MCP server
model-ledger mcp # empty inventory
model-ledger mcp --demo # sample data
model-ledger mcp --backend sqlite --path ./inventory.db # SQLite
model-ledger mcp --backend json --path ./my-inventory # JSON files
# Connect to Claude Code (one time)
claude mcp add model-ledger -- model-ledger mcp
REST API
model-ledger serve # start on port 8000
model-ledger serve --demo --port 3001 # with sample data
Auto-generated OpenAPI docs at /docs. Endpoints: POST /record, POST /discover, GET /query, GET /investigate/{name}, GET /trace/{name}, GET /changelog, GET /overview.
Discover Models From Your Systems
SQL databases
from model_ledger import Ledger, sql_connector
ledger = Ledger.from_sqlite("./inventory.db")
# Simple: discover from a registry table
models = sql_connector(
name="model_registry",
connection=my_db,
query="SELECT name, owner, status FROM ml_models WHERE active = true",
name_column="name",
)
# Advanced: auto-parse SQL to extract table dependencies
etl_jobs = sql_connector(
name="etl_scheduler",
connection=my_db,
query="SELECT job_name, raw_sql, cron FROM scheduled_jobs",
name_column="job_name",
sql_column="raw_sql", # extracts FROM/JOIN as inputs, INSERT/CREATE as outputs
)
ledger.add(models.discover())
ledger.add(etl_jobs.discover())
ledger.connect() # auto-links ETL outputs to model inputs
REST APIs
from model_ledger import rest_connector
# Works with MLflow, SageMaker, Vertex AI, or any JSON API
ml_models = rest_connector(
name="mlflow",
url="https://mlflow.internal/api/2.0/mlflow/registered-models/list",
headers={"Authorization": "Bearer ..."},
items_path="registered_models",
name_field="name",
)
GitHub repos
from model_ledger import github_connector
# Discover pipeline-as-code: Airflow DAGs, dbt projects, scoring pipelines
pipelines = github_connector(
name="ml_pipelines",
repos=["myorg/ml-scoring"],
token="ghp_...",
project_path="projects",
config_file="deploy.yaml",
parser=my_yaml_parser, # (project_name, file_content) -> DataNode
)
Custom connectors
Implement the SourceConnector protocol for anything the factories don't cover:
class SageMakerConnector:
name = "sagemaker"
def discover(self) -> list[DataNode]:
endpoints = boto3.client("sagemaker").list_endpoints()
return [
DataNode(ep["EndpointName"], platform="sagemaker",
outputs=[ep["EndpointName"]],
metadata={"status": ep["EndpointStatus"]})
for ep in endpoints["Endpoints"]
]
Storage
Storage-agnostic. Default is JSON files — human-readable, git-friendly, zero config. Upgrade when you need scale.
from model_ledger import Ledger
from model_ledger.backends.json_files import JsonFileLedgerBackend
ledger = Ledger(JsonFileLedgerBackend("./my-inventory")) # JSON files — default
ledger = Ledger.from_sqlite("./inventory.db") # SQLite — zero infrastructure
ledger = Ledger.from_snowflake(connection, schema="DB.MODEL_LEDGER") # Snowflake — production
ledger = Ledger() # In-memory — testing
JSON file layout — inspect, diff, and version-control your inventory:
my-inventory/
├── models/
│ ├── fraud_scoring.json
│ └── churn_predictor.json
├── snapshots/
│ ├── a1b2c3d4.json
│ └── e5f6g7h8.json
└── tags/
└── {model_hash}/
└── v1.json
Add community backends via entry points:
# pyproject.toml
[project.entry-points."model_ledger.backends"]
postgres = "my_package:PostgresBackend"
Additional Capabilities
Dependency tracing
ledger.trace("fraud_alerts") # Full pipeline path
ledger.upstream("fraud_alerts") # Everything that feeds this
ledger.downstream("segmentation") # Everything that depends on this
Shared table disambiguation
When multiple models write to the same table, DataPort schema matching handles precision:
from model_ledger import DataPort, DataNode
DataNode("check_rules", outputs=[DataPort("alerts", model_name="checks")])
DataNode("card_rules", outputs=[DataPort("alerts", model_name="cards")])
DataNode("check_queue", inputs=[DataPort("alerts", model_name="checks")])
# check_queue connects to check_rules only — model_name must match
Point-in-time inventory
inventory = ledger.inventory_at(datetime(2025, 12, 31))
# Every model that was active on that date
Compliance validation (plugin)
Built-in profiles for SR 11-7, EU AI Act, and NIST AI RMF. Add custom profiles for your organization's policies. See validation docs for details.
Model introspection
Extract metadata from fitted sklearn, XGBoost, and LightGBM models. Add custom introspectors via the Introspector protocol. See introspection docs for details.
Design Principles
- Agents are the primary interface — the MCP server is the product. SDK and CLI are still first-class, but the agent experience is what we optimize for.
- Fundamental, not specialized — model inventory for any company with deployed models. Not tied to a specific regulatory framework or industry.
- Everything is a DataNode — ML models, heuristic rules, ETL pipelines, alert queues. One abstraction.
- The graph builds itself — declare inputs and outputs. Dependencies follow from port matching.
- Schema-free payloads — record whatever metadata matters. No schema to maintain, no migrations.
- Change tracking is central — every mutation is an immutable Snapshot. The inventory is a living event log.
- Storage-agnostic — JSON files, SQLite, Snowflake, or bring your own via the
LedgerBackendprotocol.
For Organizations
The OSS core handles discovery, graph building, change tracking, storage, and the agent protocol. Your internal package provides:
- Connector configs — point factories at your tables and APIs
- Custom connectors — for internal platforms the factories don't cover
- Authentication — your credentials and auth wrappers
- Custom backends — Postgres, GitHub repos, or any storage via
LedgerBackendprotocol - Compliance profiles — SR 11-7, EU AI Act, or your own internal policies (plugin-based)
Your internal repo should be thin config and credentials, not reimplemented logic.
Contributing
See CONTRIBUTING.md. All commits require DCO sign-off.
License
Apache-2.0. See LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file model_ledger-0.6.0.tar.gz.
File metadata
- Download URL: model_ledger-0.6.0.tar.gz
- Upload date:
- Size: 117.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6d04e1feedc2dc442b99140a9b1e271466794259af42f861f3eec104fb493294
|
|
| MD5 |
2b042669dd2871be8ac7ad0fde6ae188
|
|
| BLAKE2b-256 |
2e4151af845170d483be472a5803d77671d9927a83cb3a971e2eb9ba8b22800b
|
Provenance
The following attestation bundles were made for model_ledger-0.6.0.tar.gz:
Publisher:
release.yml on block/model-ledger
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
model_ledger-0.6.0.tar.gz -
Subject digest:
6d04e1feedc2dc442b99140a9b1e271466794259af42f861f3eec104fb493294 - Sigstore transparency entry: 1268601377
- Sigstore integration time:
-
Permalink:
block/model-ledger@c79239f380d529366057ea2f5d1a40da872bdd37 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/block
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c79239f380d529366057ea2f5d1a40da872bdd37 -
Trigger Event:
release
-
Statement type:
File details
Details for the file model_ledger-0.6.0-py3-none-any.whl.
File metadata
- Download URL: model_ledger-0.6.0-py3-none-any.whl
- Upload date:
- Size: 100.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0308189db7dc22b1bad256e042d768175cd4036cb505f6f69fdb65618f27e16f
|
|
| MD5 |
aa99bf4751d8f2956e97277aca16fa40
|
|
| BLAKE2b-256 |
a69f93ce05c1b72e0693080366b6bccab88a4b8f844abf3897577d39ad0b83c3
|
Provenance
The following attestation bundles were made for model_ledger-0.6.0-py3-none-any.whl:
Publisher:
release.yml on block/model-ledger
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
model_ledger-0.6.0-py3-none-any.whl -
Subject digest:
0308189db7dc22b1bad256e042d768175cd4036cb505f6f69fdb65618f27e16f - Sigstore transparency entry: 1268601443
- Sigstore integration time:
-
Permalink:
block/model-ledger@c79239f380d529366057ea2f5d1a40da872bdd37 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/block
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c79239f380d529366057ea2f5d1a40da872bdd37 -
Trigger Event:
release
-
Statement type: