oas-document 1.1.1

OAS Identity Document types and validation for the Open Agent Specification
Documentation

oas-document

W3C DID Document implementation with OAS extensions: builder pattern, conformance levels, lifecycle management, verification methods, service endpoints, lineage sections, and document proofs.

Installation

[dependencies]
oas-document = "1.0"

Quick Start

use oas_document::{DocumentBuilder, ConformanceLevel, LifecycleStatus, ServiceEndpoint};
use oas_crypto::OasKeyPair;

let keypair = OasKeyPair::generate();

let doc = DocumentBuilder::new("did:oas:example:agent:analyzer", "agent")
    .controller("did:oas:example:hmr:alice")
    .add_verification_method(&keypair)
    .conformance_level(ConformanceLevel::L1)
    .lifecycle_status(LifecycleStatus::Active)
    .name("Analyzer Agent")
    .description("An agent that analyzes data")
    .add_service(ServiceEndpoint {
        id: "did:oas:example:agent:analyzer#api".to_string(),
        service_type: "AgentAPI".to_string(),
        service_endpoint: "https://api.example.com/analyzer".to_string(),
    })
    .build_and_sign(&keypair, "2026-01-15T00:00:00Z")
    .unwrap();

assert_eq!(doc.id, "did:oas:example:agent:analyzer");
assert!(doc.proof.is_some());

API Reference

OasDocument

pub struct OasDocument {
    pub context: Vec<String>,
    pub id: String,
    pub controller: Option<String>,
    pub verification_method: Vec<VerificationMethod>,
    pub authentication: Vec<String>,
    pub assertion_method: Vec<String>,
    pub capability_invocation: Vec<String>,
    pub capability_delegation: Vec<String>,
    pub service: Vec<ServiceEndpoint>,
    pub oas_version: String,
    pub kind: String,
    pub name: Option<String>,
    pub description: Option<String>,
    pub lineage: Option<LineageSection>,
    pub conformance_level: Option<ConformanceLevel>,
    pub lifecycle_status: Option<LifecycleStatus>,
    pub sequence: Option<u64>,
    pub metadata: Option<DocumentMetadata>,
    pub proof: Option<DocumentProof>,
    pub revoked: Option<bool>,
    pub revoked_at: Option<String>,
}

impl OasDocument {
    /// Returns the default context array for OAS documents.
    pub fn default_context() -> Vec<String>

    /// Whether this document represents a root entity (hmr, mhr, or enr).
    pub fn is_root(&self) -> bool

    /// Whether this document has been revoked.
    pub fn is_revoked(&self) -> bool

    /// The multibase-encoded public key of the first verification method.
    pub fn primary_public_key_multibase(&self) -> Option<&str>

    /// Find a verification method by full ID or fragment (e.g., "#key-1").
    pub fn find_verification_method(&self, id_or_fragment: &str) -> Option<&VerificationMethod>
}

DocumentBuilder

pub struct DocumentBuilder { /* private fields */ }

impl DocumentBuilder {
    /// Create a new builder with the DID and entity kind.
    pub fn new(id: &str, kind: &str) -> Self

    /// Set the controller DID.
    pub fn controller(self, controller: &str) -> Self

    /// Add a verification method from a keypair.
    /// Automatically registers it in authentication, assertionMethod,
    /// capabilityInvocation, and capabilityDelegation arrays.
    pub fn add_verification_method(self, keypair: &OasKeyPair) -> Self

    /// Set the conformance level (L0, L1, L2).
    pub fn conformance_level(self, level: ConformanceLevel) -> Self

    /// Set a human-readable name.
    pub fn name(self, name: &str) -> Self

    /// Set a human-readable description.
    pub fn description(self, description: &str) -> Self

    /// Set the lineage section for non-root entities.
    pub fn lineage(self, lineage: LineageSection) -> Self

    /// Set the lifecycle status.
    pub fn lifecycle_status(self, status: LifecycleStatus) -> Self

    /// Set the document sequence number.
    pub fn sequence(self, seq: u64) -> Self

    /// Add a service endpoint.
    pub fn add_service(self, service: ServiceEndpoint) -> Self

    /// Build the document and sign it with the provided keypair.
    /// Returns a fully constructed OasDocument with a valid proof.
    pub fn build_and_sign(
        self,
        signing_keypair: &OasKeyPair,
        created: &str,
    ) -> Result<OasDocument, DocumentError>
}

ConformanceLevel

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ConformanceLevel {
    /// Basic: DID + verification method only.
    L0,
    /// Accountable: Adds lineage and controller.
    L1,
    /// Full: Adds attestations and service endpoints.
    L2,
}

impl ConformanceLevel {
    pub fn as_str(&self) -> &'static str  // "L0", "L1", "L2"
}

LifecycleStatus

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum LifecycleStatus {
    Nascent,     // Newly created, not yet active
    Active,      // Operational
    Dormant,     // Temporarily inactive
    Suspended,   // Administratively suspended
    Terminated,  // Permanently deactivated
    Archived,    // Read-only historical record
}

