The following example provides descriptions for the various keys required by Panurus.
# ------------------- Panurus Configuration -------------------------
token:
# version is the version of this configuration structure.
# If not specified, the latest version is used.
version: v1
# enabled determines if Panurus is enabled.
enabled: true
# selector configuration allows the use of different implementations of the token selector.
# The "sherdlock" driver is the default implementation; other possible configurations are: "simple".
# If empty, the default selector is used.
selector:
driver: sherdlock
# tokens might be locked because of an ongoing transaction from the same wallet. Instead of failing immediately, the selector can retry.
# The interval is the exact amount of seconds (for simple selector) or the max amount of seconds (sherdlock) the selector waits before retrying.
# User-defined default: 5s
retryInterval: 5s
# numRetries is the number of times to retry gaining a lock on tokens before failing the transaction.
numRetries: 3
# leaseExpiry is the period a token can be locked, after which it is forcefully unlocked.
# If leaseExpiry is zero, the eviction algorithm is never executed.
leaseExpiry: 3m
# leaseCleanupTickPeriod defines how often the eviction algorithm must be executed.
# If leaseCleanupTickPeriod is zero, the eviction algorithm is never executed.
leaseCleanupTickPeriod: 90s
# Token fetcher cache configuration (sherdlock driver only)
# The fetcher uses a Ristretto cache to store tokens for efficient retrieval.
# fetcherCacheSize is the maximum number of tokens to cache. Each token consumes 1 unit of cache cost.
# If not specified or set to 0, defaults to 100 million (1e8) tokens.
fetcherCacheSize: 100000000
# fetcherCacheRefresh is the time interval after which the cache is considered stale and will be refreshed.
# A hard refresh (blocking) occurs when the cache becomes stale. If not specified or set to 0, defaults to 1 second.
fetcherCacheRefresh: 1s
# fetcherCacheMaxQueries is the number of queries after which a soft refresh (non-blocking background update) is triggered.
# This helps keep the cache fresh without blocking queries. If not specified or set to 0, defaults to 5 queries.
fetcherCacheMaxQueries: 5
# When we are interested in knowing when a transaction reaches finality, we subscribe to the Finality Listener Manager for the finality event of that transaction.
# This configuration specifies the way the manager is instantiated (i.e., how it gets notified about the finality events, how often it checks).
finality:
# Only applicable for fabric networks.
# The manager subscribes to the delivery service and receives all final transactions.
# This manager keeps two structures: an LRU cache of recently finalized transactions, and a list of listeners that are waiting for future transactions.
# When a new block comes from the delivery service, we store all new transactions in the cache and notify all interested listeners.
# When a client subscribes to the manager for a specific transaction, we go through the following steps, which correspond to all possible scenarios with decreasing probability:
# a) The transaction reached finality recently, so we perform a lookup. If not found, we proceed to step b.
# b) The transaction will reach finality shortly, so we append a listener and wait for a timeout. If the listener reaches timeout, we proceed to step c.
# c) The transaction reached finality long ago, so we query the whole ledger for this specific transaction. If the query returns no result, we proceed to step d.
# d) The transaction will reach finality at some point beyond the timeout or never, so we return Unknown. Then it is up to the client to either append another listener or accept that the transaction will never reach finality.
delivery:
# mapperParallelism is the number of goroutines that process incoming transactions in parallel. Defaults to 1.
mapperParallelism: 10
# blockProcessParallelism is the number of blocks we can process in parallel when they arrive from the delivery service.
# If the value is <= 1, then blocks are processed sequentially.
# This is the suggested configuration if we are not sure about the dependencies between blocks.
# The total go routines processing transactions will be blockProcessParallelism * mapperParallelism.
blockProcessParallelism: 1
# lruSize detects how many transactions we should guarantee to keep in our recent cache.
# If the transaction is not among these elements, we proceed to step b, as described above.
# If lruSize and lruBuffer are not set, then we will never evict past transactions (the cache will grow infinitely).
lruSize: 30
# eviction will happen when the cache size exceeds lruSize + lruBuffer.
lruBuffer: 15
# listenerTimeout is the duration to listen when we can't find a transaction in the cache (most probably it is about to become final).
# We will listen for this amount of time and then we will query the whole ledger, as described in step c.
# If the timeout is not set, then the listener will never be evicted and we will never proceed to step c.
# We will wait forever for the transaction to return (as is done for the 'committer' type).
listenerTimeout: 10s
# Only applicable for fabricx networks
# notification: The manager is notified about finality events via a notification service (e.g. for FabricX).
# When a new notification arrives, an event is added to a queue for asynchronous processing.
# When a client subscribes to the manager for a specific transaction, the transaction joins a pending set
# that the shared poller sweeps with batched status queries (see poller below).
notification:
# workers is the number of goroutines that process events in parallel. Defaults to 10.
workers: 10
# queueSize is the size of the event buffer. Defaults to 1000.
queueSize: 1000
# Only applicable for fabricx networks
# poller: resolves the status of pending transactions with periodic batched committer queries.
poller:
# interval is how often the poller sweeps the pending set. Defaults to 1s.
interval: 1s
# batchSize is the maximum number of txIDs in one committer status query. Defaults to 2000.
batchSize: 2000
# pendingTTL is how long a tx stays pending before its slot is reclaimed.
# It should exceed the longest caller finality timeout. Defaults to 10m.
pendingTTL: 10m
# fabricx configuration for FabricX-specific settings
fabricx:
# lookup configuration for the lookup service
lookup:
# permanent lookup configuration
permanent:
# interval is the polling interval for permanent lookups. Defaults to 1m.
interval: 1m
# one-time lookup configuration
once:
# deadline is the maximum time to wait for a one-time lookup. Defaults to 5m.
deadline: 5m
# interval is the polling interval for one-time lookups. Defaults to 2s.
interval: 2s
# validation configures the resource limits enforced on untrusted token requests/actions
# before they reach cryptographic verification. Process-wide (not per-TMS): both the FSC/DI
# runtime and the standalone Fabric chaincode process resolve a single ResourceLimits value.
# Every field is optional; any field left unset defaults to the safe, historical value.
# The chaincode process (which has no config service) reads the same limits from
# TOKEN_VALIDATION_MAX_* environment variables instead. See
# docs/drivers/validation-resource-limits.md for the full enforcement and consensus-safety
# contract: if you override any limit, every peer validating the same channel/namespace MUST
# be configured with the identical value.
validation:
limits:
maxRequestBytes: 262144
maxActions: 256
maxSignatures: 4096
maxSignatureBytes: 4096
maxActionBytes: 262144
maxInputs: 256
maxOutputs: 256
maxMetadataEntries: 64
maxMetadataKeyBytes: 256
maxMetadataValueBytes: 4096
maxProofBytes: 131072
maxIdentityDepth: 5
maxIdentityComponents: 16
# optional global SQL table name overrides (applied to all TMS instances).
# The value replaces the short code; the FSC-generated prefix and params still wrap it.
# Unknown keys are warned and ignored. Omit the section to keep all default names.
storage:
tableNames:
# id_signers: identity_signers
# tokens: my_tokens
tms:
mytms: # unique name of this token management system
network: default # the name of the network this TMS refers to (Fabric, etc.)
channel: testchannel # the name of the network's channel this TMS refers to, if applicable
namespace: tns # the name of the channel's namespace this TMS refers to, if applicable
# sections dedicated to the definition of the storage.
# Panurus uses multiple databases to keep track of transactions, tokens, identities, and audit records where applicable.
# These are the available databases:
# ttxdb: stores records of transactions.
# tokendb: stores information about the available tokens.
# auditdb: stores audit records about the audited transactions.
# identitydb: stores information about wallets and identities.
# The databases can be instantiated in isolation, a different backend for each db, or with a shared backend, depending on the driver used.
# In the following example, we have all databases using the same backend but tokendb.
# optional separate configuration for ttxdb, tokendb, tokenlockdb, auditdb, and identitydb
# otherwise they default to 'default', if it is defined
tokendb:
persistence: my_token_persistence
services:
# This section contains network specific configuration
network:
# Configuration related to the Fabric network
fabric:
# In Fabric, the execution of the token chaincode can be endorsed by any node equipped with
# a proper endorsement key.
# Therefore, also FSC nodes equipped with proper endorsement keys can perform the same function.
# This section is dedicated to the configuration of the endorsement of the token chaincode by
# other FSC nodes.
fsc_endorsement:
# Is this node an endorser? true/false
endorser: true
# If this node is an endorser, which Fabric identity should be used to sign the endorsement?
# If empty, the default identity will be used.
id:
# This section is used to set the policy to be used to select the endorsers to contact.
# Available policies are: `1outn`, `all`, `namespace`. Default policy is `all`.
# - `1outn`: contact one random endorser from the list below.
# - `all`: contact all the endorsers listed below.
# - `namespace`: fetch the real endorsement policy of the token namespace and contact a
# random subset of the endorsers below that satisfies it. On Fabric, the policy is
# obtained via service discovery; on FabricX, via the query service. Endorsement fails
# if the endorsers below cannot satisfy the namespace's policy.
policy:
type: 1outn
# A list of FSC node identifiers that must be contacted to obtain the endorsement.
endorsers:
- endorser1
- endorser2
- endorser2
# recovery config controls background re-registration of finality listeners
# for pending transactions that may have lost their listeners due to node restarts,
# network interruptions, or other failures.
# If omitted, the recovery manager uses its built-in defaults.
recovery:
# enabled determines whether transaction recovery runs. Default: true.
# Set to false to disable automatic recovery (not recommended for production).
enabled: true
# ttl is the minimum age of a pending transaction before it is eligible for recovery. Default: 30s.
# This prevents the recovery manager from interfering with transactions that are still being
# actively processed. Increase this value if you have long-running transaction assembly processes.
# Relationship: Should be greater than your typical transaction assembly time.
ttl: 30s
# scanInterval is how often the recovery manager scans for pending transactions. Default: 5s.
# Lower values provide faster recovery but increase database load.
# Higher values reduce overhead but delay recovery detection.
# Relationship: Should be less than ttl to ensure timely detection of eligible transactions.
# Performance impact: Each scan queries the transaction database for pending transactions.
scanInterval: 5s
# batchSize is the maximum number of pending transactions claimed per scan. Default: 100.
# Limits the number of transactions processed in a single recovery sweep to prevent
# overwhelming the system. Increase for high-throughput environments with many pending transactions.
# Performance impact: Larger batches reduce scan overhead but increase memory usage and processing time per sweep.
batchSize: 100
# workerCount is the number of local workers that process claimed transactions in parallel. Default: 4.
# Increase to improve recovery throughput in high-volume scenarios.
# Decrease to reduce resource consumption on constrained systems.
# Performance impact: More workers increase CPU and network utilization but improve recovery speed.
workerCount: 4
# leaseDuration is how long a claimed transaction remains leased to this instance before it can be reclaimed. Default: 30s.
# This prevents stuck transactions from blocking recovery indefinitely if a worker crashes.
# Should be longer than the typical time to query and process a single transaction.
# Relationship: Should be greater than the expected network latency + processing time for transaction status queries.
leaseDuration: 30s
# advisoryLockID is the PostgreSQL advisory lock identifier used for recovery leader election.
# This ensures only one replica performs recovery sweeps at a time in multi-instance deployments.
# Default: 8389190333894887286 (hex: 0x74746b7265636f76, ASCII: "ttkrecov")
# The default value is derived from the ASCII encoding of "ttkrecov" (Token Transaction Recovery).
# Only change this if you need to run multiple independent recovery managers on the same database.
# Note: PostgreSQL advisory locks use 64-bit integers. This value must be unique across your application.
advisoryLockID: 8389190333894887286
# instanceID identifies this replica as the owner of recovery claims.
# If empty, a process-local identifier is generated automatically at startup using a UUID.
# Set this explicitly in containerized environments to maintain consistent identity across restarts.
# This helps with debugging and tracking which instance processed which transactions.
instanceID:
# notFoundGracePeriod is the time after which the recovery loop promotes a
# transaction whose status query keeps returning NotFound to the terminal
# Orphan status. Without this, a transaction whose audit log was persisted
# but whose broadcast never reached the ledger would sit at the head of the
# `ORDER BY stored_at ASC LIMIT batchSize` claim query forever and prevent
# newer rows from being scanned. Default: 30m.
# The promoted row is marked Orphan (not Deleted) so operators can tell
# broadcast failures apart from ledger-rejected transactions.
# Set to 0 to disable the promotion; the row stays Pending and is re-claimed
# on every sweep until it either resolves or an operator intervenes.
notFoundGracePeriod: 30m
# storage service configuration
storage:
# cleanup config controls automatic deletion of cryptographic keys from the keystore
# for tokens that have been deleted (spent, expired, or invalidated).
# If omitted, the cleanup manager uses its built-in defaults (disabled by default).
cleanup:
# enabled determines whether keystore cleanup runs. Default: false.
# Must be explicitly enabled. This is a conservative default to prevent
# unexpected key deletion in existing deployments.
enabled: false
# ttl is the minimum age of deleted tokens before their keys are eligible for cleanup. Default: 24h.
# This ensures tokens are truly finalized before key deletion.
# Increase this value for additional safety margin in high-latency networks.
# Relationship: Should be significantly greater than transaction finality time.
ttl: 24h
# scanInterval is how often the cleanup manager scans for deleted tokens. Default: 1h.
# Lower values provide faster cleanup but increase database load.
# Higher values reduce overhead but delay key removal.
# Relationship: Should be less than ttl to ensure timely cleanup.
# Performance impact: Each scan queries the token database for deleted tokens.
scanInterval: 1h
# batchSize is the maximum number of deleted tokens processed per scan. Default: 100.
# Limits the number of tokens processed in a single cleanup sweep.
# Increase for high-volume environments with many deleted tokens.
# Performance impact: Larger batches reduce scan overhead but increase memory usage and processing time per sweep.
batchSize: 100
# workerCount is the number of local workers that process tokens in parallel. Default: 1.
# Increase to improve cleanup throughput in high-volume scenarios.
# Decrease to reduce resource consumption on constrained systems.
# Performance impact: More workers increase CPU utilization during cleanup sweeps.
workerCount: 1
# advisoryLockID is the PostgreSQL advisory lock identifier used for cleanup leader election.
# This ensures only one replica performs cleanup sweeps at a time in multi-instance deployments.
# Default: 8389190333894887277 (hex: 0x74746b636c65616e, ASCII: "ttkclean")
# The default value is derived from the ASCII encoding of "ttkclean" (Token Transaction Keystore Cleanup).
# Only change this if you need to run multiple independent cleanup managers on the same database.
# Note: PostgreSQL advisory locks use 64-bit integers. This value must be unique across your application.
advisoryLockID: 8389190333894887277
# instanceID identifies this replica in logs and monitoring.
# If empty, a unique identifier is generated automatically at startup.
# Set this explicitly in containerized environments for consistent identity across restarts.
# This helps with debugging and tracking which instance performed cleanup operations.
instanceID:
# auditor-specific settings
auditor:
# locker configures the distributed locking strategy for the auditor's
# enrollment-ID (EID) locks. These locks serialise concurrent access
# to the same EIDs across replicas when processing audit records.
locker:
# backend selects the Locker implementation.
# "memory" – in-process mutex (default, single-replica only)
# "postgres" – PostgreSQL lease-table (multi-replica)
backend: memory
# postgres section is read only when backend == "postgres".
postgres:
# ttl is the lease duration for each EID lock row.
ttl: 30s
# acquireBackoff is the wait between retry attempts when a lock is contended.
acquireBackoff: 100ms
# acquireDeadline is the total time allowed to acquire all EID locks.
acquireDeadline: 1m
# heartbeat is the interval at which held leases are renewed (~TTL/3).
heartbeat: 10s
# owner identifies this replica. Auto-generated at startup if empty.
owner:
# sections dedicated to the definition of the wallets
wallets:
# Default cache size reference that can be used by any wallet that supports caching.
defaultCacheSize: 3
# owner wallets
owners:
- id: alice # the unique identifier of this wallet. Here is an example of use: `ttx.GetWallet(context, "alice")`
default: true # is this the default owner wallet
# path to the folder containing the cryptographic material associated with the wallet.
# The content of the folder is driver dependent.
path: /path/to/alice-wallet
# Cache size, in case the wallet supports caching (e.g., idemix-based wallet).
cacheSize: 3
- id: alice.id1
path: /path/to/alice.id1-wallet
# issuer wallets
issuers:
- id: issuer # the unique identifier of this wallet. Here is an example of use: `ttx.GetIssuerWallet(context, "issuer")`
default: true # is this the default issuer wallet
# path to the folder containing the cryptographic material associated with the wallet.
# The content of the folder is driver dependent.
path: /path/to/issuer-wallet
# additional options that can be used to instantiate the wallet.
# options are driver dependent. With `fabtoken` and `dlog` drivers,
# the following options apply.
opts:
BCCSP:
Default: SW
SW:
Hash: SHA2
Security: 256
# The following only needs to be defined if the BCCSP Default is set to PKCS11.
# NOTE: in order to use pkcs11, you have to build the application with "go build -tags pkcs11"
PKCS11:
Hash: SHA2
Label: null
Library: null
Pin: null
Security: 256
# auditor wallets
auditors:
- id: auditor # the unique identifier of this wallet. Here is an example of use: `ttx.GetAuditorWallet(context, "auditor")`
default: true # is this the default auditor wallet
# path to the folder containing the cryptographic material associated with the wallet.
# The content of the folder is driver dependent.
path: /path/to/auditor-wallet
# additional options that can be used to instantiate the wallet.
# options are driver dependent. With `fabtoken` and `dlog` drivers,
# the following options apply
opts:
BCCSP:
Default: SW
PKCS11:
Hash: SHA2
Label: null
Library: null
Pin: null
Security: 256
SW:
Hash: SHA2
# Auditor lock configuration for enrollment ID locking during audit operations
# These settings control the retry behavior when multiple auditors compete for locks
auditor:
lock:
# maxRetries is the maximum number of retry attempts for lock acquisition
# Default: 10
maxRetries: 10
# initialBackoff is the initial backoff delay before the first retry
# Default: 10ms
initialBackoff: 10ms
# maxBackoff is the maximum backoff delay between retries
# Default: 5s
maxBackoff: 5s
# backoffMultiplier is the exponential backoff multiplier
# Each retry delay is multiplied by this factor
# Default: 2.0
backoffMultiplier: 2.0
# jitterFactor is the randomization factor to prevent thundering herd (0.0 to 1.0)
# Adds random jitter to break symmetry when multiple auditors retry simultaneously
# Default: 0.3 (30%)
jitterFactor: 0.3
# auditTokensRetry controls how the auditor's early validation gate
# (AuditorCheck) tolerates the read-timing race in which a referenced
# token's producing transaction is still pending, so its outputs have not
# yet been persisted to the token store by the asynchronous finality
# listener. When a lookup misses and the producing tx is still pending,
# the gate waits and retries instead of spuriously rejecting the request.
auditTokensRetry:
# numRetries is the number of token-lookup attempts before giving up.
# There are numRetries-1 delays between attempts. Values <= 0 (or an
# omitted key) keep the default; a single attempt is always made.
# Default: 3
numRetries: 3
# retryDelay is the backoff slept between attempts, as a duration string
# (e.g. 500ms, 3s). Must be > 0; an invalid or non-positive value falls
# back to the default. The backoff honors context cancellation.
# Default: 3s
retryDelay: 3s
Panurus can start with the following minimal configuration:
token:
enabled: true
tms:
default:
network: mynetwork
namespace: mynamespace
The following fields are strictly required:
networknamespaceAll other configuration sections are optional and use sensible defaults.
If not specified, the default selector implementation is used.
Default values:
Default values:
Process-wide (not per-TMS) resource limits enforced on untrusted token requests/actions before cryptographic verification. See docs/drivers/validation-resource-limits.md for the full enforcement points and the consensus-safety contract.
If not specified, the default configuration is:
token:
validation:
limits:
maxRequestBytes: 262144
maxActions: 256
maxSignatures: 4096
maxSignatureBytes: 4096
maxActionBytes: 262144
maxInputs: 256
maxOutputs: 256
maxMetadataEntries: 64
maxMetadataKeyBytes: 256
maxMetadataValueBytes: 4096
maxProofBytes: 131072
maxIdentityDepth: 5
maxIdentityComponents: 16
Default values:
Every field is optional; any field omitted (or the whole token.validation.limits key omitted)
resolves to its default. Read via the config service, so this key applies only to the FSC/DI
runtime. The standalone Fabric chaincode process (token/services/network/fabric/tcc/main) has no
config service and instead reads the equivalent TOKEN_VALIDATION_MAX_* environment variables
(e.g. TOKEN_VALIDATION_MAX_ACTIONS), with the same unset-defaults overlay.
Consensus-safety warning: the defaults above are safe and identical across every peer that
does not override them. If you override any limit, every peer validating the same
channel/namespace — and the chaincode process, if it enforces limits independently — MUST be
configured with the identical value, or endorsement determinism silently breaks (one peer accepts
a request another rejects). Treat a limits change like a driver.MaxAnchorSize change: roll it
out as a coordinated configuration change before any peer relies on the new value.
If not specified, the default configuration is:
token:
fabricx:
lookup:
permanent:
interval: 1m
once:
deadline: 5m
interval: 2s
Default values:
If not specified, the default configuration is:
token:
tms:
<name>:
services:
network:
fabric:
recovery:
enabled: true
ttl: 30s
scanInterval: 5s
batchSize: 100
workerCount: 4
leaseDuration: 30s
advisoryLockID: 8389190333894887286
instanceID:
notFoundGracePeriod: 30m
Default values:
0x74746b7265636f76)Parameter Relationships and Tuning:
ttl are considered for recovery to avoid interfering with active transaction processing.ttl, scanInterval, batchSize, workerCount, and leaseDuration are all greater than zero.advisoryLockID is used to acquire PostgreSQL advisory-lock leadership so that only one replica performs a recovery sweep at a time. The default value (8389190333894887286 or 0x74746b7265636f76) represents the ASCII string “ttkrecov” (Token Transaction Recovery) encoded as a 64-bit integer.instanceID is used as the lease owner identifier for claimed transactions; if omitted, the manager generates a unique UUID automatically at startup.notFoundGracePeriod controls how long a transaction whose status query keeps returning NotFound is left in Pending before being promoted to the terminal Orphan status. This protects the recovery sweep from being permanently blocked by transactions whose audit log was persisted but whose broadcast never reached the ledger. The promoted row is marked Orphan rather than Deleted so operators can distinguish broadcast failures from ledger-rejected transactions. Raise the default if your network has long catch-up windows after committer/orderer restarts; set it to 0 to disable the promotion entirely.Tuning Recommendations:
batchSize to 200-500 to process more transactions per sweepworkerCount to 8-16 to improve parallel processingscanInterval to 2-3s for faster recovery detectionGlobally overrides individual SQL table short codes for all TMS instances on the node. The override value replaces the short code before the FSC formatter runs, so the FSC-generated prefix and params are still applied around it. Unknown keys are warned and ignored.
If not specified (or the section is omitted entirely), all tables use their default names.
token:
storage:
tableNames:
id_signers: identity_signers # replaces the "id_signers" short code
tokens: my_tokens # replaces the "tokens" short code
All 17 configurable short codes and their defaults:
| Short code | Default table name pattern |
|---|---|
movements |
fsc_movements_<prefix>_<params> |
txs |
fsc_txs_<prefix>_<params> |
tx_ends |
fsc_tx_ends_<prefix>_<params> |
requests |
fsc_requests_<prefix>_<params> |
req_vals |
fsc_req_vals_<prefix>_<params> |
tokens |
fsc_tokens_<prefix>_<params> |
tkn_own |
fsc_tkn_own_<prefix>_<params> |
tkn_crts |
fsc_tkn_crts_<prefix>_<params> |
tkn_locks |
fsc_tkn_locks_<prefix>_<params> |
public_params |
fsc_public_params_<prefix>_<params> |
wallets |
fsc_wallets_<prefix>_<params> |
id_cfgs |
fsc_id_cfgs_<prefix>_<params> |
id_info |
fsc_id_info_<prefix>_<params> |
id_signers |
fsc_id_signers_<prefix>_<params> |
key_store |
fsc_key_store_<prefix>_<params> |
eid_leases |
fsc_eid_leases_<prefix>_<params> |
tkn_ski_cleanups |
fsc_tkn_ski_cleanups_<prefix>_<params> |
Note: When no
TablePrefixis configured and noTableNameParamsare present, the pattern simplifies tofsc_<short_code>(e.g.fsc_id_signers).
When set to true, the FSC-generated prefix is omitted from all SQL table names.
This is useful when connecting to an existing database whose tables were created without
a prefix, or when the target database already enforces schema-level isolation and a
prefix would be redundant.
Default: false (prefix is applied as normal).
token:
storage:
skipPrefix: true
With skipPrefix: true the table name pattern changes from fsc_<short_code>_<params>
to <params>_<short_code> (params still apply when provided; short-code overrides from
tableNames are also still respected).
Caution: Enabling
skipPrefixon a node that previously used the default prefix will cause the node to look for tables under different names. Make sure the underlying tables already exist under the unprefixed names before enabling this flag.
Note:
<params>is the TMS identity (network, channel, namespace)._,-and.in those values are escaped to__,_dand_f; letters, digits and underscores pass through unchanged, so a channel namedchannel1is fine. The composed name must still be a valid SQL identifier — it cannot start with a digit, which is only reachable withskipPrefix: trueand a network name starting with a digit. Any other character is a configuration error and is reported as such, not a crash. See Table Name Customisation.
If not specified, the default configuration is:
token:
tms:
<name>:
services:
storage:
cleanup:
enabled: false
ttl: 24h
scanInterval: 1h
batchSize: 100
workerCount: 1
advisoryLockID: 8389190333894887277
instanceID:
Default values:
0x74746b636c65616e)Parameter Relationships and Tuning:
ttl are considered for cleanup to ensure tokens are truly finalized before key deletion.ttl, scanInterval, batchSize, and workerCount are all greater than zero.advisoryLockID is used to acquire PostgreSQL advisory-lock leadership so that only one replica performs a cleanup sweep at a time. The default value (8389190333894887277 or 0x74746b636c65616e) represents the ASCII string “ttkclean” (Token Transaction Keystore Cleanup) encoded as a 64-bit integer.instanceID is used to identify this replica in logs and monitoring; if omitted, the manager generates a unique identifier automatically at startup.Tuning Recommendations:
batchSize to 200-500 to process more tokens per sweepworkerCount to 8-16 to improve parallel key deletionscanInterval to 30m for more frequent cleanupworkerCount to 2 to reduce CPU usagescanInterval to 2-4h to reduce database loadbatchSize to limit memory usagettl to 12h for faster key removalscanInterval to 30m for more frequent cleanupadvisoryLockID unless running multiple independent cleanup systemsinstanceID values for easier debugging and monitoringPerformance Considerations:
scanInterval directly affects database loadworkerCount affects CPU utilization during cleanup sweepsbatchSize affects memory usage and the duration of each cleanup sweepscanInterval < ttl ensures timely cleanup without premature processingIf not specified, the default configuration is:
token:
tms:
<name>:
auditor:
lock:
maxRetries: 10
initialBackoff: 10ms
maxBackoff: 5s
backoffMultiplier: 2.0
jitterFactor: 0.3
Default values:
Parameter Descriptions:
Tuning Recommendations:
maxRetries to 15-20 to handle more lock conflictsmaxBackoff to 10s to spread out retry attemptsjitterFactor at 0.3 or higher to maintain randomizationinitialBackoff to 5ms for faster initial retriesmaxBackoff to 2s to avoid long waitsbackoffMultiplier to 3.0 for faster exponential growthmaxRetries to 5 to fail fasterbatchSize to 50 to reduce memory usageworkerCount to 2 to reduce CPU loadscanInterval to 10-15s to reduce database queriesttl to 60s or more to avoid premature recovery attemptsleaseDuration is at least 2x the expected transaction processing timeadvisoryLockID unless running multiple independent recovery systemsinstanceID values for easier debugging and monitoringPerformance Impact:
scanInterval directly affects database loadworkerCount affects CPU and network utilization during recovery sweepsbatchSize affects memory usage and the duration of each recovery sweepscanInterval < ttl ensures timely detection without premature recoveryControls how the auditor’s early validation gate (AuditorCheck) tolerates the
read-timing race in which a referenced token’s producing transaction is still
pending, so its outputs have not yet been persisted to the token store by the
asynchronous finality listener. On a missing lookup whose producing transaction is
still Pending, the gate waits retryDelay and retries up to numRetries
attempts before failing, instead of spuriously rejecting a validly-audited,
quickly-chained transaction. This mirrors the tolerance already applied on the
sibling Audit() path. Applies to both the fabtoken and dlog drivers.
If not specified, the default configuration is:
token:
tms:
<name>:
auditor:
auditTokensRetry:
numRetries: 3
retryDelay: 3s
Default values:
Parameter Descriptions:
numRetries - 1 backoff delays between them (so the defaults give a ~6s grace
window: 3 attempts, 2 delays of 3s). A value <= 0, or an omitted key, keeps the
default; at least one attempt is always made. Retries are only spent while a
referenced transaction is genuinely Pending — a hard lookup failure (or a
failure to determine pending status) fails fast without consuming the budget.500ms, 3s). Must be > 0; an invalid or non-positive value logs a
warning and falls back to the default. The backoff honors context cancellation,
so a cancelled or timed-out request returns immediately rather than pinning a
goroutine for the full grace window.Tuning Recommendations:
numRetries and/or retryDelay to widen the grace window
and further reduce spurious rejections when finality persistence lags.retryDelay (e.g. 500ms) so the audit
gate fails faster when the producing transaction never becomes final, at the cost
of tolerating a shorter persistence lag.Controls the distributed locking strategy used by the auditor to serialise concurrent access to enrollment IDs (EIDs) when processing audit records.
If not specified, the default configuration is:
token:
tms:
<name>:
auditor:
locker:
backend: memory
postgres:
ttl: 30s
acquireBackoff: 100ms
acquireDeadline: 1m
heartbeat: 10s
owner:
Default values:
memory (in-process mutex, single-replica only)config.Provider.ID()). Required
when backend: postgres — if both this value and fsc.id are empty or blank, the
locker fails to start with auditor locker owner is required (see the note below).Backend Selection:
| Backend | Use case | Database requirement |
|---|---|---|
memory |
Single-replica deployments | Any (SQLite, Postgres) |
postgres |
Multi-replica deployments | PostgreSQL only |
Notes:
postgres backend uses a dedicated lease table (created automatically) with row-level locking, heartbeat renewal, and automatic expiry. It relies on PostgreSQL-specific SQL features (ON CONFLICT DO UPDATE … RETURNING, ::interval casts, TIMESTAMPTZ).memory backend uses in-process semaphores and provides no cross-replica coordination. It is suitable for single-node or development setups.postgres, all auditor replicas must share the same PostgreSQL database so that EID locks are globally visible.heartbeat to roughly ttl / 3 to ensure leases are renewed well before expiry.owner identifies the replica holding each lease and must be non-empty and unique per replica: every lease query (acquire, release, renew, and the pre-write AssertLocksHeld check) is scoped by it. If several replicas shared one owner value, those predicates would match each other’s rows and mutual exclusion would be lost cluster-wide, so an empty or blank resolved owner is rejected at startup rather than tolerated. A typical cause is a templated node configuration with fsc.id left unset. No owner is synthesized as a fallback, because an owner that changed on each restart would leave a replica unable to renew or release the leases it still holds. The memory backend has no owner and is unaffected.