Skip to content
LogoLogo

Advanced Permissioning

Centaur gives each chat context a principal in the Centaur Console. The principal controls which credentials its sandbox can use and which internal surfaces the sandbox can reach.

Use advanced permissioning when one Centaur deployment serves people or teams with different access requirements. A support channel can receive CRM access, for example, without giving the same credential to engineering channels or direct messages.

Permission Model

Credential access follows this path:

chat context -> principal -> assigned roles + direct grants -> secrets -> request rules
  • A principal is a user, channel, issue, conversation, or workflow.
  • A role is a reusable bundle of secret grants.
  • A grant connects one secret to a principal or role.
  • A secret describes a credential source and the hosts, methods, paths, and request locations where the credential may be used.

A principal receives the union of its direct grants and the grants inherited from all assigned roles. A grant does not expose the credential to the sandbox. The sandbox receives a placeholder, and iron-proxy injects the real value only when the principal has access and the outbound request matches the secret's request rules.

Sandbox capabilities are a separate layer. Each principal also controls:

CapabilityOptionsEffect
Repo cachenone, public, or allControls which repo-cache-backed workspaces and tool sources the sandbox can mount.
ObservabilityEnabled or disabledControls access to the deployment's logs and metrics surfaces.
API serverEnabled or disabledControls access from the sandbox to the api-rs control plane.

Removing a tool grant does not remove repo-cache or API access. Review both layers when designing a least-privilege role.

Principal Mapping

Centaur creates or updates the principal when a session starts. Let each target user or conversation start one session before configuring it, then select the resulting principal in Principals. This avoids manually calculating ids and preserves identity metadata from the chat ingress.

The stable principal ids follow these rules:

ContextPrincipal foreign idShared by
Slack direct messageslack-user-<team-id>-<user-id>That Slack user in that workspace
Slack channel or groupslack-channel-[<team-id>-]<channel-id>Everyone using Centaur in that channel
Discord channeldiscord-channel-<guild-id>-<channel-id>Every thread in that channel
Linear issuelinear-issue-<issue-id>Every Centaur session on that issue
Teams personal chatteams-user-<user-id>That Teams user
Teams channel or group conversationteams-conversation-<conversation-id>Everyone using Centaur in that conversation
Workflow with WORKFLOW_PRINCIPAL = Trueworkflow-<workflow-name>Runs of that workflow
Workflow with WORKFLOW_PRINCIPAL = "<foreign-id-or-oid>"The selected existing principalRuns of that workflow
Other session keythread-<session-key>That session key

Values are lowercased and converted to URL-safe slugs. The team scope is required for Slack DMs and optional for Slack channel keys. Brackets mark an optional scope, not literal characters. Display names can change when a channel is renamed, but the foreign id remains stable.

Configure the Console

The Helm chart deploys the Centaur Console and wires api-rs to it. A production infra Secret must contain the Console database, bootstrap login, API key, encryption, and signing values described in Deploying in Production. For local development, just bootstrap-secrets generates them when they are absent.

Expose the Console through console.ingress or temporarily port-forward it:

kubectl port-forward -n centaur service/centaur-centaur-console 3000:3000

Sign in, open System Settings, and choose the defaults for newly created principals:

  • Default Roles assigns selected roles only when a new principal is first created. The infra role is selected by default so new sessions can use the configured model harness.
  • Default Sandbox Capabilities sets repo-cache, observability, and API access for new principals.

Changing defaults does not rewrite existing principals. Update existing principals individually from Principals.

Two common baselines are:

BaselineDefault rolesWhen to use it
Tool access by allowlistKeep infra as a default; grant tool roles selectively.Most shared installations. Every user may run an agent, but tools differ by context.
Full deny by defaultClear all default roles; assign infra and approved tool roles to each allowed principal.Installations where even model-backed agent execution requires approval.

The infra role contains harness and platform credentials, not every tool credential. Tool-specific access normally belongs in tool-<slug> roles.

Configure the Operator CLI

Use the Console UI for individual changes. Use centaur-perms for repeatable provisioning, bulk changes, and tool-secret registration.

