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:
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:
Source: commons/models/state.rs:32
Schema ID
Defines the data structure and validation rules:
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