Skip to content

Add Tenuo authorization contrib module#1447

Open
aimable100 wants to merge 2 commits intotemporalio:mainfrom
tenuo-ai:main
Open

Add Tenuo authorization contrib module#1447
aimable100 wants to merge 2 commits intotemporalio:mainfrom
tenuo-ai:main

Conversation

@aimable100
Copy link
Copy Markdown

Summary

Adds temporalio.contrib.tenuo, a SimplePlugin that wires Tenuo warrant-based authorization into Temporal workflows. Agents (workflows) carry signed warrants specifying which tools (activities) they can call and with what argument constraints. Sub-agents (child workflows) receive attenuated warrants — capabilities can only shrink, never expand.

  • TenuoPlugin — registers client interceptor (warrant header injection), worker interceptors (PoP signing + authorization verification), and sandbox passthrough for the tenuo native extension.
  • Thin adapter — only 3 symbols exported from the contrib module (TenuoPlugin, TENUO_PLUGIN_NAME, ensure_tenuo_workflow_runner). All other types are imported from tenuo.temporal, matching the pattern established by openai_agents and other contrib modules.
  • No private imports — all tenuo.temporal internals used by the plugin are exposed through public lazy-loaded names.

Files

File Lines Purpose
temporalio/contrib/tenuo/__init__.py 27 Public API (3 exports)
temporalio/contrib/tenuo/_plugin.py 180 TenuoPlugin SimplePlugin subclass
temporalio/contrib/tenuo/README.md 371 Documentation with multi-agent delegation example
tests/contrib/tenuo/test_tenuo.py 464 Unit tests + live integration tests
tests/contrib/tenuo/test_tenuo_replay.py 243 Record-and-replay determinism tests
pyproject.toml +2 tenuo optional dependency

Replay safety

Replay determinism is verified at two levels:

  1. Static analysis — source inspection confirms workflow.now() (not time.time()), no datetime.now(), no os.urandom/random/uuid4, no time.sleep, no threading.Thread.
  2. Live record-and-replay — workflows execute against a local Temporal server, history is captured via fetch_history(), and a fresh TenuoPlugin instance replays via Replayer. Tests cover single-tool and multi-tool (sequential PoP ordering) scenarios.

Integration tests

  • test_authorized_activity_succeeds — full warrant → PoP → authorization flow
  • test_start_workflow_authorizedstart_workflow_authorized returns a handle
  • test_unauthorized_activity_is_non_retryable — unauthorized tool call produces WorkflowFailureError with ApplicationError(non_retryable=True)
  • test_duplicate_registration_raises — same plugin instance on two workers raises RuntimeError

Test plan

  • pytest tests/contrib/tenuo/test_tenuo.py -v — unit + integration tests
  • pytest tests/contrib/tenuo/test_tenuo_replay.py -v — replay determinism tests
  • ruff check temporalio/contrib/tenuo/ tests/contrib/tenuo/ — no lint errors

Adds `temporalio.contrib.tenuo`, a SimplePlugin integration for
Tenuo warrant-based authorization in Temporal workflows.

The plugin (`TenuoPlugin`) wires client interceptors, worker
interceptors, and workflow sandbox passthrough in a single line:

    from temporalio.contrib.tenuo import TenuoPlugin
    plugin = TenuoPlugin(config)
    client = await Client.connect("localhost:7233", plugins=[plugin])

Key design decisions:
- Thin adapter: only TenuoPlugin, TENUO_PLUGIN_NAME, and
  ensure_tenuo_workflow_runner are exported from the contrib module.
  All other types (TenuoPluginConfig, EnvKeyResolver, etc.) are
  imported directly from tenuo.temporal.
- No private imports: all tenuo.temporal internals used by the plugin
  are exposed through public lazy-loaded names.
- No re-exports of external package types, matching the pattern
  established by openai_agents and other contrib modules.

Files:
- temporalio/contrib/tenuo/__init__.py — public API (3 exports)
- temporalio/contrib/tenuo/_plugin.py — TenuoPlugin SimplePlugin subclass
- temporalio/contrib/tenuo/README.md — multi-agent delegation example
- tests/contrib/tenuo/test_tenuo.py — unit + live integration tests
- tests/contrib/tenuo/test_tenuo_replay.py — record-and-replay tests
- pyproject.toml — tenuo optional dependency
Add Tenuo authorization contrib module
@aimable100 aimable100 requested a review from a team as a code owner April 14, 2026 21:52
@tconley1428
Copy link
Copy Markdown
Contributor

I don't think it is likely that we are willing to accept this. We welcome folks using Temporal as a part of their solution, but including it in the SDK's contrib comes with an implication of our maintenance and ownership of the solution. From a technical perspective, you are welcome to create a plugin external to the SDK repo, and we can have a discussion about partnership. If you reach out in our community slack, I can put you in touch with the folks running AI partnership.

@aimable100
Copy link
Copy Markdown
Author

Thanks for the note. This was submitted through Temporal's AI Partner Program — I was invited and completed the submission form. Happy to move to an external plugin if that's the preferred path for partners too. Will follow up with the team to confirm.

@jssmith
Copy link
Copy Markdown
Contributor

jssmith commented Apr 14, 2026

@aimable100 - thank you for preparing this plugin. I will leave this PR open so that our team can provide feedback. You should plan to move it to one of your repositories, though.

@0xbrainkid
Copy link
Copy Markdown

Warrant-based authorization with capability attenuation for child workflows is exactly the right model for multi-agent systems — the core invariant (sub-agent warrants can only be narrower than parent warrants) prevents privilege escalation across delegation chains, which is one of the hardest problems to enforce in hierarchical agent systems.

A few observations on the design:

Warrant as behavioral trust anchor. The warrant proves what an agent is authorized to do. Combining it with a behavioral trust score (SATP or AgentFolio) proves what the agent has reliably done in practice. Together they answer both "is this agent authorized?" and "has this agent been trustworthy?". The TenuoPlugin could optionally attach a trust score assertion to the warrant — not as a hard enforcement (that is Tenuo's job) but as an observable claim that the authorizing principal can verify against a registry:

warrant = tenuo.create_warrant(
    tools=["run_query", "send_report"],
    agent_id="fleet-agent-xyz",
    behavioral_trust_url="https://agentfolio.bot/verify/fleet-agent-xyz"  # optional
)

Denial receipt propagation. When TenuoPlugin rejects a tool call (unauthorized activity), that denial event is security-relevant beyond local logging. Propagating it to a cross-org trust registry (AgentFolio) means the denial pattern becomes visible to other orchestrators evaluating whether to trust this agent — repeated warrant violations are a behavioral anomaly signal.

Replay safety note. The record-and-replay determinism approach via Replayer is thorough. One edge case worth verifying: if the behavioral trust lookup (if added) uses a network call, it needs to be deterministic or memoized for replay to work correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants