AgentFieldbuild

Access policies

Tag-based access control for cross-agent calls with ALLOW/DENY rules and tag approval workflow

Bounded autonomy — ALLOW and DENY policy rules

Control which agents can call which functions using tag-based ALLOW/DENY rules -- no hardcoded ACLs required.

AgentField evaluates access policies on every cross-agent call. Policies match on caller and target tags (not specific agent IDs), support function-level granularity with input constraints, and are sorted by priority with first-match-wins semantics. Within each policy, deny_functions are checked before allow_functions, but across policies the result is determined by the first matching policy in priority order. Fail-open by default: if no policy matches, the call is allowed (backward compatibility).

from agentfield import Agent

# The treasury agent — only callers with the right tags get through
app = Agent(
    node_id="treasury",
    version="1.0.0",
    tags=["finance", "transfers"],
    local_verification=True,  # Evaluate policies locally, no round-trip to CP
)

@app.reasoner()
async def high_value_transfer(amount: float, execution_context=None) -> dict:
    # Policy engine runs BEFORE this handler — if caller lacks "finance-ops" tag, they get 403
    caller_did = execution_context.caller_did
    app.note(f"Transfer ${amount:,.2f} authorized for {caller_did}", ["audit", "finance"])
    return await app.ai(system="Process this transfer.", user=str(amount))

# What callers see:
# ✅ Agent tagged ["finance-ops"] → calls treasury.high_value_transfer → 200 OK
# ❌ Agent tagged ["analytics"]   → calls treasury.high_value_transfer → 403 DENIED
# ❌ Agent tagged ["finance-ops"] → calls treasury.delete_account      → 403 DENIED (deny_functions)

What just happened

The example created real allow and deny rules, attached them to caller and target tags, and then showed where to inspect the resulting policy state. That is the core value to surface here: authorization decisions live in the control plane and can be reasoned about as explicit policy data.

{
  "caller_tags": ["finance-ops"],
  "target_tags": ["finance", "transfers"],
  "allow_functions": ["high_value_transfer", "balance_check"],
  "deny_functions": ["delete_account", "modify_ledger"],
  "priority": 100
}
What you get
  • Tag-based authorization -- policies match on caller and target tags, not specific agent IDs
  • ALLOW/DENY semantics -- within each policy, deny rules are checked before allow rules; across policies, highest priority wins. Fail-open by default (access allowed when no policy matches)
  • Priority ordering -- multiple policies evaluated highest-priority first; first match wins
  • Function-level granularity -- restrict access to specific reasoners and skills within an agent
  • Input constraints -- policies can enforce conditions on execution input parameters
  • Tag approval workflow -- tags can be auto-approved, require manual review, or be forbidden entirely
  • Cryptographic tag verification -- tag VCs prove an agent's tags were legitimately assigned
How policies work

When Agent A calls a function on Agent B, the control plane evaluates access policies:

Policy Evaluation Order

  1. Policies are sorted by priority (highest first), with ID as tie-breaker
  2. For each enabled policy:
    • Check if caller tags intersect with the policy's caller_tags
    • Check if target tags intersect with the policy's target_tags
    • If both match, check the function against deny_functions (deny takes precedence)
    • Then check against allow_functions (if set, function must be in the list)
    • Evaluate input constraints if present
  3. First matching policy determines the result
  4. If no policy matches, the result is "no match" (access allowed for backward compatibility)

Policy Structure

{
  "name": "Analytics can read data processors",
  "caller_tags": ["analytics"],
  "target_tags": ["data-processor"],
  "allow_functions": ["query", "aggregate", "summarize"],
  "deny_functions": ["delete", "modify"],
  "constraints": {
    "region": {"operator": "==", "value": "us-east-1"}
  },
  "action": "allow",
  "priority": 100,
  "enabled": true
}
FieldTypeDescription
namestringHuman-readable policy name
caller_tagsstring[]Tags the calling agent must have (any match)
target_tagsstring[]Tags the target agent must have (any match)
allow_functionsstring[]Functions explicitly allowed (empty = all allowed)
deny_functionsstring[]Functions explicitly denied (checked before allow)
constraintsmap[string]AccessConstraintParameter constraints with operator and value fields
actionstring"allow" or "deny"
priorityintHigher number = higher priority
enabledboolWhether the policy is active
Tag approval workflow

When agents register with tags, those tags may require approval before the agent becomes operational:

Approval Modes

ModeBehavior
autoTags are approved immediately (default)
manualTags require human approval; agent enters pending_approval state
forbiddenTags are rejected; agent cannot register with them

Configure tag rules in agentfield.yaml:

features:
  did:
    authorization:
      tag_approval_rules:
        default_mode: auto
        rules:
          - tags: ["pii-handler", "financial"]
            approval: manual
          - tags: ["admin", "root"]
            approval: forbidden

Approval Flow

When tags require manual review, the agent enters pending_approval lifecycle status. It cannot accept executions until an administrator approves or modifies its tags.

Tag VCs

Upon approval, the control plane issues a tag VC -- a cryptographically signed credential certifying the agent's authorized tags. This VC is verified during policy evaluation to ensure tags have not been tampered with.

See Credentials for details on the VC format and verification.

Patterns

Least-Privilege Agent Tags

Assign minimal tags and create explicit allow policies:

# Data processor with restricted tags
app = Agent(
    node_id="data-processor",
    version="1.0.0",
    tags=["data-processor", "read-only"],
)

# Only agents tagged "analytics" can call this agent
# Only the "query" and "aggregate" functions are exposed
{
  "name": "Read-only data access for analytics",
  "caller_tags": ["analytics"],
  "target_tags": ["data-processor", "read-only"],
  "allow_functions": ["query", "aggregate"],
  "deny_functions": ["delete", "modify", "truncate"],
  "action": "allow",
  "priority": 100
}

Environment Isolation

Use tags to separate staging from production:

{
  "name": "Block staging -> production",
  "caller_tags": ["env:staging"],
  "target_tags": ["env:production"],
  "deny_functions": ["*"],
  "action": "deny",
  "priority": 1000
}

Input Constraints

Restrict what parameters can be passed to sensitive functions:

{
  "name": "Region-locked data access",
  "caller_tags": ["analytics"],
  "target_tags": ["data-store"],
  "allow_functions": ["query"],
  "constraints": {
    "region": {"operator": "==", "value": "us-east-1"}
  },
  "action": "allow",
  "priority": 150
}

When constraints are set, the policy evaluator checks the execution's input parameters against the constraint map. Each constraint specifies an operator ("==", "<=", ">=", "<", ">", "!=") and a value. If the input does not satisfy the constraint conditions, the policy does not match (fail-closed).

API reference

List Policies

GET /api/v1/admin/policies

Returns all access policies, sorted by priority.

Create Policy

POST /api/v1/admin/policies
{
  "name": "Compliance agents can audit all functions",
  "caller_tags": ["compliance", "auditor"],
  "target_tags": ["*"],
  "allow_functions": [],
  "deny_functions": [],
  "constraints": {},
  "action": "allow",
  "priority": 200
}

Update Policy

PUT /api/v1/admin/policies/{id}

Updates an existing policy. The full policy object must be provided.

Delete Policy

DELETE /api/v1/admin/policies/{id}

Removes a policy. Active executions already authorized by this policy are not affected.

Revocations

GET /api/v1/revocations

Lists revoked DIDs. Agents cache this list for local verification to ensure revoked DIDs are rejected during policy evaluation.

Authorization Overview (UI)

GET /api/ui/v1/authorization/agents

Returns all agents with their current tags, tag VC status, and approval state.