Skip to content

Visibility Controlsยถ

Flock's visibility system provides zero-trust security for multi-agent systemsโ€”controlling which agents can see which artifacts on the blackboard.

Think of it like classified documents: not everyone in an organization can see every document. Field agents see different data than executives, and customer data stays isolated between tenants.

Unlike other frameworks, Flock has security built-in from day one.


What is Visibility?ยถ

Visibility determines which agents can consume an artifact from the blackboard.

  • Producer-controlled - The agent publishing data decides who can see it
  • Enforced automatically - Flock checks visibility before scheduling agents
  • Zero-trust architecture - Explicit access control, not "hope-based security"
  • No agent ever sees unauthorized data - Security violations are impossible

Key principle: The agent producing the artifact controls who can consume it.


The Five Visibility Typesยถ

Here's how visibility controls work:

graph TB
    subgraph "๐Ÿ“ค Artifact Published"
        Artifact[๐Ÿ“‹ Artifact<br/>with Visibility Control]
    end

    subgraph "๐Ÿ” Visibility Check"
        Check{Visibility Type?}
    end

    subgraph "๐ŸŸข Public Visibility"
        Public[โœ… All agents can access]
        A1[Agent 1] & A2[Agent 2] & A3[Agent 3]
    end

    subgraph "๐Ÿ”ต Private Visibility"
        Private[โœ… Only allowlist agents]
        A4[Agent X โœ…<br/>in allowlist]
        A5[Agent Y โŒ<br/>blocked]
    end

    subgraph "๐ŸŸก Tenant Visibility"
        Tenant[โœ… Only same tenant]
        A6[Agent A<br/>tenant: customer-1 โœ…]
        A7[Agent B<br/>tenant: customer-2 โŒ]
    end

    subgraph "๐ŸŸฃ Labelled Visibility"
        Labels[โœ… Must have required labels]
        A8[Agent with<br/>role:admin โœ…]
        A9[Agent without<br/>labels โŒ]
    end

    subgraph "๐Ÿ”ด Time Visibility"
        Time[โœ… Only within time window]
        A10[Agent<br/>within window โœ…]
        A11[Agent<br/>window expired โŒ]
    end

    Artifact --> Check

    Check -->|PublicVisibility| Public
    Public --> A1 & A2 & A3

    Check -->|PrivateVisibility| Private
    Private --> A4
    Private -.blocks.-> A5

    Check -->|TenantVisibility| Tenant
    Tenant --> A6
    Tenant -.blocks.-> A7

    Check -->|LabelledVisibility| Labels
    Labels --> A8
    Labels -.blocks.-> A9

    Check -->|TimeVisibility| Time
    Time --> A10
    Time -.blocks.-> A11

    style Artifact fill:#60a5fa,stroke:#333,stroke-width:3px,color:#000
    style Check fill:#4f46e5,stroke:#333,stroke-width:2px,color:#fff
    style Public fill:#10b981,stroke:#333,stroke-width:2px,color:#000
    style Private fill:#3b82f6,stroke:#333,stroke-width:2px,color:#fff
    style Tenant fill:#eab308,stroke:#333,stroke-width:2px,color:#000
    style Labels fill:#8b5cf6,stroke:#333,stroke-width:2px,color:#fff
    style Time fill:#ef4444,stroke:#333,stroke-width:2px,color:#fff

Access Control Flow:

  1. Artifact Published - Producer sets visibility type
  2. Visibility Check - Orchestrator validates access for each potential consumer
  3. Allowed Agents - Only authorized agents receive the artifact
  4. Blocked Agents - Unauthorized agents never see the artifact (zero-trust)

Key Security Properties: - โœ… Producer-Controlled - Publishing agent sets access rules - โœ… Automatic Enforcement - No manual checks needed - โœ… Zero-Trust - Explicit allow, not implicit deny - โœ… Audit Trail - All access checks logged in traces

1. PublicVisibility (Default)ยถ

Everyone can see the artifact.

