Skip to main content
Subjects are the fundamental data structures in TAPLE representing entities whose state evolves through events. Each subject maintains its own immutable event chain.

Subject Structure

A subject contains all necessary information about an entity in the network:
pub struct Subject {
    /// The key pair associated with the subject, if any.
    pub keys: Option<KeyPair>,
    /// The identifier of the subject.
    pub subject_id: DigestIdentifier,
    /// The identifier of the governance contract associated with the subject.
    pub governance_id: DigestIdentifier,
    /// The current sequence number of the subject.
    pub sn: u64,
    /// The version of the governance contract that created the subject.
    pub genesis_gov_version: u64,
    /// The identifier of the public key of the subject owner.
    pub public_key: KeyIdentifier,
    /// The namespace of the subject.
    pub namespace: String,
    /// The name of the subject.
    pub name: String,
    /// The identifier of the schema used to validate the subject.
    pub schema_id: String,
    /// The identifier of the public key of the subject owner.
    pub owner: KeyIdentifier,
    /// The identifier of the public key of the subject creator.
    pub creator: KeyIdentifier,
    /// The current status of the subject.
    pub properties: ValueWrapper,
    /// Indicates whether the subject is active or not.
    pub active: bool,
}
Source: commons/models/state.rs:18-45
The Subject struct contains private keys (if owned), while SubjectData is the public representation without keys.

Subject Data (Public View)

For external consumption, subjects expose a public data structure:
pub struct SubjectData {
    pub subject_id: DigestIdentifier,
    pub governance_id: DigestIdentifier,
    pub sn: u64,
    pub public_key: KeyIdentifier,
    pub namespace: String,
    pub name: String,
    pub schema_id: String,
    pub owner: KeyIdentifier,
    pub creator: KeyIdentifier,
    pub properties: ValueWrapper,
    pub active: bool,
}
Source: commons/models/state.rs:48-72

Subject Identifier Generation

Subject IDs are deterministically generated from key components:
pub fn generate_subject_id(
    namespace: &str,
    schema_id: &str,
    public_key: String,
    governance_id: String,
    governance_version: u64,
    derivator: DigestDerivator
) -> Result<DigestIdentifier, SubjectError> {
    let subject_id = DigestIdentifier::from_serializable_borsh((
        namespace,
        schema_id,
        public_key,
        governance_id,
        governance_version,
    ), derivator)
    .map_err(|_| SubjectError::ErrorCreatingSubjectId)?;
    Ok(subject_id)
}
Source: commons/models/state.rs:230-247
The subject ID is cryptographically derived from its namespace, schema, public key, governance ID, and governance version, ensuring uniqueness and verifiability.

Subject Lifecycle

1. Creation (Genesis Event)

Subjects are created through a genesis event:
pub fn from_genesis_event(
    event: Signed<Event>,
    init_state: ValueWrapper,
    keys: Option<KeyPair>,
    derivator: DigestDerivator
) -> Result<Self, SubjectError> {
    let EventRequest::Create(create_request) = event.content.event_request.content.clone() else {
        return Err(SubjectError::NotCreateEvent)
    };
    let subject_id = generate_subject_id(
        &create_request.namespace,
        &create_request.schema_id,
        create_request.public_key.to_str(),
        create_request.governance_id.to_str(),
        event.content.gov_version,
        derivator
    )?;
    Ok(Subject {
        keys,
        subject_id,
        governance_id: create_request.governance_id.clone(),
        sn: 0,
        public_key: create_request.public_key,
        namespace: create_request.namespace.clone(),
        schema_id: create_request.schema_id.clone(),
        owner: event.content.event_request.signature.signer.clone(),
        creator: event.content.event_request.signature.signer.clone(),
        properties: init_state,
        active: true,
        name: create_request.name,
        genesis_gov_version: event.content.gov_version,
    })
}
Source: commons/models/state.rs:137-169

2. State Updates

Subjects evolve through JSON Patch operations:
pub fn update_subject(
    &mut self,
    json_patch: ValueWrapper,
    new_sn: u64,
) -> Result<(), SubjectError> {
    let Ok(patch_json) = serde_json::from_value::<Patch>(json_patch.0) else {
        return Err(SubjectError::ErrorParsingJsonString("Json Patch conversion fails".to_owned()));
    };
    let Ok(()) = patch(&mut self.properties.0, &patch_json) else {
        return Err(SubjectError::ErrorApplyingPatch("Error Applying Patch".to_owned()));
    };
    self.sn = new_sn;
    Ok(())
}
Source: commons/models/state.rs:171-184
Using JSON Patch allows efficient state updates by only transmitting changes rather than full state.

3. Ownership Transfer

Subjects can be transferred to new owners:
pub fn transfer_subject(
    &mut self,
    owner: KeyIdentifier,
    public_key: KeyIdentifier,
    keys: Option<KeyPair>,
    sn: u64,
) {
    self.owner = owner;
    self.public_key = public_key;
    self.keys = keys;
    self.sn = sn;
}
Source: commons/models/state.rs:186-197

