Quick Start
In about 10 minutes, you will install the SDK, run a small lineage-tracked workflow, export an Integrity Manifest, and open it in Explorer. Along the way, you will create a software DID signer, register data, and record a computation.
1. Install
The package is published on PyPI. Install it with:
python -m pip install eqty_sdk
Verify the installation:
python -c "import eqty_sdk; print(eqty_sdk.__file__)"
2. Run a complete example
Create quick_start.py with the following code, then run
python quick_start.py.
from pathlib import Path
from eqty_sdk import (
CID,
DID,
SIGNER_ALGORITHMS,
Computation,
Dataset,
Signer,
compute,
init,
set_active_signer,
)
# Configure SDK
ctx = init().set_store_all_blobs(True).get_default_context()
# Set signer
signer = Signer.new(SIGNER_ALGORITHMS.SECP256R1)
set_active_signer(signer)
did = DID.from_signer(
signer, name="My key", description="My Ed25519 signing key for integrity statements."
)
# Create sample objects
my_object = "My Object"
my_path = "./"
my_cid = CID("bafkr4icqw77khu73vgw74jpnlnep37ec3l6jd4lg5kvw2letvqjhgk6jmi")
# Registering a serializable Python object
d0 = Dataset.from_object(my_object, name="My dataset 0", description="My description for dataset 0")
# Registering a file or directory of files from the file system
d1 = Dataset.from_path(
my_path,
name="My dataset 1",
description="My description for dataset 1",
)
# Registering a data asset or collection of assets by its CID
d2 = Dataset.from_cid(
my_cid, name="My dataset 2", description="My description for dataset 2", foo="bar"
)
# Registering a computation with builder
computation = (
Computation.new().add_input_cid(d0.cid).add_input_cid(d1.cid).add_output_cid(d2.cid).finalize()
)
@compute(
metadata={
"name": "My computation",
"description": "My description for the computation",
"foo": "bar",
}
)
def my_function(input_0: Dataset, input_1: Dataset):
my_output_object = str(input_0.value) + str(input_1.value)
output = Dataset.from_object(
my_output_object, name="My dataset", description="My description for the output dataset"
)
return output
my_function(d0, d1)
# Export manifest
path = Path("./manifest_simple.json")
ctx.export(path)
ctx.delete_tree()
## Optionally delete all the created statements
# purge_statement_store()
## Optionally delete all the stored blobs
# purge_blob_store()
The script prints the formatted prompt and writes manifests/quick-start.json. Signer.load_or_create(...) gives the process a stable, local software key and DID. With that signer active, the SDK attributes and signs the statements it produces; rerunning the script uses the same DID rather than creating a new identity.
3. View the manifest in Explorer
In Explorer, import or upload manifests/quick-start.json. Open the imported graph and select nodes to inspect their metadata, CIDs, statements, and signing identity.
The graph is a provenance map, not a control-flow trace:
- Asset nodes represent the content-addressed inputs and outputs, such as the system prompt, the user prompt, the temperature, the formatter's source code, and its result.
- Compute nodes represent an execution. Their incoming edges are the inputs used by that execution; their outgoing edges are the assets it produced.
- Statement and metadata nodes provide the assertions that connect those objects, including the DID-backed signatures.
Start at Prompt Formatter, follow its input edges to see what informed the result, then follow output edges to see what it created. Select an asset when you need its CID or metadata, and select a statement when you need to inspect the signed provenance assertion.
Assets: type and source are separate choices
An asset type describes what content represents. The SDK includes types such as Dataset, Model, Prompt, SystemPrompt, Code, Document, Agent, Tool, Configuration, Guardrail, and Custom; see the complete asset reference.
For nearly every type, choose one of three constructors based on where the content comes from:
from eqty_sdk import CID, Dataset, Document, Model
rows = Dataset.from_object([{"id": 1, "text": "hello"}])
policy = Document.from_path("policies/retention.md")
known_model = Model.from_cid(
CID("bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku")
)
from_object(...)serializes in-memory content, computes its CID, and registers it.from_path(...)hashes a file or directory, computes its CID, and registers it.from_cid(...)registers a typed reference to content already identified by a CID; it does not have a local pre-image to store.
Use Custom with an asset_type= label when none of the built-in types fits.
Compute: decorator or builder
The example uses @compute, the convenient choice when you want the SDK to capture a normal Python function's source, arguments, return value, and execution relationship.
Use the Computation builder when the work happens outside a Python function, or when you already have CIDs, paths, or objects for the inputs, output, and computation identity:
from eqty_sdk import Computation
(Computation.new(name="External Training Job", _store=False)
.add_input_path("data/train.csv")
.add_output_path("artifacts/model.bin")
.set_computation_object({"job": "train-v1", "runtime": "remote"})
.finalize())
Both approaches create the same essential lineage: the computation links its input CIDs to its output CIDs and receives metadata and signed statements. The builder's _store setting applies to every object and path it hashes.
Decide whether to retain pre-image blobs
Every object or path registration computes a CID whether or not its original bytes are retained. Retaining the bytes (the pre-image blob) makes the content available for later manifest/service workflows, but it can consume disk space and may be inappropriate for sensitive or large data.
Choose the default for the whole SDK configuration:
cfg = init()
cfg.set_store_all_blobs(True) # retain every newly hashed pre-image by default
Set it to True when you plan to register blobs with a service, need local reproducibility, or want the content available alongside its manifest. Set it to False when only CIDs and provenance are needed locally, or when retaining the source bytes would be too costly or sensitive.
Override that default for an individual data registration with _store:
public_summary = Dataset.from_object({"count": 42}, _store=True)
sensitive_rows = Dataset.from_path("private/records.csv", _store=False)
The same override is available when you register a compute with either the builder or decorator. For a decorator, it applies to the code, captured inputs, and outputs it hashes:
@compute(metadata={"name": "Sensitive transform"}, _store=False)
def transform(rows: Dataset) -> str:
return "redacted summary"
Use per-call _store=True to preserve an important, non-sensitive input or result when the global default is False; use _store=False to opt a specific sensitive or very large registration out when the global default is True. from_cid(...) has no _store option because it only refers to an existing CID and does not provide bytes for the SDK to retain.
For full configuration and service-registration details, see Init and Config and Service Registering.