C3 AI Documentation Home

Grant Privileged Actions at Runtime

You can use the PrivilegedAction Type to grant an @action(authz='always') action permission to invoke other actions that its caller would not otherwise be authorized to invoke, without rebuilding the platform.

See Define Permissions to learn more about access controls in the C3 Agentic AI Platform.

Baseline grants compared to runtime grants

The platform ships an immutable, code-reviewed baseline of privileged-action grants that is compiled into the platform and cannot be changed at runtime.

Use runtime grants to extend that baseline from a running cluster. Runtime grants are stored in the cluster-managed PrivilegedAction.Config and suit changes a cluster administrator needs to make without a platform rebuild. Runtime grants are subject to the exact same checks as the baseline: the granting action must be @action(authz='always'), and the recorded source-code fingerprint must match the action's implementation at authorization time.

This topic shows you how to use the PrivilegedAction Type to grant privileged actions at runtime.

Grant privileged actions using PrivilegedAction

The following functions allow you to grant and manage privileged actions at runtime:

To grant a privileged action at runtime, complete the following steps:

  1. Compute the source-code fingerprint of the granting action.
  2. Enable runtime grants.
  3. Add the grant.

All of the mutating functions are protected by the cluster-admin action group, so only a cluster administrator (or root) can call them. This ensures an application cannot use them to escalate its own privileges.

Compute the source-code fingerprint

Runtime grants are keyed by a source-code fingerprint that pins the grant to a specific version of the granting action's source. The fingerprint is required and is verified at authorization time to detect tampering.

Run the following code in C3 AI Console to compute the fingerprint of the granting action:

JavaScript
PrivilegedAction.fingerprint("MyApp.PrivilegedAction", "doThing")

This returns the fingerprint string you pass to PrivilegedAction#add and PrivilegedAction#remove.

Enable and add a runtime grant

Run the following code in C3 AI Console as a cluster administrator to enable runtime grants and add a grant at the desired configuration override:

JavaScript
var fingerprint = PrivilegedAction.fingerprint("MyApp.PrivilegedAction", "doThing");
PrivilegedAction.enableDynamicGrants("CLUSTER");
PrivilegedAction.add(
    PrivilegedAction.Grant.make({
        privilegedAction: "MyApp.PrivilegedAction#doThing",
        actionFingerprint: fingerprint,
        grantedChildren: ["MyApp.Config#getSecret"]
    }),
    "CLUSTER"
)

The PrivilegedAction#add function takes a PrivilegedAction.Grant and the configuration override:

  • grant.privilegedAction: The granting action, written as "<Type>#<action>".
  • grant.actionFingerprint: The source-code fingerprint of the granting action, computed with PrivilegedAction#fingerprint. This is required.
  • grant.grantedChildren: The child actions to grant, each written as "<Type>#<childAction>" or a bare "<Type>" to allow any action on that Type.
  • override: The configuration override level to persist the grant at, such as CLUSTER.

You can grant multiple child actions in a single call:

JavaScript
PrivilegedAction.add(
    PrivilegedAction.Grant.make({
        privilegedAction: "MyApp.PrivilegedAction#doThing",
        actionFingerprint: fingerprint,
        grantedChildren: ["MyApp.Config#getSecret", "MyApp.Config#setConfigValue"]
    }),
    "CLUSTER"
)

Demonstrate a runtime grant

The following example demonstrates granting MyApp.PrivilegedAction#doThing permission to invoke MyApp.Config#getSecret.

  1. Run the following code in C3 AI Console as a cluster administrator to enable runtime grants and add the grant:
JavaScript
var fingerprint = PrivilegedAction.fingerprint("MyApp.PrivilegedAction", "doThing");
PrivilegedAction.enableDynamicGrants("CLUSTER");
PrivilegedAction.add(
    PrivilegedAction.Grant.make({
        privilegedAction: "MyApp.PrivilegedAction#doThing",
        actionFingerprint: fingerprint,
        grantedChildren: ["MyApp.Config#getSecret"]
    }),
    "CLUSTER"
)

After you add the grant, MyApp.PrivilegedAction#doThing can invoke MyApp.Config#getSecret, even for callers who are not otherwise authorized to call MyApp.Config#getSecret directly.

  1. Run the following code in C3 AI Console as a cluster administrator to revoke the grant:
JavaScript
PrivilegedAction.remove(
    PrivilegedAction.Grant.make({
        privilegedAction: "MyApp.PrivilegedAction#doThing",
        actionFingerprint: PrivilegedAction.fingerprint("MyApp.PrivilegedAction", "doThing")
    }),
    "CLUSTER"
)

After you remove the grant, MyApp.PrivilegedAction#doThing can no longer invoke MyApp.Config#getSecret. Only grants added with PrivilegedAction#add can be removed; the seeded baseline is immutable. When removing a grant you only need to identify it by privilegedAction and actionFingerprint; grantedChildren is ignored.

Disable runtime grants

After you add runtime grants, you can turn them all off at once (for example, during an incident) without removing them.

Run the following code in C3 AI Console as a cluster administrator to disable runtime grants:

JavaScript
PrivilegedAction.disableDynamicGrants("CLUSTER")

While disabled, the system ignores all runtime grants and only the immutable baseline applies. Because the grants are preserved, re-enabling restores the previously added grants:

JavaScript
PrivilegedAction.enableDynamicGrants("CLUSTER")

Configuration overrides

Runtime grants are stored in PrivilegedAction.Config, so they follow the standard Config framework override precedence: a value set at a more-specific override, such as ENV, wins over one set at a less-specific override, such as CLUSTER or ROOT. This applies to both the grants and the enable switch. For example, setting the enable switch to disabled at ENV masks an enabled state set at CLUSTER or ROOT, turning off runtime grants for that environment until you clear the more-specific override.

Alternative to using PrivilegedAction

Privileged actions apply only to @action(authz='always') actions that must invoke actions on their caller's behalf. To control which users can access a Type's actions or objects instead, see Add Access Controls to a Role using Data Permissions and Add Access Controls to a Type Using AclEnabled.

Was this page helpful?