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>/www web UI (with_web_ui()). Also affects browser redirects.

  • devtest: Enable development endpoints; MUST be False in production.

  • www_auth: basic or oauth; set by with_oauth().

  • unique_creator: Enforce one actor per creator (with_unique_creator()).

  • force_email_prop_as_creator: Copy email property to creator.

  • mcp: Include MCP capability; toggle via with_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

with_web_ui()

Redirect

Use Case

Unauthenticated browser → /<actor_id>

Any

/login

Consistent login experience

Authenticated browser → /<actor_id>

True

/<actor_id>/www

Server-rendered templates

Authenticated browser → /<actor_id>

False

/<actor_id>/app

Single Page Applications

After OAuth login

True

/<actor_id>/www

Server-rendered templates

After OAuth login

False

/<actor_id>/app

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 ORM

  • database="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 .env file

  • Validates required environment variables

  • Finds alembic.ini in installed actingweb package

  • Provides 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 value field - limited to 2048 bytes

  • PostgreSQL: Index on the value column - 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:

  1. 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.

  2. 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.

  3. Phase 3 - Backfill Existing Data (Optional):

    Run a backfill script to populate lookup tables for existing actors (implementation depends on your deployment).

  4. 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:

  1. Storing the data in external storage (S3, Cloud Storage) and keeping only a reference in the property

  2. Using property lists for chunked storage of large datasets

  3. Using non-indexed properties for large values that don’t require reverse lookups

Best Practices

  1. Choose Indexed Properties Carefully: Only index properties you need for reverse lookups (e.g., oauthId, email). Each indexed property creates lookup table entries.

  2. Test Configuration Changes: Property lookup configuration requires restart. Test in staging before production.

  3. Monitor Lookup Table Size: For DynamoDB, monitor lookup table size and provision adequate capacity. For PostgreSQL, normal table monitoring applies.

  4. 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 enable

  • Profiles are stored in the peer_profiles attribute 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

sync_peer() completion

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 profile

  • peer_id: The peer whose profile is cached

  • displayname: Human-readable name

  • email: Contact email

  • description: Actor description

  • extra_attributes: Dict of additional configured attributes

  • fetched_at: ISO timestamp when profile was fetched

  • fetch_error: Error message if fetch failed

Error Handling

Profile fetch failures are handled gracefully:

  • If the peer is unavailable, a profile with fetch_error is stored

  • Missing properties are stored as None

  • Store failures are logged but don’t crash the trust approval flow

  • 403/404 responses cache an empty profile to avoid retries

Best Practices

  1. Choose Attributes Carefully: Only cache attributes you actually need. Each attribute requires a property lookup on the peer.

  2. Handle Missing Data: Profiles may have None values 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]}..."
    
  3. Check for Errors: The fetch_error field indicates if the last fetch failed:

    if profile and profile.fetch_error:
        logger.warning(f"Profile fetch failed: {profile.fetch_error}")
    
  4. 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: True when called.

  • max_age_seconds: Maximum age (in seconds) before cached capabilities are considered stale and refetched during sync_peer(). Default: 3600 (1 hour). Set to 0 to always refetch.

Default Behavior:

  • Capabilities caching is disabled by default

  • Must call with_peer_capabilities() to enable

  • Capabilities are stored in the peer_capabilities attribute 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

sync_peer() completion

Refresh cached capabilities (only if cache is older than max_age_seconds)

sync_peer(force_refresh=True)

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 name

  • description: Human-readable description

  • input_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 data

  • peer_id: The peer whose capabilities are cached

  • methods: List of CachedCapability objects for methods

  • actions: List of CachedCapability objects for actions

  • fetched_at: ISO timestamp when capabilities were fetched

  • fetch_error: Error message if fetch failed

Error Handling

Capabilities fetch failures are handled gracefully:

  • If the peer is unavailable, a capabilities object with fetch_error is stored

  • 404 responses for /methods or /actions are normal (peer may not support them)

  • Store failures are logged but don’t crash the trust approval flow

Best Practices

  1. Enable Only When Needed: Capabilities caching adds network requests during trust establishment. Enable only if you need to discover peer methods/actions.

  2. 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
    
  3. Check for Errors: The fetch_error field indicates if the last fetch failed:

    if capabilities and capabilities.fetch_error:
        logger.warning(f"Capabilities fetch failed: {capabilities.fetch_error}")
    
  4. 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: True when called.

  • auto_delete_on_revocation: When True, automatically delete cached peer data from RemotePeerStore when 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: When True, 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 enable

  • Permissions are stored in the _peer_permissions attribute 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

sync_peer() completion

Refresh cached permissions

Permission callback received

Update cached permissions (with change detection)

Permission revoked (with auto_delete_on_revocation=True)

Delete cached peer data matching revoked property patterns

Permissions stored (with notify_peer_on_change=True)

Send callback to peer’s /callbacks/permissions/{actor_id}

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:

  1. When a permission callback arrives, the library compares old and new permissions

  2. Property patterns that were in the old permissions but not in the new are identified as “revoked”

  3. All stored lists in RemotePeerStore matching the revoked patterns are deleted

  4. The permission_changes dict 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 data

  • peer_id: The peer who granted permissions

  • properties: Property permission patterns and operations

  • methods: Allowed/denied method patterns

  • actions: Allowed/denied action patterns

  • tools: Allowed/denied tool patterns (MCP)

  • resources: Allowed/denied resource patterns (MCP)

  • prompts: Allowed prompt patterns (MCP)

  • fetched_at: ISO timestamp when permissions were fetched

  • fetch_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 permission

  • has_action_access(name): Check action permission

  • has_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

  1. Enable for MCP Applications: Permission caching is particularly useful for MCP applications that need to check tool/resource permissions frequently.

  2. 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
    
  3. Graceful Degradation: If permissions aren’t cached, fall back to synchronous fetch or deny access.

  4. 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_NAME

  • AWS_EXECUTION_ENV (starts with AWS_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 request

  • peer456: 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:

  1. Incoming Requests: Extract or generate request ID from X-Request-ID header

  2. Request Context: Store request ID, actor ID, and peer ID

  3. Logging: Automatically inject context into every log statement

  4. 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, or WARN; can be overridden with env var LOG_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 for fqdn if not provided.

  • APP_HOST_PROTOCOL: Default for proto if not provided.

  • LOG_LEVEL: Overrides logging level.

  • APP_BOT_TOKEN, APP_BOT_EMAIL, APP_BOT_SECRET, APP_BOT_ADMIN_ROOM: Used by with_bot().

URLs and Base Paths

  • root: Computed as {proto}{fqdn}/. Templates receive actor_root and actor_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, mcp appears in supported options returned by meta discovery.

Notes

  • Always use ActorInterface in applications; the internal Actor class is for framework use.

  • Prefer property lists for large or growing collections; see Developer API for guidance.