What does each telemetry event carry, and when does it fire? For someone writing a consumer.

Three events fire on the request path, and one fires per adapter when the adapter deploys a policy version. The library publishes every one and stores none. A consumer attaches a handler and writes the record in the format it chooses. Every event of one operation shares one operation_id, which the caller passes in opts or the port makes.

EventWhen
[:mediate, :decision]after each port call, whatever the verdict
[:mediate, :change]inside the write's transaction, for each single-row write to an audited schema
[:mediate, :access]after each mediated read of a protected schema returns
[:mediate, <adapter>, :policy_version]when the adapter deploys a version

Decision

The port publishes it after every authorize, check, scope, and review. review publishes one decision per subject and one for the reviewer over {type, nil}. The one measurement is duration, in microseconds.

FieldTypeMeaning
subject, subject_kind{kind, id}, atomthe subject, and :user, :non_person_entity, or :privileged
operationatomthe operation asked about
object{type, id}the id is nil for scope and review
verdict:allow, :deny, :scoped, or nilnil when the call raised
reasonatom or nilone of Mediate.Answer.reasons/0, and nil when the call raised
decider, versionmodule, string or nilthe adapter, and its policy version
envmapthe environment as the caller gave it, with now stamped from the configured clock
exceptionexception or nilwhat the call raised, when it raised
decision_idstring or nilthe Mediate.Decision id the access and change events name
time, operation_idDateTime, stringthe configured clock at the call, and the operation's identifier

A denial carries a reason. A call that raised, for a bad option, a configuration that never booted, or an adapter that raised, carries verdict: nil, reason: nil, and the exception, and the port reraises after it publishes.

Change

The seam publishes it inside the write's transaction, so a consumer that writes to the same repository joins that transaction. Only a change to a column the schema declares with fact/2 or relationship/1 makes an event. The old value is the row the caller loaded, and the library takes no lock to make it current. Measurements are empty.

FieldTypeMeaning
operation:create, :update, or :deletewhat the write did
kind:user, :group, :role, or :entitywhat audited/1 declared on the schema
target{type, id}the row that changed
changesmap of field to {old, new}the declared columns that changed
actor, actor_kind{kind, id}, atomthe subject whose decision allowed the write, or the library under an exemption
decision_idstring or nilthe decision the write ran under, nil under an exemption
time, operation_idDateTime, stringthe configured clock at the write, and the operation's identifier
schemamodulethe Ecto schema

A bulk write and an upsert on an audited schema raise instead of publishing. The Mediate.Repo moduledoc has the reasons.

Access

The seam publishes it once per mediated read, after the read returns, at the one site every query verb passes through. An exempt read, a read through the owner-role repo, and a raw query publish nothing. The one measurement is count.

FieldTypeMeaning
object_typeatomthe protected schema's object type
schema, repomodule, modulethe Ecto schema and the repo the call went through
call{name, arity}the repo function
activity:query or :read:query for all, all_by, stream, and aggregate, and :read for the rest
idslistthe primary keys of the root structs the read returned, in order
countintegerthe length of ids
shape:rows, :value, or :stream:value for a scalar, boolean, map, or tuple
subject, subject_kind{kind, id}, atomfrom the decision the read ran under
decision_idstringthe decision's id
time, operation_idDateTime, stringthe configured clock after the read, and the operation's identifier

A stream publishes once, when the seam builds it, with shape: :stream and ids: []. The rows it yields get no event. A value-shaped result carries ids: []. The event names the root schema the decision was for, and a joined schema appears nowhere in it.

Policy version

Each adapter publishes one event when it deploys a version. The metadata is %{version: %Mediate.PolicyVersion{}}, and the struct's moduledoc has the fields.

FieldTypeMeaning
adaptermodulethe adapter that deployed it
versionstringthe commit, migration number, or model id, as the adapter's README says
content_hashstringa hash of the policy text
contentstring or nilthe policy text, where it is under caps[:policy_content_bytes]
pointerstring or nilwhere the text lives, where it is over the cap
author, approvalstringwho wrote it, and what approved it
atDateTimethe configured clock at deployment

What a consumer must not expect

  • No payload carries an OCSF class, category, or severity identifier. Those belong to the consumer. Example.Infrastructure.Siem shows one mapping.
  • No payload carries a value the rule read from the world. A decision says the question and the answer, never the subject's employment or the object's visibility.
  • No event says a consumer stored it, a handler stayed attached, the write committed, or the old value was current.
  • A policy version over the cap carries a pointer and no text.