cache
Since v2.40.0 · Top-level declaration
Grammar
cache <Name> {
backend: redis | in_process # optional — tier (default in_process)
ttl: <duration> # optional — TTL; REQUIRED if non-pure
key: [<param>, ...] # optional — key subset (default: all params)
default: <true|false> # optional — auto-cover pure tools (max one)
apply_to_effects: [<Effect>, ...] # optional — effects covered (default [pure])
invalidate_on: [<ChannelRef>, ...] # optional — emit on any → flush this cache
}
cache declares a named, referenced result-memoization
policy. It is the runtime dual of a proof the compiler already
has: a tool whose effects: row is exactly pure is, by the
language's own definition, safe to reuse for any input that
repeats — so caching it is not a heuristic, it is a consequence of
the purity proof (cacheability_is_a_type_not_a_convention).
A cache is referenced by a tool (tool.cache: <Name>) or a
retrieve (retrieve <Store> { cache: <Name> }), exactly the way
axonendpoint.cors: references a cors policy.
Surface
cache is a top-level declaration.
type WeatherEvent { city: String }
channel WeatherUpdated { message: WeatherEvent }
# A project-wide default: every `pure` tool is cached, no per-tool
# annotation, because the type system already proved it safe.
cache DefaultPure { default: true }
# A widened, ttl-bounded, invalidation-wired cache for a NON-pure
# result (accepting bounded staleness).
cache WeatherCache {
backend: redis
ttl: 5m
apply_to_effects: [pure, network]
invalidate_on: [WeatherUpdated]
}
tool Fingerprint { provider: http effects: <pure> parameters: { input: String } }
tool Weather {
provider: http
effects: <network>
parameters: { city: String }
cache: WeatherCache
}
Fields
backend: (optional)
in_process (the default single-replica tier — a bounded LRU with
per-entry TTL) or redis (the multi-replica tier). A closed
catalog (axon-T866 on anything else). Which backend / how much
capacity is a per-tenant, deployment-level knob;
eligibility to cache is the source-level property.
ttl: (optional — but REQUIRED for a non-pure cache)
A duration literal (10s, 5m, 1h). A pure-only cache may
omit it (caching a proven-deterministic result forever is sound). A
cache that widens apply_to_effects: beyond [pure] — or is used
by a non-pure tool or any retrieve — must carry a finite
ttl: (axon-T865): a non-deterministic result may not be cached
forever. The TTL is precisely the "how stale am I willing to be"
bound.
key: (optional)
The subset of the covered tool's parameters: whose bound
values form the cache key. Omitted ⇒ all bound parameters (the
zero-friction default). Use it when one bound arg (e.g. a
request_id) must not affect the key. The full key is
content-addressed: a hash of (tenant ‖ cache ‖ tool ‖ tool-declaration-fingerprint ‖ output_type ‖ selected params), so
a redeploy that changes the tool can never serve a stale result,
and a tenant can never read another tenant's entry.
default: (optional)
true ⇒ this cache auto-covers every eligible tool in the
module without a per-tool cache:. At most one per module
(axon-T863). A tool is eligible when its effects are a subset of
the default's apply_to_effects (with [pure] as the implicit
default set). A tool opts out with cache: none.
apply_to_effects: (optional)
The effect classes this cache is willing to memoise. Omitted ⇒
[pure] (the only provably-safe default). Any member beyond pure
is a widening (axon-W013 names each non-pure tool a
default: true cache ends up covering) and forces a finite ttl:.
invalidate_on: (optional)
Channel names (axon-T864 if undeclared). An emit on any listed
channel flushes this cache's namespace — reusing the v1.6.0 pub/sub,
not a second mechanism.
Budget interaction
A cache hit short-circuits before the underlying tool/retrieve
executes, so a budget { rate:/max: } quota (v2.28.0) is never
decremented by a hit. Hits and misses are audited
distinctly (cache:hit / cache:miss) from budget:consumed, so
an adopter sees real cost savings.
What this primitive is NOT
- Not
memory { decay: }.memoryis semantic/conversational recall state (what the agent remembers about a conversation);cacheis result-memoization of a deterministic or explicitly-accepted-stale tool/retrieve call. An agent can use both, for different reasons. - Not a way to cache a non-deterministic result forever. The
compiler forbids it (
axon-T865) — the same purity proof that authorises caching-forever is exactly what it withholds from a non-pureresult.
See also
axon://primitives/tool— declares theeffects: pureproof this primitive derives cacheability from, and thecache:reference.axon://primitives/channel— theinvalidate_on:mechanism.axon://primitives/memory— conversational recall state, a different layer.