Agent access
Attribute-based access control (ABAC) — attach policies to an API key to limit which actions and context each agent can reach.
Available to Enterprise Plan customers only.
Overview
Multi-agent systems need least privilege over context: each agent should reach only the actions and data its task requires. Agent access solves that with attribute-based access control (ABAC) — reusable policies, attached to the API key an agent authenticates with, that decide what the agent may do and which context it may see inside a project.
Agent access is one half of policy-based access control. The other half, UserGroup access, applies the same policy engine to Context MCP users through the groups they belong to.
Project-level isolation separates one project from another. Policies add a second level of control inside a project. API clients authenticate with an API key and Zep evaluates the policies attached to that key.
Policies narrow access along three dimensions:
- By action — which operations the agent may perform, such as read-only access or a block on destructive calls.
- By class of data — which context the agent may read within a graph. A single graph may hold several classes of data (by sensitivity, department, or source system, for example), and a key is restricted to the classes it may read, based on the metadata attached at ingestion.
- By graph — which standalone graphs the key may access.
A key with no ABAC policies has full read and write access to project data. Agent access is opt-in per key.
How a request is evaluated
ABAC evaluates a request in two layers, in order:
- Action layer. Determines whether the request action is permitted. The outcome applies to the whole request: it proceeds, or it returns
403. - Attribute layer. For a permitted request that returns data, determines which objects appear in the response. It filters search, list, and read results by the source metadata of each object. It never overrides an action-level denial; it only narrows what a permitted read returns.
Three controls determine the result:
- Role — the key’s base posture, either
default_allowordefault_deny. - Policy sets — reusable collections of allow and deny rules attached to the key.
- Mode — set on both the key and each policy set, one of
off,report_only, orenforce.
API key roles
Every API key has a role that sets its starting permissions before any policy is evaluated. You choose the role when you create the API key in the Zep web app.
A default_allow key with no policies has full data access, which preserves the behavior of keys created before ABAC. A default_deny key authenticates and reaches the API, but every data operation returns 403 until a policy grants access.
Use default_deny for keys issued to external integrations, partners, or limited-scope agents, where each permitted action should be enumerated explicitly.
Policy sets and modes
A policy set is a named, versioned collection of rules. One set may attach to many keys, and one key may have many sets attached. A policy set that contains retention entries deletes idle context instead of authorizing requests. Context retention describes retention policy sets. A policy set is written as YAML:
Each rule has an id, an effect of allow or deny, a non-empty list of actions, and an optional attributes map for source-based rules.
Mode exists at two levels. The key’s abac_mode is the master switch, and each policy set has its own mode. A set is evaluated only when the key is not off and the set is not off.
When several rules match, denial takes precedence, otherwise a matching allow rule produces an allow, otherwise the result is neutral and the role base decides. What a matching deny produces depends on the rule: a deny without attributes is an action-level denial and the whole request returns 403, while a deny with attributes excludes only the matching objects from an otherwise-permitted read — search and list results drop them, and single-object reads return 404.
Action-level policies
Action-level policies gate actions. An action is a dotted name that covers one or more API operations, such as thread.get, graph.search, thread.add_messages, or user.delete.
Some action names are different from the SDK method names. For example:
thread.get_user_contextcoversthread.get_context.thread.list_allcoversthread.list.user.list_orderedcoversuser.list.graph.addcoversgraph.episode.add.graph.searchcoversgraph.get_contextand the typed graph search methods, such asgraph.search_nodes.
The readonly macro
readonly is a macro that expands to every action that does not mutate project data. It is computed at evaluation time, so new read-only endpoints are covered automatically. Write actions are always listed explicitly.
This table is illustrative, not an exhaustive list of actions.
Example: a read-only key
A key that may read project data but write nothing. The recommended pattern is a default_deny key that is granted reads:
Attached to a default_deny key in enforce mode, read requests succeed and write requests return 403.
Example: read access plus one write
Allow rules combine, so a literal write grant may be added alongside the macro:
Example: blocking specific actions
Starting from a default_allow key, a deny rule blocks destructive actions regardless of other rules, because denial takes precedence:
Source-based policies
Action-level policies decide whether a read runs. Source-based policies decide which objects that read returns. They are the mechanism behind access control by class of data within a graph.
Tagging data at ingestion
Every object derived from ingestion carries effective metadata: the combined metadata of all the episodes that contributed to it. To make data eligible for source-based policies, attach metadata when you add it to the graph; see Adding business data for how. The same applies to metadata attached to a thread message through the Threads API; see Adding messages for how. Metadata accepts up to 10 keys, with scalar values or arrays of scalars.
Every node, edge, community, and summary derived from those episodes inherits the metadata in its effective metadata. See Episode metadata projection for how metadata propagates to derived objects.
Matching is exact set equality
Exact set equality is the default. A source-based rule adds an attributes map. Each key maps to a non-empty list of required values.
An object matches when its effective values for each key equal the required values.
The superset case is the important one. Effective metadata is the union over every contributing source, so an extra value means a source the grant does not name helped produce the object. Equality fails closed in that case to avoid returning data from an uncovered source.
When a constraint names several keys, they combine with AND. An object is admitted only when its effective value set matches exactly for every named key.
Opt in to subset matching
Set spec.subset_match: true on a policy set to use containment matching for that set:
With subset matching, {finance} and {finance, engineering} both satisfy department: [finance]. An absent key or a value set without finance does not match.
The setting applies to allow and deny rules in the policy set. Policy sets without subset_match: true continue to use exact equality.
Example: scope a key to one class of data
A key that may search the graph but should only return objects sourced entirely from the finance department:
The search runs, and the result set contains only objects whose effective department is exactly {finance}.
Example: hide objects with a deny rule
Attributes also work on deny rules, with the opposite effect. On a key that may already read the data, a deny rule with attributes hides the objects whose effective metadata matches. Denial takes precedence, so matching objects are removed even when an allow rule would otherwise return them.
Allow rules combine disjunctively. An unconstrained allow for the same action, such as a bare readonly grant, makes every object eligible and nullifies a sibling source constraint. To enforce a positive source restriction, do not also grant an unconstrained allow for that action.
What a caller sees
Filtering happens during the read, so pagination and limits operate over the visible set.
An edge can be visible while one of its endpoint nodes is hidden. In this case, edge and context responses keep the visible edge but omit the hidden node’s projected name, labels, and summary. The edge does not grant access to attributes of its endpoint nodes.
Graph selectors
Graph selectors limit a policy rule to specific standalone graphs. A policy set opts in by setting resource_selector_version: 1 in its spec.
Each rule can include a resources mapping with exactly one selector key. canonical_graph_uuids uses the graph uuid returned by the v4 graph API, not graph_id. A policy written for v3 can use graph_uuids with the graph uuid returned by the v3 graph API. Both keys select the same graph.
With no other grants, a default_deny API key whose abac_mode is enforce can list, search, and add data to the listed graph. The graph list shows only this graph.
A rule without resources is action-wide and applies to every graph, including graphs created later. The zepctl api-key policy-sets attach <key-uuid> <policy-set-uuid> command attaches the set to an API key. Zep rejects a malformed selector document when you create the policy set or attach it to an API key. zepctl policy-set validate does not check the resources mappings in a set without resource_selector_version. It can report that set as valid, but zepctl policy-set create rejects it with 400.
Evaluate graph requests
Graph selectors match standalone graphs. They do not match user graphs, Users, or threads. Action-wide rules and the key role decide those requests.
A request can identify its graph by UUID or by a node, edge, or episode in that graph. For each graph, an enforced matching deny takes precedence over any matching allow. If no enforced deny matches, an enforced matching allow permits the request. Otherwise, the key role decides: default_allow allows and default_deny denies. A report_only set records its result and does not change the response.
In enforce mode, a graph-selector deny returns 404 with the error code not_found, the same response as a graph that does not exist. For a default_deny key, a graph request that no rule allows also returns 404. The caller cannot learn whether the graph exists. An action-wide deny returns 403.
An attributes constraint in a rule with resources applies only to objects in the selected graphs.
List graphs
For an API key with a graph-selector set in enforce mode, the v4 graph list includes only standalone graphs allowed by graph.search. The key must also be allowed graph.list_all, which is an action-wide action. A default_deny key without an allow for graph.list_all gets 403 from the graph list. A default_allow key sees all graphs except denied graphs. A default_deny key sees only explicitly allowed graphs. Pages and counts include visible graphs only. A report_only set does not filter the list. The Python v4 SDK method is client.graph.list().
Clone and project-level operations
A graph clone requires an allow for the source graph. The target graph does not exist yet. Graph selectors do not match the target graph. Action-wide rules and the key role decide whether the target is allowed. For a default_deny key, an allow for graph.clone that selects only the source graph is not sufficient, and the clone returns 404. An action-wide allow for graph.clone permits the clone.
Graph selectors do not match graph creation or project-level settings, including project custom instructions and project entity types. Action-wide rules and the key role decide access to those operations.
The Batch ingestion guide describes how graph selectors affect batch items.
Managing policies
ABAC management is restricted to account administrators; project API keys are the subjects of ABAC and may not manage it. Policy sets are created, attached, and detached with the zepctl CLI. The dashboard is used to set an API key’s role and to review ABAC decisions.
Dashboard
When ABAC is enabled for an account, the dashboard exposes the key role at creation, an editable role with a security-change warning, a computed capabilities badge, and the ABAC decision for each request on the API log view.
zepctl
zepctl authenticates through the browser and manages policy sets and keys:
An attach or a detach takes effect across the whole service before the command reports success. When Zep cannot complete that step, the command fails with a retryable error instead. Run the same command again: attach and detach are both idempotent, so a repeat is safe.
Rolling out with report-only
report_only mode evaluates policies and records the result without changing the response. Use it to measure the impact of a policy before enforcing it.
Author and attach the policy
Write the policy set in enforce mode and attach it to the key, but set the key’s abac_mode to report_only. No request is blocked or filtered yet.
Let traffic flow
Each request records the decision it would have reached under enforcement, including the action, the outcome, and whether the result differs from the key’s current behavior.
Auditing
ABAC produces two kinds of records:
- Management operations, such as creating, updating, or deleting a policy set, attaching or detaching a set, and changing a key’s role or mode, are recorded as audit events.
- Runtime decisions are recorded on each API log record, with the evaluated action, the outcome, and the mode.
The API log view filters by ABAC outcome and mode, which supports both the report-only rollout above and ongoing review of enforced policies.