Aperture by Tailscale

Aperture is Tailscale’s AI gateway. It sits between AI agents and LLM providers, routing requests through the tailnet with visibility into agent activity. The Cerbos integration adds fine-grained, policy-driven authorization to every tool call that passes through Aperture.

When an agent makes a tool call, Aperture intercepts the request before it reaches the LLM. It extracts the user identity, tool name, and tool parameters and forwards them to a Cerbos PDP running on the tailnet. Cerbos evaluates the request against authorization policies and returns an allow or deny decision. Aperture enforces that decision: permitted tool calls proceed, denied tool calls are blocked before execution.

Architecture

User -> AI Agent -> Tailscale Aperture -> LLM
                          |         ^
                     Cerbos PDP  Allow/Deny
Aperture

AI gateway and enforcement point. Intercepts tool calls, forwards metadata to Cerbos, and enforces the authorization decision.

Cerbos PDP

Evaluates tool calls against authorization policies. Enriches requests with context from external systems (identity providers, HR platforms, on-call systems) before making a decision.

Cerbos Hub

Manages the policy lifecycle: authoring, version control, testing, distribution, and audit logging. Every decision is logged back to Hub as audit evidence.

What is evaluated

Every tool call produces an authorization request containing:

Principal attributes

The identity of whoever or whatever initiated the tool call, sourced from Tailscale. This can be enriched at decision time with real-time context from external systems such as Okta, Workday, ServiceNow, or PagerDuty: department, cost center, on-call status, project assignments.

Resource attributes

The tool name and its parameters (the database being queried, the CRM object being accessed, the API endpoint being called), along with metadata about the model and provider.

Request context

Runtime signals such as time of day, device posture, and Tailscale app capabilities.

The policy engine evaluates all inputs together. A decision reflects who or what is making the call, what they are attempting, and what the organization knows about both at that moment.

Prerequisites

  • A Tailscale account with an Aperture instance set up

  • Docker installed on a machine connected to your tailnet

Setup

Configure Tailscale ACLs

Define a tag and service for the Cerbos container in your tailnet access controls:

  1. Create a tag (e.g. tag:tainaron-host)

  2. Create a service named cerbos-tainaron on port 443 with that tag

  3. Add the following ACL grants:

    {
        "tagOwners": {
            "tag:tainaron-host": ["autogroup:member"]
        },
        "autoApprovers": {
            "services": {
                "svc:cerbos-tainaron": ["tag:tainaron-host"]
            }
        },
        "grants": [{
            "src": ["*"],
            "dst": ["svc:cerbos-tainaron"],
            "ip": ["*"]
        }]
    }
  4. Generate a Tailscale auth key with the tag assigned. Ephemeral mode is recommended.

Connect Cerbos Hub from Aperture

  1. In your Aperture instance, select the Cerbos integration. This redirects you to Cerbos Hub to create a workspace linked to your Aperture instance.

  2. Complete the Cerbos Hub sign-up. The workspace is automatically provisioned with a deployment and credentials for your Aperture instance.

Download the configuration file

After the workspace is provisioned, the Cerbos Hub dashboard provides a configuration file with your credentials pre-filled.

  1. Navigate to the Aperture configuration page in your workspace

  2. Click Generate and download config file

  3. Open the downloaded aperture.yaml in a text editor

  4. Replace <paste-your-tailscale-auth-key-here> with the Tailscale auth key generated in the previous step

The configuration file contains the Cerbos Hub credentials, PDP settings, and the Aperture extension configuration:

server:
  tailscale:
    authKey: <paste-your-tailscale-auth-key-here>
    serviceName: cerbos-tainaron

pdp:
  inProcess:
    audit:
      enabled: true
      backend: "hub"
      hub:
        storagePath: /tmp/hub_auditlog

    hub:
      credentials:
        clientID: <your-client-id>
        clientSecret: <your-client-secret>

    storage:
      driver: "hub"
      hub:
        remote:
          deploymentID: <aperture-deployment-id>

extensions:
  routeExtensions:
    aperture:
      extension:
        extensionURL: /extensions/tailscale/aperture.star
      routes:
        "/aperture": ["GET", "POST"]

Alternatively, all credentials can be passed as environment variables instead of embedding them in the configuration file:

