Skip to content

API Reference

from glacis import Glacis
# Online mode (default)
glacis = Glacis(api_key="glsk_live_...")
# Offline mode
glacis = Glacis(mode="offline", signing_seed=my_32_byte_seed)
# Offline with sampling config
from glacis.config import SamplingConfig
glacis = Glacis(
mode="offline",
signing_seed=my_32_byte_seed,
storage_backend="sqlite",
sampling_config=SamplingConfig(l1_rate=0.1, l2_rate=0.01),
)

Parameters:

ParameterTypeRequiredDescription
api_keyOptional[str]OnlineAPI key (glsk_live_...). Required for online mode.
base_urlstrNoAPI base URL. Default: https://api.glacis.io
debugboolNoEnable debug logging. Default: False
timeoutfloatNoRequest timeout in seconds. Default: 30.0
max_retriesintNoMaximum retries for transient errors. Default: 3
base_delayfloatNoBase delay (seconds) for exponential backoff. Default: 1.0
max_delayfloatNoMaximum delay (seconds) for backoff. Default: 30.0
modeLiteral["online", "offline"]NoOperating mode. Default: "online"
signing_seedOptional[bytes]Offline32-byte Ed25519 seed. Required for offline mode.
policy_keyOptional[bytes]No32-byte HMAC key for sampling decisions. Falls back to signing_seed if not provided.
db_pathOptional[Path]NoSQLite database path for offline receipts. Default: ~/.glacis/glacis.db
storage_backendstrNoStorage backend type: "sqlite" or "json". Default: "sqlite"
storage_pathOptional[Path]NoBase path for storage. Overrides db_path.
sampling_configOptional[SamplingConfig]NoSampling configuration (l1_rate, l2_rate). Default: l1_rate=1.0, l2_rate=0.0

The client supports context manager usage:

with Glacis(api_key="glsk_live_...") as glacis:
attestation = glacis.attest(...)

Create an attestation for an AI operation. Input and output are hashed locally using RFC 8785 canonical JSON + SHA-256. Only the hash is sent to the server — the actual data never leaves your infrastructure.

attestation = glacis.attest(
service_id="my-service",
operation_type="inference",
input={"prompt": "Hello"},
output={"response": "Hi there"},
metadata={"model": "gpt-4", "temperature": "0.7"},
control_plane_results=cpr,
operation_id="op-uuid",
operation_sequence=0,
supersedes="att_previous_id",
)

Parameters:

ParameterTypeRequiredDescription
service_idstrYesIdentifier for your service
operation_typestrYesType of operation (e.g., "inference", "embedding", "completion", "classification")
inputAnyYesInput data (hashed locally, never sent to the server)
outputAnyYesOutput data (hashed locally, never sent to the server)
metadataOptional[dict[str, str]]NoAdditional metadata (stored locally for evidence)
control_plane_resultsOptional[ControlPlaneResults | dict]NoControl plane results (typed model or dict)
operation_idOptional[str]NoUUID linking attestations in the same operation
operation_sequenceOptional[int]NoOrdinal sequence within the operation
supersedesOptional[str]NoAttestation ID this replaces (revision chains)

Returns: Attestation

Raises:

  • GlacisApiError on API errors (online mode)
  • GlacisRateLimitError when rate limited (online mode)

Verify an attestation. For online attestations, calls the server API. For offline attestations, verifies the Ed25519 signature locally.

# Verify with an Attestation object
result = glacis.verify(attestation)
# Verify with an attestation ID string
result = glacis.verify("att_abc123")
# Online verification
if isinstance(result, VerifyResult):
print(result.valid)
print(result.verification.signature_valid)
# Offline verification
if isinstance(result, OfflineVerifyResult):
print(result.valid)
print(result.signature_valid)
print(result.witness_status) # "UNVERIFIED"

Parameters:

ParameterTypeRequiredDescription
receiptstr | AttestationYesAttestation ID string or Attestation object

Returns: VerifyResult (online) or OfflineVerifyResult (offline)


