Skip to content

Cedar Policies

Griptape Nodes permission templates use Cedar, Amazon's open-source authorization language. In the Permission Editor, the Permission Builder turns your choices into Cedar. Use this page when you need to write a Raw Cedar template or audit the Cedar generated by the builder.

Choose Raw Cedar only when the builder's capability catalog cannot express the rule you need.

Policy inputs

Cedar makes each decision from four inputs: (principal, action, resource, context). Griptape Nodes maps each authorization checkpoint and its license data into these Cedar inputs:

Part What it holds Use it for
principal A fixed placeholder, User::"<anonymous>". Nothing. Leave it unconstrained.
action The authorization checkpoint, e.g. Action::"LoadLibrary". Naming which operation a rule covers.
resource The checkpoint's resolved library, node type, project, model, or codec. Matching a specific thing, or a fact about it.
context Active project, engine, library, and license facts. Scoping a rule to one project, or to a license type.

Combining decisions

Cedar follows two rules when it combines templates:

  1. At least one matching permit must allow the operation.
  2. A matching forbid overrides every permit, including permits from other templates.

The builder produces these Cedar patterns:

  • Exploration (Allow all) starts with permit(principal, action, resource);, then adds forbid statements for exceptions.
  • Production (Deny All) omits the catch-all permit and adds permit statements for allowed operations.

Default deny and hard blocks

Cedar denies an operation unless at least one statement permits it. A policy set containing only forbid statements therefore denies every operation, not just the ones those statements match. To build a blocklist, pair the forbids with a catch-all permit:

permit(principal, action, resource);

forbid(principal, action, resource)
when { resource has lifecycle_stage && resource.lifecycle_stage == "LABS" };

Omitting a permit is not a hard block. A permit from another template can allow the operation. Use forbid when no permit should override the denial. Cedar evaluates all templates attached to a license key as one policy set, so the permit and forbid can live in separate templates.

Checkpoints

Each action below identifies an operation that the engine gates. The result of a denial depends on the checkpoint.

Action Fires when Resource A denial looks like
Action::"LoadLibrary" A library loads past its metadata stage. Library The library is marked unusable, with the reason on its error icon.
Action::"InstantiateNode" A node is created. NodeType An Error Proxy node in place of the real one. Denied node types are also listed on the library ahead of time.
Action::"LoadProject" A project template is read. Project The project fails to load.
Action::"ActivateProject" A project becomes the current one. Project The switch fails and the current project stays put.
Action::"OfferModel" A model picker is built. Model The model is filtered out of the picker.
Action::"InvokeModel" A node calls a model. Model The invocation fails.
Action::"ReadVideoCodec" Video is about to be read, and when a picker is built. VideoCodec The read is refused, or the codec is filtered out.
Action::"WriteVideoCodec" Video is about to be written, and when a picker is built. VideoCodec The write is refused, or the codec is filtered out.

Resource attributes

Unless a table says otherwise, each resource attribute is optional. Use has before reading an optional attribute. See Guard optional attributes for the required pattern.

Library

Attribute Type Present when
id string Always. The library name.
lifecycle_stage string The library declares a stage.

NodeType

Attribute Type Present when
id string Always. The node type name.
executes_arbitrary_code bool Always. true when the node's library declaration marks arbitrary Python execution.
lifecycle_stage string The node declares a stage, or inherits one from its library.
model_ids set The node declares model usage.
provider_ids set The node declares model or provider usage.
model_families set The node's declared models resolve to families in the library's model catalog.

Project

Attribute Type Present when
id string Always. The project's opaque id, a GUID for projects created in the editor.
name string The template has loaded far enough to know its name.

Use name for a human-readable rule and id when the match must be exact. Prefer id in a permit. The project name becomes available only after the template has loaded, and a permit that does not match results in a denial.

Model

Attribute Type Present when
id string Always. The stable catalog model key.
provider_id string The key resolves in the model catalog.
model_families set The resolved model declares a family. Carries that one family.
To match a provider rather than a single model, see Entity hierarchy.

VideoCodec

Attribute Type Present when
id string Always. The codec name as probed, e.g. h264, hevc.
container_format string The container was known, e.g. mp4, mov.

Entity hierarchy

ModelProvider

Each Model sits under the provider that offers it, so in can cover all of a provider's models without naming them individually.

For example:

forbid(principal, action, resource)
unless { resource in ModelProvider::"anthropic" };

