sarek-core
Python client for the Sarek Core Service. Python 3.10 or later. Depends only
on httpx and cryptography.
pip install sarek-coreSarekCore
The main entry point. Use it as a context manager so the HTTP client is closed cleanly.
Signature
class SarekCore:
def __init__(
self,
name: str = 'default',
url: str | None = None,
timeout: int | None = None,
retries: int | None = None,
persist: bool = True,
)Parameters
| Name | Type | Default | Description |
|---|---|---|---|
name | str | 'default' | Agent name. Used for the local identity and proof store. |
url | str | from sarek-core/config.json | Core Service endpoint. |
timeout | int | 30 | HTTP timeout in seconds. |
retries | int | 3 | Retries on 5xx and network errors. 4xx is never retried. |
persist | bool | True | False disables all file system access. |
Example
from sarek_core import SarekCore
with SarekCore() as sarek:
...stamp()
Hashes data locally with SHA3-256, stamps the hash, and stores the record and proof on disk. Blocks until the proof is anchored (around 10 seconds).
The method is overloaded: a single input returns a single StampResult, a
list returns list[StampResult]. A batch is stamped in one transaction.
Signature
def stamp(
self,
data: str | bytes | list[str | bytes],
metadata: dict | None = None,
) -> StampResult | list[StampResult]Parameters
| Name | Type | Description |
|---|---|---|
data | str | bytes | list[str | bytes] | The data to stamp. A 64-character hex string is treated as a precomputed hash and passed through. bytes are always hashed. |
metadata | dict | None | Optional context stored in the local record. |
Returns
@dataclass
class StampResult:
id: str # generated UUID
hash: str # SHA3-256 of the input
ldgp: str # base64-encoded proof file
tx_hash: str
block_number: int
timestamp: str # ISO 8601
metadata: dict | None = None
signature: str | None = None
public_key: str | None = None
salt: str | None = NoneExample
proof = sarek.stamp(patient_report, metadata={
'type': 'risk-assessment', 'patient_id': '4821', 'version': '3.2',
})Errors
Raises LedgerError on HTTP failures and TimeoutError if the Core Service
does not answer within timeout.
verify()
Verifies a proof. Recognizes proof input by its TERHUA prefix (the base64
encoding of the proof file magic bytes), so argument order does not matter.
Passing a SignResult instead verifies a signature. See
Verification for what each check means.
Signature
def verify(
self,
input_: str | SignResult,
data: str | None = None,
) -> VerifyResultParameters
| Name | Type | Description |
|---|---|---|
input_ | str | SignResult | A base64 proof, raw data (if the proof is in data), or a SignResult. |
data | str | None | Optional original data, enables the data integrity check. |
Returns
@dataclass
class VerifyResult:
valid: bool
tree_integrity: bool
on_chain: bool
on_chain_leaf: boolExample
# Proof only
r1 = sarek.verify(proof.ldgp)
# Proof plus original data, in either order
r2 = sarek.verify(proof.ldgp, 'loan #4821 approved')
r3 = sarek.verify('loan #4821 approved', proof.ldgp)Errors
Raises LedgerError if neither argument is a proof.
register()
Opt-in identity. Generates a P-256 key pair, registers it with the Core
Service, and persists it under sarek-core/id/<name>/. Call it once per agent.
Signature
def register(self) -> NoneExample
sarek.register()Errors
Raises IdentityExistsError (HTTP 409) if the identity is already registered.
sign()
Signs data locally with the registered identity. Runs offline.
Signature
def sign(self, data: str) -> SignResultReturns
@dataclass
class SignResult:
message: str
signature: str # hex
public_key: str # PEM
salt: str # hexExample
sig = sarek.sign('loan #4821 approved')
check = sarek.verify(sig)Errors
Raises IdentityNotRegisteredError if register() has not been
called. This is a local state error, not an HTTP error.
revoke()
Revokes the registered identity. Irreversible.
Signature
def revoke(self) -> NoneExample
sarek.revoke()Errors
Raises IdentityRevokedError (HTTP 409) if already revoked, and
IdentityNotRegisteredError if no identity exists locally.
identity
Property. Returns the locally stored identity, or None if none exists.
Signature
@property
def identity(self) -> Identity | NoneExample
if sarek.identity is not None:
sarek.identity.identity_idError classes
All errors are importable from sarek_core.
| Class | Meaning |
|---|---|
LedgerError | Base class. Carries status, path and body. |
IdentityNotRegisteredError | sign() or revoke() called before register(). Local, not HTTP. |
IdentityExistsError | 409 from register(): identity already registered. |
IdentityRevokedError | 409 from revoke(): identity already revoked. |
NotFoundError | 404: resource does not exist. |
TimeoutError | 408: no answer within the configured timeout. |
from sarek_core import SarekCore, IdentityNotRegisteredError
with SarekCore() as sarek:
try:
sarek.sign('loan #4821 approved')
except IdentityNotRegisteredError:
sarek.register()Local files
With persist=True (the default) the SDK maintains a sarek-core/ directory
in the working directory:
sarek-core/
config.json url, timeout, retries
.gitignore created automatically, protects private.key
records/
records.jsonl one line per stamp
proofs/
<name>.ldgp append-only proof store per agent name
id/
<name>/
identity.json public_key, identity_id, commitment, salt
private.key P-256 private key (chmod 600)
index.json offset and size per proof id, O(1) lookup