Decompose a batch attestation into individual item attestations. All decomposed items share the same operation_id as the parent, with incrementing operation_sequence starting after the parent’s sequence.

parent = glacis.attest(
service_id="my-service",
operation_type="batch",
input={"document": "..."},
output={"items": [item1, item2, item3]},
)
children = glacis.decompose(
attestation=parent,
items=[item1, item2, item3],
operation_type="qa_pair",
source_data={"document": "..."},
)

Parameters:

ParameterTypeRequiredDescription
attestationAttestationYesThe parent batch attestation
itemslist[dict[str, Any]]YesIndividual items to attest
operation_typestrNoOperation type for decomposed items. Default: "item"
source_dataAnyNoShared input data for all items. When falsy (None, {}, etc.), defaults to {"parent_attestation_id": attestation.id}

Returns: list[Attestation]


Deterministic sampling decision using nested L1/L2 tiers. Given the same evidence_hash + policy_key (or signing_seed), always returns the same decision. Uses HMAC-SHA256 with a "sample:v1" domain separator to produce a deterministic, auditor-reproducible tag.

Tier logic (nested — L2 implies L1):

  • L2 if sample_valuel2_rate threshold (deep inspection)
  • L1 if sample_valuel1_rate threshold (evidence collection)
  • L0 otherwise (control plane results only)
decision = glacis.should_review(attestation, sampling_rate=0.1)
print(decision.level) # "L0", "L1", or "L2"
print(decision.sample_value) # uint64 derived from HMAC

Parameters:

ParameterTypeRequiredDescription
attestationAttestationYesThe attestation to evaluate for sampling
sampling_rateOptional[float]NoExplicit L1 probability override (0.0-1.0). If None, uses l1_rate from sampling config.

Returns: SamplingDecision

Raises: ValueError if called without policy_key or signing_seed (requires offline mode)


Query the public transparency log with optional filters and pagination.

result = glacis.query_log(
service_id="my-service",
start="2025-01-01T00:00:00Z",
end="2025-12-31T23:59:59Z",
limit=100,
)
for entry in result.entries:
print(entry.attestation_id, entry.evidence_hash)
if result.has_more:
next_page = glacis.query_log(cursor=result.next_cursor)

Parameters:

ParameterTypeRequiredDescription
org_idOptional[str]NoFilter by organization ID
service_idOptional[str]NoFilter by service ID
startOptional[str]NoStart timestamp (ISO 8601)
endOptional[str]NoEnd timestamp (ISO 8601)
limitOptional[int]NoMaximum results. Default: 50, max: 1000
cursorOptional[str]NoPagination cursor from previous result
operation_idOptional[str]NoFilter by operation ID

Returns: LogQueryResult


Get the current signed tree head. This is a public endpoint that does not require authentication.

tree_head = glacis.get_tree_head()
print(tree_head.tree_size, tree_head.root_hash)

Returns: TreeHeadResponse


Create an OperationContext for grouping related attestations under the same operation_id with auto-incrementing sequence numbers.

op = glacis.operation()
r1 = glacis.attest(
service_id="my-service",
operation_type="step-1",
input=data_in,
output=data_out,
operation_id=op.operation_id,
operation_sequence=op.next_sequence(), # 0
)
r2 = glacis.attest(
service_id="my-service",
operation_type="step-2",
input=data_in_2,
output=data_out_2,
operation_id=op.operation_id,
operation_sequence=op.next_sequence(), # 1
)

Parameters:

ParameterTypeRequiredDescription
operation_idOptional[str]NoExplicit operation ID. Default: auto-generated UUID

Returns: OperationContext


Hash a payload using RFC 8785 canonical JSON + SHA-256. Produces identical hashes across Python, TypeScript, and Rust runtimes.

h = glacis.hash({"b": 2, "a": 1})
# Equivalent to hash_payload({"b": 2, "a": 1})

Parameters:

ParameterTypeRequiredDescription
payloadAnyYesAny JSON-serializable value

Returns: str — hex-encoded SHA-256 hash (64 characters)


