Use case

Cooldowns that clean up after themselves

Set a key with a lifetime and its existence answers the question. When the window passes it disappears on its own, which is the whole trick.

The problem

An agent that can act repeatedly will, given the chance, act repeatedly. It retries a failing API three times a second. It alerts you about the same broken thing on every pass. It re-deploys because the last deploy has not finished yet.

What you want is a window: once this happens, do not let it happen again for an hour. Expressing that without somewhere to remember means asking the model to store a timestamp, work out the current time, subtract one from the other, and compare against a threshold. Every step there is a chance to be wrong, and the failure is silent.

The pattern

Write a key with a ttl in seconds. Nothing else. The key existing means you are inside the window; the key being gone means you are not:

# Am I clear to act?
get_value("deploys:staging:cooldown")
→ { exists: false }

# Yes. Act, then hold the window for an hour.
set_value("deploys:staging:cooldown", "held", ttl: 3600)
→ { created: true, expires_at: "2026-08-15T10:00:00+00:00" }

Anything that runs in the next hour gets a different answer:

get_value("deploys:staging:cooldown")
→ { exists: true, value: "held", expires_at: "2026-08-15T10:00:00+00:00" }

No subtraction. No date parsing. The question "has an hour passed" becomes the question "does this key exist", which a model gets right every time.

Tell your agent

Before alerting me about a failing check, use Junkt to
get_value the key "alerts:health-check:cooldown".

If it exists, stay quiet — I have already been told
recently. Do not alert.

If it does not exist, send the alert, then set_value that
key to "sent" with a ttl of 3600.

Backoff

Growing the window after repeated failures needs the retry count as well, which is a counter. The two work together:

increment("jobs:sync:failures")
→ { value: 3 }

# back off for 2^3 minutes
set_value("jobs:sync:cooldown", "backing off", ttl: 480)

On success, delete the failure counter so the next problem starts from a short window again. This is one of the few places where you do want the model doing arithmetic, and it is simple enough to be safe — but pin the exponent table in the prompt rather than leaving the schedule to its judgement.

Cooldowns are not locks

Worth being straight about this. The check-then-set sequence above has a gap between the read and the write, so two runs starting at the same instant can both see an empty key and both proceed. For rate limiting, alerting and backoff — where the cost of an occasional double is low — that is fine, and this pattern is the right amount of machinery.

If a double would be genuinely expensive, do not use this. Use increment instead, which is atomic, and treat a returned value of 1 as "you have the lock":

increment("deploys:staging:lock")
→ { value: 1, created: true }   # you got it

increment("deploys:staging:lock")
→ { value: 2 }                  # someone else is in there

Set a TTL on the key first so a crashed holder does not block the lock forever, and delete it when you are done.

TTL and your key budget

Expired keys stop counting toward your 25 the moment they elapse, before the hourly prune physically removes them. Short-lived state is effectively free, which is why 25 keys goes further than it sounds when most of them clean up after themselves.

Worth knowing

  • Re-setting rewrites the TTL. Calling set_value again on a live cooldown extends it from now rather than topping it up.
  • Setting without a ttl clears it. A later write that forgets the ttl turns a self-cleaning key into a permanent one.
  • The minimum is one second. Anything shorter is rejected.
  • Put the window in the key name if it helpsalerts:health-check:cooldown-1h — so a glance at list_keys tells you what you set up months ago.

State that tidies itself away

Set a ttl and stop thinking about it. Free tier, 25 keys, no card — and expired keys do not count against them.

Ask an AI

Junkt is built for agents, so ask one

Pick a question and where to ask it. Each link opens a fresh chat with the question already written, including a nudge to give you a straight answer rather than a sales pitch.