Gating when a policy is active
Turnkey policies support a top-leveltime field alongside consensus and condition which answers the question When is this policy active.
Like the consensus and condition fields, the time field must evaluate to a bool. It is evaluated by comparing the particular policy’s time field against
trusted server time to see whether the trusted server timestamp falls within the window considered active (an evaluation of true) or not.
NOTE: the trusted server time is NOT a client-supplied timestamp and cannot be spoofed by the caller.
The time field (like the condition and consensus fields) is optional. When it is absent or an empty string, the policy is always active with
respect to time. When it evaluates to true, the policy is active and participates in evaluation as
usual. When it evaluates to false, the policy is skipped entirely for that request: it neither
allows nor denies, and does not participate in the outcome.
A
false time result removes the policy from consideration for that request — this is not the
same as an EFFECT_DENY. A time-gated EFFECT_DENY only denies while its time expression is
true; outside that window the deny does not apply.time.now— a keyword of typetimestampholding the trusted server time for the request. A single value is used for the entire request, so every comparison within a policy sees a consistent instant.Timestamp('<rfc3339>')— constructs atimestampfrom an RFC 3339 string. Timestamps must be UTC: the string must end inZ. Non-zero offsets (e.g.-05:00) are rejected.
timestamp values support the comparison operators (<, >, <=, >=, ==, !=), which is what
makes time-bounding possible. For the full type and function signatures, see Time
expressions in the language reference.
Time-bound policies (one-shot)
To make a policy active only during a fixed, one-time window, comparetime.now against explicit
start and end timestamps. The convention is start-inclusive, end-exclusive:
2025-01-01T00:00:00Z, becomes inactive at 2025-02-01T00:00:00Z,
and is skipped before and after.
NOTE: Given that this pattern involves comparing each activity’s timestamp (denoted by time.now) to specific timestamps,
you can define a policy’s active time in ways beyond a single start time and a single end time (like allow after timestamp, allow before timestamp, multiple specific time windows etc)
Use this pattern for one-off grants: a temporary elevated
permission, a scheduled migration window, or an expiring approval.
Active time spans (recurring)
For policies that should be active on a repeating schedule — every weekday morning, the first of every month, and so on — use theCronSpan function:
CronSpan('<cron>', '<duration>', '<tz>') -> bool
CronSpan models a schedule as a series of fires plus a duration. Each time the cron
expression fires at instant f, it opens an active window [f, f + duration). The function returns
true when time.now falls inside any such window; the union of all windows defines when the
policy is active.
Its three arguments are:
Cron expressions use a strict 5-field subset. Numeric fields, ranges (
1-5), lists (1,3,5),
and * are supported. The following are not supported: step values (*/n), macros (@daily,
@hourly), month and day names (JAN, MON), and a seconds field.
Durations are Go-style, composed of days, hours, and minutes (d, h, m) — for example 8h,
90m, or 1d12h. A seconds component is not allowed, and the total duration must be 7 days or
less.
The <tz> argument determines when fires occur and makes windows daylight-saving aware, so a
schedule pinned to local business hours stays correct across DST transitions.
Business hours, done correctly
To keep a policy active Monday–Friday from 9:00 AM to 5:00 PM Eastern, fire once at 9:00 AM on weekdays and hold each window open for 8 hours:Author business hours as a single fire plus a duration, not as an hour range.
CronSpan('0 9 * * 1-5', '8h', 'America/New_York') opens one 8-hour window per weekday. Do not
use an hour-range expression like 0 9-17 * * 1-5 to mean “9 to 5”: under the fire-plus-duration
model each fire opens its own window, so an hour range produces a separate window every hour rather
than one continuous span, and will not behave the way you expect.Overnight windows (crossing midnight)
Because a window is simply[fire, fire + duration), spans that cross midnight need no special
handling — fire in the evening and give a duration that runs into the next day:
Composability
Thetime field is an ordinary boolean expression, so you can combine multiple spans and windows
with the logical operators &&, ||, and !:
- Union (
||) — active if any span matches. Useful for “business hours or the monthly close window.” - Intersection (
&&) — active only if all sub-expressions match. Useful for bounding a recurring span to a fixed date range. - Negation (
!) — active outside a span. Useful for “any time except the nightly maintenance window.”
&&
makes the whole expression false.
Combining time with consensus and condition
Thetime field composes with the other two fields at the policy level: a policy applies only when
its consensus, condition, and time all hold. This lets you express rules like “members of
the ops team may sign transactions to the treasury address, but only during business hours”:
time field evaluates to false and the policy is skipped, so
the same signing request is no longer allowed by this policy.