Set the admin API connection. For a local port-forward, the URL is http://localhost:3000. In Kubernetes, the API key is the IRON_CONTROL_INITIAL_API_KEY value from the infra Secret.

export IRON_CONTROL_URL=http://localhost:3000
export IRON_CONTROL_API_KEY="$(
  kubectl get secret -n centaur centaur-infra-env \
    -o jsonpath='{.data.IRON_CONTROL_INITIAL_API_KEY}' | openssl base64 -d -A
)"

Point the CLI at the same tool directories api-rs uses. Explicit --tools-dir values are read first, followed by the colon-separated TOOL_DIRS value. Later directories shadow earlier ones, matching overlay ordering.

export TOOL_DIRS="$PWD/tools:$HOME/centaur-overlay/tools"
cd services/api-rs
cargo run -p centaur-perms -- --help

The examples below assume the current directory is services/api-rs.

Register and Grant a Tool

The --tool flow reads the tool's pyproject.toml, registers its declared secrets, creates or updates its tool-<slug> role, grants the secrets to that role, and assigns the role to the principal.

cargo run -p centaur-perms -- \
  --tools-dir ../../tools \
  principals grant slack-user-t123-u123 \
  --tool github

Use the same secret-source policy as the deployed ironProxy.secretSource. The CLI defaults to env.

cargo run -p centaur-perms -- \
  --source-policy onepassword-connect \
  --op-vault Engineering \
  --tools-dir ../../tools \
  principals grant slack-user-t123-u123 \
  --tool github
CLI policyHelm valueSecret source
environProxy.secretSource: envEnvironment variable on the proxy
onepasswordironProxy.secretSource: onepassword1Password service account
onepassword-connectironProxy.secretSource: onepassword-connectIn-cluster 1Password Connect

Using the wrong policy registers a valid-looking secret with the wrong source, so verify this value before provisioning.

Grant Users and Channels

In the Console, open Principals, select a principal, then use:

  • Roles to assign a reusable role.
  • Direct Grants to grant one secret without creating a role.
  • Effective Grants to inspect the combined result.
  • Sandbox Access to configure repo-cache, observability, and API access.

Grant a whole tool to one Slack user:

cargo run -p centaur-perms -- \
  principals grant slack-user-t123-u123 \
  --tool github

Grant an existing role:

cargo run -p centaur-perms -- \
  principals grant slack-user-t123-u123 \
  --role tool-github

Grant one existing secret directly by OID:

cargo run -p centaur-perms -- \
  principals grant slack-user-t123-u123 \
  --secret ssr_...

Grant tools to everyone using Centaur in a Slack channel:

cargo run -p centaur-perms -- \
  principals grant slack-channel-c456 \
  --tool linear \
  --tool github

The CLI also accepts a canonical thread key and derives its principal. For a Slack DM, include both the team in the thread key and the acting user because the DM principal is scoped to that user in that workspace:

cargo run -p centaur-perms -- \
  principals grant 'slack:T123:D456:1780000000.0001' \
  --slack-user U123 \
  --tool github

Prefer selecting an existing foreign id from the Console or principals list when possible.

Enable Centaur for External Slack Channels

Slack Connect channels can include participants from other workspaces. Configure the workspace and permission controls before adding Centaur to an external channel.

Allow External Workspaces

Allowlist each external workspace by its Slack team id. Messages and Block Kit actions attributed to any other external workspace are ignored.

slackbotv2:
  externalOrgAllowlist: "T01234567,T07654321"

Restrict the Shared Infra Role

In the Console, reduce the shared infra role to the minimum harness and platform secrets every admitted principal needs. Remove tool grants from this role. Then disable automatic infra-secret synchronization so a later tool change cannot add them back:

apiRs:
  syncInfraSecrets: false

The equivalent environment variable is IRON_CONTROL_SYNC_INFRA_SECRETS=false. With synchronization disabled, manage changes to the infra role and its secrets explicitly in the Console.

Set Defaults for External Channels

Open System Settings and configure Default Roles for new principals. Select the reduced infra role plus only the other roles that every new external channel should inherit. Put tools approved for broad use in a separate role such as company-shared, then add that role to the defaults only if all admitted external channels may use those tools. Grant narrower tool roles to individual channel principals after reviewing who can participate in each channel. For a restrictive sandbox baseline, set repo-cache access to none and disable observability and API server access.

