Skip to main content
@hexr_agent is the core decorator in the Hexr SDK. Apply it to any function or class and the runtime assigns a cryptographic SPIFFE identity to that process, initializes OpenTelemetry instrumentation, scopes cloud credential access via OPA policies, and optionally starts an A2A bridge so other agents can discover and invoke yours. Everything else in the SDK — hexr_tool, hexr.vault, hexr.sandbox — depends on this identity being present.

Signature

@hexr_agent(
    name: str,
    tenant: str,
    resources: list[str] = None,
    subprocess_support: bool = False,
    regions: dict = None,
    a2a: bool = False,
    skills: list[dict] = None,
    description: str = None,
    version: str = None,
)

Parameters

name
string
required
The agent’s name. Used in SPIFFE ID generation, Kubernetes resource naming, and observability spans.Must be kebab-case: research-analyst, content-crew, data-pipeline.
tenant
string
required
The tenant (organization) this agent belongs to. Maps to a Kubernetes namespace: tenant-{tenant}.
resources
list[str]
default:"None"
Cloud resources this agent needs access to. OPA uses this list to scope which credential exchanges are permitted.Examples: ["aws_s3", "gcp_bigquery", "azure_storage"]Fine-grained access: ["aws_s3:read"]
subprocess_support
bool
default:"False"
Enable subprocess role management. When True, child processes can declare different roles and receive distinct SPIFFE identities within the same pod.
regions
dict
default:"None"
Multi-region credential configuration. Specifies which region to use per cloud provider.Example: {"aws": "us-west-2", "gcp": "us-central1"}
a2a
bool
default:"False"
Enable Agent-to-Agent communication. Starts an HTTP server on port 8080 that receives JSON-RPC messages from the A2A sidecar. Serves an Agent Card at /.well-known/agent.json.
skills
list[dict]
default:"None"
A2A skill declarations for the Agent Card. Each skill requires id, name, and description fields.
skills=[
    {"id": "research", "name": "Research", "description": "Deep web research on any topic"},
    {"id": "summarize", "name": "Summarize", "description": "Summarize long documents"}
]
description
string
default:"None"
Human-readable description for the Agent Card. Visible to other agents during A2A discovery.
version
string
default:"None"
Agent version string. Included in the Agent Card and all OTel spans.

Basic usage

Function decorator

from hexr import hexr_agent, hexr_tool

@hexr_agent(name="research-analyst", tenant="acme-corp")
def analyze(topic: str) -> str:
    s3 = hexr_tool("aws_s3")
    # ... your agent logic
    return result

Class decorator

from hexr import hexr_agent

@hexr_agent(name="content-crew", tenant="acme-corp")
class ContentCrewAgent:
    def run(self, brief: str) -> str:
        # ... multi-step content creation
        return article

What @hexr_agent does

When you decorate a function or class, the following happens at startup:
1

Sets agent context

Calls HexrContext.set_agent_context() which sets ContextVars (tenant, agent_name, framework, resources), writes a process context marker file to /tmp/hexr-context/, and registers the process with the Auto-Registrar.
2

Initializes OpenTelemetry

Creates a TracerProvider and MeterProvider pointing at the OTel Collector. All subsequent SDK calls emit spans and metrics automatically.
3

Starts A2A bridge (if a2a=True)

Starts an HTTP server on port 8080 that receives /execute calls from the A2A sidecar, calls your function with the message content, and returns the result as a Task artifact.
4

Wraps execution

Every invocation of your function or class is wrapped with:
  • hexr.agent.invoke OTel span
  • hexr.agent.invocations metric counter
  • hexr.agent.duration histogram
  • Error capture and status propagation

Framework detection

hexr build uses AST analysis to auto-detect your agent framework. The result is stored in the HEXR_FRAMEWORK environment variable and included in all OTel spans:
FrameworkDetection pattern
CrewAIfrom crewai import Agent, Crew
LangChainfrom langchain import ..., from langchain_core import ...
AutoGenfrom autogen import ..., AssistantAgent, UserProxyAgent
Strands Agentsfrom strands import Agent, @tool decorator
OpenAI Swarmfrom swarm import Swarm
Pure PythonNo framework detected — uses @hexr_agent directly

A2A agent card

When a2a=True, your agent is discoverable by other agents via the A2A protocol. The card is served at GET /.well-known/agent.json:
{
  "name": "research-analyst",
  "description": "Deep research and analysis agent",
  "url": "http://research-analyst-a2a.tenant-acme-corp.svc:8090",
  "version": "1.0.0",
  "capabilities": {
    "streaming": true,
    "pushNotifications": false,
    "stateTransitionHistory": true
  },
  "skills": [
    {
      "id": "research",
      "name": "Research",
      "description": "Deep web research on any topic"
    }
  ],
  "securitySchemes": {
    "spiffe": {
      "type": "mtls",
      "trustDomain": "hexr.cloud"
    }
  }
}

Examples

Multi-cloud agent

@hexr_agent(
    name="multi-cloud-pipeline",
    tenant="acme-corp",
    resources=["aws_s3", "gcp_bigquery", "azure_storage"],
    regions={"aws": "us-west-2", "gcp": "us-central1"}
)
def pipeline():
    s3 = hexr_tool("aws_s3")
    bq = hexr_tool("gcp_bigquery")
    blob = hexr_tool("azure_storage")

CrewAI with A2A

from crewai import Agent, Crew, Task
from hexr import hexr_agent

@hexr_agent(
    name="content-crew",
    tenant="acme-corp",
    a2a=True,
    skills=[{"id": "content", "name": "Content Creation", "description": "Blog posts and articles"}],
    description="Multi-agent content creation crew"
)
def create_content(brief: str) -> str:
    researcher = Agent(role="Research Analyst", ...)
    writer = Agent(role="Content Writer", ...)
    
    crew = Crew(agents=[researcher, writer], tasks=[...])
    return crew.kickoff()

Subprocess support

When subprocess_support=True, child processes each receive distinct SPIFFE identities scoped to their role within the parent agent:
@hexr_agent(
    name="distributed-processor",
    tenant="acme-corp",
    subprocess_support=True
)
def process(data: list):
    # Child processes get distinct SPIFFE IDs:
    # spiffe://hexr.cloud/agent/acme-corp/distributed-processor/worker-1
    # spiffe://hexr.cloud/agent/acme-corp/distributed-processor/worker-2
    with ProcessPoolExecutor() as pool:
        results = pool.map(process_chunk, data)