Skip to main content

wyrd_runtime/
audit.rs

1//! Audit envelope construction shell and the audit staging seam.
2
3use wyrd_spec::DataTenantId;
4use wyrd_spec::vala::api::AuditEvent;
5use wyrd_spec::{
6    actor::Actor, redaction::RedactionPolicy, request_id::RequestId, trace::TraceContext,
7};
8
9use crate::outbox::{Outbox, OutboxSink};
10
11/// Non-blocking handoff every audited surface stages its decisions through.
12///
13/// Permissions block; audits do not. A surface evaluates its permission,
14/// enforces the verdict, and stages the decision here, which only queues it:
15/// no request waits for, or is refused by, the audit write. The server
16/// implements it with its one Scribe outbox; lower crates hold it as
17/// `dyn AuditStage` so they never name the server's sink.
18pub trait AuditStage: Send + Sync {
19    /// Queues `event` for `tenant` and returns at once; never fails.
20    fn stage(&self, tenant: DataTenantId, event: AuditEvent);
21}
22
23impl<S> AuditStage for Outbox<S>
24where
25    S: OutboxSink,
26    S::Item: From<AuditEvent>,
27{
28    /// Stages `event` on this outbox through its ordinary non-blocking
29    /// [`Outbox::stage`].
30    fn stage(&self, tenant: DataTenantId, event: AuditEvent) {
31        Self::stage(self, tenant, event);
32    }
33}
34
35/// Inputs required to prepare an audit envelope.
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct AuditEnvelopeSeed {
38    /// Operation name, such as `card.register`.
39    pub operation: String,
40    /// Optional resource subject for the operation.
41    pub subject: Option<String>,
42    /// Request ID associated with the operation.
43    pub request_id: RequestId,
44    /// Optional W3C trace context for the operation.
45    pub trace_context: Option<TraceContext>,
46    /// Actor that initiated the operation.
47    pub actor: Actor,
48    /// Redaction policy applied before durable audit emission.
49    pub redaction_policy: RedactionPolicy,
50}
51
52/// Prepared audit envelope draft.
53///
54/// This crate does not emit or persist audit events. The draft is the typed
55/// handoff point that downstream server code consumes when assembling request,
56/// trace, actor, and redaction context.
57#[derive(Debug, Clone, PartialEq, Eq)]
58pub struct AuditEnvelopeDraft {
59    /// Operation name, such as `card.register`.
60    pub operation: String,
61    /// Optional resource subject for the operation.
62    pub subject: Option<String>,
63    /// Request ID associated with the operation.
64    pub request_id: RequestId,
65    /// Optional W3C trace context for the operation.
66    pub trace_context: Option<TraceContext>,
67    /// Actor that initiated the operation.
68    pub actor: Actor,
69    /// Redaction policy applied before durable audit emission.
70    pub redaction_policy: RedactionPolicy,
71}
72
73/// Build an audit envelope draft from a seed.
74#[must_use]
75pub fn prepare(seed: AuditEnvelopeSeed) -> AuditEnvelopeDraft {
76    AuditEnvelopeDraft {
77        operation: seed.operation,
78        subject: seed.subject,
79        request_id: seed.request_id,
80        trace_context: seed.trace_context,
81        actor: seed.actor,
82        redaction_policy: seed.redaction_policy,
83    }
84}