impl LifecycleStatus {
    pub fn as_str(&self) -> &'static str
}

VerificationMethod

pub const VERIFICATION_METHOD_TYPE: &str = "Ed25519VerificationKey2020";

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerificationMethod {
    pub id: String,
    pub method_type: String,
    pub controller: String,
    pub public_key_multibase: String,
}

impl VerificationMethod {
    /// Create a new verification method.
    /// The ID is formatted as `{did}#key-{key_num}`.
    pub fn new(did: &str, key_num: u32, public_key_multibase: &str) -> Self

    /// The key ID fragment (e.g., "key-1").
    pub fn key_id(&self) -> &str
}

ServiceEndpoint

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceEndpoint {
    pub id: String,
    pub service_type: String,
    pub service_endpoint: String,
}

LineageSection

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LineageSection {
    /// DID of the ultimate human root.
    pub human_root_did: String,
    /// DID of the immediate parent/creator.
    pub creator_did: String,
    /// Generation depth (0 = root, 1 = direct child, etc.).
    pub generation: u32,
    /// Cryptographic proof of derivation from parent.
    pub derivation_proof: Option<AgentLineageProof>,
    /// Chain of DIDs from this entity back to the human root.
    pub human_root_chain: Vec<String>,
}

DocumentProof

pub const DOCUMENT_PROOF_TYPE: &str = "Ed25519Signature2020";

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DocumentProof {
    pub proof_type: String,
    pub created: String,
    pub verification_method: String,
    pub proof_purpose: String,
    pub proof_value: String,
}

impl DocumentProof {
    /// Generate a proof by signing the document JSON with JCS canonicalization.
    pub fn generate(
        document_json: &serde_json::Value,
        keypair: &OasKeyPair,
        verification_method_id: &str,
        created: &str,
    ) -> Result<Self, DocumentError>

    /// Verify the proof against the document JSON and a public key.
    pub fn verify(
        &self,
        document_json: &serde_json::Value,
        public_key_bytes: &[u8],
    ) -> Result<(), DocumentError>
}

GovernanceSection (ENR entities only)

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GovernanceSection {
    pub current_mhr: String,             // Governing MHR DID
    pub epoch: u64,                      // Monotonic epoch counter
    pub transitions: Vec<GovernanceTransition>,
    pub policy: GovernancePolicy,
}

pub struct GovernanceTransition {
    pub from: Option<String>,            // Outgoing MHR DID
    pub to: String,                      // Incoming MHR DID
    pub epoch: u64,
    pub proof: GovernanceTransitionProof,
}

pub struct GovernanceTransitionProof {
    pub proof_type: String,              // "EnrGovernanceTransitionProof2025"
    pub mhr_threshold_signature: String, // FROST threshold sig (base64url)
    pub timestamp: String,               // ISO 8601
}

pub struct GovernancePolicy {
    pub transition_requires: String,             // "mhr_threshold"
    pub signing_key_rotation_requires: String,   // "mhr_threshold"
    pub max_transitions_per_day: u32,            // Default: 1
    pub timelock_hours: u32,                     // Default: 24
}

DocumentMetadata

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DocumentMetadata {
    pub created: String,
    pub updated: String,
    pub version: u64,
}

Constants

pub const OAS_CONTEXT: &str = "https://openagent.id/ns/oas/v1";
pub const DID_CONTEXT: &str = "https://www.w3.org/ns/did/v1";
pub const ED25519_CONTEXT: &str = "https://w3id.org/security/suites/ed25519-2020/v1";
pub const OAS_VERSION: &str = "1.0.0";

Error Type

#[derive(Debug, Error)]
pub enum DocumentError {
    #[error("missing required field: {field}")]
    MissingField { field: String },

    #[error("invalid field value: {field}: {reason}")]
    InvalidField { field: String, reason: String },

    #[error("serialization error: {0}")]
    Serialization(#[from] serde_json::Error),

    #[error("crypto error: {0}")]
    Crypto(#[from] oas_crypto::CryptoError),

    #[error("DID error: {0}")]
    Did(#[from] oas_did::DidError),

    #[error("proof generation failed: {reason}")]
    ProofGeneration { reason: String },

    #[error("proof verification failed: {reason}")]
    ProofVerification { reason: String },

    #[error("invalid proof: {reason}")]
    InvalidProof { reason: String },

    #[error("invalid context: {reason}")]
    InvalidContext { reason: String },

    #[error("document error: {reason}")]
    Other { reason: String },
}

Dependencies

Crate Version Purpose
oas-crypto workspace Signing, encoding, JCS
oas-did workspace DID parsing and validation
serde 1.0 Serialization with derive
serde_json 1.0 JSON support
chrono 0.4 Timestamp handling
thiserror 2.0 Error derive macros

License

Copyright © 2026 L1fe Labs, Inc.

Licensed under the MIT license.