Get the most recent offline attestation from local storage. Only available in offline mode.

last = glacis.get_last_receipt()
if last:
print(last.id, last.evidence_hash)

Returns: Optional[Attestation]

Raises: RuntimeError if called in online mode


Asynchronous version of the Glacis client for use with asyncio. Supports online mode only.

from glacis import AsyncGlacis
async with AsyncGlacis(api_key="glsk_live_...") as glacis:
attestation = await glacis.attest(
service_id="my-service",
operation_type="inference",
input={"prompt": "Hello"},
output={"response": "Hi there"},
)
result = await glacis.verify(attestation.id)
entries = await glacis.query_log(service_id="my-service")
tree_head = await glacis.get_tree_head()

Constructor parameters: api_key (str, required), base_url, debug, timeout, max_retries, base_delay, max_delay (same types and defaults as Glacis for the remaining parameters).

Async methods (must be awaited):

MethodSignature
attest()Same parameters as Glacis.attest(). Returns Attestation.
verify()Accepts str (attestation ID). Returns VerifyResult.
query_log()Same parameters as Glacis.query_log(). Returns LogQueryResult.
get_tree_head()No parameters. Returns TreeHeadResponse.

Synchronous methods (no await):

MethodDescription
hash(payload)RFC 8785 + SHA-256. Returns str.

Tracks operation_id and auto-increments operation_sequence for grouping related attestations.

from glacis import OperationContext
op = OperationContext() # auto-generated UUID
op = OperationContext("my-custom-id") # explicit ID
op.operation_id # str — the operation ID
op.next_sequence() # 0
op.next_sequence() # 1
op.next_sequence() # 2
Attribute / MethodTypeDescription
operation_idstrThe operation ID (UUID or custom string)
next_sequence()intReturns the next sequence number (starts at 0, auto-increments)

All models are Pydantic BaseModel subclasses importable from glacis.models.

from glacis.models import (
Attestation,
Receipt,
VerifyResult,
OfflineVerifyResult,
Evidence,
Review,
SamplingDecision,
ControlPlaneResults,
PolicyContext,
ModelInfo,
Determination,
ControlExecution,
InclusionProof,
SignedTreeHead,
TransparencyProofs,
LogQueryResult,
LogEntry,
TreeHeadResponse,
GlacisApiError,
GlacisRateLimitError,
)

Unified attestation model. Returned by glacis.attest().

class Attestation(BaseModel):
id: str
operation_id: str
operation_sequence: int
service_id: str
operation_type: str
evidence_hash: str
cpr_hash: Optional[str]
supersedes: Optional[str]
control_plane_results: Optional[dict[str, Any]]
evidence: Optional[Evidence]
review: Optional[Review]
public_key: str
signature: str
sampling_decision: Optional[SamplingDecision]
is_offline: bool
timestamp: Optional[int]
@property
def witness_status(self) -> str: ... # "WITNESSED" or "UNVERIFIED"
FieldTypeDescription
idstrAttestation ID (att_xxx online, oatt_xxx offline)
operation_idstrUUID linking attestations in the same operation
operation_sequenceintOrdinal sequence within the operation
service_idstrService identifier
operation_typestrType of operation
evidence_hashstrSHA-256 of canonical JSON {"input": ..., "output": ...}
cpr_hashOptional[str]SHA-256 of canonical JSON control plane results
supersedesOptional[str]Attestation ID this replaces (revision chains)
control_plane_resultsOptional[dict[str, Any]]Control plane results (dict on wire)
evidenceOptional[Evidence]L1 sampled evidence payload
reviewOptional[Review]L2 deep review record
public_keystrArbiter Ed25519 public key (hex)
signaturestrArbiter Ed25519 signature (hex)
sampling_decisionOptional[SamplingDecision]Server-assigned sampling tier
is_offlineboolWhether this is an offline attestation
timestampOptional[int]Unix timestamp in milliseconds
witness_statusstr (property)"WITNESSED" if online, "UNVERIFIED" if offline