4. End of Life

Subjects can be marked as inactive:
pub fn eol_event(&mut self) {
    self.active = false;
}
Source: commons/models/state.rs:207-209

State Hash Calculation

State integrity is maintained through cryptographic hashing:
pub fn get_state_hash(&self, derivator: DigestDerivator) -> Result<DigestIdentifier, SubjectError> {
    Ok(
        DigestIdentifier::from_serializable_borsh(&self.properties, derivator).map_err(|_| {
            SubjectError::CryptoError(String::from("Error calculating the hash of the state"))
        })?,
    )
}
Source: commons/models/state.rs:199-205

Predictive State Hashing

Calculate state hash after applying a patch without modifying the subject:
pub fn state_hash_after_apply(
    &self,
    json_patch: ValueWrapper,
    derivator: DigestDerivator
) -> Result<DigestIdentifier, SubjectError> {
    let mut subject_properties = self.properties.clone();
    let json_patch = serde_json::from_value::<Patch>(json_patch.0)
        .map_err(|_| SubjectError::CryptoError(String::from("Error parsing the json patch")))?;
    patch(&mut subject_properties.0, &json_patch).map_err(|_| {
        SubjectError::CryptoError(String::from("Error applying the json patch"))
    })?;
    Ok(
        DigestIdentifier::from_serializable_borsh(&subject_properties, derivator).map_err(|_| {
            SubjectError::CryptoError(String::from("Error calculating the hash of the state"))
        })?,
    )
}
Source: commons/models/state.rs:211-227

Subject Context for Evaluation

When evaluating events, subjects provide context:
pub fn get_subject_context(&self, invoker: KeyIdentifier) -> SubjectContext {
    SubjectContext {
        governance_id: self.governance_id.clone(),
        schema_id: self.schema_id.clone(),
        is_owner: invoker == self.owner,
        state: self.properties.clone(),
        namespace: self.namespace.clone(),
    }
}
Source: commons/models/state.rs:127-135

Subject Properties

Immutable Properties

These properties are set at creation and never change:
  • subject_id: Unique identifier
  • governance_id: Governing contract
  • genesis_gov_version: Original governance version
  • creator: Original creator’s key identifier
  • namespace: Subject’s namespace
  • schema_id: Schema type

Mutable Properties

These properties can change through events:
  • sn: Sequence number (increments with each event)
  • owner: Current owner
  • public_key: Subject’s current public key
  • properties: Current state (via JSON Patch)
  • active: Active/inactive status
  • name: Subject name
Changing ownership (transfer_subject) also updates the public key and optionally the key pair.

Ownership Model

Owner vs Creator

  • Creator: The identity that initially created the subject (immutable)
  • Owner: The current controller of the subject (mutable through transfers)
pub owner: KeyIdentifier,
pub creator: KeyIdentifier,
Source: commons/models/state.rs:38, 40

Keys Management

Subjects may hold their own cryptographic keys:
pub keys: Option<KeyPair>,
Source: commons/models/state.rs:20
Only subjects owned by the node have keys stored locally. External subjects don’t include keys.

Sequence Numbers

Each event increments the subject’s sequence number:
pub sn: u64,
Source: commons/models/state.rs:26 Sequence numbers:
  • Start at 0 for the genesis event
  • Increment by 1 for each subsequent event
  • Ensure event ordering and prevent replay attacks
  • Enable efficient event retrieval

Namespaces and Schemas

Namespace

Logical grouping for subjects:
pub namespace: String,
Source: commons/models/state.rs:32

Schema ID

Defines the data structure and validation rules:
pub schema_id: String,
Source: commons/models/state.rs:36
Use namespaces to organize subjects by business domain or application context.

Working with Subjects

Creating a Subject

let create_subject_request = EventRequest::Create(StartRequest {
    governance_id: DigestIdentifier::default(),
    name: "My Subject".to_string(),
    namespace: "namespace".to_string(),
    schema_id: "schema".to_string(),
    public_key: subject_key,
});
Source: lib.rs:50-55 (adapted)

Retrieving Subject Data

let subject = api
    .get_subject(DigestIdentifier::from_str(&subject_id).unwrap())
    .await
    .unwrap_or_else(|_| panic!("Error getting subject"));

println!("Subject ID: {}", subject.subject_id.to_str());
Source: lib.rs:77-82

Best Practices

Namespace Design

Use hierarchical namespaces for better organization

Schema Selection

Choose appropriate schemas defined in governance

State Management

Keep state minimal and normalized for efficiency

Key Security

Protect subject keys with appropriate security measures

Next Steps

Events

Learn how events modify subjects

Governance

Understand how governance controls subjects

Build docs developers (and LLMs) love