Provider ids are the provider keys in a library's model_catalog, such as anthropic or ollama.

Context facts

Guard context facts

All context facts are optional. Griptape Nodes includes the facts it can resolve and omits the rest, so check each one with has before reading it.

Guard the record, then read through it:

when {
  context has loaded_libraries &&
  context.loaded_libraries.names.contains("My Library")
}
Fact Type Notes
active_project.id string Opaque project id used as the engine registry key.
active_project.name string Its display name, when the template has loaded. Needs its own has guard.
engine.id string The active engine's id.
loaded_libraries.names set Names of the libraries loaded so far.
license_id string The license key's id.
org_id string The organization the license belongs to.
license_type string Either "headless" or "interactive".

Project scope

The builder adds this condition to every statement in a Project-scoped template, using the linked project:

when { context has active_project && context.active_project.id == "<project id>" }

Griptape Nodes evaluates the policy for each project in the active project's chain. This has two consequences.

Include ancestor projects. When a project inherits from a parent, the policy runs for each project in the chain: first the active project, then each ancestor. Every run must allow the operation. A forbid scoped to a parent therefore blocks its children, and a project allow-list must include every project in the chain.

For example, suppose Shot 42 inherits from Studio Defaults. Allowing libraries in Shot 42 requires a permit for both project ids:

permit(principal, action == Action::"LoadLibrary", resource)
when {
  context has active_project &&
  context.active_project.id == "<Shot 42 id>"
};

permit(principal, action == Action::"LoadLibrary", resource)
when {
  context has active_project &&
  context.active_project.id == "<Studio Defaults id>"
};

With only the first permit, the Shot 42 check succeeds, but the Studio Defaults check is denied. A forbid matching Studio Defaults also denies the operation in Shot 42.

Account for the default project. Unless otherwise set, the engine boots into the default project, whose id is <system-defaults>. You do not need to explicitly permit this id. Explicit forbid rules still apply.

Denial annotations

Cedar identifies the rule that denied an operation, but it does not explain what the user is missing. Griptape Nodes uses three annotations to add that information. All three are Griptape conventions, and Cedar ignores them during policy evaluation.

Annotation Purpose
@id("<slug>") Stable name for the rule. Shown in the denial instead of a positional fallback.
@capability("<name>") What the user lacks, e.g. arbitrary-code-execution.
@advice("<text>") What to do about it. This is the sentence the user reads.
@id("nodes/no-arbitrary-code")
@capability("arbitrary-code-execution")
@advice("Nodes that run arbitrary code are not available on this license. Ask your studio admin to enable them.")
forbid(principal, action == Action::"InstantiateNode", resource)
when { resource has executes_arbitrary_code && resource.executes_arbitrary_code };

Add these annotations to every forbid you write. Without @advice, the user sees a policy id but no instruction for resolving the denial.

The value of @capability is free-form, and Cedar does not validate it. Consider reusing the names generated by the builder to keep denials consistent across templates:

  • library
  • library-lifecycle
  • node-lifecycle
  • arbitrary-code-execution
  • project
  • model
  • model-provider
  • model-family
  • video-codec

Examples

Each example below is a complete Raw Cedar policy.

Block arbitrary-code nodes

This example includes the catch-all permit, making it a complete blocklist template:

permit(principal, action, resource);

@id("nodes/no-arbitrary-code")
@capability("arbitrary-code-execution")
@advice("Nodes that run arbitrary code are not available on this license.")
forbid(principal, action == Action::"InstantiateNode", resource)
when { resource has executes_arbitrary_code && resource.executes_arbitrary_code };

Block unstable libraries and nodes

Use separate statements for libraries and nodes. Each denial then names the capability the user lacks.

permit(principal, action, resource);

@id("lifecycle/no-experimental-libraries")
@capability("library-lifecycle")
@advice("Experimental libraries are blocked on this license. Ask your studio admin which libraries are approved.")
forbid(principal, action == Action::"LoadLibrary", resource)
when {
  resource has lifecycle_stage &&
  (resource.lifecycle_stage == "LABS" || resource.lifecycle_stage == "ALPHA")
};

@id("lifecycle/no-experimental-nodes")
@capability("node-lifecycle")
@advice("Experimental nodes are blocked on this license. Use a STABLE or BETA alternative.")
forbid(principal, action == Action::"InstantiateNode", resource)
when {
  resource has lifecycle_stage &&
  (resource.lifecycle_stage == "LABS" || resource.lifecycle_stage == "ALPHA")
};