Notary receipt wrapping an Attestation with transparency proofs.

class Receipt(BaseModel):
schema_version: str
attestation: Attestation
timestamp: int
epoch_id: str
heartbeat_epoch: int
attestation_hash: str
binary_hash: str
network_state_hash: str
mono_counter: int
wall_time_ns: str
transparency_proofs: Optional[TransparencyProofs]
public_key: str
signature: str
FieldTypeDescription
schema_versionstrSchema version. Default: "1.0"
attestationAttestationThe attestation this receipt covers
timestampintUnix epoch timestamp in milliseconds
epoch_idstrEpoch identifier
heartbeat_epochintHeartbeat epoch counter
attestation_hashstrSHA-256 of canonical attestation
binary_hashstrHash of the notary binary
network_state_hashstrHash of the network state
mono_counterintMonotonic counter
wall_time_nsstrWall clock time in nanoseconds
transparency_proofsOptional[TransparencyProofs]RFC 6962 transparency proofs
public_keystrNotary Ed25519 public key
signaturestrNotary Ed25519 signature

Result of verifying an online attestation. Returned by glacis.verify() for online attestations.

class VerifyResult(BaseModel):
valid: bool
attestation: Optional[AttestationEntry]
org: Optional[OrgInfo]
verification: Optional[Verification]
proof: Optional[InclusionProof]
tree_head: Optional[SignedTreeHead]
error: Optional[str]
FieldTypeDescription
validboolOverall validity of the attestation
attestationOptional[AttestationEntry]The attestation entry from the log
orgOptional[OrgInfo]Organization information
verificationOptional[Verification]Verification details (contains signature_valid, proof_valid)
proofOptional[InclusionProof]Merkle inclusion proof
tree_headOptional[SignedTreeHead]Signed tree head at verification time
errorOptional[str]Error message if invalid

Access signature and proof validity through the nested verification object:

result = glacis.verify("att_abc123")
if result.valid:
print(result.verification.signature_valid) # bool
print(result.verification.proof_valid) # bool

Result of verifying an offline attestation. Returned by glacis.verify() for offline attestations.

class OfflineVerifyResult(BaseModel):
valid: bool
witness_status: Literal["UNVERIFIED"]
signature_valid: bool
attestation: Optional[Attestation]
error: Optional[str]
FieldTypeDescription
validboolWhether the signature is valid
witness_statusLiteral["UNVERIFIED"]Always "UNVERIFIED" for offline attestations
signature_validboolWhether the Ed25519 signature is valid
attestationOptional[Attestation]The verified attestation
errorOptional[str]Error message if invalid

L1 sampled evidence payload attached to attestations when sampled at L1 or L2 tier.

class Evidence(BaseModel):
sample_probability: float # 0.0 - 1.0
data: dict[str, Any]
FieldTypeDescription
sample_probabilityfloatProbability this evidence was sampled (0.0-1.0)
datadict[str, Any]The evidence payload (typically {"input": ..., "output": ...})

L2 deep review record.

class Review(BaseModel):
sample_probability: float
judge_ids: list[str]
conformity_score: float
recommendation: Literal["uphold", "borderline", "escalate"]
rationale: str
FieldTypeDescription
sample_probabilityfloatProbability this item was sampled for review (0.0-1.0)
judge_idslist[str]IDs of judges that evaluated this item
conformity_scorefloatAggregate conformity score (0.0-1.0)
recommendationLiteral["uphold", "borderline", "escalate"]Derived action
rationalestrExplanation for the recommendation

Deterministic, auditor-reproducible sampling tier assignment.

class SamplingDecision(BaseModel):
level: str # "L0", "L1", or "L2"
sample_value: int # uint64 from HMAC-SHA256
prf_tag: list[int] # Full HMAC-SHA256 tag bytes
FieldTypeDescription
levelstrSampling tier: "L0", "L1", or "L2"
sample_valueintFirst 8 bytes of prf_tag as big-endian uint64
prf_taglist[int]Full HMAC-SHA256 tag over the evidence hash

