Configuration Reference
This page summarizes configuration for building applications with the ActingWeb Python library. Use the fluent ActingWebApp API in applications; it produces a Config object (actingweb.config.Config) that drives behavior.
Quick Start
from actingweb.interface import ActingWebApp
app = ActingWebApp(
aw_type="urn:actingweb:example.com:myapp",
database="postgresql", # or "dynamodb" (default)
fqdn="myapp.example.com",
proto="https://"
).with_oauth(provider="google", client_id="...", client_secret="...") \
.with_web_ui(enable=True) \
.with_devtest(enable=False) \
.add_actor_type("myself", relationship="friend")
config = app.get_config() # actingweb.config.Config
Core Identity
aw_type: ActingWeb type URI for your app (required).fqdn: Hostname used for URLs (e.g.,myapp.example.com).proto: URL scheme (https://recommended).version: Populated from library version; can be displayed to clients.
Runtime Switches
ui: Enable/<actor_id>/wwwweb UI (with_web_ui()). Also affects browser redirects.devtest: Enable development endpoints; MUST beFalsein production.www_auth:basicoroauth; set bywith_oauth().unique_creator: Enforce one actor per creator (with_unique_creator()).force_email_prop_as_creator: Copyemailproperty tocreator.mcp: Include MCP capability; toggle viawith_mcp().sync_subscription_callbacks: Force synchronous subscription callbacks (with_sync_callbacks()). Required for Lambda/serverless deployments where async fire-and-forget callbacks may be lost when the function freezes. Affects both diff callbacks and resync callbacks. Do NOT enable in local/container deployments to avoid blocking and self-deadlock. See Deployment for details.
Browser Redirect Behavior
The ui setting (with_web_ui()) controls where browsers are redirected:
Scenario |
|
Redirect |
Use Case |
|---|---|---|---|
Unauthenticated browser → |
Any |
|
Consistent login experience |
Authenticated browser → |
|
|
Server-rendered templates |
Authenticated browser → |
|
|
Single Page Applications |
After OAuth login |
|
|
Server-rendered templates |
After OAuth login |
|
|
Single Page Applications |
API clients (Accept: application/json) always receive JSON responses, not redirects.
For SPAs, you must provide /login and /<actor_id>/app routes. See SPA Authentication Guide.
OAuth2
Configured by with_oauth(...). Common fields:
provider: Provider name ("google"or"github"). Multiple calls with different providers configure multiple providers simultaneously.client_id,client_secret: Provider credentials.redirect_uri: Defaults to{proto}{fqdn}/oauth.auth_uri,token_uri: Authorization and token endpoints.scope: Provider-specific scopes.
Multi-provider example:
app = ActingWebApp(...) \
.with_oauth(provider="google", client_id="...", client_secret="...") \
.with_oauth(provider="github", client_id="...", client_secret="...")
When multiple providers are configured, config.oauth_providers contains per-provider credential dicts. config.oauth still points to the first provider for backward compatibility.
Actors Registry
actors maps short names to known actor factories:
app.add_actor_type("myself", factory=f"{app.proto}{app.fqdn}/", relationship="friend")
Database Backend
ActingWeb supports two database backends:
database="dynamodb"(default) - AWS DynamoDB with PynamoDB ORMdatabase="postgresql"- PostgreSQL with psycopg3 and Alembic migrations
Backend selection is controlled by the database parameter in ActingWebApp() or via the DATABASE_BACKEND environment variable.
Choosing a Backend:
Feature |
DynamoDB |
PostgreSQL |
|---|---|---|
Setup Complexity |
Low (auto-creates tables) |
Medium (requires migrations) |
Local Development |
DynamoDB Local (Docker) |
PostgreSQL (Docker/native) |
Scaling |
Automatic, serverless |
Manual (vertical/horizontal) |
Cost Model |
Pay-per-request or provisioned |
Instance-based |
Query Flexibility |
Limited (key-based + GSI) |
Full SQL with JOINs |
Latency (local) |
Higher (network overhead) |
Lower (direct connection) |
Production Management |
Fully managed (AWS) |
Self-managed or RDS |
Multi-region |
Built-in global tables |
Manual replication setup |
See Database Backends Reference for detailed comparison and migration guide.
DynamoDB Setup
Installation
pip install 'actingweb[dynamodb]'
Local Development (DynamoDB Local)
docker run -p 8000:8000 amazon/dynamodb-local
export DATABASE_BACKEND=dynamodb # Optional, default
export AWS_ACCESS_KEY_ID=local
export AWS_SECRET_ACCESS_KEY=local
export AWS_DEFAULT_REGION=us-east-1
export AWS_DB_HOST=http://localhost:8000 # PynamoDB host override for local
Point your app to DynamoDB Local via these environment variables (no code changes needed). The library uses its bundled PynamoDB models to create/access required tables at runtime.
Production (AWS DynamoDB)
Configure IAM with least-privilege on the app’s tables:
dynamodb:GetItem,PutItem,UpdateItem,DeleteItem,Query,Scan.Ensure tables exist (actor, properties, attributes, subscriptions, trust, and related indexes) before first traffic; the library’s DB modules are under
actingweb.db.dynamodb.Set region/credentials via standard AWS mechanisms (env vars, instance roles, profiles).
PostgreSQL Setup
Installation
# pip installation
pip install 'actingweb[postgresql]'
# poetry installation
poetry add 'actingweb[postgresql]'
Local Development (Complete Setup)
Step 1: Start PostgreSQL
# Using Docker (recommended)
docker run -d \
--name actingweb-postgres \
-e POSTGRES_USER=actingweb \
-e POSTGRES_PASSWORD=devpassword \
-e POSTGRES_DB=actingweb \
-p 5432:5432 \
postgres:16-alpine
Step 2: Configure Environment
Create a .env file in your project root:
DATABASE_BACKEND=postgresql
PG_DB_HOST=localhost
PG_DB_PORT=5432
PG_DB_NAME=actingweb
PG_DB_USER=actingweb
PG_DB_PASSWORD=devpassword
Step 3: Setup Migration Helper (Recommended)
# Download migration helper script (one-time)
mkdir -p scripts
curl -o scripts/migrate_db.py https://raw.githubusercontent.com/actingweb/actingweb/main/scripts/migrate_db.py
The helper script automatically:
Loads
.envfileValidates required environment variables
Finds
alembic.iniin installed actingweb packageProvides simple migration commands
Step 4: Run Migrations (REQUIRED)
# Apply all migrations
python scripts/migrate_db.py upgrade head
# Verify current version
python scripts/migrate_db.py current
Common Migration Commands:
python scripts/migrate_db.py upgrade head # Apply all pending migrations
python scripts/migrate_db.py current # Show current version
python scripts/migrate_db.py downgrade -1 # Rollback one migration
python scripts/migrate_db.py history # Show migration history
Alternative: Direct Alembic (Advanced Users)
If you prefer not to use the helper script:
python -c "import actingweb; from pathlib import Path; print(Path(actingweb.__file__).parent / 'db' / 'postgresql')" | xargs -I{} alembic -c {}/alembic.ini upgrade head
Production (Managed PostgreSQL)
Environment Configuration:
export DATABASE_BACKEND=postgresql
export PG_DB_HOST=postgres.example.com
export PG_DB_PORT=5432
export PG_DB_NAME=actingweb_prod
export PG_DB_USER=actingweb
export PG_DB_PASSWORD=<secure-password>
# Optional: Connection pool tuning
export PG_POOL_MIN_SIZE=2 # Minimum pool connections
export PG_POOL_MAX_SIZE=20 # Maximum pool connections
export PG_POOL_TIMEOUT=30 # Connection timeout (seconds)
Migrations in Production:
Use the same migration helper script or CI/CD-friendly one-liner:
# Using helper script
python scripts/migrate_db.py upgrade head
# CI/CD one-liner (no .env file)
python -c "import actingweb; from pathlib import Path; print(Path(actingweb.__file__).parent / 'db' / 'postgresql')" | xargs -I{} alembic -c {}/alembic.ini upgrade head
Recommended Managed Services:
AWS RDS PostgreSQL - Fully managed, automatic backups, Multi-AZ
Google Cloud SQL - Managed PostgreSQL with high availability
Azure Database for PostgreSQL - Enterprise-grade managed service
DigitalOcean Managed PostgreSQL - Simple, affordable managed database
Docker Compose Example:
services:
app:
environment:
- DATABASE_BACKEND=postgresql
- PG_DB_HOST=postgres
- PG_DB_PORT=5432
- PG_DB_NAME=actingweb
- PG_DB_USER=actingweb
- PG_DB_PASSWORD=devpassword
depends_on:
postgres:
condition: service_healthy
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: actingweb
POSTGRES_PASSWORD: devpassword
POSTGRES_DB: actingweb
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U actingweb"]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
Common Issues and Solutions:
1. “Connection refused” error
# Check PostgreSQL is running
docker ps | grep postgres
# Or for native installations
pg_isready -h localhost -p 5432
2. “relation does not exist” error
This means migrations haven’t been run yet:
python scripts/migrate_db.py upgrade head
3. “permission denied” errors in queries
Check your PostgreSQL user has proper permissions:
psql -U postgres
GRANT ALL PRIVILEGES ON DATABASE actingweb TO actingweb;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO actingweb;
4. “password authentication failed”
Verify your PG_DB_PASSWORD matches what you set when creating the database:
# Reset password
psql -U postgres -c "ALTER USER actingweb WITH PASSWORD 'newpassword';"
# Update .env file with new password
Property Reverse Lookup
ActingWeb supports efficient reverse lookups (find actor by property value) using a dedicated lookup table. This is particularly useful for OAuth authentication where you need to find an actor by their OAuth provider ID.
Why Use Lookup Tables?
By default, ActingWeb uses database indexes for reverse lookups:
DynamoDB: Global Secondary Index (GSI) on the
valuefield - limited to 2048 bytesPostgreSQL: Index on the
valuecolumn - works but less efficient for large values
The lookup table approach removes size limits and improves query performance for configured properties.
Configuration
Enable lookup tables via API or environment variables:
from actingweb.interface import ActingWebApp
app = ActingWebApp(
aw_type="urn:actingweb:example.com:myapp",
fqdn="myapp.example.com"
).with_indexed_properties(["oauthId", "email", "externalUserId"])
# Enable lookup table mode (disable legacy indexes)
app.with_legacy_property_index(enable=False) # Recommended for new deployments
Environment Variables:
export USE_PROPERTY_LOOKUP_TABLE=true # Enable lookup table
export INDEXED_PROPERTIES=oauthId,email,externalUserId # Which properties to index
Default Configuration:
USE_PROPERTY_LOOKUP_TABLE:false(uses legacy GSI/index for backward compatibility)INDEXED_PROPERTIES:["oauthId", "email", "externalUserId"]
Usage Example
Finding an actor by OAuth ID:
from actingweb.db import get_property
# Get property database interface with configuration
db_property = get_property(config)
# Reverse lookup: find actor by OAuth ID
actor_id = db_property.get_actor_id_from_property(
name="oauthId",
value="github:12345"
)
if actor_id:
print(f"Found actor: {actor_id}")
The lookup happens automatically when you set indexed properties:
from actingweb.db import get_property
# Get property database interface
db_property = get_property(config)
# Setting an indexed property creates lookup entry automatically
db_property.set(
actor_id="actor-123",
name="oauthId",
value="github:12345"
)
# Lookup table entry is created behind the scenes
# You can now find the actor by this OAuth ID
Migration Guide
New Deployments: Enable lookup tables from the start:
app.with_legacy_property_index(enable=False)
Existing Deployments: Use dual-mode for gradual migration:
Phase 1 - Deploy with Lookup Table Support:
# Keep legacy mode enabled (default) app = ActingWebApp(...) # use_lookup_table defaults to False
Deploy this version. Both legacy and lookup table code paths are available.
Phase 2 - Enable Lookup Tables:
export USE_PROPERTY_LOOKUP_TABLE=true
Restart the application. New writes will populate lookup tables. Legacy GSI/index still used for reads.
Phase 3 - Backfill Existing Data (Optional):
Run a backfill script to populate lookup tables for existing actors (implementation depends on your deployment).
Phase 4 - Disable Legacy Index:
Once backfill is complete and you’ve verified lookup tables work correctly, you can disable the legacy GSI/index at the database level.
Rollback: Set USE_PROPERTY_LOOKUP_TABLE=false to revert to legacy GSI/index mode.
Size Limits
Configuration |
DynamoDB Limit |
PostgreSQL Limit |
|---|---|---|
Legacy GSI/Index |
2048 bytes |
8191 bytes (btree index) |
Lookup Table |
No limit (400KB item) |
No limit (TEXT column) |
Recommendation: Use lookup tables if you need to store OAuth tokens, long external IDs, or any property values exceeding 2KB.
Practical Size Limits:
While lookup tables remove hard limits, consider these guidelines for optimal performance:
DynamoDB: Hard limit of 400KB per item (applies to entire property record)
PostgreSQL: No hard limit (TEXT column), but very large values impact query performance
Recommended: Keep indexed property values under 10KB for best query performance
Performance impact: Property values over 100KB may cause slower writes and increased memory usage
For extremely large data (>100KB), consider:
Storing the data in external storage (S3, Cloud Storage) and keeping only a reference in the property
Using property lists for chunked storage of large datasets
Using non-indexed properties for large values that don’t require reverse lookups
Best Practices
Choose Indexed Properties Carefully: Only index properties you need for reverse lookups (e.g.,
oauthId,email). Each indexed property creates lookup table entries.Test Configuration Changes: Property lookup configuration requires restart. Test in staging before production.
Monitor Lookup Table Size: For DynamoDB, monitor lookup table size and provision adequate capacity. For PostgreSQL, normal table monitoring applies.
Cleanup: Lookup entries are automatically deleted when properties or actors are deleted. No manual cleanup needed.
Peer Profile Caching
ActingWeb can automatically cache profile attributes from peer actors with established trust relationships. This is useful for displaying peer information (display names, emails, etc.) without making repeated API calls.
Configuration
Enable peer profile caching via the fluent API:
from actingweb.interface import ActingWebApp
app = ActingWebApp(
aw_type="urn:actingweb:example.com:myapp",
fqdn="myapp.example.com"
).with_peer_profile(attributes=["displayname", "email", "description"])
Parameters:
attributes: List of property names to cache from peer actors. Default when enabled:["displayname", "email", "description"]Pass an empty list to explicitly disable caching.
Default Behavior:
Profile caching is disabled by default
Must call
with_peer_profile()to enableProfiles are stored in the
peer_profilesattribute bucket
Automatic Profile Updates
When enabled, profiles are automatically updated:
Event |
Action |
|---|---|
Trust fully approved (local) |
Fetch and cache peer profile |
Trust fully approved (remote) |
Fetch and cache peer profile |
|
Refresh cached profile |
Trust deleted |
Delete cached profile |
Accessing Peer Profiles
Use the TrustManager to access 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}")
# Manual refresh (sync)
profile = actor.trust.refresh_peer_profile(peer_id)
# Manual refresh (async - for FastAPI)
profile = await actor.trust.refresh_peer_profile_async(peer_id)
PeerProfile Attributes:
actor_id: The actor caching this profilepeer_id: The peer whose profile is cacheddisplayname: Human-readable nameemail: Contact emaildescription: Actor descriptionextra_attributes: Dict of additional configured attributesfetched_at: ISO timestamp when profile was fetchedfetch_error: Error message if fetch failed
Error Handling
Profile fetch failures are handled gracefully:
If the peer is unavailable, a profile with
fetch_erroris storedMissing properties are stored as
NoneStore failures are logged but don’t crash the trust approval flow
403/404 responses cache an empty profile to avoid retries
Best Practices
Choose Attributes Carefully: Only cache attributes you actually need. Each attribute requires a property lookup on the peer.
Handle Missing Data: Profiles may have
Nonevalues for attributes the peer doesn’t have. Always check before using:if profile and profile.displayname: display_text = profile.displayname else: display_text = f"Peer {peer_id[:8]}..."
Check for Errors: The
fetch_errorfield indicates if the last fetch failed:if profile and profile.fetch_error: logger.warning(f"Profile fetch failed: {profile.fetch_error}")
Refresh When Needed: Use
refresh_peer_profile()after significant peer changes or if cached data might be stale.
Peer Capabilities Caching
ActingWeb can automatically cache methods and actions that peer actors expose. This is useful for discovering what RPC methods and state-modifying actions are available on trusted peers without making repeated API calls.
Configuration
Enable peer capabilities caching via the fluent API:
from actingweb.interface import ActingWebApp
app = ActingWebApp(
aw_type="urn:actingweb:example.com:myapp",
fqdn="myapp.example.com"
).with_peer_capabilities(enable=True, max_age_seconds=3600)
Parameters:
enable: Boolean to enable/disable capabilities caching. Default:Truewhen called.max_age_seconds: Maximum age (in seconds) before cached capabilities are considered stale and refetched duringsync_peer(). Default:3600(1 hour). Set to0to always refetch.
Default Behavior:
Capabilities caching is disabled by default
Must call
with_peer_capabilities()to enableCapabilities are stored in the
peer_capabilitiesattribute bucket
Automatic Capabilities Updates
When enabled, capabilities are automatically updated:
Event |
Action |
|---|---|
Trust fully approved (local) |
Fetch and cache peer methods/actions |
Trust fully approved (remote) |
Fetch and cache peer methods/actions |
|
Refresh cached capabilities (only if cache is older than |
|
Always refresh cached capabilities, bypassing staleness check |
Trust deleted |
Delete cached capabilities |
Accessing Peer Capabilities
Use the TrustManager to access cached capabilities:
# Get all cached capabilities
capabilities = actor.trust.get_peer_capabilities(peer_id)
if capabilities:
print(f"Methods: {capabilities.get_method_names()}")
print(f"Actions: {capabilities.get_action_names()}")
# Get specific method
method = capabilities.get_method("get_data")
if method:
print(f"{method.name}: {method.description}")
print(f"Input schema: {method.input_schema}")
# Convenience methods for methods/actions only
methods = actor.trust.get_peer_methods(peer_id)
actions = actor.trust.get_peer_actions(peer_id)
# Manual refresh (sync)
capabilities = actor.trust.refresh_peer_capabilities(peer_id)
# Manual refresh (async - for FastAPI)
capabilities = await actor.trust.refresh_peer_capabilities_async(peer_id)
CachedCapability Attributes:
name: Method or action namedescription: Human-readable descriptioninput_schema: JSON Schema for parameters (optional)output_schema: JSON Schema for return value (optional)capability_type:"method"or"action"
CachedPeerCapabilities Attributes:
actor_id: The actor caching this datapeer_id: The peer whose capabilities are cachedmethods: List of CachedCapability objects for methodsactions: List of CachedCapability objects for actionsfetched_at: ISO timestamp when capabilities were fetchedfetch_error: Error message if fetch failed
Error Handling
Capabilities fetch failures are handled gracefully:
If the peer is unavailable, a capabilities object with
fetch_erroris stored404 responses for
/methodsor/actionsare normal (peer may not support them)Store failures are logged but don’t crash the trust approval flow
Best Practices
Enable Only When Needed: Capabilities caching adds network requests during trust establishment. Enable only if you need to discover peer methods/actions.
Handle Missing Capabilities: Check that methods/actions exist before using:
method = capabilities.get_method("expected_method") if method: # Method is available, safe to call pass
Check for Errors: The
fetch_errorfield indicates if the last fetch failed:if capabilities and capabilities.fetch_error: logger.warning(f"Capabilities fetch failed: {capabilities.fetch_error}")
Refresh When Needed: Use
refresh_peer_capabilities()if cached data might be stale or after a peer upgrade.
Peer Permissions Caching
When peer permissions caching is enabled, the library automatically caches what permissions peer actors have granted to your actor. This enables efficient permission checking without network requests.
from actingweb.interface import ActingWebApp
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 permissions revoked
notify_peer_on_change=True # Auto-notify peers when their permissions change
)
Parameters:
enable: Boolean to enable/disable permissions caching. Default:Truewhen called.auto_delete_on_revocation: WhenTrue, automatically delete cached peer data fromRemotePeerStorewhen the peer revokes property access. This ensures that when a peer revokes access to certain data (e.g.,memory_*properties), the locally cached copies are deleted. Default:False.notify_peer_on_change: WhenTrue, automatically notify peers when their permissions change by sending a callback to their/callbacks/permissions/{actor_id}endpoint. This is fire-and-forget (failures are logged but don’t block the store operation). Default:True.
Default Behavior:
Permissions caching is disabled by default
Must call
with_peer_permissions()to enablePermissions are stored in the
_peer_permissionsattribute bucket (note the_prefix for library-internal buckets)Auto-delete on revocation is disabled by default
Notify peer on change is enabled by default
Automatic Permission Updates
When enabled, permissions are automatically updated:
Event |
Action |
|---|---|
Trust fully approved |
Fetch and cache peer’s granted permissions |
|
Refresh cached permissions |
Permission callback received |
Update cached permissions (with change detection) |
Permission revoked (with |
Delete cached peer data matching revoked property patterns |
Permissions stored (with |
Send callback to peer’s |
Trust deleted |
Delete cached permissions |
Auto-Delete on Permission Revocation
When auto_delete_on_revocation=True is set and a peer revokes access to certain
properties, the library automatically deletes the corresponding cached data from
RemotePeerStore. This is useful when you’re caching remote peer data via
subscription callbacks and want to ensure that revoked data is cleaned up.
How it works:
When a permission callback arrives, the library compares old and new permissions
Property patterns that were in the old permissions but not in the new are identified as “revoked”
All stored lists in
RemotePeerStorematching the revoked patterns are deletedThe
permission_changesdict is passed to the callback hook with details
Example: If the old permissions had patterns ["memory_*", "profile_*"] and the
new permissions only have ["memory_*"], then profile_* is considered revoked.
Any cached lists matching profile_* (like profile_info) will be deleted.
Callback Hook Data:
When handling permission callbacks, the hook data includes a permission_changes dict:
@app.callback_hook("permissions")
def on_permissions_callback(actor, name, data):
changes = data.get("permission_changes", {})
if changes.get("is_initial"):
print("First permission callback from this peer")
if changes.get("has_revocations"):
print(f"Revoked patterns: {changes['revoked_patterns']}")
if changes.get("granted_patterns"):
print(f"Newly granted: {changes['granted_patterns']}")
return True
Accessing Peer Permissions
Use the PeerPermissionStore to check 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"):
print("Can read memory_travel from peer")
# Check method access
if perms.has_method_access("sync_data"):
print("Can call sync_data on peer")
# Check tool access (MCP)
if perms.has_tool_access("search"):
print("Can use search tool on peer")
PeerPermissions Attributes:
actor_id: The actor caching this datapeer_id: The peer who granted permissionsproperties: Property permission patterns and operationsmethods: Allowed/denied method patternsactions: Allowed/denied action patternstools: Allowed/denied tool patterns (MCP)resources: Allowed/denied resource patterns (MCP)prompts: Allowed prompt patterns (MCP)fetched_at: ISO timestamp when permissions were fetchedfetch_error: Error message if fetch failed
Permission Check Methods:
has_property_access(name, operation): Check property permission (operation: read, write, subscribe, delete)has_method_access(name): Check method permissionhas_action_access(name): Check action permissionhas_tool_access(name): Check tool permission (MCP)has_resource_access(uri): Check resource permission (MCP)has_prompt_access(name): Check prompt permission (MCP)
Permission Callbacks
When a peer modifies permissions granted to your actor, they can send a permission
callback to notify you immediately. This requires the permissioncallback option
to be advertised in the peer’s /meta/actingweb/supported endpoint.
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.
Effective Permissions in Callbacks:
Permission callbacks send the full effective permissions — base trust-type defaults
merged with per-trust overrides. This ensures the receiving peer can accurately detect
what changed. The GET /{actor_id}/permissions/{peer_id} endpoint also returns
effective permissions for consistency.
Incremental Sync on Permission Grant:
When a permission callback grants access to new property patterns, the library performs
an incremental sync — fetching only the newly granted properties instead of a full
sync_peer() call. This reduces HTTP requests from ~7 to 1-2 per permission grant.
Best Practices
Enable for MCP Applications: Permission caching is particularly useful for MCP applications that need to check tool/resource permissions frequently.
Handle Missing Permissions: Always check for permissions before operations:
perms = store.get_permissions(actor.id, peer_id) if perms and perms.has_property_access("data", "read"): # Safe to read pass else: # No cached permissions or access denied pass
Graceful Degradation: If permissions aren’t cached, fall back to synchronous fetch or deny access.
Refresh When Needed: Use manual fetch if cached permissions might be stale:
from actingweb.peer_permissions import fetch_peer_permissions_async perms = await fetch_peer_permissions_async(actor.id, peer_id, actor.config)
Permission Query Endpoint:
The library provides a built-in handler for the GET /{actor_id}/permissions/{peer_id}
endpoint. This allows peers to proactively query what permissions they’ve been granted,
supporting initial discovery and refresh scenarios.
The endpoint is automatically available when using the ActingWeb framework and requires no additional configuration. Peers must have a valid trust relationship to query permissions.
This pull-based query endpoint complements the push-based permission callback mechanism, forming a robust hybrid architecture for permission discovery and synchronization
FastAPI Performance Tuning
When using FastAPI integration, you can configure the thread pool size to optimize performance for your deployment scenario.
Thread Pool Configuration
ActingWeb uses a thread pool to execute synchronous handlers (database operations, HTTP requests) without blocking the async event loop:
from actingweb.interface import ActingWebApp
app = ActingWebApp(
aw_type="urn:actingweb:example.com:myapp",
fqdn="myapp.example.com"
).with_thread_pool_workers(20)
Tuning Guidelines:
Deployment Scenario |
Workers |
Rationale |
|---|---|---|
Default |
10 |
Suitable for most applications |
Low traffic / Lambda |
5-10 |
Reduces memory overhead, matches Lambda concurrency |
High traffic / Container |
20-50 |
Handles more concurrent requests |
Container (CPU-bound) |
2-5 per CPU core |
Balances CPU utilization |
Development |
5 |
Minimal resource usage |
Memory Overhead: Approximately 8MB per worker thread on average.
Valid Range: 1-100 workers (enforced by validation).
Lambda Deployment
When deploying to AWS Lambda or other serverless platforms, ActingWeb automatically detects the environment and issues a warning if asynchronous subscription callbacks are enabled (the default). This is because fire-and-forget callbacks may be lost when the Lambda function freezes after returning a response.
Recommended Lambda Configuration:
from actingweb.interface import ActingWebApp
app = ActingWebApp(
aw_type="urn:actingweb:example.com:myapp",
fqdn="myapp.example.com"
).with_sync_callbacks(enable=True) # Required for Lambda
.with_thread_pool_workers(5) # Optional: reduce memory usage
Detection: The library automatically detects Lambda via these environment variables:
AWS_LAMBDA_FUNCTION_NAMEAWS_EXECUTION_ENV(starts withAWS_Lambda_)
Warning Message: If running in Lambda without sync callbacks, you’ll see:
WARNING: Running in AWS Lambda with async subscription callbacks enabled.
Fire-and-forget callbacks may be lost when Lambda function freezes.
Consider enabling sync callbacks with .with_sync_callbacks()
Why Sync Callbacks for Lambda:
Lambda freezes execution after the response is sent
Asynchronous (fire-and-forget) callbacks may not complete before freeze
Synchronous callbacks block until HTTP request completes
Guarantees callback delivery before function freeze
Logging and Request Correlation
ActingWeb provides comprehensive logging with automatic request correlation, making it easy to trace requests across distributed actor-to-actor communication.
Quick Setup
from actingweb.logging_config import (
configure_actingweb_logging,
enable_request_context_filter
)
import logging
# Configure base logging
configure_actingweb_logging(logging.INFO)
# Enable automatic context injection
enable_request_context_filter()
This configuration automatically adds request context to every log statement:
2024-01-15 10:23:45,123 [a1b2c3d4:actor123:peer456] actingweb.handlers:INFO: Property updated
Where:
a1b2c3d4: Request ID (last 8 chars)actor123: Actor handling the requestpeer456: Peer making the request (or-if creator/trustee)
Convenience Functions
Pre-configured logging setups:
from actingweb.logging_config import (
configure_production_logging,
configure_development_logging,
configure_testing_logging
)
# Production: WARNING level, minimal output
configure_production_logging()
# Development: DEBUG level, verbose output
configure_development_logging()
# Testing: WARNING level, suppress most output
configure_testing_logging()
Per-Component Configuration
Control logging levels for specific components:
configure_actingweb_logging(
level=logging.INFO, # Default level
db_level=logging.WARNING, # Database operations
auth_level=logging.INFO, # Authentication
handlers_level=logging.INFO, # Request handlers
proxy_level=logging.DEBUG # Peer communication
)
Request Correlation
ActingWeb automatically tracks requests through multiple layers:
Incoming Requests: Extract or generate request ID from
X-Request-IDheaderRequest Context: Store request ID, actor ID, and peer ID
Logging: Automatically inject context into every log statement
Outgoing Requests: Propagate request IDs to peer actors with parent tracking
This enables easy grepping and request chain tracing:
# Find all logs for a specific request
grep "a1b2c3d4" application.log
# Find all logs for a specific actor
grep ":actor123:" application.log
# Find child requests
grep "parent_id=a1b2c3d4" application.log
Environment Variables
Configure logging via environment:
export ACTINGWEB_LOG_LEVEL=INFO
export ACTINGWEB_DB_LOG_LEVEL=WARNING
Legacy Configuration
logLevel:DEBUG,INFO, orWARN; can be overridden with env varLOG_LEVEL
For detailed information including grepping patterns, request chain tracing, and framework integration, see Logging and Request Correlation.
Environment Variables
Convenience env vars read by the interface layer:
APP_HOST_FQDN: Default forfqdnif not provided.APP_HOST_PROTOCOL: Default forprotoif not provided.LOG_LEVEL: Overrides logging level.APP_BOT_TOKEN,APP_BOT_EMAIL,APP_BOT_SECRET,APP_BOT_ADMIN_ROOM: Used bywith_bot().
URLs and Base Paths
root: Computed as{proto}{fqdn}/. Templates receiveactor_rootandactor_www(see WWW Handler and Templates).Deployments under a base path are supported by integrations; avoid relative paths in templates.
MCP Capability
Toggle with
ActingWebApp.with_mcp(enable=True|False).When enabled,
mcpappears in supported options returned by meta discovery.
Notes
Always use
ActorInterfacein applications; the internalActorclass is for framework use.Prefer property lists for large or growing collections; see Developer API for guidance.