Allow two model providers

The unless clause inverts the match, blocking every provider except the two named below. The in operator returns false, rather than an error, when a resource has no matching ancestor, so it does not need a has guard.

Name both model checkpoints when restricting models. OfferModel removes denied models from pickers. InvokeModel blocks nodes already bound to them.

permit(principal, action, resource);

@id("models/approved-providers")
@capability("model-provider")
@advice("Only Anthropic and OpenAI models are approved on this license.")
forbid(principal, action in [Action::"OfferModel", Action::"InvokeModel"], resource)
unless { resource in ModelProvider::"anthropic" || resource in ModelProvider::"openai" };

Block a model family

Because model_families is a set, use contains to match a value:

permit(principal, action, resource);

@id("models/no-claude-3")
@capability("model-family")
@advice("Claude 3 models are retired on this license. Use Claude 4.")
forbid(principal, action in [Action::"OfferModel", Action::"InvokeModel"], resource)
when { resource has model_families && resource.model_families.contains("Claude 3") };

Allow specific projects

The engine gates project loading and activation separately, so the rule names both checkpoints.

permit(principal, action, resource);

@id("projects/approved")
@capability("project")
@advice("This project is not on your license. Ask your studio admin for access.")
forbid(principal, action in [Action::"LoadProject", Action::"ActivateProject"], resource)
unless {
  resource has id &&
  (resource.id == "8f2c1a04-9d3e-4b7a-9f10-2c5d6e8a1b33" || resource.id == "c07b5e91-4a2d-4f88-bd63-1e9f7a205c48")
};

Restrict codec writes

This statement names only the write checkpoint.

permit(principal, action, resource);

@id("video/no-prores-writes")
@capability("video-codec")
@advice("Writing ProRes is not permitted on this license. Render to H.264 instead.")
forbid(principal, action == Action::"WriteVideoCodec", resource)
when { resource has id && resource.id == "prores" };

Scope a rule to one project

Cedar combines multiple when clauses with AND, so you can keep the project scope separate from the resource condition:

permit(principal, action, resource);

@id("show-a/no-experimental-nodes")
@capability("node-lifecycle")
@advice("Show A is locked to stable nodes.")
forbid(principal, action == Action::"InstantiateNode", resource)
when {
  context has active_project &&
  context.active_project has name &&
  context.active_project.name == "Show A"
}
when { resource has lifecycle_stage && resource.lifecycle_stage == "LABS" };

Restrict headless licenses

permit(principal, action, resource);

@id("headless/no-model-invocation")
@capability("model")
@advice("Headless licenses cannot call models on this plan.")
forbid(principal, action == Action::"InvokeModel", resource)
when { context has license_type && context.license_type == "headless" };

Gotchas

Guard optional attributes

Reading an attribute that is missing produces an error. Cedar treats a condition containing an error as unsatisfied, while the engine denies any operation it cannot evaluate cleanly. An unguarded read can therefore make a forbid miss resources without the attribute and block valid operations that do not carry that fact.

// Wrong. Denies every node type with no declared lifecycle stage.
forbid(principal, action == Action::"InstantiateNode", resource)
when { resource.lifecycle_stage == "LABS" };

// Right.
forbid(principal, action == Action::"InstantiateNode", resource)
when { resource has lifecycle_stage && resource.lifecycle_stage == "LABS" };

Because && short-circuits, put the guard first.

Guard nested facts at each level. context has active_project does not guarantee that the active project has a name:

when {
  context has active_project &&
  context.active_project has name &&
  context.active_project.name == "Show A"
}

Check names for typos

The engine does not validate Cedar policies against a schema. Typos such as resource.lifecycle_stge or Action::"LoadLibrry" parse successfully but match nothing, leaving you with a rule that appears not to work.

Permit every required checkpoint

If a Production template permits only LoadLibrary, library loading works, but default deny blocks other checkpointed operations. Name every operation the workflow needs, or attach another template that permits them.

// Permits only library loading.
permit(principal, action == Action::"LoadLibrary", resource);

// Broad permit covering every current checkpoint.
permit(principal, action in [
  Action::"LoadLibrary",
  Action::"InstantiateNode",
  Action::"LoadProject",
  Action::"ActivateProject",
  Action::"OfferModel",
  Action::"InvokeModel",
  Action::"ReadVideoCodec",
  Action::"WriteVideoCodec"
], resource);