Control plane results. Accepted by glacis.attest() as a typed model or plain dict.

class ControlPlaneResults(BaseModel):
policy: PolicyContext
determination: Determination
controls: list[ControlExecution]
FieldTypeDescription
policyPolicyContextPolicy metadata
determinationDeterminationWhether the request was forwarded or blocked
controlslist[ControlExecution]Records of individual control executions

class PolicyContext(BaseModel):
id: str
version: str
model: Optional[ModelInfo]
environment: str # Default: "development"
tags: list[str]
FieldTypeDescription
idstrPolicy identifier
versionstrPolicy version
modelOptional[ModelInfo]Model information
environmentstrEnvironment (e.g., "production", "staging")
tagslist[str]Custom tags

class ModelInfo(BaseModel):
model_id: str
provider: str
system_prompt_hash: Optional[str]
temperature: Optional[float]
FieldTypeDescription
model_idstrModel identifier
providerstrProvider name (e.g., "openai", "anthropic")
system_prompt_hashOptional[str]SHA-256 hash of the system prompt
temperatureOptional[float]Temperature setting

class Determination(BaseModel):
action: Literal["forwarded", "blocked"]
FieldTypeDescription
actionLiteral["forwarded", "blocked"]Whether the request was forwarded or blocked

Record of an individual control execution in the pipeline.

class ControlExecution(BaseModel):
id: str
type: ControlType
version: str
provider: str
latency_ms: int
status: ControlStatus
score: Optional[float]
result_hash: Optional[str]
stage: Literal["input", "output"]
FieldTypeDescription
idstrControl execution identifier
typeControlTypeControl type (see below)
versionstrControl version
providerstrControl provider
latency_msintProcessing time in milliseconds
statusControlStatusStatus: "forward", "flag", "block", or "error"
scoreOptional[float]Numeric score (e.g., jailbreak probability)
result_hashOptional[str]Hash of the control result
stageLiteral["input", "output"]Pipeline stage. Default: "input"

Type aliases:

ControlType = Literal[
"content_safety", "pii", "jailbreak", "topic",
"prompt_security", "grounding", "word_filter", "custom",
]
ControlStatus = Literal["forward", "flag", "block", "error"]

RFC 6962 Merkle inclusion proof.

class InclusionProof(BaseModel):
leaf_index: int
tree_size: int
hashes: list[str]
root_hash: Optional[str]
FieldTypeDescription
leaf_indexintLeaf index in tree (0-based)
tree_sizeintTree size when proof was generated
hasheslist[str]Sibling hashes (hex-encoded)
root_hashOptional[str]Root hash (hex-encoded)

Cryptographic commitment to Merkle tree state.

class SignedTreeHead(BaseModel):
tree_size: int
timestamp: str
root_hash: str
public_key: Optional[str]
signature: str
FieldTypeDescription
tree_sizeintTotal number of leaves
timestampstrISO 8601 timestamp when signed
root_hashstrRoot hash (hex-encoded)
public_keyOptional[str]Ed25519 public key (hex)
signaturestrEd25519 signature (hex-encoded)

RFC 6962 transparency proof structure.

class TransparencyProofs(BaseModel):
inclusion_proof: InclusionProof
sth_curr: SignedTreeHead
sth_prev: SignedTreeHead
consistency_path: list[str]
FieldTypeDescription
inclusion_proofInclusionProofMerkle inclusion proof
sth_currSignedTreeHeadCurrent signed tree head
sth_prevSignedTreeHeadPrevious signed tree head
consistency_pathlist[str]Consistency proof hashes

Paginated result from glacis.query_log().

class LogQueryResult(BaseModel):
entries: list[LogEntry]
has_more: bool
next_cursor: Optional[str]
count: int
tree_head: Optional[SignedTreeHead]
FieldTypeDescription
entrieslist[LogEntry]Log entries
has_moreboolWhether more results exist
next_cursorOptional[str]Cursor for the next page
countintNumber of entries returned
tree_headOptional[SignedTreeHead]Current tree head

Individual log entry in query results.

