Beginners Guide
New to Nexus Attest? This page walks you through the core ideas and gives you working code you can run immediately. No prior knowledge of MCP, event sourcing, or cryptographic attestation is required.
What problem does Nexus Attest solve?
Section titled “What problem does Nexus Attest solve?”When AI agents execute tools in production, there is no built-in governance. An agent can rotate API keys, deploy code, or modify databases with no approval step and no audit trail. If something goes wrong, there is no record of who authorized the action or what constraints were in place.
Nexus Attest adds a governance layer that sits between the request and the execution:
- Someone (human or system) requests an action
- The request gets a policy that defines what is allowed
- One or more people approve the request
- Only then does the action execute
- A cryptographic audit package records everything that happened
Every step is recorded in an immutable event log. The audit trail is tamper-evident --- if anyone modifies it after the fact, verification fails.
Installation
Section titled “Installation”Install from PyPI with pip:
pip install nexus-attestYou need Python 3.11 or later. To verify the installation:
import nexus_attestprint(nexus_attest.__version__) # Should print 1.0.1No additional services or databases are required. Nexus Attest uses SQLite by default, which is included with Python.
Your first governance workflow
Section titled “Your first governance workflow”Here is a complete example that creates a request, collects approvals, and exports an audit package. You can paste this into a Python file and run it directly.
from nexus_attest import NexusControlToolsfrom nexus_attest.events import Actor
# Step 1: Initialize# Uses an in-memory SQLite database (no files created)tools = NexusControlTools()
# Step 2: Create a request# This declares what you want to do and how many approvals are neededresult = tools.request( goal="Rotate staging API keys", actor=Actor(type="human", id="alice@example.com"), mode="dry_run", # Safe mode for first try min_approvals=1, # Only one approval needed labels=["staging"],)request_id = result.data["request_id"]print(f"Created request: {request_id}")print(f"State: {result.data['state']}") # pending_approval
# Step 3: Approve the requestapproval = tools.approve( request_id, actor=Actor(type="human", id="alice@example.com"),)print(f"Approved: {approval.data['is_approved']}") # Trueprint(f"State: {approval.data['state']}") # approvedThe request starts in pending_approval state. After enough approvals, it moves to approved and is ready for execution.
Understanding actors and approvals
Section titled “Understanding actors and approvals”An Actor represents who is performing an action. Every actor has a type (either "human" or "system") and a unique id:
from nexus_attest.events import Actor
# Human actors represent peoplealice = Actor(type="human", id="alice@example.com")bob = Actor(type="human", id="bob@example.com")
# System actors represent automated processesscheduler = Actor(type="system", id="cron-scheduler")Approvals are counted by distinct actor.id. The same person cannot approve the same request twice. When multiple approvals are required, different actors must provide them:
# Require 2 approvalsresult = tools.request( goal="Deploy to production", actor=alice, mode="apply", min_approvals=2, labels=["prod"],)request_id = result.data["request_id"]
# First approval (1 of 2)tools.approve(request_id, actor=alice)
# Second approval (2 of 2) - must be a different actortools.approve(request_id, actor=bob)Approvals can include a comment explaining the rationale, and an optional expiration time after which the approval no longer counts:
from datetime import datetime, timedelta, UTC
tools.approve( request_id, actor=bob, comment="Reviewed the key rotation plan, looks good", expires_at=datetime.now(UTC) + timedelta(hours=4),)Inspecting decisions
Section titled “Inspecting decisions”The inspect tool gives you a complete view of a decision’s current state, including its lifecycle, blocking reasons, and timeline:
inspection = tools.inspect(request_id)
# The rendered field contains human-readable markdownprint(inspection.data["rendered"])This shows a structured report with sections for the decision status, approval progress, policy constraints, and a timeline of all events. If the decision cannot be executed yet, the blocking reasons tell you exactly why (for example, missing approvals or a policy constraint violation).
You can also check the status programmatically:
status = tools.status(request_id)print(f"State: {status.data['state']}")print(f"Goal: {status.data['goal']}")print(f"Approved: {status.data['is_approved']}")Verifying audit packages
Section titled “Verifying audit packages”After a decision has been executed, you can export an audit package that cryptographically binds the governance decision to the execution outcome:
from nexus_attest import verify_audit_package
# Export the audit packageaudit = tools.export_audit_package(request_id)package = audit.data["package"]
# Verify the package independentlyverification = verify_audit_package(package)print(f"Verification passed: {verification.ok}")
# Each of the 6 checks is reported individuallyfor check in verification.checks: status_label = "PASS" if check.passed else "FAIL" print(f" [{status_label}] {check.name}")The six verification checks ensure that the audit package has not been tampered with:
- binding_digest --- the overall hash is correct
- control_bundle_digest --- the governance data is intact
- binding_control_match --- the binding references the correct governance data
- binding_router_match --- the binding references the correct execution data
- binding_link_match --- the link between governance and execution is valid
- router_digest --- the execution data itself is intact (for embedded mode)
All six checks run regardless of failures, so you always get the complete picture in a single verification pass.
Common patterns for beginners
Section titled “Common patterns for beginners”Using templates for repeatable policies
Section titled “Using templates for repeatable policies”Instead of specifying min_approvals, allowed_modes, and other policy fields every time, create a template once and reuse it:
# Create a template (immutable after creation)tools.template_create( name="staging-deploy", actor=Actor(type="human", id="platform-team"), description="Standard staging deployment policy", min_approvals=1, allowed_modes=["dry_run", "apply"], labels=["staging"],)
# Use the template when creating requestsresult = tools.request( goal="Deploy v1.2.0 to staging", actor=Actor(type="human", id="alice@example.com"), template_name="staging-deploy",)Dry-run before apply
Section titled “Dry-run before apply”Always test with mode="dry_run" first. Dry-run requests go through the full governance workflow (request, approve, execute) but the router execution is simulated. The audit package is still produced so you can verify the flow works before running with mode="apply".
Exporting decisions for archival
Section titled “Exporting decisions for archival”Decisions can be exported as portable JSON bundles for backup or cross-system transfer:
bundle = tools.export_bundle(request_id)bundle_json = bundle.data["bundle"]# Save to file, send to another system, etc.Bundles include a digest for integrity verification. On import, the system recomputes the digest and rejects the bundle if it has been modified.
Next steps
Section titled “Next steps”Once you are comfortable with the basics, explore the rest of the handbook:
- Concepts --- deeper look at event sourcing, lifecycle, and policy mechanics
- MCP Tools --- full reference for all 11 tools with parameters
- Templates & Bundles --- advanced template patterns and bundle import/export
- Attestation --- XRPL witness proofs and cryptographic narrative reports