Skip to main content
Attesta provides full implementations in both Python and TypeScript. The two SDKs share the same approval pipeline, risk scoring logic, and audit format, but differ in naming conventions and language-specific patterns. This page documents every significant difference.

Naming Conventions

Python uses snake_case for all identifiers. TypeScript uses camelCase for properties and methods, and PascalCase for types and classes.
PythonTypeScriptContext
risk_scorerriskScorerConstructor / gate option
audit_loggerauditLoggerConstructor / gate option
risk_hintsriskHintsGate option
min_review_secondsminReviewSecondsGate option / policy key
fail_modefailModeTimeout policy option (deny | allow | escalate)
timeout_secondsapprovalTimeoutSecondsTimeout threshold (seconds)
agent_idagentIdGate option / context field
session_idsessionIdGate option / context field
challenge_mapchallengeMapPolicy key
function_namefunctionNameActionContext field
function_docfunctionDocActionContext field
risk_assessmentriskAssessmentApprovalResult field
challenge_resultchallengeResultApprovalResult field
review_time_secondsreviewTimeSecondsApprovalResult field
from_configConstructor (new Attesta())No TS equivalent; use constructor
render_approvalrenderApprovalRenderer method
render_challengerenderChallengeRenderer method
render_inforenderInfoRenderer method
render_auto_approvedrenderAutoApprovedRenderer method
challenge_typechallengeTypeChallengeProtocol property

Gate Syntax

The @gate decorator in Python has no direct equivalent in TypeScript. Instead, TypeScript uses gate() as a higher-order function.
Python supports three calling styles:
from attesta import gate

# Style 1: Bare decorator
@gate
def read_file(path: str) -> str:
    return open(path).read()

# Style 2: Empty parentheses
@gate()
def list_users() -> list[str]:
    return ["alice", "bob"]

# Style 3: With options
@gate(risk="high", risk_hints={"production": True})
def deploy(service: str, version: str) -> str:
    return f"Deployed {service} v{version}"

Async Patterns

Python’s asyncio and TypeScript’s Promise model have significant differences that affect how Attesta works in each language.
The Python SDK supports both sync and async functions. When a sync function is decorated with @gate, Attesta internally bridges to async using asyncio.run() or schedules a task if a loop is already running (e.g., Jupyter).
from attesta import gate

# Sync function -- Attesta handles the event loop
@gate
def sync_deploy(service: str) -> str:
    return f"Deployed {service}"

result = sync_deploy("api")  # No await needed

# Async function -- caller must await
@gate
async def async_deploy(service: str) -> str:
    return f"Deployed {service}"

result = await async_deploy("api")
The sync_timeout parameter (default 300s) controls how long Attesta waits when bridging from sync to async.
In TypeScript, if you wrap a synchronous function with gate(), it is automatically treated as an async function. The return type becomes Promise<T> regardless of the original function’s return type.

Configuration Loading

from attesta import Attesta
from pathlib import Path

# From string path
attesta = Attesta.from_config("attesta.yaml")

# From Path object
attesta = Attesta.from_config(Path("/etc/attesta/config.yaml"))

# Programmatic
attesta = Attesta(
    policy={"min_review_seconds": 3.0},
    risk_scorer=my_scorer,
)
from_config() is synchronous and returns an Attesta instance directly.

Evaluate API

The evaluate() method is available on both SDKs and runs the full approval pipeline without raising AttestaDenied. The caller is responsible for checking the verdict.
from attesta import Attesta, ActionContext, Verdict

attesta = Attesta.from_config("attesta.yaml")

ctx = ActionContext(
    function_name="deploy",
    args=("api-gateway", "2.1.0"),
    function_doc="Deploy a service to production.",
    environment="production",
    agent_id="deploy-bot",
)

result = await attesta.evaluate(ctx)

if result.verdict == Verdict.APPROVED:
    deploy("api-gateway", "2.1.0")
