XID Provenance Marks
January 31, 2026 · View on GitHub
XID documents can include provenance marks that provide a verifiable chain of custody and state transitions. A provenance mark is a cryptographic proof that shows the document's history, using a hash-based chain structure. Each mark contains:
- A sequence number (starting from 0 for the genesis mark)
- A date timestamp
- Optional structured information (as any UR type)
- A chain code linking to previous marks
Provenance marks are particularly useful for tracking XID document updates over time, ensuring that modifications follow a verifiable sequence.
Creating XID Documents with Provenance Marks
When creating a new XID document, you can include a genesis provenance mark using the --generator option:
ALICE_PRVKEYS=ur:crypto-prvkeys/lftansgohdcxdntswmjerdqdoxhnguzsdrhfcmjsfewkhkvezohkeycpasdysrvdgypeoemtgywztansgehdcxisespmvlhflnweksvyfnmhvofysnhyztpyhlftluweaoemenurstreckoybbfroektnncyls
envelope xid new $ALICE_PRVKEYS --generator include | envelope format
│ XID(93a4d4e7) [
│ 'key': PublicKeys(cab108a0, SigningPublicKey(93a4d4e7, SchnorrPublicKey(26712894)), EncapsulationPublicKey(00b42db3, X25519PublicKey(00b42db3))) [
│ {
│ 'privateKey': PrivateKeys(8624d38b, SigningPrivateKey(096547df, SchnorrPrivateKey(74343f20)), EncapsulationPrivateKey(d8e2032d, X25519PrivateKey(d8e2032d)))
│ } [
│ 'salt': Salt
│ ]
│ 'allow': 'All'
│ ]
│ 'provenance': ProvenanceMark(53929e33) [
│ {
│ 'provenanceGenerator': Bytes(32) [
│ 'isA': "provenance-generator"
│ "next-seq": 1
│ "res": 3
│ "rng-state": Bytes(32)
│ "seed": Bytes(32)
│ ]
│ } [
│ 'salt': Salt
│ ]
│ ]
│ ]
The --generator include option includes the provenance generator in the document. This generator contains the cryptographic state needed to create subsequent marks in the chain. The generator is included as a salted assertion on the provenance mark itself.
Generator Options
The --generator option accepts several values:
omit(default): Do not include a provenance markinclude: Include a provenance mark with the generator in plaintextencrypt: Include a provenance mark with an encrypted generator (requires--encrypt-password)elide: Not supported for new documents (will produce an error)
Custom Dates and Information
When creating a genesis mark, you can specify a custom date and attach structured information:
ALICE_PRVKEYS=ur:crypto-prvkeys/lftansgohdcxdntswmjerdqdoxhnguzsdrhfcmjsfewkhkvezohkeycpasdysrvdgypeoemtgywztansgehdcxisespmvlhflnweksvyfnmhvofysnhyztpyhlftluweaoemenurstreckoybbfroektnncyls
DIGEST_UR=`envelope generate digest "Hello"`
envelope xid new $ALICE_PRVKEYS --generator include --date 2025-01-15 --info $DIGEST_UR | envelope format
│ XID(93a4d4e7) [
│ 'key': PublicKeys(cab108a0, SigningPublicKey(93a4d4e7, SchnorrPublicKey(26712894)), EncapsulationPublicKey(00b42db3, X25519PublicKey(00b42db3))) [
│ {
│ 'privateKey': PrivateKeys(8624d38b, SigningPrivateKey(096547df, SchnorrPrivateKey(74343f20)), EncapsulationPrivateKey(d8e2032d, X25519PrivateKey(d8e2032d)))
│ } [
│ 'salt': Salt
│ ]
│ 'allow': 'All'
│ ]
│ 'provenance': ProvenanceMark(2bbbd1e1) [
│ {
│ 'provenanceGenerator': Bytes(32) [
│ 'isA': "provenance-generator"
│ "next-seq": 1
│ "res": 3
│ "rng-state": Bytes(32)
│ "seed": Bytes(32)
│ ]
│ } [
│ 'salt': Salt
│ ]
│ ]
│ ]
The --date option accepts ISO 8601 format dates (e.g., "2025-01-15"). If omitted, the current date is used.
The --info option accepts any UR type (envelope, digest, ARID, etc.). This information is embedded in the provenance mark and can be used to attach context or metadata to the mark.
Encrypted Generators
For additional security, the provenance generator can be encrypted:
ALICE_PRVKEYS=ur:crypto-prvkeys/lftansgohdcxdntswmjerdqdoxhnguzsdrhfcmjsfewkhkvezohkeycpasdysrvdgypeoemtgywztansgehdcxisespmvlhflnweksvyfnmhvofysnhyztpyhlftluweaoemenurstreckoybbfroektnncyls
envelope xid new $ALICE_PRVKEYS --generator encrypt --encrypt-password "secret" | envelope format
│ XID(93a4d4e7) [
│ 'key': PublicKeys(cab108a0, SigningPublicKey(93a4d4e7, SchnorrPublicKey(26712894)), EncapsulationPublicKey(00b42db3, X25519PublicKey(00b42db3))) [
│ {
│ 'privateKey': PrivateKeys(8624d38b, SigningPrivateKey(096547df, SchnorrPrivateKey(74343f20)), EncapsulationPrivateKey(d8e2032d, X25519PrivateKey(d8e2032d)))
│ } [
│ 'salt': Salt
│ ]
│ 'allow': 'All'
│ ]
│ 'provenance': ProvenanceMark(10bd3a28) [
│ {
│ 'provenanceGenerator': ENCRYPTED [
│ 'hasSecret': EncryptedKey(Argon2id)
│ ]
│ } [
│ 'salt': Salt
│ ]
│ ]
│ ]
Encrypted generators protect the cryptographic state while still allowing the provenance mark itself to be read. To advance the mark later, you'll need to provide the decryption password.
xid provenance get: Extract the Provenance Mark
The xid provenance get command extracts the provenance mark from a XID document and returns it as a standalone UR:
ALICE_PRVKEYS=ur:crypto-prvkeys/lftansgohdcxdntswmjerdqdoxhnguzsdrhfcmjsfewkhkvezohkeycpasdysrvdgypeoemtgywztansgehdcxisespmvlhflnweksvyfnmhvofysnhyztpyhlftluweaoemenurstreckoybbfroektnncyls
XID_WITH_PROV=`envelope xid new $ALICE_PRVKEYS --generator include`
envelope xid provenance get $XID_WITH_PROV
│ ur:provenance/lfaxhdimpkwlsektpsataagwutlpdspmbtjkfrprmoptdtlrftbwdkdlvalrytchlrtdsavtplvsltsgbdjlwyhfvawejlvtutfmondavarnylstbncplymtatmkjkpylrclpkrlaoisgoinmkpyssmydlotfrmhdszeftmwgsluclrerlnlwtemcholrlrojpiyhksplsldztspihlkbentgwfsdnmomtax
If the document does not have a provenance mark, the command returns an empty string:
ALICE_PRVKEYS=ur:crypto-prvkeys/lftansgohdcxdntswmjerdqdoxhnguzsdrhfcmjsfewkhkvezohkeycpasdysrvdgypeoemtgywztansgehdcxisespmvlhflnweksvyfnmhvofysnhyztpyhlftluweaoemenurstreckoybbfroektnncyls
XID_NO_PROV=`envelope xid new $ALICE_PRVKEYS`
envelope xid provenance get $XID_NO_PROV
│
This command works with both plaintext and encrypted generators. It also supports signature verification with the --verify option (see Signed XID Documents).
xid provenance next: Advance the Provenance Mark
The xid provenance next command advances the provenance mark to the next state in the chain. This creates a new mark with an incremented sequence number and a hash linking to the previous mark:
ALICE_PRVKEYS=ur:crypto-prvkeys/lftansgohdcxdntswmjerdqdoxhnguzsdrhfcmjsfewkhkvezohkeycpasdysrvdgypeoemtgywztansgehdcxisespmvlhflnweksvyfnmhvofysnhyztpyhlftluweaoemenurstreckoybbfroektnncyls
XID_WITH_PROV=`envelope xid new $ALICE_PRVKEYS --generator include --date 2025-01-15`
envelope xid provenance next --date 2025-01-20 $XID_WITH_PROV | envelope format
│ XID(93a4d4e7) [
│ 'key': PublicKeys(cab108a0, SigningPublicKey(93a4d4e7, SchnorrPublicKey(26712894)), EncapsulationPublicKey(00b42db3, X25519PublicKey(00b42db3))) [
│ {
│ 'privateKey': PrivateKeys(8624d38b, SigningPrivateKey(096547df, SchnorrPrivateKey(74343f20)), EncapsulationPrivateKey(d8e2032d, X25519PrivateKey(d8e2032d)))
│ } [
│ 'salt': Salt
│ ]
│ 'allow': 'All'
│ ]
│ 'provenance': ProvenanceMark(bf27fca8) [
│ {
│ 'provenanceGenerator': Bytes(32) [
│ 'isA': "provenance-generator"
│ "next-seq": 2
│ "res": 3
│ "rng-state": Bytes(32)
│ "seed": Bytes(32)
│ ]
│ } [
│ 'salt': Salt
│ ]
│ ]
│ ]
Notice that the provenance mark's identifier has changed (from 53929e33 to bf27fca8 in this example), and the next-seq value in the generator has incremented from 1 to 2.
Using an Embedded Generator
When the XID document contains an embedded generator (either plaintext or encrypted), the next command uses it automatically:
- Plaintext generators: No password needed
- Encrypted generators: Require
--passwordto decrypt
ALICE_PRVKEYS=ur:crypto-prvkeys/lftansgohdcxdntswmjerdqdoxhnguzsdrhfcmjsfewkhkvezohkeycpasdysrvdgypeoemtgywztansgehdcxisespmvlhflnweksvyfnmhvofysnhyztpyhlftluweaoemenurstreckoybbfroektnncyls
XID_ENC_GEN=`envelope xid new $ALICE_PRVKEYS --generator encrypt --encrypt-password "secret" --date 2025-01-15`
envelope xid provenance next --date 2025-01-20 --password "secret" --encrypt-password "secret" $XID_ENC_GEN | envelope format
│ XID(93a4d4e7) [
│ 'key': PublicKeys(cab108a0, SigningPublicKey(93a4d4e7, SchnorrPublicKey(26712894)), EncapsulationPublicKey(00b42db3, X25519PublicKey(00b42db3))) [
│ {
│ 'privateKey': PrivateKeys(8624d38b, SigningPrivateKey(096547df, SchnorrPrivateKey(74343f20)), EncapsulationPrivateKey(d8e2032d, X25519PrivateKey(d8e2032d)))
│ } [
│ 'salt': Salt
│ ]
│ 'allow': 'All'
│ ]
│ 'provenance': ProvenanceMark(8205b945) [
│ {
│ 'provenanceGenerator': Bytes(32) [
│ 'isA': "provenance-generator"
│ "next-seq": 2
│ "res": 3
│ "rng-state": Bytes(32)
│ "seed": Bytes(32)
│ ]
│ } [
│ 'salt': Salt
│ ]
│ ]
│ ]
Note: When advancing with an encrypted generator, the --password option decrypts it for use, and --encrypt-password re-encrypts it in the output. If you want to change from encrypted to plaintext, you can omit --encrypt-password.
Attaching Information to New Marks
Like the genesis mark, you can attach custom dates and structured information when advancing:
ALICE_PRVKEYS=ur:crypto-prvkeys/lftansgohdcxdntswmjerdqdoxhnguzsdrhfcmjsfewkhkvezohkeycpasdysrvdgypeoemtgywztansgehdcxisespmvlhflnweksvyfnmhvofysnhyztpyhlftluweaoemenurstreckoybbfroektnncyls
ARID_UR=`envelope generate arid`
XID_WITH_PROV=`envelope xid new $ALICE_PRVKEYS --generator include --date 2025-01-15`
envelope xid provenance next --date 2025-01-20 --info $ARID_UR $XID_WITH_PROV | envelope format
│ XID(93a4d4e7) [
│ 'key': PublicKeys(cab108a0, SigningPublicKey(93a4d4e7, SchnorrPublicKey(26712894)), EncapsulationPublicKey(00b42db3, X25519PublicKey(00b42db3))) [
│ {
│ 'privateKey': PrivateKeys(8624d38b, SigningPrivateKey(096547df, SchnorrPrivateKey(74343f20)), EncapsulationPrivateKey(d8e2032d, X25519PrivateKey(d8e2032d)))
│ } [
│ 'salt': Salt
│ ]
│ 'allow': 'All'
│ ]
│ 'provenance': ProvenanceMark(79a0d8f3) [
│ {
│ 'provenanceGenerator': Bytes(32) [
│ 'isA': "provenance-generator"
│ "next-seq": 2
│ "res": 3
│ "rng-state": Bytes(32)
│ "seed": Bytes(32)
│ ]
│ } [
│ 'salt': Salt
│ ]
│ ]
│ ]
The --info parameter can be any UR type, making it flexible for various use cases such as attaching transaction references, update notes, or other contextual data.