top of page

Filling the Temporal Gap in Cedar with Dogwood

  • 6 days ago
  • 5 min read

Filling the Temporal Gap in Cedar with Dogwood

Dogwood

Written by Minhyeok Cha



목차



Introduction


The real trouble with AI agents tends to begin after they reach production.

During development, you tend to move on once each tool call works as expected. In a live service, however, issues start to surface that are easy to overlook during development: an agent invokes a tool without going through an approval step, or calls a delete tool before a file has been backed up.

Existing policy languages typically evaluate each request in isolation. This works well for authorizing individual agent actions, but it leaves a gap in the security design of production agents—particularly when authorization depends on approvals or actions that occurred earlier.

To address this gap, AWS recently released an open-source project called Dogwood.


AgentCore Policy supports Dogwood, but the reference interpreter on GitHub is not intended for production enforcement.

Dogwood is a temporal policy language that extends AWS's Cedar policy language, allowing authorization decisions to account for not only the current request but also the sequence of events that preceded it.



What Is Cedar?


Cedar is an open-source authorization policy language created by AWS. In the context of AgentCore Policy, three characteristics are particularly relevant.

1. It's fast

By excluding constructs such as loops and stateful operations, Cedar keeps policy evaluation fast and predictable, allowing authorization decisions to be made on individual agent calls with low evaluation latency.

2. It's readable

With AgentCore Policy, you can describe a policy in natural language and use an LLM to translate it into Cedar.

For example:


// Allow a bulk discount only when a Platinum-tier customer orders 50 or more units
permit (
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"ApplyBulkDiscount",
  resource
)
when {
  principal.hasTag("customer_tier") &&
  principal.getTag("customer_tier") == "Platinum" &&
  context.input.orderQuantity >= 50
}
unless {
  context.input.productTypes.containsAny(["limited_edition", "seasonal_specials"])
};

3. It's analyzable through automated reasoning

Cedar supports automated reasoning about policies.

For instance, if you accidentally write conditions that cannot be true at the same time—such as requiring the customer tier to be both Gold and Platinum—the resulting policy would never permit a request.

Cedar's analysis capabilities can identify these kinds of contradictions and logical errors before they cause unexpected authorization behavior.



Cedar's Limitation


Spend some time with Cedar and you'll run into one important limitation.

Cedar evaluates each request independently of previous requests, using only the information available at that point in time. This stateless model is what makes authorization decisions predictable: given the same request and context, Cedar produces the same decision regardless of what happened beforehand.

The catch is that the point where agents actually cause problems is often not a single action, but a sequence of actions.

  • Has this transfer already been approved?

  • How many transfers have been made in the past hour?

Rules like these run up against the limits of Cedar's point-in-time model, which evaluates the current request without maintaining a history of previous actions.



Dogwood


Dogwood is not a replacement for Cedar. It extends Cedar into an area Cedar was not originally designed to address: temporal policies governing sequences of actions.

Two characteristics are particularly important:

  • Syntactic backward compatibility: Every syntactically valid Cedar policy is also a valid Dogwood policy. In other words, existing Cedar policy sets can be used as-is, without requiring a migration.

  • Preserved semantics: Like Cedar, Dogwood retains default deny and the rule that forbid overrides permit. The guarantees that auditing and enforcement depend on therefore remain intact.

What Dogwood introduces is the when temporal { ... } clause, which allows a policy to reference not only the current request but also a record of past events.

Examples

Combining Temporal and Cedar Conditions

When a policy needs both a condition on the current request and a condition based on past history, Dogwood treats temporal as an expression that can be embedded directly within Cedar's existing when clause.


For example:


permit ( principal, action == AgentCore::Action::"SellShares", resource )
when {
    context.input.shares <= 100
    && temporal {
        formerly within 1h AgentCore::Action::"ApproveSale"::response{
            input.stock:     context.input.stock,
            input.shares:    context.input.shares,
            output.approved: true
        }
    }
};

Count-Based Limits

count_within counts the number of events, while count_distinct_within counts the number of distinct values for a specified field. This makes it possible to distinguish between repeatedly sending transfers to the same recipient and increasing the number of different recipients.


// Forbid more than 5 transfers within 1 hour, regardless of amount
forbid ( principal, action == AgentCore::Action::"Transfer", resource )
when temporal {
    count_within(1h, AgentCore::Action::"Transfer"::request{ input.amount: _ }) > 5
};

// Forbid more than 3 distinct recipients within 1 hour
forbid ( principal, action == AgentCore::Action::"Transfer", resource )
when temporal {
  count_distinct_within(u, 1h, AgentCore::Action::"Transfer"::request{ input.user: u }) > 3
};

Cedar vs. Dogwood

Aspect

Cedar

Dogwood

Nature

AgentCore Policy's base authorization language

A temporal policy language that extends Cedar

Basis of decision

Current request only (point-in-time)

Current request + history of past events (trace)

Signature clause

when { ... }

when temporal { ... } (can also be nested inside Cedar's when)

What it can express

Allow/deny decisions for a single action

Sequences, ordering, counts, cumulative values, and rate limits

Statefulness

Stateless

Tracks an event log as state (stateful)

Automated reasoning (Cedar Analysis)

Supported—mathematically verifies policy conflicts, contradictions, and logical errors

Not yet supported (temporal conditions fall outside automated reasoning)



What Dogwood Cannot Express


1. When It Isn't a Rule About a Specific Action

If no action, field, or condition is specified, what you have is effectively a code of conduct or a training guideline—not an authorization rule.

A policy engine is a tool for deciding whether to permit an agent call.

2. When It Calls for an Action Rather Than a Verdict

A policy engine can make two decisions: allow or deny.

A rule such as "reject any submission containing a Social Security number" can be explicitly expressed. However, "remove the number and continue saving" is a different matter.

3. When It Exceeds the Language's Expressive Range

Dogwood's time-related features can handle specific points in time, time offsets, and time differences, but it does not provide an accessor for determining the day of the week or concepts such as a holiday calendar.

4. When It Goes Beyond Session Scope

Dogwood evaluates event flows on a per-session basis.

When a temporal operator looks up what happened in the past, the scope of that lookup is limited to events tagged with the current session ID. Events from other sessions are not included in the lookup.



Closing Thoughts


Having worked with AgentCore-based billing chatbots and cost-related agents, I find the gap that Dogwood fills particularly useful.

  • Billing agents: "Block refunds once the cumulative daily refund limit is exceeded." Logic that previously had to be managed through counters in application code or Lambda can be moved into a policy at the Gateway layer.

  • Support case and ticket-handling agents: Rate limits such as "prevent repeated handling of the same customer within a short period" can be applied through Gateway configuration without modifying the agent code.

  • Approval workflows: Prerequisite policies such as "no execution without prior approval" can be expressed as auditable policies rather than in application code.

Dogwood is not yet at the production-enforcement stage. When I tried it through the console, I found that natural-language-to-policy conversion still had some limitations, so I ended up writing Cedar statements directly through the CLI and console.

 
 
bottom of page