AI agents often get access the way scripts used to: a shared key, standing permissions and no record of who started the run. Formal manages AI agent access control and human access from one Control Plane, with policy as code in Rego, one identity model and one audit log.

What is AI access control?

AI access control is the set of rules that decide what an AI agent can reach, under whose identity, and with what record. Some vendors use the term for AI features inside identity tools. Here it means access control for AI agents, applied to the same databases, servers and APIs your engineers use.

Formal enforces it with two proxies. Formal Endpoint runs on laptops, VMs and pods and inspects outbound traffic. The Connector, the reverse proxy behind Formal PAM, sits in your VPC in front of databases, SSH servers, Kubernetes clusters and HTTP APIs. Both get their policies, identities and configuration from the Control Plane.

That’s what unified access control means on this page: one policy language, one identity model and one log for every request, whether a person or an agent sent it.

It’s also centralized access management without a central data path. The Control Plane can’t interact with your data; only the Connector and the Endpoint can. The Control Plane holds configuration and shows the logs the proxies send (architecture).

Policy as code: how do Rego policies work?

Every Formal policy is an OPA policy: a Rego module evaluated by Open Policy Agent. Each Rego policy has a name, a description and code, and runs at up to three stages: session (when a connection opens), request (before a query or call reaches the resource) and response (when data comes back).

The decision is a small object: an action such as allow, block, mask, filter, rewrite or mfa, plus a reason that lands in the logs. Inputs cover the user, the end user behind an application, the resource, the parsed SQL query or the HTTP request. Policies can call 189 standard OPA built-ins; Formal blocks the ones that reach the network.

Rollout is where policy as code usually breaks, so the lifecycle matters. A policy is in draft (not enforced), dry-run (logs what it would have done) or active. Impact reports backtest a policy against up to 31 days of past traffic (with policy input retention turned on), and every code change creates a version you can roll back to. The policies docs cover each step.

On the Endpoint, network rules decide which traffic gets intercepted in the first place. They are CEL conditions over the process, the destination and the detected protocol. process.is_agent, for example, selects flows from known AI agents only.

How does Formal tell a human from an agent?

Access management for AI agents starts with identity. Every Formal user is either human or machine, and policies read it as input.user.type. People come from your IdP: SSO for Control Plane login (Okta, Entra ID, Google Workspace, any SAML 2.0 or OIDC provider) and SCIM for provisioning, deprovisioning and group sync.

Agents and workloads get machine users. With an OIDC integration, a workload presents a short-lived JWT from its identity provider. Formal checks the issuer, the audience and a claim condition, then runs the call as the mapped machine user. The same trust can mint a short-lived federation token for Connector access, with no long-lived Formal API key.

AI agent permissions get precise when policies see both identities. End-user identity propagation gives a policy the machine user and the person behind it (input.end_user), so an agent acting for someone is checked against both. Cursor runs its Cloud Agents this way: a dedicated Formal identity with limited access, plus the employee who started the task.

What do the audit logs record?

Every query, command and API call that goes through Formal is logged. Each log has a source: connector for Connector traffic, desktop for the Endpoint, control-plane for authenticated Control Plane API calls (Terraform included) and workflow for workflow runs.

Non-HTTP Connector traffic is grouped into sessions, one per TCP connection, which you can watch live and terminate. For SSH and Kubernetes, a session analysis computes a risk score. The access graph turns the same logs into a map of which humans, agents and machines reached which resources, through which Connectors.

For AI agent audit logs, this means agent and human traffic share the same Logs page, the same search and the same export. Formal forwards logs to AWS S3, Google Cloud Storage, Azure Blob Storage, Datadog, Splunk or Panther (log integrations).

Just-in-time access with Workflows and policy suspensions

Just-in-time access in Formal is a policy suspension: an exception that exempts one user or group from one policy, until it expires. A CEL input condition can narrow it to matching requests, such as one database. A one-off suspension covers the first matching request; a time-bound one covers every matching request until it expires.

Formal removes expired suspensions automatically, and deleting one revokes it immediately. Each suspension carries a reason for the audit trail. There are three ways to create one (suspensions docs):

  • Access Requests: an employee asks for an exception on a blocked action in the employee Catalog, as a one-time exception or time-bound access from 30 minutes to 7 days. An admin approves it in the Control Plane.
  • Workflows: a Slack form or an API request starts an access request workflow; after a Yes, a formal-app-command action creates the suspension with expiration_minutes.
  • API: CreatePolicySuspension, for your own tooling. Without an expiration, the suspension lasts 24 hours.

Here’s the catch: Access Requests doesn’t send notifications. For time-sensitive requests, build the Slack flow with Workflows; both can run in the same organization. That’s the difference from a separate access request tool: an approved one-time or time-bound exception becomes a suspension that the proxies enforce.

For temporary access by contractors, temporary users go further: Formal deletes them automatically when their time runs out.

Ship policy as code with Terraform, Pulumi or the Kubernetes operator

Everything in the Control Plane is an API call. The API is gRPC with Protocol Buffers, and it also accepts JSON over HTTP through the Connect protocol, so curl works. On top of it:

  • Terraform: the formalco/formal provider manages policies, workflows, forms, network rules, permissions, users, groups and the rest of your Formal objects.
  • Pulumi: the same objects from TypeScript, Python, Go or C#.
  • Kubernetes operator: a FormalPolicy custom resource declares a policy from Rego, next to the services it protects.
  • CLI: formal ls lists the resources you can reach, and formal form fill submits a form from the terminal.
  • Formal MCP: claude mcp add formal https://mcp.formal.ai/mcp --transport http connects Claude Code. Calls run as your identity, and changes need a client you approved for write.
  • Go SDK: for custom tools and reports.

In the Control Plane, the Formal Agent drafts policies, permissions, workflows, hooks and network rules, validates the drafts, and applies a change only after you approve it.

ManoMano runs Formal this way: it deployed Formal with the Terraform provider and enforces policies on Okta groups across its data stores.