from flock.visibility import PublicVisibility

agent = (
    flock.agent("reporter")
    .consumes(Draft)
    .publishes(Article, visibility=PublicVisibility())
)

Use when: - Shared data across all agents - Public announcements - Non-sensitive information - Default behavior (no visibility specified)

Example: Blog posts, public reports, shared metrics


2. PrivateVisibility (Allowlist)ยถ

Only specified agents can see the artifact.

from flock.visibility import PrivateVisibility

field_agent = (
    flock.agent("field_agent")
    .consumes(Mission)
    .publishes(
        RawIntelligence,
        visibility=PrivateVisibility(agents={"intelligence_analyst"})
    )
)

Use when: - Sensitive data for specific processors - Explicit allowlist access control - HIPAA compliance (patient data โ†’ treating physician) - One-to-one data handoff

Example: Medical records, financial transactions, classified intelligence


3. TenantVisibility (Multi-Tenancy)ยถ

Only agents belonging to the same tenant can see the artifact.

from flock.visibility import TenantVisibility

analyzer = (
    flock.agent("analyzer")
    .consumes(CustomerEvent)
    .publishes(
        Analysis,
        visibility=TenantVisibility(tenant_id="customer_123")
    )
)

Use when: - SaaS platforms with multiple customers - Complete data isolation between tenants - Customer A can't see Customer B's data - Multi-organization systems

Example: SaaS customer data, multi-tenant analytics, isolated workspaces


4. LabelledVisibility (RBAC)ยถ

Only agents with required labels can see the artifact.

from flock.visibility import LabelledVisibility, AgentIdentity

# Agent with security clearance
analyst = (
    flock.agent("analyst")
    .identity(AgentIdentity(
        name="analyst",
        labels={"clearance:secret", "role:intelligence"}
    ))
    .consumes(RawIntelligence)
    .publishes(
        AnalysisReport,
        visibility=LabelledVisibility(required_labels={"clearance:secret"})
    )
)

Use when: - Role-Based Access Control (RBAC) - Multi-level security clearances - Tiered access (free vs pro users) - Organizational hierarchies

Example: Security clearances, subscription tiers, organizational roles


5. AfterVisibility (Time-Delayed)ยถ

Artifact becomes visible after a time delay.

from datetime import timedelta
from flock.visibility import AfterVisibility, PublicVisibility

pr_team = (
    flock.agent("pr_team")
    .consumes(NewsStory)
    .publishes(
        PressRelease,
        visibility=AfterVisibility(
            ttl=timedelta(hours=24),
            then=PublicVisibility()
        )
    )
)

Use when: - Press embargoes - Scheduled releases - Quarterly report delays - Time-sensitive disclosures

Example: Earnings reports, press releases, clinical trial results


Agent Identityยถ

Agents need identities to interact with visibility controls:

from flock.visibility import AgentIdentity

agent = (
    flock.agent("analyst")
    .identity(AgentIdentity(
        name="analyst",
        labels={"clearance:secret", "role:analyst", "tier:pro"}
    ))
    .consumes(SensitiveData)
    .publishes(Report)
)

Identity components: - name - Unique agent identifier - labels - Set of access control labels

Label format convention: category:value - clearance:secret - Security clearance level - role:analyst - Organizational role - tier:pro - Subscription tier - region:us-east - Geographic scope


How Visibility Enforcement Worksยถ

Publish-Time Assignmentยถ

When an agent publishes an artifact, visibility is set:

# Producer controls visibility
await flock.publish(
    SensitiveData(value="secret"),
    visibility=PrivateVisibility(agents={"trusted_agent"})
)

Scheduling-Time Checkยถ

Before scheduling an agent, Flock checks visibility:

# Flock's internal logic (you don't write this)
for agent in potential_consumers:
    if artifact.visibility.allows(agent.identity):
        schedule_agent(agent, artifact)  # โœ… Allowed
    else:
        skip_agent(agent)  # ๐Ÿ”’ Blocked