elif result.verdict == Verdict.DENIED:
    print(f"Risk score: {result.risk_assessment.score}")
Key differences:
  • Python ActionContext uses keyword arguments to the constructor; TypeScript uses an object literal matching the interface shape.
  • Python uses == for enum comparison; TypeScript uses ===.
  • Python accesses result.risk_assessment; TypeScript accesses result.riskAssessment.

Custom Implementations

Both SDKs use structural typing for pluggable components (scorers, renderers, loggers), but the mechanism differs.
Python uses Protocol classes with @runtime_checkable. Any object with the right methods satisfies the protocol — no inheritance needed.
from attesta import RiskScorer, ActionContext

class MyScorer:
    @property
    def name(self) -> str:
        return "my-scorer"

    def score(self, ctx: ActionContext) -> float:
        return 0.5

# Duck typing -- no inheritance required
assert isinstance(MyScorer(), RiskScorer)

Error Handling

from attesta import gate, AttestaDenied

@gate(risk="critical")
def dangerous_action() -> str:
    return "done"

try:
    dangerous_action()
except AttestaDenied as e:
    print(e.message)           # Human-readable denial message
    print(e.result)            # ApprovalResult or None
    print(e.result.verdict)    # Verdict enum
AspectPythonTypeScript
Exception classAttestaDeniedAttestaDenied (extends Error)
Null result checkif e.result:error.result?.verdict (optional chaining)
Catch syntaxexcept AttestaDenied as ecatch (error) + instanceof check

Feature Parity

Most features are available in both SDKs. The table below documents known differences as of v0.1.x.
FeaturePythonTypeScriptNotes
gate() / @gateYesYesDecorator vs. wrapper function
Attesta classYesYes
Attesta.fromConfig()Yes (sync)No (use constructor)TypeScript uses new Attesta()
evaluate()YesYes
DefaultRiskScorerYesYes
CompositeRiskScorerYesYes
MaxRiskScorerYesYes
Domain profilesYesNoPython-only; use attesta.yaml domain config with the CLI
Multi-party challengeYesYesSupported in core; renderer support is implementation-specific
Trust engineYesYes
Audit logger (JSONL)YesYes
Hash-chain verificationYesYesPython: verify_chain() / CLI, TypeScript: verifyChain()
TerminalRenderer (rich UI)YesBasicTypeScript uses a basic console renderer
LangChain integrationYesYes
OpenAI Agents SDKYesNoPython-only
Anthropic ClaudeYesNoPython-only
CrewAIYesNoPython-only
MCP decoratorYesNoPython-only
MCP proxy (MCPProxy)YesNoUse attesta mcp wrap CLI from TypeScript projects
Vercel AI SDK integrationNoYesTypeScript-only
sync_timeout bridgingYesN/ATypeScript is always async
CLI (attesta)YesN/ACLI is Python-only; usable from any project
Even if you are building a TypeScript project, you can install the Python package (pip install attesta) to use the attesta CLI for audit verification, trust management, and MCP wrapping. The CLI operates on the shared .attesta/ data directory and attesta.yaml config file.

Audit Log Compatibility

Both SDKs use hash-chained JSONL audit logs, but the serialized field names differ (snake_case in Python, camelCase in TypeScript). Do not mix Python- and TypeScript-produced entries in the same file if you need chain verification to pass.
{
  "entryId": "a4f8b2c1e3d5...",
  "actionName": "deploy",
  "verdict": "approved",
  "riskLevel": "high",
  "riskScore": 0.78,
  "agentId": "deploy-bot",
  "previousHash": "e3d5a4f8b2c1..."
}
For now, pick one SDK as the writer for a given audit log file. Cross-SDK mixed logs can break hash-chain verification.

Next Steps

TypeScript Getting Started

Install and configure the TypeScript SDK

Attesta Class

Full API reference for the Attesta class

Protocols

Implement custom scorers, renderers, and loggers

Vercel AI SDK

TypeScript-native Vercel AI integration