Architecture
The Quidnug reference node is a single Go binary organized around six cooperating subsystems, an HTTP+signature REST API, and a peer-to-peer gossip surface.
┌────────── Client SDKs (Py · Go · JS · Rust · …) ─────────┐ │ │ ▼ ▼ ┌───────────────────────────────────────────────────────────────┐ │ HTTP REST API · /api/v1 · /api/v2 │ └───────────────────────────────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────────┐ │ QuidnugNode (Go) │ │ │ │ Trust Engine · Nonce Ledger · Guardian Registry │ │ Block Engine · Push Gossip · Bootstrap / Forks │ └───────────────────────────────────────────────────────────────┘ ▲ ▲ ▲ ▲ HTTP+sig Gossip Probes Snapshot pull │ │ │ │ ▼ ▼ ▼ ▼ ┌───────────────────────────────────────────────────────────────┐ │ Peer QuidnugNode instances (P2P) │ └───────────────────────────────────────────────────────────────┘Subsystems
Section titled “Subsystems”Trust Engine, computes trust along depth-bounded BFS paths in the trust graph. Paths are cached and invalidated when new trust edges land. See trust.
Nonce Ledger, enforces per-signer monotonic nonce ordering. See QDP-0001.
Guardian Registry, tracks the M-of-N quorum for each subject, in-flight recoveries, time-locks, and resignations. See QDP-0002 and QDP-0006.
Block Engine, validates, tiers, and applies blocks using Proof-of-Trust acceptance. See consensus.
Push Gossip, propagates fresh rotations to interested peers in seconds rather than polling cycles. See QDP-0005.
Bootstrap / Forks, fresh nodes seed state from a quorum of trusted peers (K-of-K); operators coordinate protocol upgrades at future block heights; light clients verify anchors via compact Merkle proofs. See QDP-0008, QDP-0009, QDP-0010.
Go package layout
Section titled “Go package layout”cmd/quidnug/, the binary entry point.internal/core/, the node runtime (subsystems above).pkg/client/, the Go SDK.pkg/signer/{hsm,webauthn}/, pluggable signing backends.schemas/, language-agnostic JSON schemas for canonical-bytes verification across SDKs.
API surface
Section titled “API surface”/api/v1, core: identities, trust, titles, events, anchors, queries./api/v2, guardians, push gossip, bootstrap, Merkle proofs./api/health, liveness + readiness./api/info, node version and feature flags./metrics, Prometheus scrape endpoint.
The full OpenAPI 3.0 spec lives in
docs/openapi.yaml.
A Postman collection at
docs/postman/quidnug.postman_collection.json
mirrors every endpoint.
Further reading
Section titled “Further reading”- Rogue-node security, how the protocol handles hostile peers in a consortium.
- Integration guide, deployment topologies (single node, three-node consortium, TLS termination, HMAC auth, observability).
- Design proposals, the numbered QDPs that govern protocol evolution.
Quidnug Architecture
Section titled “Quidnug Architecture”Overview
Section titled “Overview”Quidnug is a trust protocol implementation consisting of a Go-based node server and optional client libraries. This document describes the internal architecture for developers implementing or extending Quidnug.
Component Architecture
Section titled “Component Architecture”┌─────────────────────────────────────────────────────────────┐│ HTTP Layer ││ handlers.go (API endpoints) + middleware.go (rate limiting) │└─────────────────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────┐│ Business Logic ││ node.go (QuidnugNode) - Transaction processing, blocks │└─────────────────────────────────────────────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐│ validation.go │ │ registry.go │ │ crypto.go ││ Tx validation │ │ State storage │ │ ECDSA signing │└─────────────────┘ └─────────────────┘ └─────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────┐│ Network Layer ││ network.go - Node discovery, broadcasting, cross-domain │└─────────────────────────────────────────────────────────────┘Core Components
Section titled “Core Components”QuidnugNode (node.go)
Section titled “QuidnugNode (node.go)”The central struct managing all node state:
- Blockchain: Ordered list of validated blocks
- PendingTxs: Transactions awaiting inclusion in blocks
- TrustDomains: Domains this node manages/participates in
- KnownNodes: Discovered network peers
- Registries: Materialized views of blockchain state (Trust, Identity, Title)
Each registry has its own sync.RWMutex for granular concurrent access.
Relational Trust Model
Section titled “Relational Trust Model”Trust in Quidnug is relational, not absolute. There is no global “trust score” for any quid-trust is always computed dynamically from an observer’s perspective to a target.
Key Principles
Section titled “Key Principles”-
Observer → Target: Every trust query specifies an observer (who is asking) and a target (who is being assessed). The same target may have different trust levels when viewed by different observers.
-
Transitive with Multiplicative Decay: Trust propagates through the network with decay. If A trusts B at 0.8 and B trusts C at 0.7, then A’s transitive trust in C is 0.8 × 0.7 = 0.56.
-
Best Path Selection: When multiple paths exist, the algorithm finds and returns the path with maximum trust.
-
Same Entity: An observer has full trust (1.0) in itself.
-
No Path: If no path exists from observer to target, trust level is 0.
Trust Graph Structure
Section titled “Trust Graph Structure”Trust edges are stored in the registry:
// TrustRegistry maps: truster -> trustee -> trustLevelTrustRegistry map[string]map[string]float64Trust values are computed on-demand via ComputeRelationalTrust():
type RelationalTrustResult struct { Observer string // Who is asking Target string // Who is being assessed TrustLevel float64 // Computed transitive trust (0.0-1.0) TrustPath []string // Path of quid IDs for best route PathDepth int // Number of hops Domain string // Trust domain (optional)}Transaction Types (types.go)
Section titled “Transaction Types (types.go)”| Type | Purpose | Key Fields |
|---|---|---|
TRUST | Establish trust between quids | truster, trustee, trustLevel (0.0-1.0) |
IDENTITY | Define quid attributes | quidId, name, attributes, updateNonce |
TITLE | Declare asset ownership | assetId, owners (must sum to 100%) |
EVENT | Record events for subjects | subjectId, subjectType, sequence, eventType, payload/payloadCid |
All transactions require cryptographic signatures (ECDSA P-256).
Validation (validation.go)
Section titled “Validation (validation.go)”Transactions are validated before entering the pending pool:
- Trust Domain Check: Domain must exist or be empty (default domain)
- Signature Verification: ECDSA P-256 signature must be valid
- Format Validation: Quid IDs must be 16-char lowercase hex
- Business Rules:
- Trust levels: 0.0 <= level <= 1.0, no NaN/Inf
- Identity updates:
updateNoncemust increase - Titles: ownership percentages must sum to exactly 100.0
- Events: see Event Transaction validation below
Event Transaction Validation
Section titled “Event Transaction Validation”Event transactions have additional validation rules:
| Rule | Description |
|---|---|
| TrustDomain | Must exist (no empty/default domain for events) |
| SubjectID | Must be valid 16-character hex quid format |
| SubjectType | Must be "QUID" or "TITLE" |
| EventType | Required, maximum 64 characters |
| Payload/PayloadCID | Either payload or payloadCid must be provided (not both empty) |
| PayloadCID Format | If provided, must be a valid IPFS CID (CIDv0 or CIDv1) |
| Payload Size | Inline payload maximum 64KB (MaxPayloadSize) |
| Sequence | Must be monotonically increasing (> LatestSequence, or 0/1 for new streams) |
| Signature | Required, signer must be the subject owner |
Block Validation with Relational Trust
Section titled “Block Validation with Relational Trust”Validators assess blocks based on their relational trust in the block creator:
type TrustProof struct { TrustDomain string // Domain for this block ValidatorID string // QuidID of the validator ValidatorTrustInCreator float64 // Validator's relational trust in block creator ValidatorSigs []string ValidationTime int64}The ValidatorTrustInCreator field is computed at validation time using ComputeRelationalTrust(validatorID, creatorID, maxDepth). This means:
- Different validators may have different trust levels for the same block creator
- Trust is evaluated dynamically, reflecting the current state of the trust graph
- There is no static “trust score” stored for any quid
Block Generation
Section titled “Block Generation”Blocks are generated periodically (configurable via BLOCK_INTERVAL):
- Filter pending transactions by trust domain
- Create block with trust proof (validator signature)
- Add to blockchain
- Process transactions to update registries
- Broadcast to domain peers
Network Operations (network.go)
Section titled “Network Operations (network.go)”Node Discovery: Periodically queries seed nodes for peer lists.
Transaction Broadcasting: Fire-and-forget POST to domain peers.
Cross-Domain Queries: Hierarchical domain walking (e.g., sub.domain.com -> domain.com -> com) to find authoritative nodes.
Domain Gossip Protocol
Section titled “Domain Gossip Protocol”Nodes advertise their supported domains to the network using a gossip protocol. This enables efficient discovery of which nodes support which domains without requiring centralized coordination.
Purpose
Section titled “Purpose”The Domain Gossip Protocol allows nodes to:
- Announce the trust domains they support to the network
- Discover other nodes that support specific domains
- Route domain-specific queries to appropriate nodes
- Maintain an up-to-date view of the network’s domain topology
DomainGossip Structure
Section titled “DomainGossip Structure”type DomainGossip struct { NodeID string `json:"nodeId"` // QuidID of the originating node Domains []string `json:"domains"` // List of domains the node supports Timestamp int64 `json:"timestamp"` // Unix timestamp when gossip was created TTL int `json:"ttl"` // Time-to-live (maximum hop count) HopCount int `json:"hopCount"` // Current hop count (incremented on forward) MessageID string `json:"messageId"` // Unique identifier for deduplication}| Field | Description |
|---|---|
NodeID | QuidID of the node that originally created this gossip message |
Domains | Array of domain names the originating node supports |
Timestamp | Unix timestamp (seconds) when the gossip was created |
TTL | Maximum number of hops before the message stops propagating |
HopCount | Number of hops this message has traveled (starts at 0) |
MessageID | Unique identifier used to prevent duplicate processing |
Protocol Flow
Section titled “Protocol Flow”┌─────────────────────────────────────────────────────────────────┐│ Domain Gossip Flow │└─────────────────────────────────────────────────────────────────┘
Node A Node B Node C │ │ │ │ 1. Create gossip │ │ │ (HopCount=0) │ │ │ │ │ │──── POST /api/gossip/domains ────►│ │ │ │ │ │ 2. Check MessageID │ │ in GossipSeen │ │ │ │ │ 3. Process & store │ │ domain info │ │ │ │ │ 4. HopCount < TTL? │ │ Yes: Forward │ │ │ │ │ │──── Forward (HopCount+1) ────►│ │ │ │ │ │ 5. Check MessageID │ │ (already seen?) │ │ │ │ │ 6. Process if newBroadcast Cycle
Section titled “Broadcast Cycle”- Periodic Broadcasting: Each node runs a background goroutine (
runDomainGossip) that broadcasts domain info at configurable intervals - Gossip Creation:
createDomainGossip()generates a new message with:- The node’s QuidID
- Current supported domains (from
SupportedDomainsconfig or registeredTrustDomains) - Current Unix timestamp
- Configured TTL from
GossipTTL - HopCount starting at 0
- Unique MessageID (UUID)
- Broadcast:
BroadcastDomainInfo()sends the gossip to all known nodes
Reception and Forwarding
Section titled “Reception and Forwarding”When a node receives gossip via ReceiveDomainGossip():
- Validation: Rejects gossip with empty
NodeIDor negativeTTL - Self-Check: Ignores gossip originating from itself
- Deduplication: Checks
GossipSeenmap usinghasSeenGossip(messageID)- If seen: Silently ignore (prevents loops and duplicate processing)
- If new: Mark as seen with
markGossipSeen(messageID)and continue
- Processing:
processDomainGossip()updates theDomainRegistrywith the originator’s domain information - Forwarding: If
HopCount < TTL, incrementHopCountand forward to known nodes (excluding the originator)
Deduplication and Cleanup
Section titled “Deduplication and Cleanup”The GossipSeen map tracks processed messages:
GossipSeen map[string]int64 // MessageID -> timestamp when seenGossipSeenMutex sync.RWMutex // Protects concurrent accesshasSeenGossip(messageID): Returns true if the message was already processedmarkGossipSeen(messageID): Records the message ID with current timestampcleanupGossipSeen(): Periodically removes old entries to prevent unbounded growth
Configuration
Section titled “Configuration”| Environment Variable | Default | Description |
|---|---|---|
DOMAIN_GOSSIP_INTERVAL | 2m | Interval between gossip broadcasts |
DOMAIN_GOSSIP_TTL | 3 | Maximum hop count before gossip stops propagating |
Example configuration:
# Broadcast domain info every 5 minutes, allow up to 5 hopsDOMAIN_GOSSIP_INTERVAL=5m DOMAIN_GOSSIP_TTL=5 ./quidnug-nodeOr in config.yaml:
domain_gossip_interval: "2m"domain_gossip_ttl: 3API Endpoints
Section titled “API Endpoints”| Method | Endpoint | Description |
|---|---|---|
GET | /api/node/domains | Get this node’s currently supported domains |
POST | /api/node/domains | Update this node’s supported domains (triggers immediate gossip) |
POST | /api/gossip/domains | Receive domain gossip from another node |
GET /api/node/domains
Section titled “GET /api/node/domains”Returns the node’s supported domains:
{ "nodeId": "a1b2c3d4e5f6g7h8", "domains": ["example.com", "api.example.com"]}POST /api/node/domains
Section titled “POST /api/node/domains”Update supported domains and trigger immediate gossip broadcast:
{ "domains": ["example.com", "api.example.com", "new.example.com"]}POST /api/gossip/domains
Section titled “POST /api/gossip/domains”Receives gossip from other nodes (node-to-node communication):
{ "nodeId": "b2c3d4e5f6g7h8i9", "domains": ["other.com"], "timestamp": 1699900000, "ttl": 3, "hopCount": 1, "messageId": "550e8400-e29b-41d4-a716-446655440000"}Thread Safety
Section titled “Thread Safety”The gossip protocol uses dedicated mutexes:
| Mutex | Protects |
|---|---|
GossipSeenMutex | GossipSeen map for deduplication |
DomainRegistryMutex | DomainRegistry map storing node→domains mappings |
KnownNodesMutex | KnownNodes map when iterating for broadcast |
Cryptographic Operations (crypto.go)
Section titled “Cryptographic Operations (crypto.go)”| Function | Purpose |
|---|---|
SignData(data []byte) | Sign with node’s private key (ECDSA P-256) |
VerifySignature(pubKey, data, sig) | Verify signature against public key |
calculateBlockHash(block) | SHA-256 hash of block contents |
Key Format: Public keys are uncompressed P-256 (65 bytes: 0x04 || X || Y), hex-encoded.
Signature Format: 64 bytes (r || s, each 32 bytes), hex-encoded.
IPFS Integration
Section titled “IPFS Integration”Quidnug integrates with IPFS for storing large event payloads that exceed the inline size limit.
IPFSClient Interface
Section titled “IPFSClient Interface”type IPFSClient interface { Pin(ctx context.Context, data []byte) (cid string, err error) Get(ctx context.Context, cid string) (data []byte, err error) IsAvailable() bool}| Method | Purpose |
|---|---|
Pin(ctx, data) | Store data in IPFS and return the content identifier (CID) |
Get(ctx, cid) | Retrieve data from IPFS by CID |
IsAvailable() | Check if IPFS integration is enabled and reachable |
Implementations
Section titled “Implementations”| Implementation | Description |
|---|---|
HTTPIPFSClient | Production client that communicates with an IPFS gateway via HTTP API |
NoOpIPFSClient | Stub implementation when IPFS is disabled; IsAvailable() returns false |
The implementation is selected at startup based on the QUIDNUG_IPFS_ENABLED configuration:
- When
QUIDNUG_IPFS_ENABLED=true:HTTPIPFSClientconnects toQUIDNUG_IPFS_GATEWAY_URL - When
QUIDNUG_IPFS_ENABLED=false(default):NoOpIPFSClientis used
Payload Storage Strategy
Section titled “Payload Storage Strategy”Event payloads can be stored in two ways:
| Strategy | Condition | Storage |
|---|---|---|
| Inline | Payload ≤ 64KB (MaxPayloadSize) | Stored directly in the payload field |
| IPFS | Payload > 64KB or explicitly pinned | Content stored in IPFS, CID in payloadCid field |
When processing an event with payloadCid:
- The node retrieves the content from IPFS using
IPFSClient.Get() - Content is validated and processed
- Applications can cache retrieved payloads locally
Note: If IPFS is unavailable and only payloadCid is provided, the event data cannot be fully retrieved until IPFS becomes available.
State Persistence
Section titled “State Persistence”The node persists everything that should survive a restart to DATA_DIR.
File-by-file:
| File | Owner | Lifecycle |
|---|---|---|
node_key.json | state_persist.go:loadOrCreateNodeKey | Generated on first boot; loaded on every subsequent boot. NodeID = sha256(publicKey)[:16] is stable across restarts. ID is cross-checked against the public key, a tampered file refuses boot. |
blockchain.json | state_persist.go:SaveBlockchain | Snapshot of the full block history. Saved every 30 s by runStatePersistLoop + on shutdown. Loaded after NewQuidnugNode builds the genesis-only chain, replacing it with the persisted history. |
trust_domains.json | state_persist.go:SaveTrustDomains | Snapshot of TrustDomains + DomainRegistry. Same lifecycle as blockchain.json. Dynamic POST /api/v1/domains registrations land here. |
pending_transactions.json | persistence.go:SavePendingTransactions | Pending tx queue. Saved on shutdown, restored on boot. |
peer_scores.json | peer_score.go:persistOnce | Per-peer composite scores, severe-event totals, quarantine state, and the recent-event ring. Snapshot every 5 min + on shutdown. |
audit-log.jsonl (optional) | internal/audit | Append-only operator audit log. Path is configurable via audit_log_path. |
All writes are atomic (tmp file + rename) through internal/safeio so
a crash mid-flush can’t leave a corrupt file. Files are versioned
(schemaVersion: 1); future bumps fail loudly rather than silently
regenerating.
Operator-quid file (OPERATOR_QUID_FILE) is intentionally outside
DATA_DIR. It carries the long-lived operator identity that may be
shared across multiple nodes the same operator runs, and is typically
read-only-mounted at /etc/quidnug/operator.quid.json. The
operator quid is separate from node_key.json: the per-process
NodeID identifies this specific instance for gossip dedup and fork
detection, while the operator quid is the identity that accumulates
trust grants. See README.md for the
operator-vs-node split and the home-operator playbook for the
provisioning flow.
HTTP Middleware (middleware.go)
Section titled “HTTP Middleware (middleware.go)”- Rate Limiting: Token bucket per IP (default 100 req/min)
- Body Size Limit: Prevents oversized payloads (default 1MB)
- Input Validation: Quid ID format, string sanitization
Relational Trust Algorithm (registry.go)
Section titled “Relational Trust Algorithm (registry.go)”The ComputeRelationalTrust function computes transitive trust from an observer to a target:
func (node *QuidnugNode) ComputeRelationalTrust( observer, target string, maxDepth int,) (float64, []string, error)Algorithm Details
Section titled “Algorithm Details”- BFS Traversal: Uses breadth-first search starting from the observer
- Multiplicative Decay: Path trust = product of all edge trust levels along the path
- Cycle Avoidance: Tracks visited nodes in each path to prevent infinite loops
- Best Path Selection: Returns the maximum trust found across all explored paths
- Depth Limiting: Respects
maxDepthparameter (defaults to 5 if not specified)
Example Computation
Section titled “Example Computation”Trust Graph: A → B (0.8) A → C (0.6) B → D (0.7) C → D (0.9)
Query: ComputeRelationalTrust("A", "D", 5)
Paths explored: A → B → D: 0.8 × 0.7 = 0.56 A → C → D: 0.6 × 0.9 = 0.54
Result: TrustLevel: 0.56 (maximum) TrustPath: ["A", "B", "D"] PathDepth: 2Special Cases
Section titled “Special Cases”| Scenario | Result |
|---|---|
| Observer equals target | TrustLevel: 1.0, Path: [observer] |
| No path exists | TrustLevel: 0.0, Path: empty |
| Direct trust only | TrustLevel: direct edge value, Path: [observer, target] |
Event Stream Registry
Section titled “Event Stream Registry”The node maintains registries for tracking event streams associated with quids and titles.
Data Structures
Section titled “Data Structures”// Metadata about each subject's event streamEventStreamRegistry map[string]*EventStream // keyed by subjectId
// Actual event transactions for each subjectEventRegistry map[string][]EventTransaction // keyed by subjectIdEventStream Metadata
Section titled “EventStream Metadata”type EventStream struct { SubjectID string `json:"subjectId"` // Quid or asset ID SubjectType string `json:"subjectType"` // "QUID" or "TITLE" LatestSequence int64 `json:"latestSequence"` // Highest sequence number EventCount int64 `json:"eventCount"` // Total events in stream CreatedAt int64 `json:"createdAt"` // Unix timestamp of first event UpdatedAt int64 `json:"updatedAt"` // Unix timestamp of last event LatestEventID string `json:"latestEventId"` // Transaction ID of most recent event}Registry Update Process
Section titled “Registry Update Process”When processing a block containing event transactions, updateEventStreamRegistry performs:
- Lock acquisition: Acquires write lock on
EventStreamMutex - Stream initialization: Creates new
EventStreamif subject has no prior events - Event append: Adds the
EventTransactiontoEventRegistry[subjectId] - Metadata update: Updates
EventStreamRegistrywith:- Incremented
EventCount - Updated
LatestSequenceto the event’s sequence - Updated
UpdatedAttimestamp - Updated
LatestEventID
- Incremented
Querying Events
Section titled “Querying Events”| Function | Purpose |
|---|---|
GetEventStream(subjectId) | Returns stream metadata, or false if no stream exists |
GetStreamEvents(subjectId, limit, offset) | Returns paginated events ordered by sequence (ascending) |
Events are returned in sequence order to support chronological replay of a subject’s history.
Proof of Trust Consensus
Section titled “Proof of Trust Consensus”Quidnug implements a novel consensus mechanism called Proof of Trust where block validation is subjective-each node validates blocks based on its own relational trust in the block’s validator.
Cryptographic vs Trust Validation
Section titled “Cryptographic vs Trust Validation”Block validation is split into two distinct phases:
┌─────────────────────────────────────────────────────────────────┐│ ValidateBlockCryptographic() ││ • Hash verification ││ • Signature verification ││ • Chain linkage (prevHash, index) ││ • Transaction format validation ││ ││ UNIVERSAL: All honest nodes agree on this │└─────────────────────────────────────────────────────────────────┘ │ ▼┌─────────────────────────────────────────────────────────────────┐│ ValidateTrustProofTiered() ││ • Compute relational trust: node → validator ││ • Compare against domain threshold ││ • Return acceptance tier ││ ││ SUBJECTIVE: Different nodes may have different results │└─────────────────────────────────────────────────────────────────┘This separation allows nodes to:
- Agree on cryptographic validity (objective)
- Disagree on whether to accept the block (subjective, based on trust)
Tiered Block Acceptance
Section titled “Tiered Block Acceptance”The BlockAcceptance type defines four tiers:
type BlockAcceptance int
const ( BlockTrusted BlockAcceptance = iota // Integrate into main chain BlockTentative // Store separately, don't build on BlockUntrusted // Extract data, relay, don't store block BlockInvalid // Reject entirely)ReceiveBlock() Decision Flow
Section titled “ReceiveBlock() Decision Flow” Incoming Block │ ▼ ┌───────────────────────┐ │ ValidateBlockCrypto() │ └───────────────────────┘ │ ┌─────┴─────┐ │ Valid? │ └─────┬─────┘ No │ Yes ┌───────────┴───────────┐ ▼ ▼ BlockInvalid ┌────────────────────────┐ │ Extract trust edges │ │ (store as unverified) │ └────────────────────────┘ │ ▼ ┌────────────────────────┐ │ ValidateTrustProof │ │ Tiered() │ └────────────────────────┘ │ ┌───────────────────────┼───────────────────────┐ │ │ │ ▼ ▼ ▼ trust >= threshold distrust < trust trust <= distrust │ < threshold │ ▼ │ ▼ BlockTrusted ▼ BlockUntrusted │ BlockTentative │ │ │ │ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ Add to main │ │ Store in │ │ Edges already │ │ chain │ │ TentativeBlocks │ │ extracted │ │ Process txs │ │ Don't build on │ │ Block discarded │ │ Promote edges │ └─────────────────┘ └─────────────────┘ │ to verified │ └─────────────────┘Dual-Layer Trust Registry
Section titled “Dual-Layer Trust Registry”The node maintains two separate trust edge registries:
// Edges from trusted validators (high-assurance decisions)VerifiedTrustEdges map[string]map[string]TrustEdge
// Edges from all cryptographically valid blocks (discovery with discounting)UnverifiedTrustRegistry map[string]map[string]TrustEdgeWhen to Use Each Registry
Section titled “When to Use Each Registry”| Registry | Source | Use Case |
|---|---|---|
| VerifiedTrustEdges | Blocks from trusted validators | High-stakes decisions, authentication |
| UnverifiedTrustRegistry | All cryptographically valid blocks | Discovery, exploration, lower-stakes queries |
When querying trust with includeUnverified=true, the algorithm can traverse unverified edges but applies appropriate discounting to the trust levels.
Trust Edge Provenance
Section titled “Trust Edge Provenance”Every trust edge tracks its origin:
type TrustEdge struct { Truster string // Who is trusting Trustee string // Who is being trusted TrustLevel float64 // Trust level 0.0-1.0 SourceBlock string // Block hash where this edge was recorded ValidatorQuid string // Quid of validator who signed the block Verified bool // True if from a trusted validator Timestamp int64 // When recorded}This provenance enables:
- Auditing trust relationships back to their source
- Promoting edges when validator trust changes
- Demoting edges if a validator becomes untrusted
Tentative Block Storage
Section titled “Tentative Block Storage”Blocks from partially-trusted validators are stored separately:
TentativeBlocks map[string][]Block // keyed by trust domainManagement Functions
Section titled “Management Functions”| Function | Purpose |
|---|---|
StoreTentativeBlock(block) | Add block to tentative storage |
GetTentativeBlocks(domain) | Retrieve tentative blocks for a domain |
ReEvaluateTentativeBlocks(domain) | Check if any can now be promoted |
Promotion Flow
Section titled “Promotion Flow”When trust relationships change (e.g., you establish trust in a new validator), call ReEvaluateTentativeBlocks() to check if any tentative blocks can now be promoted to the main chain:
// After establishing trust in a new validatornode.AddTrustTransaction(trustTx)node.ReEvaluateTentativeBlocks("example.com")Trust-Aware Transaction Filtering
Section titled “Trust-Aware Transaction Filtering”During block generation, FilterTransactionsForBlock() ensures only trusted transactions are included:
func (node *QuidnugNode) FilterTransactionsForBlock( txs []interface{}, domain string,) []interface{}For each pending transaction:
- Extract the creator quid (truster, creator, or first owner depending on type)
- Compute relational trust from node to creator
- Include only if
trustLevel >= node.TransactionTrustThreshold
This prevents a node from propagating transactions it doesn’t trust, even if they’re cryptographically valid.
Enhanced Trust Queries
Section titled “Enhanced Trust Queries”The EnhancedTrustResult provides additional provenance information:
type EnhancedTrustResult struct { RelationalTrustResult Confidence string // "high", "medium", "low" UnverifiedHops int // Number of unverified edges traversed VerificationGaps []VerificationGap // Details of unverified hops}
type VerificationGap struct { From string // Source quid of the gap To string // Target quid of the gap ValidatorQuid string // Validator who recorded this edge ValidatorTrust float64 // Node's trust in that validator}Confidence levels are determined by:
- High: All edges in path are verified
- Medium: Some unverified edges, but validators have partial trust
- Low: Significant unverified hops or low validator trust
Thread Safety
Section titled “Thread Safety”The codebase uses granular mutexes following Go best practices:
| Mutex | Protects |
|---|---|
BlockchainMutex | Blockchain slice |
PendingTxsMutex | PendingTxs slice |
TrustDomainsMutex | TrustDomains map |
KnownNodesMutex | KnownNodes map |
TrustRegistryMutex | TrustRegistry map |
IdentityRegistryMutex | IdentityRegistry map |
TitleRegistryMutex | TitleRegistry map |
TentativeBlocksMutex | TentativeBlocks map |
UnverifiedRegistryMutex | UnverifiedTrustRegistry map |
EventStreamMutex | EventStreamRegistry and EventRegistry maps |
Always acquire read locks for queries and write locks for mutations.
Note: ComputeRelationalTrust acquires a read lock on TrustRegistryMutex via GetDirectTrustees() for each node visited during BFS traversal.