Result: No agent ever sees data it shouldn't access.


Real-World Use Casesยถ

Healthcare (HIPAA Compliance)ยถ

Challenge: Patient data must only be accessible to treating physicians.

from flock.visibility import PrivateVisibility, LabelledVisibility

# Patient record only for treating physician
doctor = (
    flock.agent("doctor")
    .consumes(PatientVisit)
    .publishes(
        PatientRecord,
        visibility=PrivateVisibility(agents={"treating_physician"})
    )
)

# Lab results for anyone with medical clearance
lab_tech = (
    flock.agent("lab_tech")
    .identity(AgentIdentity(name="lab_tech", labels={"role:medical"}))
    .consumes(LabOrder)
    .publishes(
        LabResults,
        visibility=LabelledVisibility(required_labels={"role:medical"})
    )
)

Financial Services (Multi-Tenancy)ยถ

Challenge: Complete data isolation between customers.

from flock.visibility import TenantVisibility

# Each customer's portfolio is isolated
trading_agent = (
    flock.agent("trader")
    .consumes(MarketData)
    .publishes(
        CustomerPortfolio,
        visibility=TenantVisibility(tenant_id=customer.id)
    )
)

# Customer A's agent can't see Customer B's data

Enterprise SaaS (Tiered Access)ยถ

Challenge: Free users see basic features, pro users see advanced analytics.

from flock.visibility import LabelledVisibility

# Basic reports for free and pro users
analyzer = (
    flock.agent("analyzer")
    .consumes(UserActivity)
    .publishes(
        BasicReport,
        visibility=LabelledVisibility(
            required_labels={"tier:free", "tier:pro"}  # Either label works
        )
    )
)

# Advanced analytics only for pro users
advanced_analyzer = (
    flock.agent("advanced")
    .identity(AgentIdentity(labels={"tier:pro"}))
    .consumes(UserActivity)
    .publishes(
        AdvancedAnalytics,
        visibility=LabelledVisibility(required_labels={"tier:pro"})
    )
)

Government/Intelligence (Security Clearances)ยถ

Challenge: Multi-level security clearances (Unclassified โ†’ Secret โ†’ Top Secret).

from flock.visibility import LabelledVisibility, AgentIdentity

# Field agent collects classified intel
field_ops = (
    flock.agent("field_ops")
    .identity(AgentIdentity(labels={"clearance:field_ops"}))
    .consumes(Mission)
    .publishes(
        ClassifiedIntel,
        visibility=LabelledVisibility(required_labels={"clearance:secret"})
    )
)

# Only analysts with secret clearance can see it
analyst = (
    flock.agent("analyst")
    .identity(AgentIdentity(labels={"clearance:secret"}))
    .consumes(ClassifiedIntel)
    .publishes(ThreatAssessment)
)

๐Ÿ‘‰ See complete intelligence example

Press & Media (Embargoes)ยถ

Challenge: Press releases visible only after embargo period.

from datetime import timedelta
from flock.visibility import AfterVisibility, PublicVisibility

# Embargo for 7 days, then public
pr_team = (
    flock.agent("pr_team")
    .consumes(NewsStory)
    .publishes(
        PressRelease,
        visibility=AfterVisibility(
            ttl=timedelta(days=7),
            then=PublicVisibility()
        )
    )
)

Comparison to Other Frameworksยถ

Traditional Frameworksยถ

# โŒ NO built-in security
# Every agent sees everything on the blackboard
agent1.publishes(SensitiveData(...))
# Any agent can consume it - hope they don't!

# โŒ "Bring your own" security
# You implement access control yourself
if user.has_permission("read_sensitive"):
    process(data)  # Manual checks everywhere

Flockยถ

# โœ… Producer-controlled access
agent1.publishes(
    SensitiveData(...),
    visibility=PrivateVisibility(agents={"trusted_agent"})
)
# Only trusted_agent can consume - enforced automatically!

