Pkcs11ex.Audit.Storage behaviour (pkcs11ex_audit v0.1.0)

Copy Markdown View Source

Pluggable storage backend for Pkcs11ex.Audit.

Audit entries can be persisted anywhere — Postgres, SQLite, S3 with Object Lock, append-only files, etc. The library ships Pkcs11ex.Audit.Storage.InMemory for tests and dev; production deployments implement this behaviour against their own durable store.

Concurrency contract

Implementations MUST guarantee that append/2 is atomic with respect to head/1 reads — Pkcs11ex.Audit.append/3 reads head to compute the next sequence + prev_hash, then calls append. If two processes race that pattern, the storage must serialize them (via a process, database transaction, etc.) to keep sequence numbers contiguous.

The InMemory adapter uses a single Agent to serialize naturally.

Summary

Callbacks

Iterate the log in order (seq=1 → head). Implementations may stream; callers that walk the whole log (e.g., Pkcs11ex.Audit.verify/1) treat the return as Enumerable.t/0.

Append entry as the new head.

Look up an entry by its :seq.

Return the most recently appended entry.

Types

storage_handle()

@type storage_handle() :: term()

Callbacks

all(storage_handle)

@callback all(storage_handle()) :: Enumerable.t()

Iterate the log in order (seq=1 → head). Implementations may stream; callers that walk the whole log (e.g., Pkcs11ex.Audit.verify/1) treat the return as Enumerable.t/0.

append(storage_handle, t)

@callback append(storage_handle(), Pkcs11ex.Audit.Entry.t()) :: :ok | {:error, term()}

Append entry as the new head.

at(storage_handle, pos_integer)

@callback at(storage_handle(), pos_integer()) ::
  {:ok, Pkcs11ex.Audit.Entry.t()} | {:error, :not_found}

Look up an entry by its :seq.

head(storage_handle)

@callback head(storage_handle()) :: {:ok, Pkcs11ex.Audit.Entry.t()} | {:error, :empty}

Return the most recently appended entry.