TS_AUTHKEY

The Tailscale auth key that was generated above.

CERBOS_HUB_DEPLOYMENT_ID

The deployment ID to load policies from. Find this on the deployment page in Cerbos Hub.

CERBOS_HUB_CLIENT_ID

Client ID from the deployment’s Client credentials tab.

CERBOS_HUB_CLIENT_SECRET

Client secret from the deployment’s Client credentials tab.

CERBOS_HUB_PDP_ID

Optional. A name for this PDP instance, shown in the Cerbos Hub monitoring page. If not provided, a random value is used.

When using environment variables, the configuration file reduces to:

pdp:
  inProcess:
    audit:
      enabled: true
      backend: "hub"
      hub:
        storagePath: /tmp/hub_auditlog

    storage:
      driver: "hub"

extensions:
  routeExtensions:
    aperture:
      extension:
        extensionURL: /extensions/tailscale/aperture.star
      routes:
        "/aperture": ["GET", "POST"]

Run the Cerbos container

From the directory where you saved aperture.yaml, start the Cerbos service:

docker run \
  --rm \
  --tmpfs /tmp \
  -v $(pwd)/aperture.yaml:/config/aperture.yaml \
  cerbos/tainaron:latest server --conf.path=/config/aperture.yaml

The container joins your tailnet automatically and begins accepting authorization requests.

Configure Aperture

Open the Aperture settings page at http://ai/ui from a device on your tailnet.

Add a hook named cerbos pointing to your Cerbos service:

{
    "hooks": {
        "cerbos": {
            "url": "https://cerbos-tainaron/ext/aperture"
        }
    }
}

Add a grant to route tool calls to Cerbos for authorization. Set the src array to the users whose traffic should be evaluated, or use * to apply to all users:

{
    "src": ["*"],
    "grants": [
        {
            "hook": {
                "match": {
                    "providers": ["*"],
                    "models": ["*"],
                    "events": ["tool_call_entire_request"]
                },
                "hook": "cerbos",
                "fields": ["tools"]
            }
        }
    ]
}

Verify the connection

After starting the container, check the Cerbos Hub dashboard to confirm the PDP has connected. The Decision points tab on your deployment page shows all connected PDP instances.

Observe

Start with a default allow-all policy. All tool calls are evaluated and logged, but nothing is blocked. Use the audit data to build an evidence base of what agents are doing.

Author policies

Write rules that reflect the organization’s requirements. Start narrow: restrict a single tool or constrain a specific parameter. Expand coverage incrementally.

Enforce

Push policies through Cerbos Hub. They take effect immediately across all connected PDPs. No agent modifications required.

Policy examples

Cerbos policies are declarative and version-controlled. Each rule targets a set of actions (tool names), applies to specific roles, and specifies conditions under which the rule takes effect. Conditions can reference any attribute of the principal, the resource, or the request context.

A single policy can express requirements such as the following:

  1. Read-only tools are always permitted. File reads, searches, and documentation lookups are allowed unconditionally for all authenticated users.

  2. CRM access is scoped by team and operation. An agent can read account metadata from Salesforce via an MCP tool, but only for users in the sales org. Updating deal stages or exporting contact lists requires a more privileged role.

  3. Production database access is restricted. An agent calling a database tool against a production connection string is denied unless the user is currently on call, verified in real time against PagerDuty. The same query against a staging environment is permitted for any engineer.

  4. HR and compensation data require explicit authorization. Tools that access BambooHR, Workday, or any system containing employee PII are denied by default. Access is granted only to users in HR or People Ops, and only from managed devices.

  5. Different rules apply to different clients. A tool call from a sanctioned coding agent (Claude Code) is permitted, while the same call from an unsanctioned client is denied. The user-agent is an input to the policy.

These rules compose. A single tool call can be evaluated against multiple conditions simultaneously. The policy engine resolves the outcome deterministically.

Audit logging

Every authorization decision creates a log entry in Cerbos Hub recording:

  • The principal: who or what triggered the tool call

  • The resource: which tool, with what parameters

  • The decision: allow or deny

  • The policy: which version, which rule matched

  • The context: all attributes that were evaluated

This is a per-decision record that traces from a specific tool call to a specific policy evaluation. See Audit log collection for details on accessing and exporting audit data.