# โœ… Zero-trust by default
# No agent sees unauthorized data - impossible to bypass

Advanced Patternsยถ

Multi-Level Securityยถ

Create hierarchical clearance levels:

# Unclassified โ†’ Confidential โ†’ Secret โ†’ Top Secret
junior_analyst = flock.agent("junior").identity(AgentIdentity(
    labels={"clearance:confidential"}
))

senior_analyst = flock.agent("senior").identity(AgentIdentity(
    labels={"clearance:secret"}
))

# Junior can't see secret data
.publishes(SecretReport, visibility=LabelledVisibility(
    required_labels={"clearance:secret"}
))

Organizational Hierarchyยถ

Implement reporting structures:

# Junior analysts can't see senior work
junior = flock.agent("junior").identity(AgentIdentity(
    labels={"level:junior"}
))

# Senior work requires senior label
senior = flock.agent("senior").identity(AgentIdentity(
    labels={"level:senior"}
)).publishes(
    SeniorAnalysis,
    visibility=LabelledVisibility(required_labels={"level:senior"})
)

Time-Based Access Expiryยถ

Data becomes more restricted over time:

# Public for 1 hour, then private archival
.publishes(
    TemporaryData,
    visibility=AfterVisibility(
        ttl=timedelta(hours=1),
        then=PrivateVisibility(agents={"archiver"})
    )
)

Combined Patternsยถ

Mix visibility types for complex requirements:

# Tenant-specific + role-based
.publishes(
    CustomerReport,
    visibility=TenantVisibility(tenant_id="customer_123")
    # AND agent needs role:analyst label (custom logic)
)

Security Best Practicesยถ

โœ… Doยถ

  • Principle of Least Privilege - Give minimum necessary access
  • Use PrivateVisibility for sensitive data - Explicit allowlists
  • Enable audit logging - Track who accessed what (via tracing)
  • Review access regularly - Periodically audit agent permissions
  • Test security boundaries - Verify agents can't bypass visibility
  • Document security model - Explain why each visibility choice was made

โŒ Don'tยถ

  • Don't make everything public - Default to restricted
  • Don't rely on single layer - Defense in depth (visibility + identity + audit)
  • Don't skip identity assignment - Agents need identities for RBAC
  • Don't forget to test - Verify access control works
  • Don't set-and-forget - Access needs regular review
  • Don't ignore audit logs - Monitor for security violations

Debugging Visibility Issuesยถ

Agent Not Triggering?ยถ

Check visibility match:

# Enable tracing to see if agent was blocked
export FLOCK_AUTO_TRACE=true
export FLOCK_TRACE_FILE=true

# Run and check agent scheduling
await flock.publish(artifact)
await flock.run_until_idle()

# Query DuckDB for visibility blocks
import duckdb
conn = duckdb.connect('.flock/traces.duckdb')
# Check if agent was considered but blocked

Common issues: - Agent missing required labels - Agent not in private allowlist - Tenant ID mismatch - Time delay not elapsed

Verify Agent Identityยถ

# Print agent identity
print(agent.identity)
# AgentIdentity(name='analyst', labels={'clearance:secret'})

# Check if identity matches visibility
artifact_visibility = PrivateVisibility(agents={"analyst"})
can_access = artifact_visibility.allows(agent.identity)
print(f"Can access: {can_access}")

Check Artifact Visibilityยถ

# Retrieve artifact and check visibility
artifacts = await flock.store.get_artifacts_by_type("SensitiveData")
for artifact in artifacts:
    print(f"Visibility: {artifact.visibility}")
    print(f"Type: {type(artifact.visibility).__name__}")

Common Patternsยถ

Public by Default, Private Exceptionsยถ

# Most data is public
reporter = flock.agent("reporter").publishes(Article)  # PublicVisibility (default)

# Sensitive data is private
auditor = flock.agent("auditor").publishes(
    AuditReport,
    visibility=PrivateVisibility(agents={"compliance_officer"})
)

Progressive Disclosureยถ

