Documentation
¶
Overview ¶
Package chronicle provides a composable, immutable audit trail for Go.
Chronicle records tamper-proof audit events linked by SHA-256 hash chains, supports tenant-scoped queries, GDPR crypto-erasure, compliance reporting (SOC2, HIPAA, EU AI Act), and pluggable sinks and reporters.
It is designed as a library — import it into your application, configure a store backend, and start recording events. No separate daemon required.
c, err := chronicle.New(
chronicle.WithStore(memStore),
chronicle.WithBatchSize(50),
)
Chronicle integrates with the Forge ecosystem via forge.Scope for automatic tenant scoping and the Emitter interface for dependency injection.
Index ¶
- Variables
- type BackfillInput
- type BackfillRecovered
- type BackfillRefused
- type BackfillRejected
- type BackfillReport
- type Chronicle
- func (c *Chronicle) Aggregate(ctx context.Context, q *audit.AggregateQuery) (*audit.AggregateResult, error)
- func (c *Chronicle) BackfillRetention(ctx context.Context, in *BackfillInput) (*BackfillReport, error)
- func (c *Chronicle) ByUser(ctx context.Context, userID string, opts audit.TimeRange) (*audit.QueryResult, error)
- func (c *Chronicle) Critical(ctx context.Context, action, resource, resourceID string) *EventBuilder
- func (c *Chronicle) Health(ctx context.Context) error
- func (c *Chronicle) Info(ctx context.Context, action, resource, resourceID string) *EventBuilder
- func (c *Chronicle) Query(ctx context.Context, q *audit.Query) (*audit.QueryResult, error)
- func (c *Chronicle) Record(ctx context.Context, event *audit.Event) error
- func (c *Chronicle) RecordOnce(ctx context.Context, request acceptance.Request) (*acceptance.Receipt, error)
- func (c *Chronicle) RecordRetention(ctx context.Context, ref audit.RetentionRef, events []*audit.Event) ([]id.ID, error)
- func (c *Chronicle) Store() Storer
- func (c *Chronicle) VerifyChain(ctx context.Context, input *verify.Input) (*verify.Report, error)
- func (c *Chronicle) VerifyEvent(ctx context.Context, eventID id.ID) (bool, error)
- func (c *Chronicle) Warning(ctx context.Context, action, resource, resourceID string) *EventBuilder
- type Config
- type Emitter
- type Entity
- type EventBuilder
- func (b *EventBuilder) AppID(aid string) *EventBuilder
- func (b *EventBuilder) Category(cat string) *EventBuilder
- func (b *EventBuilder) Event() *audit.Event
- func (b *EventBuilder) IP(ip string) *EventBuilder
- func (b *EventBuilder) Meta(key string, value any) *EventBuilder
- func (b *EventBuilder) Outcome(o string) *EventBuilder
- func (b *EventBuilder) Reason(r string) *EventBuilder
- func (b *EventBuilder) Record() error
- func (b *EventBuilder) RequestID(rid string) *EventBuilder
- func (b *EventBuilder) SessionID(sid string) *EventBuilder
- func (b *EventBuilder) SubjectID(sid string) *EventBuilder
- func (b *EventBuilder) TenantID(tid string) *EventBuilder
- func (b *EventBuilder) UserAgent(ua string) *EventBuilder
- func (b *EventBuilder) UserID(uid string) *EventBuilder
- type EventSealer
- type ID
- type Option
- func WithBatchSize(n int) Option
- func WithCheckpointSigner(s checkpoint.Signer) Option
- func WithCryptoErasure(enabled bool) Option
- func WithDigestScheme(s hash.Scheme) Option
- func WithFlushInterval(d time.Duration) Option
- func WithKeyProvider(p keys.Provider) Option
- func WithLogger(l log.Logger) Option
- func WithMetrics(m gometrics.Metrics) Option
- func WithSealer(s EventSealer) Option
- func WithStore(s Storer) Option
- type Prefix
- type StoredReader
- type Storer
- type StreamInfo
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNoStore is returned when no store has been configured. ErrNoStore = errors.New("chronicle: no store configured") // ErrEventNotFound is returned when an audit event cannot be found. ErrEventNotFound = errors.New("chronicle: event not found") // ErrStreamNotFound is returned when a hash chain stream cannot be found. ErrStreamNotFound = errors.New("chronicle: stream not found") // ErrChainBroken is returned when hash chain verification detects tampering. ErrChainBroken = errors.New("chronicle: hash chain broken") // ErrSubjectNotFound is returned when a GDPR subject cannot be found. ErrSubjectNotFound = errors.New("chronicle: subject not found") // ErrPolicyNotFound is returned when a retention policy cannot be found. ErrPolicyNotFound = errors.New("chronicle: retention policy not found") // ErrReportNotFound is returned when a compliance report cannot be found. ErrReportNotFound = errors.New("chronicle: report not found") // ErrErasureNotFound is returned when an erasure record cannot be found. ErrErasureNotFound = errors.New("chronicle: erasure not found") // ErrErasureKeyNotFound is returned when a per-subject encryption key cannot be found. ErrErasureKeyNotFound = errors.New("chronicle: erasure key not found") // ErrInvalidQuery is returned when a query has invalid parameters. ErrInvalidQuery = errors.New("chronicle: invalid query") ErrUnauthorized = errors.New("chronicle: unauthorized") // ErrStoreClosed is returned when an operation is attempted on a closed store. ErrStoreClosed = errors.New("chronicle: store closed") // ErrMigrationFailed is returned when database migrations fail. ErrMigrationFailed = errors.New("chronicle: migration failed") // without a sealer to perform the encryption. // // Accepting the flag without one would store subject data as plaintext while // the operator believed a key deletion made it unrecoverable. Refusing to // start is the safer failure: a compliance guarantee that silently does not // hold is worse than one that is explicitly unavailable. ErrCryptoErasureUnavailable = errors.New( "chronicle: crypto-erasure is enabled but no sealer was provided; " + "pass chronicle.WithSealer(crypto.NewSealer(keyStore)) so subject " + "payloads are encrypted, or disable enable_crypto_erasure", ) // without a key provider to supply the key. // // The reasoning matches ErrCryptoErasureUnavailable: an operator who set // digest: hmac believes their chain cannot be recomputed from the stored // rows alone. Writing unkeyed digests under that belief is worse than // refusing to start. ErrHMACKeyUnavailable = errors.New( "chronicle: digest scheme hmac is selected but no key provider was given; " + "pass chronicle.WithKeyProvider(keys.NewFileProvider(path)) so digests " + "are keyed, or leave the digest scheme unset", ) // ErrSchemeWeakeningRefused is returned when a stream is pinned to a // stronger digest scheme than the one this process is configured to write. // // Chronicle moves a stream's pin up on its own, because that is the only way // turning HMAC on takes effect on streams that already exist. It will not // move one down. A pin that drops on its own is indistinguishable from an // attacker lowering it, and once it has dropped every event written under // the weaker scheme verifies clean, so the operator sees green while the // guarantee they configured is gone. Refusing the write is loud, reversible, // and leaves the existing chain intact. ErrSchemeWeakeningRefused = errors.New( "chronicle: this stream is pinned to a stronger digest scheme than this " + "process writes; restore the stronger scheme (tamper_evidence.digest " + "and its key source, or chronicle.WithDigestScheme plus " + "chronicle.WithKeyProvider), or if the weaker scheme is genuinely " + "intended, record that decision and lower the stream's pin " + "deliberately through the store", ) // ErrRetentionWithheld is returned when retention was asked to remove // events that no longer verify. // // Purging an event destroys the evidence of anything done to it, and a // retention record would then vouch for its hashes as if it had been // intact. So an event whose digest fails is kept, with its tampering still // on the record for verification to report, and the rest of the batch is // purged as usual. The same holds for an event that cannot be verified at // all, such as one digested under a key that can no longer be resolved. ErrRetentionWithheld = errors.New( "chronicle: retention withheld events that do not verify; they are kept as evidence", ) // ErrBackfillUnkeyed is returned when a retention backfill is asked to // run against a stream that is not pinned to chronicle/v5, or by a // process that does not write chronicle/v5. // // A backfill turns gaps into retained sequences on the strength of // archived copies of the purged events. That is only evidence when // nobody but the chain writer could have produced those copies. Under an // unkeyed scheme anyone with write access to the store can compute a // valid digest, fabricate an archive to match, and have the backfill // excuse a deletion. The result would be an attestation dressed up as a // proof, so it is refused outright. ErrBackfillUnkeyed = errors.New( "chronicle: retention backfill needs a keyed chronicle/v5 chain; under an unkeyed " + "scheme an archived copy proves nothing, because anyone who can write the store " + "can fabricate one", ) )
Sentinel errors for Chronicle operations.
Functions ¶
This section is empty.
Types ¶
type BackfillInput ¶
type BackfillInput struct {
// AppID and TenantID select the stream. AppID is required.
AppID string
TenantID string
// Archive is where the policy that purged the events archived them.
Archive sink.ArchiveReader
// DryRun reports what would be recovered and writes nothing.
DryRun bool
}
BackfillInput says which stream to backfill and where its purged events were archived.
type BackfillRecovered ¶
type BackfillRecovered struct {
Seq uint64 `json:"seq"`
Hash string `json:"hash"`
Location string `json:"location"`
}
BackfillRecovered is one gap an archived copy accounts for.
type BackfillRefused ¶
BackfillRefused is one gap the backfill left alone.
type BackfillRejected ¶
type BackfillRejected struct {
Seq uint64 `json:"seq"`
Location string `json:"location"`
Reason string `json:"reason"`
}
BackfillRejected is one archived copy that failed a check.
type BackfillReport ¶
type BackfillReport struct {
StreamID id.ID `json:"stream_id"`
DryRun bool `json:"dry_run,omitempty"`
// Gaps are the sequences verification could not account for before the
// backfill ran.
Gaps []uint64 `json:"gaps"`
// Recovered are the gaps an archived copy proved, in sequence order. On a
// dry run nothing was written for them.
Recovered []BackfillRecovered `json:"recovered,omitempty"`
// Refused are the gaps left as gaps, each with the reason.
Refused []BackfillRefused `json:"refused,omitempty"`
// Rejected are archived copies that failed a check. A copy that claims to
// be a purged event of this stream and does not verify under its key is
// worth looking at on its own account: nothing the chain writer produced
// looks like that.
Rejected []BackfillRejected `json:"rejected,omitempty"`
// Records are the sequences of the retention records written.
Records []uint64 `json:"records,omitempty"`
// After is the stream's verification once the records were written. Nil
// on a dry run or when nothing was recovered.
After *verify.Report `json:"after,omitempty"`
}
BackfillReport is what a backfill found and did.
type Chronicle ¶
type Chronicle struct {
// contains filtered or unexported fields
}
Chronicle is the root audit trail engine. It orchestrates event recording, hash chain computation, batching, sink fan-out, plugin hooks, and query execution.
func (*Chronicle) Aggregate ¶
func (c *Chronicle) Aggregate(ctx context.Context, q *audit.AggregateQuery) (*audit.AggregateResult, error)
Aggregate returns grouped counts/stats, scoped to the caller's tenant.
func (*Chronicle) BackfillRetention ¶
func (c *Chronicle) BackfillRetention(ctx context.Context, in *BackfillInput) (*BackfillReport, error)
BackfillRetention writes retention records for gaps left by purges that ran before retention records existed, where the purged events survive in an archive and can be proven to be the events that were removed.
It is an operator tool, run once per stream after an upgrade. Nothing calls it automatically, and there is deliberately no way to tell it to accept a gap without evidence: a tool that marks gaps as authorised on someone's say so cannot be told apart from one covering up a deletion.
A gap is recovered only when all of this holds:
- The stream is pinned to chronicle/v5 and this process writes it. Anything else returns ErrBackfillUnkeyed before reading the archive.
- The archive holds a copy of the event at that sequence, for this stream and scope, that is not itself a retention record.
- The copy's digest recomputes under the stream's pin with the HMAC key it names, and it claims chronicle/v5. That is what shows the chain writer produced it and nobody has edited it since. Copies below the pin under an unkeyed scheme are refused, whatever the rest of the stream is.
- Every sequence in the same run of consecutive gaps has such a copy, and the run links end to end: the first copy's PrevHash is the hash of the event before the run, each copy's PrevHash is the one before's Hash, and the last copy's Hash is the PrevHash of the event after the run. The neighbours can be present events that verify cleanly, sequences an existing record already accounts for, genesis, or the stream head.
A run is recovered whole or not at all, so the verifier can always link across what a backfilled record lists. One sequence nothing can prove leaves its whole run as gaps, and a sequence deleted by anything other than retention stays exactly what it is.
The records are written the same way RecordRetention writes them, marked with the archive's name, and verification reports those sequences as retained with RetainedRange.Backfill set. Verification never re-reads the archive. What it relies on afterwards is the record, which is as hard to forge as any other v5 event.
What this proves, and what it does not: a recovered sequence held an event the chain writer wrote, whose complete content is in the archive. It does not prove a retention policy removed it rather than someone who copied the row into the archive first. Either way nothing was hidden, since the content is still there to read, and that is the property a gap exists to protect. Keep the archive.
func (*Chronicle) ByUser ¶
func (c *Chronicle) ByUser(ctx context.Context, userID string, opts audit.TimeRange) (*audit.QueryResult, error)
ByUser returns events for a specific user within a time range.
func (*Chronicle) Critical ¶
func (c *Chronicle) Critical(ctx context.Context, action, resource, resourceID string) *EventBuilder
Critical creates an EventBuilder for a critical-severity event.
func (*Chronicle) Info ¶
func (c *Chronicle) Info(ctx context.Context, action, resource, resourceID string) *EventBuilder
Info creates an EventBuilder for an info-severity event.
func (*Chronicle) Record ¶
Record persists an audit event through the Chronicle pipeline. Pipeline: apply scope → assign ID/timestamp → validate → resolve stream → compute hash chain → store.Append → update stream head
Each call is one "record event" span, with the store's own query spans under it, and one observation on the record metrics when WithMetrics was given.
func (*Chronicle) RecordOnce ¶
func (c *Chronicle) RecordOnce(ctx context.Context, request acceptance.Request) (*acceptance.Receipt, error)
RecordOnce recovers or atomically accepts one source delivery. Your host must supply trusted producer/installation and authorized explicit event scope. Recovery does not seal, resolve keys, or change a stream. Unsupported stores fail explicitly. Record retains its separate, non-idempotent contract.
Each call is one "accept event" span and one observation on the record metrics, labelled record_once. A recovered receipt counts as ok.
func (*Chronicle) RecordRetention ¶
func (c *Chronicle) RecordRetention(ctx context.Context, ref audit.RetentionRef, events []*audit.Event) ([]id.ID, error)
RecordRetention writes a retention record into the chain of every stream the given events belong to, and returns the IDs of the events it recorded. Only those may be purged.
This is what keeps retention from breaking verification. A policy removes events scattered through a stream, one category at a time, and without a record a verifier cannot tell those holes from an attacker's. The record lists each removed event's sequence and the hashes either side of it, so the chain still links end to end across the hole, and it is itself a chained event: forging one takes the same key forging any event does, and removing one leaves a gap of its own.
The record is written before anything is deleted. A crash between the two leaves a record naming events that are still present, which verifies cleanly, rather than deletions nothing explains.
Every event is verified in its stored form first, under its stream's pin. An event that fails is withheld: it stays in the store, its tampering stays visible, and the returned error wraps ErrRetentionWithheld. Purging it would erase the evidence and have the record vouch for hashes that no longer match their content. Events already gone are skipped silently, since there is nothing left to purge.
On error, the returned IDs are still exactly the events whose record was durably appended, and purging them is still correct.
func (*Chronicle) VerifyChain ¶
VerifyChain verifies the integrity of a hash chain for a stream.
A caller that supplies AppID (the scope GetStreamByScope needs) gets HeadSeq/HeadHash and Pin filled in from the actual stream row for whichever of those it left entirely unset, so it gets head anchoring and pin-aware downgrade detection without having to resolve the stream itself. Three things keep that fill from doing the wrong thing silently:
- It never mutates the caller's *Input. It fills a local copy and only verifies against that copy, so a caller reusing one Input across a loop of streams does not have it progressively overwritten by whichever stream happened to resolve first.
- It only applies when the resolved stream's ID matches the StreamID being verified. A caller whose StreamID does not belong to the AppID/TenantID scope it also supplied must not get that scope's unrelated stream silently anchoring its tail -- a false "tampered" from an audit tool is an expensive thing to be wrong about.
- HeadSeq and HeadHash fill as a pair, only when both are unset. A caller who already supplied one half (a HeadHash sourced out of band, from a checkpoint or an external notary) keeps both, rather than having the value it specifically wanted cross-checked silently replaced.
The StreamID fills from the resolved stream too, whenever the caller left it nil. That is the call every doc page shows -- scope in, nothing else -- and without it the range query ran against a nil ID and came back empty, which the head fill then turned into a confident report naming every sequence in the stream as a gap. Verifying a healthy five-event stream reported five deleted events.
A caller with only a StreamID in hand -- no scope, no stream row -- gets the old, narrower behaviour: no head anchoring, no downgrade detection, matching Input.Pin's documented contract for callers with no stream in hand.
func (*Chronicle) VerifyEvent ¶
VerifyEvent recomputes and verifies a single event's hash.
type Config ¶
type Config struct {
// BatchSize is the maximum number of events to accumulate before flushing.
//
// Not applied by [Chronicle.Record], which writes each event synchronously:
// linking an event into the hash chain needs the previous event's hash, so
// appends are inherently sequential. The batcher package implements batching
// for callers that drive it directly.
BatchSize int
// FlushInterval is the maximum time to wait before flushing accumulated events.
//
// Not applied by [Chronicle.Record]. See BatchSize.
FlushInterval time.Duration
// ShutdownTimeout is the maximum time to wait for graceful shutdown.
ShutdownTimeout time.Duration
// EnableCryptoErasure encrypts each subject's personal payload under a
// per-subject key, so destroying the key makes it irrecoverable.
//
// Requires a sealer from [WithSealer]; see [ErrCryptoErasureUnavailable].
EnableCryptoErasure bool
// RetentionCheckInterval is how often the retention enforcer runs.
RetentionCheckInterval time.Duration
// DigestScheme selects how each event's digest is computed. The default,
// hash.SchemePlainV4, is unkeyed and reproducible by anyone who can read
// the store, so it detects corruption rather than tampering.
//
// hash.SchemeHMACV5 requires a key provider; see [ErrHMACKeyUnavailable].
//
// hash.SchemePlain and hash.SchemeHMAC are verify-only. Setting either one
// fails at New rather than at the first event, because their content
// encoding is ambiguous and a digest written under them can be forged
// without the key.
DigestScheme hash.Scheme
}
Config holds the configuration for a Chronicle instance.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns a Config with sensible defaults.
type Emitter ¶
type Emitter interface {
Record(ctx context.Context, event *audit.Event) error
Info(ctx context.Context, action, resource, resourceID string) *EventBuilder
Warning(ctx context.Context, action, resource, resourceID string) *EventBuilder
Critical(ctx context.Context, action, resource, resourceID string) *EventBuilder
}
Emitter is the interface Forge extensions use to emit audit events. Extensions receive this via DI — they don't import Chronicle directly.
type Entity ¶
type Entity struct {
CreatedAt time.Time `json:"created_at" bun:"created_at,notnull,default:current_timestamp"`
UpdatedAt time.Time `json:"updated_at" bun:"updated_at,notnull,default:current_timestamp"`
}
Entity is the base type embedded by all Chronicle entities. It provides common timestamp fields.
type EventBuilder ¶
type EventBuilder struct {
// contains filtered or unexported fields
}
EventBuilder provides a fluent API for constructing and recording audit events.
func (*EventBuilder) AppID ¶
func (b *EventBuilder) AppID(aid string) *EventBuilder
AppID sets the application scope explicitly.
func (*EventBuilder) Category ¶
func (b *EventBuilder) Category(cat string) *EventBuilder
Category sets the event category.
func (*EventBuilder) Event ¶
func (b *EventBuilder) Event() *audit.Event
Event returns the underlying event without recording it. Useful for inspection/testing.
func (*EventBuilder) IP ¶
func (b *EventBuilder) IP(ip string) *EventBuilder
IP sets the client address the action came from.
func (*EventBuilder) Meta ¶
func (b *EventBuilder) Meta(key string, value any) *EventBuilder
Meta adds a key-value pair to the event metadata.
func (*EventBuilder) Outcome ¶
func (b *EventBuilder) Outcome(o string) *EventBuilder
Outcome sets the event outcome.
func (*EventBuilder) Reason ¶
func (b *EventBuilder) Reason(r string) *EventBuilder
Reason sets the event reason.
func (*EventBuilder) Record ¶
func (b *EventBuilder) Record() error
Record persists the built event through the Chronicle pipeline.
func (*EventBuilder) RequestID ¶
func (b *EventBuilder) RequestID(rid string) *EventBuilder
RequestID sets the HTTP request correlation id.
func (*EventBuilder) SessionID ¶
func (b *EventBuilder) SessionID(sid string) *EventBuilder
SessionID sets the authenticated session the action ran under.
func (*EventBuilder) SubjectID ¶
func (b *EventBuilder) SubjectID(sid string) *EventBuilder
SubjectID sets the GDPR subject ID for crypto-erasure.
func (*EventBuilder) TenantID ¶
func (b *EventBuilder) TenantID(tid string) *EventBuilder
TenantID sets the tenant scope explicitly.
func (*EventBuilder) UserAgent ¶
func (b *EventBuilder) UserAgent(ua string) *EventBuilder
UserAgent sets the client's User-Agent string.
func (*EventBuilder) UserID ¶
func (b *EventBuilder) UserID(uid string) *EventBuilder
UserID sets the user who performed the action.
type EventSealer ¶
type EventSealer interface {
// Seal encrypts the event's personal payload in place.
Seal(event *audit.Event) error
}
EventSealer encrypts an event's personal payload before the event is hashed and stored, and is what makes crypto-erasure possible: destroying the subject's key leaves the payload unrecoverable.
crypto.Sealer implements this. It is an interface here so the root package does not import crypto, which imports this package for its error sentinels.
type Option ¶
Option configures a Chronicle instance.
func WithBatchSize ¶
WithBatchSize sets the maximum batch size before flushing.
Record writes each event synchronously, so this does not currently change how events are persisted; see Config.BatchSize.
func WithCheckpointSigner ¶
func WithCheckpointSigner(s checkpoint.Signer) Option
WithCheckpointSigner supplies the signer VerifyChain checks signed checkpoints under. Pass the same signer the Checkpointer writes with.
It is a separate option rather than something derived from WithKeyProvider because the two key sources are genuinely independent. A deployment can checkpoint a plain, unkeyed chain, which has no key provider at all; another can digest with HMAC from one keyset and sign checkpoints from a different one. Deriving the signer from the digest's provider got both of those wrong: the first never checked a checkpoint, and the second failed every verification with "key not found" because the signature had been made under a key the digest's provider had never heard of.
Leave it unset and verification behaves exactly as it did before checkpoints existed: no checkpoint is fetched, and coverage tops out at keyed.
func WithCryptoErasure ¶
WithCryptoErasure enables or disables per-subject encryption for GDPR.
Enabling it requires a sealer from WithSealer; without one, New returns ErrCryptoErasureUnavailable rather than accepting a flag that would promise an Article 17 guarantee nothing implements.
With it on, every event carrying a SubjectID has its metadata, reason and IP encrypted under a per-subject key before being hashed and stored. Destroying that key leaves the payload unrecoverable while the operational record and the hash chain stay intact. See crypto.Sealer for exactly which fields are covered.
func WithDigestScheme ¶
WithDigestScheme selects how event digests are computed.
hash.SchemeHMACV5 requires WithKeyProvider; without one, New returns ErrHMACKeyUnavailable rather than accepting a flag that promises a guarantee nothing implements. The two options may be given in either order.
hash.SchemePlain and hash.SchemeHMAC are verify-only and rejected here. They hash an ambiguous content encoding, so a digest written under either can be forged without the key; use hash.SchemePlainV4 or hash.SchemeHMACV5.
func WithFlushInterval ¶
WithFlushInterval sets the maximum time between flushes.
Record writes each event synchronously, so this does not currently change how events are persisted; see Config.BatchSize.
func WithKeyProvider ¶
WithKeyProvider supplies the key material for keyed digest schemes.
Rotation lives in the provider: retiring a key stops new digests using it while keeping old events verifiable through Provider.ByID.
func WithMetrics ¶
WithMetrics records Chronicle's metrics on m: a counter and a latency histogram for every Record and RecordOnce call. In a Forge app pass app.Metrics(), and the series land on /_/metrics as forge_chronicle_events_recorded_total and forge_chronicle_record_duration_seconds. The Forge extension does this for you.
Spans need no option: Chronicle starts them on the global tracer provider, which forge.New installs. A nil m records nothing, which is the default.
func WithSealer ¶
func WithSealer(s EventSealer) Option
WithSealer supplies the encryptor used when crypto-erasure is enabled.
Pass crypto.NewSealer(keyStore). The key store owns the per-subject keys, so its durability decides whether sealed events can be read back: losing it is indistinguishable from erasing every subject.
type StoredReader ¶
type StoredReader interface {
// GetStored returns an event exactly as persisted, without decrypting.
GetStored(ctx context.Context, eventID id.ID) (*audit.Event, error)
}
StoredReader is implemented by stores that decrypt on read, to expose the stored representation that hash verification requires.
type Storer ¶
type Storer interface {
// Audit event operations.
Append(ctx context.Context, event *audit.Event) error
AppendBatch(ctx context.Context, events []*audit.Event) error
Get(ctx context.Context, eventID id.ID) (*audit.Event, error)
Query(ctx context.Context, q *audit.Query) (*audit.QueryResult, error)
Aggregate(ctx context.Context, q *audit.AggregateQuery) (*audit.AggregateResult, error)
ByUser(ctx context.Context, userID string, opts audit.TimeRange) (*audit.QueryResult, error)
Count(ctx context.Context, q *audit.CountQuery) (int64, error)
LastSequence(ctx context.Context, streamID id.ID) (uint64, error)
LastHash(ctx context.Context, streamID id.ID) (string, error)
// Stream operations for hash chain (uses StreamInfo to avoid import cycle).
CreateStreamInfo(ctx context.Context, s *StreamInfo) error
GetStreamByScope(ctx context.Context, appID, tenantID string) (*StreamInfo, error)
UpdateStreamHead(ctx context.Context, streamID id.ID, hash string, seq uint64) error
UpdateStreamScheme(ctx context.Context, streamID id.ID, scheme string, since uint64) error
// Verification operations.
EventRange(ctx context.Context, streamID id.ID, fromSeq, toSeq uint64) ([]*audit.Event, error)
Gaps(ctx context.Context, streamID id.ID, fromSeq, toSeq uint64) ([]uint64, error)
// Lifecycle.
Migrate(ctx context.Context) error
Ping(ctx context.Context) error
Close() error
}
Storer is the minimal store interface used by Chronicle to avoid import cycles. The full store.Store interface is defined in the store package.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
_examples
|
|
|
basic
command
Example: basic usage of Chronicle audit trail.
|
Example: basic usage of Chronicle audit trail. |
|
compliance
command
Example: Chronicle compliance report generation.
|
Example: Chronicle compliance report generation. |
|
forge
command
Example: Chronicle as a Forge extension.
|
Example: Chronicle as a Forge extension. |
|
gdpr
command
Example: GDPR crypto-erasure with Chronicle.
|
Example: GDPR crypto-erasure with Chronicle. |
|
hash-chain
command
Example: Hash chain verification with Chronicle.
|
Example: Hash chain verification with Chronicle. |
|
plugins
command
Example: Chronicle plugin system.
|
Example: Chronicle plugin system. |
|
Package acceptance defines recoverable audit ingestion contracts.
|
Package acceptance defines recoverable audit ingestion contracts. |
|
Package audit defines the core audit event types, query types, and the event store interface for Chronicle.
|
Package audit defines the core audit event types, query types, and the event store interface for Chronicle. |
|
Package batcher provides batched event writing for Chronicle.
|
Package batcher provides batched event writing for Chronicle. |
|
Package checkpoint records signed statements about where a hash chain stood, so that rewriting an event after the fact becomes provable.
|
Package checkpoint records signed statements about where a hash chain stood, so that rewriting an event after the fact becomes provable. |
|
Package compliance generates audit-based compliance reports for Chronicle.
|
Package compliance generates audit-based compliance reports for Chronicle. |
|
Package crypto provides AES-256-GCM encryption for GDPR crypto-erasure.
|
Package crypto provides AES-256-GCM encryption for GDPR crypto-erasure. |
|
Package erasure defines GDPR crypto-erasure entities and store interface.
|
Package erasure defines GDPR crypto-erasure entities and store interface. |
|
erasuretest
Package erasuretest holds checks every erasure.Store implementation must pass, so each backend runs the same scenario against its own engine.
|
Package erasuretest holds checks every erasure.Store implementation must pass, so each backend runs the same scenario against its own engine. |
|
Package extension provides a Forge-compatible extension for Chronicle.
|
Package extension provides a Forge-compatible extension for Chronicle. |
|
contract
Package contract wires chronicle into the Forge dashboard's contract path.
|
Package contract wires chronicle into the Forge dashboard's contract path. |
|
Package handler provides the Forge-style HTTP admin API for the Chronicle audit trail library.
|
Package handler provides the Forge-style HTTP admin API for the Chronicle audit trail library. |
|
Package hash provides SHA-256 hash chain computation for audit events.
|
Package hash provides SHA-256 hash chain computation for audit events. |
|
Package id defines TypeID-based identity types for all Chronicle entities.
|
Package id defines TypeID-based identity types for all Chronicle entities. |
|
internal
|
|
|
acceptancetest
Package acceptancetest exercises the common reliable backend contract.
|
Package acceptancetest exercises the common reliable backend contract. |
|
metajson
Package metajson decodes stored event metadata without losing numbers.
|
Package metajson decodes stored event metadata without losing numbers. |
|
pgtest
Package pgtest confines each test that writes to a live PostgreSQL to a schema of its own.
|
Package pgtest confines each test that writes to a live PostgreSQL to a schema of its own. |
|
redistest
Package redistest keeps each test that runs against a live redis inside a key prefix of its own.
|
Package redistest keeps each test that runs against a live redis inside a key prefix of its own. |
|
retentiontest
Package retentiontest holds checks that every retention.Store backend must pass, so the five stores are held to one definition of a purge scope.
|
Package retentiontest holds checks that every retention.Store backend must pass, so the five stores are held to one definition of a purge scope. |
|
telemetry
Package telemetry is the tracing and metrics plumbing Chronicle's packages share: the tracer every span comes from, the attribute keys spans carry, how a failed span is marked, and the metric series each component records.
|
Package telemetry is the tracing and metrics plumbing Chronicle's packages share: the tracer every span comes from, the attribute keys spans carry, how a failed span is marked, and the metric series each component records. |
|
telemetrytest
Package telemetrytest records what Chronicle's telemetry emits, for tests: a span recorder installed as the global tracer provider, and a go-utils metrics sink that remembers every series registered on it.
|
Package telemetrytest records what Chronicle's telemetry emits, for tests: a span recorder installed as the global tracer provider, and a go-utils metrics sink that remembers every series registered on it. |
|
Package keys supplies the key material that keyed digests and signed checkpoints depend on.
|
Package keys supplies the key material that keyed digests and signed checkpoints depend on. |
|
Package plugin provides the extensibility system for Chronicle.
|
Package plugin provides the extensibility system for Chronicle. |
|
Package retention defines retention policy and archive entities.
|
Package retention defines retention policy and archive entities. |
|
Package scope provides context-based scope extraction for audit events and queries.
|
Package scope provides context-based scope extraction for audit events and queries. |
|
Package sink defines the Sink interface and built-in sink implementations.
|
Package sink defines the Sink interface and built-in sink implementations. |
|
Package store defines the composite store interface for Chronicle.
|
Package store defines the composite store interface for Chronicle. |
|
memory
Package memory provides an in-memory implementation of the Chronicle store.
|
Package memory provides an in-memory implementation of the Chronicle store. |
|
mongo
Package mongo implements the Chronicle store interface using grove ORM with the MongoDB driver.
|
Package mongo implements the Chronicle store interface using grove ORM with the MongoDB driver. |
|
postgres
Package postgres implements the Chronicle store interface using grove ORM.
|
Package postgres implements the Chronicle store interface using grove ORM. |
|
redis
Package redis implements the Chronicle store interface using Grove KV with the Redis driver.
|
Package redis implements the Chronicle store interface using Grove KV with the Redis driver. |
|
sealedstore
Package sealedstore decrypts sealed audit events on the way out of a store.
|
Package sealedstore decrypts sealed audit events on the way out of a store. |
|
sqlite
Package sqlite implements the Chronicle store interface using grove ORM with the SQLite driver.
|
Package sqlite implements the Chronicle store interface using grove ORM with the SQLite driver. |
|
Package stream defines the hash chain stream entity and store interface.
|
Package stream defines the hash chain stream entity and store interface. |
|
Package verify provides hash chain verification for Chronicle audit streams.
|
Package verify provides hash chain verification for Chronicle audit streams. |