class LogEntry(BaseModel):
attestation_id: str
entry_id: Optional[str]
timestamp: Optional[str]
org_id: Optional[str]
org_name: Optional[str]
service_id: Optional[str]
operation_type: Optional[str]
evidence_hash: Optional[str]
signature: Optional[str]
leaf_index: Optional[int]
leaf_hash: Optional[str]
FieldTypeDescription
attestation_idstrAttestation ID
entry_idOptional[str]Log entry ID
timestampOptional[str]Timestamp (ISO 8601)
org_idOptional[str]Organization ID
org_nameOptional[str]Organization name
service_idOptional[str]Service ID
operation_typeOptional[str]Operation type
evidence_hashOptional[str]Evidence hash
signatureOptional[str]Signature
leaf_indexOptional[int]Leaf index in the Merkle tree
leaf_hashOptional[str]Leaf hash

Response from glacis.get_tree_head().

class TreeHeadResponse(BaseModel):
tree_size: int
root_hash: str
timestamp: str
signature: str
FieldTypeDescription
tree_sizeintTotal number of leaves
root_hashstrMerkle root hash (hex-encoded)
timestampstrISO 8601 timestamp
signaturestrEd25519 signature

Operating mode enum for the Glacis client.

from glacis import GlacisMode
class GlacisMode(str, Enum):
ONLINE = "online"
OFFLINE = "offline"

Importable from glacis.crypto.

Hash a payload using RFC 8785 canonical JSON + SHA-256. This is the primary hashing function for the transparency log. Produces identical hashes across Python, TypeScript, and Rust runtimes.

from glacis.crypto import hash_payload
# Key order does not matter — canonical JSON sorts keys
hash1 = hash_payload({"b": 2, "a": 1})
hash2 = hash_payload({"a": 1, "b": 2})
assert hash1 == hash2

Parameters:

ParameterTypeRequiredDescription
dataAnyYesAny JSON-serializable value

Returns: str — hex-encoded SHA-256 hash (64 characters)

Serialize data to RFC 8785 canonical JSON. Deterministic JSON that is identical across all runtimes.

from glacis.crypto import canonical_json
canonical_json({"b": 2, "a": 1})
# '{"a":1,"b":2}'

Parameters:

ParameterTypeRequiredDescription
dataAnyYesAny JSON-serializable value

Returns: str — canonical JSON string

Raises: ValueError if data contains non-serializable values (NaN, Infinity)


Error from the Glacis API. Base class for API-related exceptions.

from glacis.models import GlacisApiError
try:
attestation = glacis.attest(...)
except GlacisApiError as e:
print(e.status) # HTTP status code (int)
print(e.code) # Error code string (Optional[str])
print(e.details) # Error details dict (Optional[dict])
AttributeTypeDescription
statusintHTTP status code
codeOptional[str]Error code string
detailsOptional[dict[str, Any]]Error details

Subclass of GlacisApiError raised when the API returns HTTP 429.

from glacis.models import GlacisRateLimitError
try:
attestation = glacis.attest(...)
except GlacisRateLimitError as e:
print(e.retry_after_ms) # Retry delay in milliseconds (Optional[int])
AttributeTypeDescription
retry_after_msOptional[int]Suggested retry delay in milliseconds

Raised when a control blocks the request. Available from integrations.

from glacis.integrations.base import GlacisBlockedError
try:
response = wrapped_client.chat.completions.create(...)
except GlacisBlockedError as e:
print(e.control_type) # e.g., "jailbreak"
print(e.score) # e.g., 0.95
AttributeTypeDescription
control_typestrType of control that caused the block
scoreOptional[float]Confidence score from the blocking control

Error from cryptographic operations (e.g., missing PyNaCl dependency).

from glacis.crypto import CryptoError
try:
from glacis.crypto import get_ed25519_runtime
runtime = get_ed25519_runtime()
except CryptoError as e:
print(e) # "PyNaCl not installed. Install with: pip install pynacl"

from glacis import __version__
print(__version__) # "0.5.0"