# Start private, become public later
.publishes(
    QuarterlyReport,
    visibility=AfterVisibility(
        ttl=timedelta(days=90),  # 90-day embargo
        then=PublicVisibility()
    )
)

Multi-Tenant Isolationยถ

# Complete isolation between tenants
for tenant in tenants:
    tenant_agent = flock.agent(f"agent_{tenant.id}").publishes(
        TenantData,
        visibility=TenantVisibility(tenant_id=tenant.id)
    )

Clearance Hierarchyยถ

# Confidential < Secret < Top Secret
clearance_levels = ["confidential", "secret", "top_secret"]

for level in clearance_levels:
    agent = flock.agent(f"agent_{level}").identity(
        AgentIdentity(labels={f"clearance:{level}"})
    ).publishes(
        ClassifiedData,
        visibility=LabelledVisibility(required_labels={f"clearance:{level}"})
    )

Next Stepsยถ


Complete Exampleยถ

Here's everything togetherโ€”a secure multi-agent system:

import asyncio
from datetime import timedelta
from pydantic import BaseModel, Field
from flock import Flock, flock_type
from flock.visibility import (
    PublicVisibility,
    PrivateVisibility,
    TenantVisibility,
    LabelledVisibility,
    AfterVisibility,
    AgentIdentity
)

# Define artifacts
@flock_type
class Mission(BaseModel):
    mission_id: str
    objective: str

@flock_type
class RawIntelligence(BaseModel):
    mission_id: str
    observations: list[str]
    sensitivity: str

@flock_type
class AnalysisReport(BaseModel):
    mission_id: str
    threat_assessment: str
    confidence: float

@flock_type
class ExecutiveBrief(BaseModel):
    mission_id: str
    summary: str
    next_steps: list[str]

# Create orchestrator
flock = Flock("openai/gpt-4.1")

# Field agent - collects classified intel
field_agent = (
    flock.agent("field_agent")
    .identity(AgentIdentity(name="field_agent", labels={"clearance:field_ops"}))
    .consumes(Mission)
    .publishes(
        RawIntelligence,
        visibility=PrivateVisibility(agents={"analyst"})  # ๐Ÿ”’ Only analyst sees
    )
)

# Analyst - requires secret clearance
analyst = (
    flock.agent("analyst")
    .identity(AgentIdentity(name="analyst", labels={"clearance:secret"}))
    .consumes(RawIntelligence)
    .publishes(
        AnalysisReport,
        visibility=LabelledVisibility(required_labels={"clearance:secret"})  # ๐Ÿท๏ธ Need clearance
    )
)

# Director - creates public brief
director = (
    flock.agent("director")
    .identity(AgentIdentity(name="director", labels={"clearance:secret", "role:leadership"}))
    .consumes(AnalysisReport)
    .publishes(ExecutiveBrief, visibility=PublicVisibility())  # ๐ŸŒ Everyone sees
)

# Use it
async def main():
    # Start mission
    await flock.publish(Mission(
        mission_id="OP-BLACKBIRD-2025",
        objective="Investigate cyber threats"
    ))

    # Cascade through security layers
    await flock.run_until_idle()

    # Get public brief
    briefs = await flock.store.get_by_type(ExecutiveBrief)
    print(f"Executive Brief: {briefs[0].summary}")

asyncio.run(main())

What happened: 1. โœ… Mission published (public) 2. โœ… field_agent produces RawIntelligence (private to analyst) 3. โœ… analyst produces AnalysisReport (requires secret clearance) 4. โœ… director produces ExecutiveBrief (public to leadership) 5. โœ… All visibility controls enforced automatically

Security guarantees: - ๐Ÿ”’ Only analyst sees RawIntelligence - ๐Ÿท๏ธ Only agents with secret clearance see AnalysisReport - ๐ŸŒ Everyone sees ExecutiveBrief - โœ… No agent can bypass visibility


Ready to secure your agents? Start with the Quick Start Guide or explore the complete intelligence example.