Entry Extensions
Chronicle supports custom entry extensions that run before the built-in pipeline stages.
This is the main package-level extension point when you need to enrich, validate, or apply policy to entries before Chronicle canonicalizes and hashes them.
Registration
Register extensions in config:
'extensions' => [
App\Chronicle\ResolveTenantContext::class,
],
Or register them at runtime:
use Chronicle\Facades\Chronicle;
Chronicle::extendEntry(App\Chronicle\ResolveTenantContext::class);
Extension contract
An extension must implement Chronicle\Contracts\EntryExtension:
use Chronicle\Contracts\EntryExtension;
use Chronicle\Entry\PendingEntry;
use Chronicle\Extensions\ExtensionStage;
final class ResolveTenantContext implements EntryExtension
{
public function stage(): ExtensionStage
{
return ExtensionStage::RESOLVE_CONTEXT;
}
public function process(PendingEntry $entry): PendingEntry
{
$context = $entry->attribute('context', []);
$context['tenant_id'] = tenant('id');
$entry->setAttribute('context', $context);
return $entry;
}
}
Available stages
Chronicle currently exposes these ordered stages:
VALIDATERESOLVE_CONTEXTPOLICYPROCESS
All extensions run before Chronicle’s built-in canonicalization, payload hashing, chain hashing, and persistence processors.
Priority ordering
If you need deterministic ordering within a stage, also implement Chronicle\Contracts\PrioritizedEntryExtension:
use Chronicle\Contracts\PrioritizedEntryExtension;
public function priority(): int
{
return -10;
}
Lower priority values execute first.
The registry sorts extensions by:
- stage
- priority
- class name
- registration order
What extensions should do
Good use cases:
- inject deterministic context
- enforce domain-specific validation
- apply auditing policy
- attach metadata derived from request or tenant state
Avoid non-deterministic mutations that would make reproducibility harder, especially anything that depends on unstable ambient state without recording it into the entry itself.
Working with PendingEntry
Extensions receive a PendingEntry, which gives access to the mutable pre-persistence attributes and payload.
Typical operations:
attribute()setAttribute()payload()setPayload()
When not to use extensions
Do not use extensions when a simple explicit call in application code is clearer. Chronicle favors explicit audit intent, so extensions should be reserved for cross-cutting concerns rather than routine domain logging.