# [Usage](../../../usage.md) > Caching

The framework caches component closures, queries and validation contexts automatically — you normally do nothing. This page explains what is cached, the security checks on every read, and the only two things you may need to do: run the prune command and pick a persistent cache driver.

## How it works

- **Buffered writes.** Code stages values with `CacheBuffer::put($key, $value, $ttl)` (default TTL 1 day). Nothing hits the store immediately; on `terminating` the provider calls `CacheBuffer::flush()`, which groups staged values by equivalent TTL and persists them with `Cache::putMany()`. One batch per TTL, one request.
- **What is cached.** Action closures (`action_{uuid}` + `action_end_message_{uuid}`), progress bars (`progress_bar_{uuid}`), load/update-change payloads, full components (`kfw_component_{uuid}`), form validation + context (`kfw_form_validation_{uuid}`, `kfw_form_context_{uuid}`), table state (`table_query_{uuid}`, `table_buttons_{uuid}`, `table_custom_filters_*`, `table_query_filtered_{uuid}`, `table_accordable_schema_{uuid}`, `column_group_description_{uuid}`), dynamic views (`kfw_view_{uuid}`) and the assets version (`kfwAssetsVersion`). Query builders are not stored raw — they go through query serialization first; closures/views/dates are normalized, `gzencode`d (level scales with size) and restored on read.
- **Pruning.** Expired `database` / `file` entries are removed with `php artisan cache:clear-expired` (see [Clear expired cache](clear_expired_cache.md)). Other drivers expire by themselves.

## Checks and security on every read

- **HMAC signature.** Every value is wrapped as `{'__kfw_cache_signed': 1, 'payload': ..., 'signature': hash_hmac('sha256', $key."\0".serialize($payload), app.key)}` (base64 app keys decoded first). `get()/getMany()/getObject()` recompute with `hash_equals()` — tampered or cross-key payloads return the default/`null`.
- **Ownership.** `putOwner($key)` binds an entry to the generating user id (`0` = public, first write wins). `checkOwner($key)` then requires the same authenticated user for private entries; missing/legacy owner keys are treated as public. Forms, components and table queries all check ownership before use (`Forbidden` / re-render when it fails).
- **Safe unserialize.** Payloads are `gzdecode`d then unserialized only if every `O:/C:` class is trusted: PHP-internal, inside `base_path()`, inside `/vendor/`, or inside the package itself. Anything else throws and the read falls back to the default.
- **Context binding.** Form validation additionally checks `kfw_form_uuid` presence, cache hit and `kfw_form_context` equality (`Form not found` / `Form context mismatch` otherwise), so a payload stolen from another form is useless.

## How to use

- **Nothing to call** for components, forms, tables, calendars, views and actions — caching, signing and ownership happen inside `SchemaHelper` / `TableHelper` / `FormHelper` / validation middleware.
- **Do:** use a persistent `CACHE_STORE` (`database` or `file` for pruning support, `redis` also fine) in production, keep `APP_KEY` stable (it signs the cache), and schedule `cache:clear-expired` if you use `database`/`file`.
- **Do not** write or read `kfw_*`, `table_*`, `action_*`, `progress_bar_*` keys manually, and do not reuse a `uuid` across users — generate components with `::make()` and let the framework own the keys.

## See also

- [Usage](../../../usage.md)
- [Clear expired cache](clear_expired_cache.md) — prune command + schedule
- [Security](security.md) — throttles, MIME list, role checks around cached validation
- [Table](../../components/visuals/table.md) / [Form](../../components/forms/form.md) — features powered by this cache
