KAL state
Durable per-script memory: STATE GET/PUT/APPEND/INCREMENT/DECREMENT/DELETE, size limits and rollback semantics.
KAL scripts get durable state β a small key/value store that survives restarts and is private to your script on your server. It is the memory that turns one-shot reactions into systems: counters, queues, per-user records.
The six statements#
STATE GET "key" INTO target; // read (also usable as an expression)
STATE PUT "key" = <value>; // write any JSON-able value
STATE APPEND <value> TO "key"; // push onto a stored array
STATE INCREMENT "key" BY <n>; // numeric += (missing key counts as 0)
STATE DECREMENT "key" BY <n>; // numeric -=
STATE DELETE "key"; // remove
Note the word order on APPEND: value first, then TO the key.
A working counter#
KAL VERSION 2.0;
AUTOMATION member_counter {
CONFIG {
log_channel: CHANNEL [
label: "Log channel",
description: "Where membership changes are posted",
required: true
];
}
WHEN EVENT member_join {
STATE INCREMENT "joins_total" BY 1;
DECLARE total = STATE GET "joins_total";
SEND MESSAGE "Member #" + ("" + total) + " joined!" TO config.log_channel;
}
}
Storing structures#
State values are JSON-able: strings, numbers, booleans, arrays and maps.
KAL VERSION 2.0;
AUTOMATION queue_demo {
WHEN COMMAND queue_add (item AS STRING) {
STATE APPEND item TO "queue";
DECLARE queue = STATE GET "queue";
REPLY "Queue now holds " + ("" + LENGTH(queue)) + " items.";
}
WHEN COMMAND queue_reset {
STATE DELETE "queue";
REPLY "Queue cleared.";
}
}
Limits#
| Rule | Value |
|---|---|
| Value size | 2,048 bytes each (JSON-serialised) |
| Keys per script | 100 |
| Total per script | 512 KiB |
| Key length | β€ 256 characters |
Exceeding a limit raises a state error at runtime; the write is refused, not truncated.
Scoping and lifetime#
- State is scoped per server and per script β two scripts never see each other's keys, and the same script on two servers has separate state.
- State survives restarts, toggles and version updates.
- Deleting or uninstalling the script deletes its state.
- Kharat itself writes two convenience keys after successful sends:
last_message_idandlast_channel_id.
Rollback semantics#
Each run's state writes are committed as a unit. If a run crashes β budget exceeded, unhandled error β the state changes of that run are rolled back. State is never left half-written by a failed run.
Reading with defaults#
A missing key reads as null. Combine with EXISTS/NOT_EXISTS or the increment shortcut:
IF NOT_EXISTS(STATE GET "seen:" + member.id) THEN {
STATE PUT "seen:" + member.id = NOW();
}