invoance 0.2.0

Official Rust SDK for the Invoance compliance API
Documentation
//! Audit Logs: create an org, append a signed event, then verify it offline.
//!
//! ```bash
//! INVOANCE_API_KEY=inv_live_... cargo run --example audit
//! ```

use invoance::models::{CreateAuditOrgParams, IngestAuditEventParams};
use invoance::{verify_audit_event, InvoanceClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = InvoanceClient::new()?;
    let audit = client.audit();

    audit
        .orgs
        .create(CreateAuditOrgParams {
            organization_id: "org_customer_1".into(),
            name: Some("Acme Corp".into()),
        })
        .await
        .ok(); // ignore "already exists" in this demo

    // Append a signed event. An Idempotency-Key is derived automatically.
    let ingested = audit
        .events
        .ingest(IngestAuditEventParams {
            organization_id: "org_customer_1".into(),
            action: "user.role.granted".into(),
            actor: json!({ "type": "user", "id": "admin_1" })
                .as_object()
                .unwrap()
                .clone(),
            targets: Some(vec![json!({ "type": "user", "id": "u_9" })]),
            ..Default::default()
        })
        .await?;
    println!("appended: {}", serde_json::to_string_pretty(&ingested)?);

    // List, then verify the latest event's signature offline.
    let listed = audit
        .events
        .list(invoance::models::ListAuditEventsParams {
            organization_id: Some("org_customer_1".into()),
            limit: Some(1),
            ..Default::default()
        })
        .await?;

    if let Some(first) = listed.events.first() {
        let as_value = serde_json::to_value(first)?;
        let res = verify_audit_event(&as_value, None);
        println!(
            "offline verify: valid={} reason={:?}",
            res.valid, res.reason
        );
    }

    Ok(())
}