Trust Manager
Overview
Manage peer relationships between actors and (optionally) customize permissions per relationship.
Basic Usage
# Create relationship
rel = actor.trust.create_relationship(
peer_url="https://peer.example.com/actor123",
relationship="friend",
)
# Inspect relationships
for rel in actor.trust.relationships:
print(rel.peer_id, rel.relationship)
# Approve
actor.trust.approve_relationship(peer_id="peer123")
Permissions (Per Relationship)
For apps using unified access control, you can set per‑relationship overrides. See ActingWeb Access Control (Simple Guide) for the simple guide.
Programmatic example:
from actingweb.trust_permissions import get_trust_permission_store, create_permission_override
store = get_trust_permission_store(config)
perms = create_permission_override(
actor_id=actor.id,
peer_id="peer123",
trust_type="friend",
permission_updates={
"properties": {"patterns": ["public/*", "notes/*"], "operations": ["read", "write"]},
"methods": {"allowed": ["get_*", "create_*"], "denied": ["delete_*"]},
},
)
store.store_permissions(perms)
REST API
GET /{actor_id}/trust/{relationship}/{peer_id}?permissions=truePUT /{actor_id}/trust/{relationship}/{peer_id}/permissionsGET /{actor_id}/trust/{relationship}/{peer_id}/permissionsDELETE /{actor_id}/trust/{relationship}/{peer_id}/permissions
See also: ActingWeb Unified Access Control System for the full system.
Trust and Subscriptions Lifecycle
Trust relationships and subscriptions are tightly coupled. Understanding their lifecycle is essential for reliable peer communication.
Subscription Requirements
Subscriptions require an established trust relationship:
# 1. First establish trust
rel = actor.trust.create_relationship(
peer_url="https://peer.example.com/actor123",
relationship="friend",
)
# 2. Approve the relationship (if needed)
actor.trust.approve_relationship(peer_id="peer123")
# 3. Now subscriptions work
actor.subscriptions.subscribe_to_peer(
peer_id="peer123", target="properties"
)
Trust States and Subscription Behavior
Trust State |
Subscription Behavior |
|---|---|
Pending (not approved) |
Subscriptions can be created but callbacks may be rejected |
Approved |
Full subscription functionality, callbacks delivered |
Deleted |
All subscriptions terminated, peer data cleaned up (if |
Automatic Cleanup on Trust Deletion
When auto_cleanup=True is enabled (default), deleting a trust relationship triggers automatic cleanup:
# Enable automatic cleanup
app.with_subscription_processing(auto_cleanup=True)
# When trust is deleted...
actor.trust.delete_relationship(peer_id="peer123")
# The following happens automatically:
# 1. All subscriptions with this peer are terminated
# 2. RemotePeerStore data for this peer is deleted
# 3. Pending callbacks for this peer are discarded
# 4. Callback state (sequence tracking) is cleared
Manual Cleanup
If you need manual control over cleanup, disable auto_cleanup and handle it in your trust hook:
app.with_subscription_processing(auto_cleanup=False)
@app.trust_hook("delete")
def on_trust_deleted(actor, peerid, relationship, trust_data):
# Custom cleanup logic
from actingweb.remote_storage import RemotePeerStore
from actingweb.callback_processor import CallbackProcessor
# Clean up peer data
store = RemotePeerStore(actor, peerid)
store.delete_all()
# Clear callback state
processor = CallbackProcessor(actor)
# Note: subscription_id needed - iterate if multiple
processor.clear_state(peerid, subscription_id)
# Application-specific cleanup
notify_user(f"Connection with {peerid} ended")
Pending Callbacks When Trust Ends
When trust is deleted, any pending (out-of-order) callbacks for that peer are discarded:
The CallbackProcessor checks trust status before processing
Pending callbacks from untrusted peers are rejected
No resync is triggered for deleted trust relationships
Re-establishing Trust After Deletion
When trust is re-established with a previously connected peer:
# Re-create trust
actor.trust.create_relationship(
peer_url="https://peer.example.com/actor123",
relationship="friend",
)
actor.trust.approve_relationship(peer_id="peer123")
# Re-subscribe (state starts fresh)
actor.subscriptions.subscribe_to_peer(
peer_id="peer123", target="properties"
)
# Peer sends initial resync with full current state
Important: Sequence numbers start fresh. The first callback after re-subscription triggers a full resync to establish baseline state.
Trust Hooks and Subscription Events
Use trust hooks to react to lifecycle events:
@app.trust_hook("create")
def on_trust_created(actor, peerid, relationship, approved, trust_data):
# Optionally auto-subscribe when trust is established
if approved:
actor.subscriptions.subscribe_to_peer(
peer_id=peerid, target="properties"
)
@app.trust_hook("delete")
def on_trust_deleted(actor, peerid, relationship, trust_data):
# Application-specific cleanup (storage cleanup is automatic)
notify_websocket_clients(f"Peer {peerid} disconnected")
log_audit_event("trust_deleted", peer_id=peerid)
Peer Profile Caching
ActingWeb can automatically cache profile attributes from trusted peers, making it easy to display peer information without repeated API calls.
Enable Profile Caching
app = ActingWebApp(
aw_type="urn:actingweb:example.com:myapp",
fqdn="myapp.example.com"
).with_peer_profile(attributes=["displayname", "email", "description"])
When enabled, profiles are:
Automatically fetched when trust is fully approved (both sides)
Refreshed during
sync_peer()operationsCleaned up when trust is deleted
Accessing Cached Profiles
# Get cached profile
profile = actor.trust.get_peer_profile(peer_id)
if profile:
print(f"Connected with {profile.displayname}")
print(f"Email: {profile.email}")
# Access additional attributes
avatar = profile.get_attribute("avatar_url")
# Check for fetch errors
if profile and profile.fetch_error:
print(f"Warning: {profile.fetch_error}")
Manual Profile Refresh
# Sync version
profile = actor.trust.refresh_peer_profile(peer_id)
# Async version (for FastAPI)
profile = await actor.trust.refresh_peer_profile_async(peer_id)
Custom Attributes
Cache any property the peer exposes:
app.with_peer_profile(attributes=[
"displayname",
"email",
"avatar_url",
"timezone",
"organization",
])
See Configuration Reference for detailed configuration options.
Peer Capabilities Caching
ActingWeb can automatically cache the methods and actions that trusted peers expose, making it easy to discover what functionality is available without repeated API calls.
Enable Capabilities Caching
app = ActingWebApp(
aw_type="urn:actingweb:example.com:myapp",
fqdn="myapp.example.com"
).with_peer_capabilities(enable=True, max_age_seconds=3600)
Parameters:
enable: Enable/disable capabilities caching. Default:Truewhen called.max_age_seconds: Maximum cache age in seconds before capabilities are refetched duringsync_peer(). Default:3600(1 hour). Set to0to always refetch.
When enabled, capabilities are:
Automatically fetched when trust is fully approved (both sides)
Refreshed during
sync_peer()operations (only if cache is older thanmax_age_seconds)Always refreshed when
sync_peer(force_refresh=True)is calledCleaned up when trust is deleted
Accessing Cached Capabilities
# Get all cached capabilities
capabilities = actor.trust.get_peer_capabilities(peer_id)
if capabilities:
# List available methods and actions
print(f"Methods: {capabilities.get_method_names()}")
print(f"Actions: {capabilities.get_action_names()}")
# Get specific method details
method = capabilities.get_method("get_data")
if method:
print(f"{method.name}: {method.description}")
if method.input_schema:
print(f"Expects: {method.input_schema}")
# Convenience methods for just methods or actions
methods = actor.trust.get_peer_methods(peer_id)
actions = actor.trust.get_peer_actions(peer_id)
# Check for fetch errors
if capabilities and capabilities.fetch_error:
print(f"Warning: {capabilities.fetch_error}")
Manual Capabilities Refresh
# Sync version
capabilities = actor.trust.refresh_peer_capabilities(peer_id)
# Async version (for FastAPI)
capabilities = await actor.trust.refresh_peer_capabilities_async(peer_id)
Use Case: Method Discovery
Peer capabilities caching is especially useful for MCP (Model Context Protocol) integration, where you need to discover what tools/methods a peer exposes:
# Check if peer supports a specific method before calling
capabilities = actor.trust.get_peer_capabilities(peer_id)
if capabilities:
if capabilities.get_method("summarize"):
# Safe to call the summarize method
result = actor.trust.call_peer_method(peer_id, "summarize", data)
else:
# Use fallback behavior
result = default_summarize(data)
See Configuration Reference for detailed configuration options.
Peer Permissions Caching
ActingWeb can automatically cache what permissions peer actors have granted to your actor. This enables efficient permission checking without network requests.
Enable Permissions Caching
app = ActingWebApp(
aw_type="urn:actingweb:example.com:myapp",
fqdn="myapp.example.com"
).with_peer_permissions(
enable=True,
auto_delete_on_revocation=True, # Delete cached data when revoked
notify_peer_on_change=True # Auto-notify peers (default)
)
Configuration Options:
enable: Enable peer permissions caching. Default:Truewhen called.auto_delete_on_revocation: Delete cached peer data when permissions revoked. Default:False.notify_peer_on_change: Auto-notify peers when their permissions change. Default:True.
When enabled, permissions are:
Fetched when trust relationships are fully approved
Updated when permission callbacks are received from peers
Sent to peers automatically when you change their permissions (if
notify_peer_on_change=True)Refreshed during
sync_peer()operationsDeleted when trust relationships are removed
Accessing Cached Permissions
from actingweb.peer_permissions import get_peer_permission_store
store = get_peer_permission_store(actor.config)
# Get cached permissions
perms = store.get_permissions(actor.id, peer_id)
if perms:
# Check property access
if perms.has_property_access("memory_travel", "read"):
data = actor.trust.get_peer_property(peer_id, "memory_travel")
# Check method access
if perms.has_method_access("sync_data"):
result = actor.trust.call_peer_method(peer_id, "sync_data", params)
# Check tool access (MCP)
if perms.has_tool_access("search"):
# Safe to use the search tool
pass
# Check for fetch errors
if perms and perms.fetch_error:
print(f"Warning: {perms.fetch_error}")
Manual Permissions Refresh
from actingweb.peer_permissions import fetch_peer_permissions
# Synchronous refresh
perms = fetch_peer_permissions(actor, peer_id)
# Async refresh (for FastAPI)
perms = await fetch_peer_permissions_async(actor, peer_id)
Permission Callbacks
When a peer modifies permissions granted to your actor, they can send a permission callback to notify you immediately. The callback is sent to:
POST /{your_actor_id}/callbacks/permissions/{peer_actor_id}
The library automatically handles these callbacks and updates the local cache. Permission
callbacks contain the full effective permissions (base trust-type defaults merged with
per-trust overrides), enabling accurate change detection. When new property patterns are
granted, an incremental sync automatically fetches only the newly granted properties.
Peers supporting this feature advertise permissioncallback in their
/meta/actingweb/supported endpoint.
Use Cases for MCP
Peer permissions caching is especially useful for MCP (Model Context Protocol) integration, where you need to check if you have access to a peer’s tools or resources:
from actingweb.peer_permissions import get_peer_permission_store
store = get_peer_permission_store(actor.config)
perms = store.get_permissions(actor.id, peer_id)
if perms:
# Check tool access before calling
if perms.has_tool_access("search"):
result = await actor.trust.call_peer_tool(peer_id, "search", query)
else:
# Access denied - use alternative approach
result = local_search(query)
# Check resource access
if perms.has_resource_access("data://shared/*"):
# Can access shared data resources
pass
See Configuration Reference for detailed configuration options.