Restrict registry access
Create access policies with specific permissions and map IdP groups to those policies to control what users can do in the registry.
Solo Enterprise for agentregistry lets you restrict what individual users or groups can do in the registry by creating AccessPolicy resources and mapping them to JWT group claims from your OIDC provider. For a conceptual overview of how AccessPolicy works across all access control layers, see About access control.
Before you begin
Follow one of the quickstart guides to install Solo Enterprise for agentregistry and deploy your first agent. The quickstart also sets up Keycloak as an OIDC provider and creates an admin user that you use to create AccessPolicies in this guide.
Step 1: Create a Keycloak group and user
Use the Keycloak Admin REST API to create a developers group and a user who belongs to it.
Get an admin token for the Keycloak Admin REST API.
export KEYCLOAK_ADMIN_TOKEN=$(curl -s \ -d "client_id=admin-cli" -d "username=admin" -d "password=admin" \ -d "grant_type=password" \ "${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token" \ | jq -r .access_token)Create the
developersgroup.curl -s -X POST \ -H "Authorization: Bearer ${KEYCLOAK_ADMIN_TOKEN}" \ -H "Content-Type: application/json" \ "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/groups" \ -d '{"name": "developers"}'Create a
dev-useraccount and assign it to thedevelopersgroup.curl -s -X POST \ -H "Authorization: Bearer ${KEYCLOAK_ADMIN_TOKEN}" \ -H "Content-Type: application/json" \ "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users" \ -d '{ "username": "dev-user", "email": "dev-user@example.com", "firstName": "Dev", "lastName": "User", "enabled": true, "credentials": [{"type": "password", "value": "password", "temporary": false}] }' export DEV_USER_ID=$(curl -s \ -H "Authorization: Bearer ${KEYCLOAK_ADMIN_TOKEN}" \ "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?username=dev-user" \ | jq -r '.[0].id') export DEVELOPERS_GROUP_ID=$(curl -s \ -H "Authorization: Bearer ${KEYCLOAK_ADMIN_TOKEN}" \ "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/groups?search=developers" \ | jq -r '.[0].id') curl -s -X PUT \ -H "Authorization: Bearer ${KEYCLOAK_ADMIN_TOKEN}" \ "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users/${DEV_USER_ID}/groups/${DEVELOPERS_GROUP_ID}"
Step 2: Create an access policy
Create an AccessPolicy resource that defines the operations that users from the developers group can perform. In this example, you allow read access to all agents and MCP servers.
Log in to Solo Enterprise for agentregistry as an admin user (
admin-user/password). The admin user has the permission to create AccessPolicies.export OIDC_ISSUER=$KEYCLOAK_ISSUER export OIDC_CLIENT_ID=ar-cli-interactive arctl user loginCreate the AccessPolicy. The following example creates a read-only policy for the
developersgroup that allows listing and viewing all agents and MCP servers, but does not permit publishing or deploying them.cat <<EOF | arctl apply -f - apiVersion: ar.dev/v1alpha1 kind: AccessPolicy metadata: name: developers spec: description: "Read-only access to all artifacts" principals: - kind: Role name: developers rules: - actions: - "registry:read" resources: - kind: server name: "*" - kind: agent name: "*" EOFExample output:
✓ AccessPolicy/developers createdVerify that the policy is created.
arctl get accesspoliciesExample output:
NAME DESCRIPTION developers Read-only access to all artifacts
Step 3: Verify access as the new user
Log out of the current CLI session.
arctl user logoutComplete the device authorization in your browser by using the URL and code that is shown in your CLI output. Log in with the
dev-userusername andpasswordpassword.arctl user loginInspect the JWT that is returned by Keycloak to confirm that the
developersgroup claim is in the token.arctl user info --show-tokens \ | jq -r '.access_token | split(".") | .[1] | @base64d | fromjson | {sub, email, Groups}'Example output:
{ "sub": "fbb5e028-4ef7-41de-8a6b-88365c26287d", "email": "dev-user@example.com", "Groups": [ "developers" ] }Verify the user’s identity. In your CLI output, verify that the
developersrole has a status ofconfigured. This setting means that the registry controller found a corresponding AccessPolicy for users with thedevelopersclaim value.arctl user whoamiExample output:
FIELD VALUE Subject fbb5e028-... Email dev-user@example.com Issuer http://172.18.0.15:8080/realms/agentregistry Superuser false ROLE STATUS developers configuredConfirm that the
dev-usercan list agents in the catalog.arctl get agentsExample output:
NAME TAG FRAMEWORK LANGUAGE PROVIDER MODEL myagent latest adk python gemini gemini-2.5-flashConfirm that the
dev-useruser cannot publish an agent.arctl apply -f - <<EOF apiVersion: ar.dev/v1alpha1 kind: Agent metadata: name: test-agent spec: description: Test agent language: python framework: adk modelProvider: gemini modelName: gemini-2.5-flash source: image: localhost:5001/myagent:latest EOFExample output:
✗ Agent/test-agent failed: forbidden: forbidden Error: one or more resources failed to applyConfirm that the
dev-useruser cannot create a deployment.arctl apply -f - <<EOF apiVersion: ar.dev/v1alpha1 kind: Deployment metadata: name: test-agent spec: targetRef: kind: Agent name: myagent tag: "latest" runtimeRef: kind: Runtime name: local EOFExpected output:
✗ Deployment/test-agent failed: forbidden: forbidden Error: one or more resources failed to apply
Example access policies
The following examples show common permission configurations.
Read and deploy, but not publish or delete:
cat <<EOF | arctl apply -f -
apiVersion: ar.dev/v1alpha1
kind: AccessPolicy
metadata:
name: deployers
spec:
principals:
- kind: Role
name: deployers
rules:
- actions:
- "registry:read"
- "registry:deploy"
resources:
- kind: agent
name: "*"
- kind: server
name: "*"
EOFFull access except delete:
cat <<EOF | arctl apply -f -
apiVersion: ar.dev/v1alpha1
kind: AccessPolicy
metadata:
name: publishers
spec:
principals:
- kind: Role
name: publishers
rules:
- actions:
- "registry:read"
- "registry:publish"
- "registry:edit"
- "registry:deploy"
resources:
- kind: agent
name: "*"
- kind: server
name: "*"
EOFFull access including delete:
cat <<EOF | arctl apply -f -
apiVersion: ar.dev/v1alpha1
kind: AccessPolicy
metadata:
name: admins
spec:
principals:
- kind: Role
name: admins
rules:
- actions:
- "registry:*"
resources:
- kind: agent
name: "*"
- kind: server
name: "*"
EOFScoped access to a specific agent: Use this pattern when a team owns a particular agent and needs full publish rights for it, but needs only read access to everything else.
cat <<EOF | arctl apply -f -
apiVersion: ar.dev/v1alpha1
kind: AccessPolicy
metadata:
name: myagent-owners
spec:
principals:
- kind: Role
name: myagent-team
rules:
- actions:
- "registry:read"
- "registry:publish"
- "registry:edit"
- "registry:deploy"
resources:
- kind: agent
name: myagent
- actions:
- "registry:read"
resources:
- kind: agent
name: "*"
- kind: server
name: "*"
EOFRead access to the catalog and chat invocation: Use this pattern for users who need to browse the catalog and chat with agents through the UI, but do not need to publish or deploy.
cat <<EOF | arctl apply -f -
apiVersion: ar.dev/v1alpha1
kind: AccessPolicy
metadata:
name: catalog-viewers
spec:
principals:
- kind: Role
name: viewers
rules:
- actions:
- "registry:read"
resources:
- kind: agent
name: "*"
- kind: server
name: "*"
- actions:
- "runtime:*"
resources:
- kind: agent
name: "*"
EOF