Identity & Access

Every agent and key has a scoped identity, with least privilege by default

Overview

Governance starts with identity. On Recursiv every actor that touches the platform carries a scoped identity: API keys are scoped to the orgs or project they can reach, agents are bound to an owner and an organization and granted access to specific projects, and organization membership and security policy bound what a person can do. Nothing has ambient access. You grant the narrowest scope that works, then widen only when needed.

This page covers API key scoping, granting and revoking agent project access, and organization-level security policy.

API key scoping

API keys are the primary identity for SDK and MCP access. A key is created from an active user session (not from another key) and carries an explicit list of scopes plus an optional binding.

1const apiKey = await r.auth.createApiKey(
2 {
3 name: 'CI/CD pipeline',
4 scopes: ['projects:read', 'projects:write'],
5 organizationId: 'org_123', // bind to one org
6 },
7 sessionToken,
8);

A key is scoped in two independent ways.

Permission scopes decide which kinds of operations the key can perform: projects:read, agents:write, databases:read, and so on. Anything not in the list is denied. The full scope list is in Auth & API Keys.

Workspace binding decides which workspaces the key can reach:

BindingSet viaReaches
All orgsneither organizationId nor projectIdevery org the owner belongs to
Org-boundorganizationIdexactly one organization
Project-boundprojectIdexactly one project (an app-member key)

organizationId and projectId are mutually exclusive. Project-bound keys are the right choice for customer-facing apps: the customer becomes a project_member rather than landing on your team’s org roster.

Roadmap: a select-orgs binding (a key scoped to a chosen subset of your organizations rather than all or exactly one) is being productized. Until then, prefer an org-bound key for a single tenant and an all-orgs key only for cross-org automation you control.

Least privilege in practice

  • Give each app, pipeline, or agent its own key. Do not share one key across surfaces.
  • Request only the scopes the surface uses. A read-only dashboard should never hold a :write scope.
  • Bind to a project or org rather than all orgs unless the work genuinely spans orgs.
  • The full secret is shown once at creation. Store it in a secrets manager, never in a client bundle. See token storage best practices.

Agent project access

An agent does not get access to a project by default. You grant it explicitly, with a specific permission set, and you can revoke it at any time. This is the per-agent least-privilege control.

1// Grant an agent scoped access to one project
2const { data: access } = await r.agents.grantProjectAccess('agent_123', {
3 project_id: 'proj_456',
4 permissions: ['sandbox:execute', 'database:read'],
5});
6
7console.log(access.permissions); // ['sandbox:execute', 'database:read']
8
9// Revoke it when the work is done or trust changes
10await r.agents.revokeProjectAccess('agent_123', 'proj_456');

The grant is recorded against the agent, so project access is itself an auditable fact: you can list which agents can reach a project and with which permissions. Revocation takes effect for subsequent operations.

Organization security policy

Identity controls at the org level are set through r.organizationSecurity. This governs how human members authenticate and how long sessions live.

1// Read the current policy
2const { data: policy } = await r.organizationSecurity.get('org_123');
3
4// Tighten it
5await r.organizationSecurity.update('org_123', {
6 require_two_factor: true,
7 two_factor_grace_period_days: 7,
8 session_timeout_minutes: 480,
9 idle_timeout_minutes: 30,
10 ip_allowlist: ['203.0.113.0/24'],
11 ip_allowlist_enabled: true,
12});
FieldTypeControls
require_two_factorbooleanWhether members must enroll in 2FA.
two_factor_grace_period_daysnumberDays a new member has before 2FA is enforced.
session_timeout_minutesnumber | nullAbsolute session lifetime.
idle_timeout_minutesnumber | nullInactivity timeout.
ip_allowliststring[]CIDR ranges permitted to access the org.
ip_allowlist_enabledbooleanWhether the allowlist is enforced.

Membership and roles

Org membership and member roles determine what a person can do inside an organization, and project membership determines what an app’s users can do inside a project. Roles are assigned per member and govern administrative actions such as managing keys, members, and settings. Org and project membership are deliberately separate so that your customers (project members) never appear on your internal team roster (org members).

Roadmap: SSO (SAML / OIDC) and fine-grained RBAC role definitions are on the roadmap as a productized identity layer. Today, identity is enforced through scoped API keys, agent project-access grants, organization security policy, and membership roles. SSO and custom role definitions are not yet generally available.

What this proves

Because identity is scoped and grants are recorded, you can answer at any time:

  • Which key acted, what scopes it held, and which workspace it was bound to
  • Which agents can reach a given project, and with which permissions
  • Whether an org enforces 2FA, session limits, and an IP allowlist

That is the identity foundation the rest of govern builds on.