forge-telemetry
ANVIL Telemetry Contract: span collection, audit trails, and observability for the Forge SDK
ANVIL Telemetry Contract: span collection, audit trails, and observability for the Forge SDK
Package contract
| Field | Value |
|---|---|
| Language | rust |
| Source version | 0.2.0 |
| Manifest | forge-rs/crates/forge-telemetry/Cargo.toml |
| Source files | 6 |
| Evidence | Source reference; registry publication and runtime conformance are separate checks |
Import boundary
use forge_telemetry;Use a source checkout or your verified private registry. Manifest coordinates identify the package; they do not establish that a public registry release exists.
Crate boundary
The following entries are taken from src/lib.rs. Feature conditions in the exact source still apply.
pub mod audit;
pub mod collector;
pub mod contract;
pub mod error;
pub mod signed_audit;
pub mod prelude;
pub use crate::audit::{AuditEntry, AuditTrail};
pub use crate::collector::{
CompletedSpan, InMemoryCollector, InMemoryTelemetry, NoopCollector, SpanCollector,
};
pub use crate::contract::{AuditEvent, NoopTelemetry, SpanId, TelemetryContract};
pub use crate::error::TelemetryError;
pub use crate::signed_audit::{SignedAuditEntry, SignedAuditTrail};Feature flags
| Feature | Enables |
|---|---|
default | signed-audit |
signed-audit | dep:forge-identity |
Source reference
Download package reference JSON. Each original source file and generated declaration artifact has its own SHA-256 digest. Function bodies and constant values are omitted from downloads. These are source declaration inventories, not compiler-resolved rustdoc, TypeDoc, DocC, or Dokka output. Private modules can contain public declarations that are not reachable through the package boundary; consult the entry point before importing.
audit.rs
Read declaration text · 10 declaration entries
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEntry {
/// Sequential index in the trail (0-based).
pub index: usize,
/// The audit event.
pub event: AuditEvent
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditTrail {
}
pub fn new(agent_did: String) -> Self;
pub fn append(&mut self, event: AuditEvent);
pub fn entries(&self) -> &[AuditEntry];
pub fn len(&self) -> usize;
pub fn is_empty(&self) -> bool;
pub fn last(&self) -> Option<&AuditEntry>;
pub fn agent_did(&self) -> &str;
pub fn filter_by_kind(&self, kind: &AuditEventKind) -> Vec<&AuditEntry>;collector.rs
Read declaration text · 12 declaration entries
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompletedSpan {
/// The unique identifier for this span.
pub span_id: SpanId,
/// The span name (e.g., `"anvil.generate"`, `"anvil.tool.invoke"`).
pub name: String,
/// Key-value attributes associated with this span.
pub attributes: Vec<(String, String)>,
/// ISO 8601 timestamp of when the span was started.
pub start_time: String,
/// ISO 8601 timestamp of when the span was ended.
pub end_time: String,
/// The agent's OAS DID, if available.
pub agent_did: Option<String>
}
pub trait SpanCollector: Send + Sync {
/// Record a completed span.
///
/// # Arguments
///
/// * `span` - The completed span to record.
fn record_span(&self, span: CompletedSpan);
/// Retrieve all recorded spans.
///
/// # Returns
///
/// A vector of all completed spans recorded so far.
fn spans(&self) -> Vec<CompletedSpan>;
/// Clear all recorded spans.
fn clear(&self);
/// Number of recorded spans.
fn len(&self) -> usize;
/// Returns `true` if no spans have been recorded.
fn is_empty(&self) -> bool;
}
pub struct NoopCollector;
pub struct InMemoryCollector {
}
pub fn new() -> Self;
pub struct InMemoryTelemetry {
}
pub fn new(agent_did: Option<String>) -> Self;
pub fn completed_spans(&self) -> Vec<CompletedSpan>;
pub fn events(&self) -> Vec<AuditEvent>;
pub fn clear(&self);
pub fn span_count(&self) -> usize;
pub fn event_count(&self) -> usize;contract.rs
Read declaration text · 9 declaration entries
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SpanId(pub String);
pub fn generate() -> Self;
pub fn from_string(id: String) -> Self;
pub fn as_str(&self) -> &str;
pub trait TelemetryContract: Send + Sync {
/// Start a new span with the given name and attributes.
///
/// Returns a `SpanId` that must be passed to `end_span` when the
/// operation completes.
///
/// # Arguments
///
/// * `name` - The span name (use dotted notation, e.g., `"anvil.generate"`).
/// * `attributes` - Key-value pairs of span attributes.
///
/// # Returns
///
/// A unique `SpanId` for the started span.
fn start_span(&self, name: &str, attributes: &[(&str, &str)]) -> SpanId;
/// End a previously started span.
///
/// Marks the span as completed with the current timestamp. If the span
/// has already been ended, the behavior is implementation-defined (some
/// implementations may silently ignore, others may log a warning).
///
/// # Arguments
///
/// * `span_id` - The ID returned by `start_span`.
fn end_span(&self, span_id: &SpanId);
/// Emit an audit event for the agent's audit trail.
///
/// Events are buffered until `flush` is called, or may be written
/// immediately depending on the implementation.
///
/// # Arguments
///
/// * `event` - The audit event to record.
fn emit_event(&self, event: AuditEvent);
/// Flush any buffered telemetry data to the backend.
///
/// # Errors
///
/// Returns `TelemetryError::FlushFailed` if the flush operation fails.
fn flush(&self) -> Result<(), TelemetryError>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEvent {
/// The kind of event.
pub kind: AuditEventKind,
/// The agent's OAS DID.
pub agent_did: String,
/// ISO 8601 timestamp of when the event occurred.
pub timestamp: String,
/// Event-specific details as a JSON value.
pub details: serde_json::Value,
/// Ed25519 signature (hex-encoded), if signed.
pub signature: Option<String>
}
pub fn new(kind: AuditEventKind, agent_did: String, details: serde_json::Value) -> Self;
pub fn with_signature(mut self, signature: String) -> Self;
pub struct NoopTelemetry;error.rs
Read declaration text · 1 declaration entries
#[derive(Debug, Error)]
pub enum TelemetryError {
/// A span with the given ID was not found in the collector.
#[error("span '{span_id}' not found in telemetry collector")]
SpanNotFound {
/// The span ID that was not found.
span_id: String,
},
/// An attempt was made to close a span that is already closed.
#[error("span '{span_id}' has already been closed and cannot be ended again")]
SpanAlreadyClosed {
/// The span ID that was already closed.
span_id: String,
},
/// An audit entry failed validation.
#[error("audit entry is invalid: {reason}")]
AuditEntryInvalid {
/// The reason the entry is invalid.
reason: String,
},
/// The audit trail integrity check failed at the given index.
#[error("audit trail corrupted at index {index}: {reason}")]
AuditTrailCorrupted {
/// The index where corruption was detected.
index: usize,
/// The reason for the corruption.
reason: String,
},
/// Flushing buffered telemetry data to the backend failed.
#[error("failed to flush telemetry data: {reason}")]
FlushFailed {
/// The reason the flush failed.
reason: String,
},
/// The span collector has reached its capacity limit.
#[error("telemetry collector is full (capacity: {capacity} spans)")]
CollectorFull {
/// The maximum number of spans the collector can hold.
capacity: usize,
},
/// Exporting telemetry data to an external system failed.
#[error("failed to export telemetry data: {reason}")]
ExportFailed {
/// The reason the export failed.
reason: String,
},
/// An audit trail signature or hash chain verification failed.
///
/// This indicates either a tampered entry, a wrong signer, or a
/// corrupted hash chain in the signed audit trail.
///
/// # ANVIL Spec §14.2
#[error("audit trail verification failed: {reason}")]
AuditVerificationFailed {
/// Human-readable description of the verification failure.
reason: String,
},
}lib.rs
Read declaration text · 11 declaration entries
pub mod audit;
pub mod collector;
pub mod contract;
pub mod error;
pub mod signed_audit;
pub mod prelude;
pub use crate::audit::{AuditEntry, AuditTrail};
pub use crate::collector::{
CompletedSpan, InMemoryCollector, InMemoryTelemetry, NoopCollector, SpanCollector,
};
pub use crate::contract::{AuditEvent, NoopTelemetry, SpanId, TelemetryContract};
pub use crate::error::TelemetryError;
pub use crate::signed_audit::{SignedAuditEntry, SignedAuditTrail};signed_audit.rs
Read declaration text · 14 declaration entries
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignedAuditEntry {
/// Sequential index in the trail (0-based).
pub index: usize,
/// The audit event data.
pub event: AuditEvent,
/// BLAKE3 hash of the previous entry (or genesis hash for index 0).
pub previous_hash: String,
/// Hex-encoded 64-byte Ed25519 signature over the canonical content.
pub signature_hex: String,
/// BLAKE3 hash of this entry's signed content (used as `previous_hash` by the next entry).
pub entry_hash: String
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignedAuditTrail {
}
#[cfg(feature = "signed-audit")]
pub fn new(identity: &forge_identity::agent_identity::ForgeAgentIdentity) -> Self;
pub fn from_parts(agent_did: String, verifying_key_hex: String) -> Self;
#[cfg(feature = "signed-audit")]
pub fn append_signed(
&mut self,
event: AuditEvent,
identity: &forge_identity::agent_identity::ForgeAgentIdentity,
);
pub fn verify_entry(&self, index: usize) -> Result<(), TelemetryError>;
pub fn verify_all(&self) -> Result<(), TelemetryError>;
pub fn entries(&self) -> &[SignedAuditEntry];
pub fn len(&self) -> usize;
pub fn is_empty(&self) -> bool;
pub fn last(&self) -> Option<&SignedAuditEntry>;
pub fn agent_did(&self) -> &str;
pub fn verifying_key_hex(&self) -> &str;
pub fn filter_by_kind(&self, kind: &AuditEventKind) -> Vec<&SignedAuditEntry>;Continue
forge-settings
Forge — provider settings introspection. Each provider crate exposes its configurable fields through `ProviderSettings`, letting external runtimes (TUIs, dashboards) render and edit those fields without coupling to each provider's concrete `Config` type.
forge-tool
Tool definition, execution, approval, and registry for the Forge SDK