Console defaults apply only when a principal is first created. If Centaur has already received a message from an external channel, open Principals and restrict that channel principal directly. Disabling synchronization does not remove existing secrets or grants, and changing default roles does not update existing principals.

Optional: Allow Bots to Mention Centaur

Human-authored mentions work without this setting. If another bot needs to trigger Centaur, add it to slackbotv2.triggerBotAllowlist. Centaur ignores bot-authored messages that are not on this list, including messages that mention Centaur.

For an exact Slack bot id from an event's bot_id field, prefix the B... id with bot::

slackbotv2:
  triggerBotAllowlist: "bot:B01234567,bot:B07654321"

Use bot ids that start with B, not Slack app ids that start with A. A bare B... value is not accepted. Bot member ids that start with U or W are also supported without a prefix.

Build Reusable Roles

Create a custom role such as tool-support from Roles in the Console. Then attach existing secrets or register selected tool secrets with the CLI.

Grant an existing secret to the role:

cargo run -p centaur-perms -- \
  roles grant tool-support \
  --secret ssr_...

Register every secret declared by a tool and grant it to the custom role:

cargo run -p centaur-perms -- \
  --tools-dir ../../tools \
  roles grant tool-support \
  --tool github

Register only selected secrets from that tool:

cargo run -p centaur-perms -- \
  --tools-dir ../../tools \
  roles grant tool-support \
  --tool github \
  --secret-name GITHUB_TOKEN

Assign the finished role to users, channels, or workflows:

cargo run -p centaur-perms -- \
  principals grant slack-channel-c456 \
  --role tool-support

Verify Effective Access

List managed principals and inspect one principal's assigned roles, direct grants, and effective secret placeholders:

cargo run -p centaur-perms -- principals list --managed --filter slack
cargo run -p centaur-perms -- principals show slack-user-t123-u123

Inspect the role and registered secrets:

cargo run -p centaur-perms -- roles show tool-github
cargo run -p centaur-perms -- secrets list --managed --filter github

From a running sandbox, inspect the same principal's redacted live view:

centaur-console permissions

The response includes effective credential metadata and sandbox capabilities, but never returns credential values. Finish verification with one real allowed tool request and one request from a principal that should be denied.

Revoke Access

In the Console, open the principal and remove a role from Roles or revoke a secret from Direct Grants.

Revoke a tool role from a principal:

cargo run -p centaur-perms -- \
  principals revoke slack-user-t123-u123 \
  --tool github

Revoke one direct secret or a known grant:

cargo run -p centaur-perms -- \
  principals revoke slack-user-t123-u123 \
  --secret ssr_...
 
cargo run -p centaur-perms -- \
  principals revoke slack-user-t123-u123 \
  --grant-id grant_...

Revoking a role assignment leaves the role and its secrets available to other principals. Deleting a secret removes every grant that points to it. After a revocation, verify the denied path from a new turn and inspect centaur-console permissions again.

OAuth and Workflow Principals

OAuth consent creates a grantable wrapper secret for the resulting broker credential. Grant that wrapper secret to a user, channel, or role like any other secret. See OAuth Apps for registration and consent.

Workflows opt into isolated permissions with WORKFLOW_PRINCIPAL = True, which derives workflow-<workflow-name>. They can instead set WORKFLOW_PRINCIPAL to an existing principal foreign ID or prn_-prefixed OID. Agent turns can select an existing principal with ctx.agent_turn(..., principal="<foreign-id>"). Unknown foreign IDs fail instead of falling back to a broader identity. Grant workflows and agent turns only the roles or secrets they need. See Creating Workflows.

Operational Checklist

  • Keep request rules narrow. A principal grant does not replace host, method, path, header, query, or Postgres routing restrictions on the secret.
  • Prefer roles for repeatable access packages and direct grants for exceptions.
  • Review default roles and default sandbox capabilities before inviting users.
  • Remember that changes to defaults affect new principals only.
  • Test both an allowed principal and a denied principal after every policy change.
  • Revoke stale assignments instead of deleting a shared role or secret unless every consumer should lose access.