Skip to content

Decentralized Credit & Reputation

**FinTech · Consumer rights · Anti-social-credit · Data sovereignty**

Overview

Decentralized Credit & Reputation

FinTech · Consumer rights · Anti-social-credit · Data sovereignty

A replacement for credit bureaus and social-credit systems. No central scorer. No single database that can leak. No universal number that follows you forever. Instead: a cryptographically-signed, borrower-owned history where every potential lender computes their own view and the borrower controls who sees what.


The two problems, one shape

Credit bureaus (Equifax / Experian / TransUnion, and their global peers)

Three private companies hold the financial-reputation destinies of hundreds of millions of people. The status-quo is well- documented to be broken:

  • Errors are rampant. FTC studies find 20–25% of credit reports contain errors; ~5% contain errors significant enough to cause loan denial. Correcting them takes 30–180 days of disputes, and bureaus often fail to investigate at all, merely forwarding the dispute to the lender who made the claim.
  • Breaches are catastrophic. The 2017 Equifax breach exposed SSNs, birthdates, addresses, and credit details of 147 million Americans. A centralized database is a centralized target.
  • Opacity. The FICO scoring formula is proprietary. VantageScore is proprietary. Consumers can’t replicate the calculation. “Why was I denied?” is answered with a boilerplate reason code, not the actual math.
  • Thin-file exclusion. Young adults, immigrants, the underbanked, and anyone who avoids credit cards has no score, and no access to mainstream credit, because the bureau data model doesn’t recognize alternative indicators of creditworthiness.
  • You don’t own the data. Credit bureaus gather information about you without consent, sell it, and you have no mechanism to opt out of the system.
  • Universal score. The same 720 follows you to the mortgage lender, the car dealer, the apartment landlord, the business-loan underwriter. Each has very different risk models, but the score doesn’t know that.
  • Gaming. “Authorized user” scams, credit-repair manipulators, and thin-to-thick-file tricks exist because the scoring is formulaic and gameable.

Social-credit systems (state-operated reputation)

Some jurisdictions have experimented with state-run reputation systems that aggregate financial, legal, behavioral, and social data into a single score that gates access to travel, loans, jobs, school admissions, and more.

Even where critics of these systems and proponents disagree on whether they “work,” both sides agree on the structural risks:

  • Concentration of judgment. A single authority decides what is “good citizenship” and what is not. Dissent becomes a score penalty.
  • Irreversible effects. A low score can lock someone out of public services with no clear path to rehabilitate.
  • Behavioral drift. Citizens self-censor because anything might affect the score.
  • Due process vacuum. Appeals and dispute mechanisms are ad hoc; the algorithm is the judge.
  • Discriminatory aggregation. Political activity, social associations, religious practice, whatever the authority considers “bad”, can accumulate into economic exclusion.

Why they share a shape

Both systems concentrate the power to judge in one entity and produce a universal number that affects access to opportunity. The judge has no skin in the game for your outcomes. The number follows you without your consent. Fixing errors means petitioning the judge.

The root problem is architectural: centralized judgment applied to an entire population.


The Quidnug alternative: relational trust + signed history

In Quidnug, there is no score. There is:

  1. A history of signed events attached to the subject (the borrower/citizen): loans taken, payments made, defaults, renegotiations, alternative-data attestations.
  2. Trust edges from lenders/attesters to the subject in specific domains: credit.mortgage.us, credit.auto-loan.us, credit.small-business.consumer-goods, etc.
  3. Each potential counterparty computes their own evaluation using their own trust graph. Lender A’s view of a borrower might be 0.85; Lender B’s view of the same borrower might be 0.60; neither is “the score”, both are correct from each lender’s perspective.

The subject owns their history. They control who sees it. Lenders build reputation by their own accuracy; a lender who files false defaults finds other lenders devaluing their attestations. Government observers can watch public events but have no protocol authority to impose a universal score.

Mapping the problems to Quidnug primitives

ProblemQuidnug primitive
”Bureau is a central point of failure”No bureau. Subject’s quid is the canonical id.
”Errors take months to fix”Dispute event on-chain; visible to all counterparties instantly.
”I don’t own my data”Subject’s quid + borrower-controlled encryption keys
”One score everywhere”Per-domain relational trust; different views per lender
”Thin-file exclusion”Alternative data sources (utilities, rent) as first-class signers
”Opaque scoring formula”Each lender’s algorithm is their own; consumers can run their own.
”Social-credit concentration”No protocol authority to issue a universal score
”Data breach exposure”Distributed + encrypted off-chain payloads
”Lender defamation”Dispute + lender reputation via consumer trust
”Lender collusion (fake credit)“Cross-lender trust means colluders need to trick everyone

Core design

Quid types

Quid typeOwned byExample
SubjectThe individual / business (BYOQ)subject-alice-chen-xyz
LenderA financial institutionlender-chase-bank
Alt-data sourceUtility, landlord, employerutility-con-edison-nyc
Identity verifierKYC provider / governmentverifier-dmv-texas
Dispute arbiterVoluntary mediator (CFPB-style, private)arbiter-consumer-financial-watch
GuarantorA party that co-signs / underwritesguarantor-parent-bob-chen

All are regular Quidnug quids. What distinguishes them is the domains they operate in and the trust edges others have issued to them.

Domain hierarchy

credit (top-level)
├── credit.reports (generic credit-event stream)
├── credit.mortgage.<country>
├── credit.auto-loan.<country>
├── credit.personal-loan.<country>
├── credit.small-business.<country>
├── credit.credit-card.<country>
├── credit.student-loan.<country>
├── credit.alternative-data.utilities
├── credit.alternative-data.rent
├── credit.alternative-data.employment
├── credit.disputes
└── credit.identity-verification

Each lender / attester / verifier operates in the domains where they have legitimate authority. A utility company signs events in credit.alternative-data.utilities but not in credit.mortgage.us.

The three things that replace a credit bureau

1. Credit events (the “history”)

Every financial event between a subject and a counterparty is a signed event on the subject’s event stream. The on-chain record carries only metadata; the sensitive details are encrypted off-chain.

{
"subjectId": "subject-alice-chen-xyz",
"subjectType": "QUID",
"eventType": "credit.loan.originated",
"payload": {
"counterparty": "lender-chase-bank",
"category": "auto-loan",
"principalBand": "20k-30k", // coarse public bucket
"termMonths": 60,
"originationDate": 1713400000,
"detailCID": "bafy...", // encrypted blob, IPFS
"detailHash": "<sha256>", // binds encrypted blob
"accessGrantPolicy": "subject-approved-only"
},
"signer": "lender-chase-bank"
}

Subsequent events on the same subject’s stream:

credit.loan.payment-received (signed by lender)
credit.loan.payment-late (signed by lender, within dispute window)
credit.loan.default-declared (signed by lender, with dispute window)
credit.loan.restructured (signed by lender + subject)
credit.loan.paid-off (signed by lender)
credit.dispute.opened (signed by subject)
credit.dispute.resolved (signed by subject + lender)
credit.guarantor.cosigned (signed by guarantor)

Each event is append-only, signed by its actor, and time-ordered. Over years, a subject accumulates a rich history.

2. Trust edges (the “score inputs”)

After each completed credit relationship, the lender issues a trust edge to the subject in the relevant domain:

{
"truster": "lender-chase-bank",
"trustee": "subject-alice-chen-xyz",
"trustLevel": 0.92,
"domain": "credit.auto-loan.us",
"description": "60-month auto loan, paid as agreed, zero late payments",
"validUntil": 1776844800,
"nonce": 47
}
  • trustLevel reflects the lender’s view. A lender who was paid on time + early would issue a higher level than one who was paid late-but-eventually.
  • validUntil is typically 2–5 years; after that the edge expires unless reaffirmed. Forces fresh data over ancient.
  • domain scopes the endorsement. A mortgage lender issuing trust in credit.auto-loan.us doesn’t automatically endorse the borrower for credit.mortgage.us.
  • nonce provides replay protection and lets the lender update (e.g., downgrade if they later reopen a claim).

Alternative-data sources issue the same kinds of edges in their own domains:

{
"truster": "utility-con-edison-nyc",
"trustee": "subject-alice-chen-xyz",
"trustLevel": 0.88,
"domain": "credit.alternative-data.utilities",
"description": "36 months on-time payment history",
"validUntil": 1776844800
}

3. Per-lender relational trust evaluation (the “score”)

When a prospective lender evaluates an applicant, they run their own relational-trust computation:

Chase-Auto-Lending: evaluating Alice for a new auto loan.
Direct history:
Chase ──0.92──► Alice (prior auto loan, paid as agreed)
→ direct trust: 0.92 (strongest signal)
Transitive history:
Wells-Fargo ──0.85──► Alice (mortgage, paid)
Capital-One ──0.80──► Alice (credit card, paid)
Chase already trusts Wells-Fargo and Capital-One as credible
lenders (the industry-peer trust graph).
Chase ──0.9──► Wells-Fargo
Chase ──0.9──► Capital-One
Transitive trust via Wells-Fargo: 0.9 × 0.85 = 0.765
Transitive trust via Capital-One: 0.9 × 0.80 = 0.72
Alternative-data:
Con-Edison ──0.88──► Alice
Chase trusts Con-Edison in utilities-payment domain at 0.7.
Transitive: 0.7 × 0.88 = 0.616
Aggregation (Chase's own formula):
max of direct/transitive = 0.92
sum of alt-data reinforcement = +0.05
Final: 0.97
Chase's decision: approve at standard rate.

Another lender, say a new fintech startup, runs a different formula and gets a different number. That’s fine. There’s no “right” number, there’s each lender’s judgment, made with their own trust graph and their own risk tolerance.


Privacy model: what’s on-chain, what’s off-chain

Credit history is sensitive. Quidnug’s public chain is, well, public. The design balances these:

On-chain (public, auditable)

  • Subject’s quid (public key only)
  • Event type (credit.loan.originated, etc.)
  • Counterparty identity
  • Coarse metadata: category, principal-band (e.g., “20k-30k” not “$23,451”), term length, origination date
  • Hash of the encrypted detail blob
  • IPFS CID of the encrypted blob
  • Trust edges (truster → trustee + level + domain)
  • Dispute events

This level of public visibility is deliberately similar to what lenders already learn from credit reports: “this person has an auto loan with Chase from 2024.” What’s NOT public: the exact principal, rate, payment amounts, etc.

Off-chain (encrypted, subject-controlled)

  • Exact principal amount
  • Interest rate and terms
  • Monthly payment amount
  • Specific payment dates and amounts
  • Notes / memos from either party
  • Correspondence / dispute details

Stored in IPFS (or S3, or any blob store) as JSON/CBOR encrypted with a symmetric key. The decryption key lives on the subject’s device.

How a lender accesses the encrypted details

1. Subject grants access to a specific lender:
- Generates an ephemeral encryption key for the lender
- Uses ECIES (elliptic-curve integrated encryption scheme)
to encrypt the symmetric key with the lender's public key
- Publishes the encrypted key as an access-grant event:
eventType: "credit.access-grant"
subjectId: subject-alice-chen-xyz
payload:
grantedTo: lender-chase-bank
scope: ["credit.loan.originated:*", "credit.loan.payment-received:*"]
encryptedKey: <ECIES(key, chase.pubkey)>
validUntil: <now + 30 days>
signer: subject-alice-chen-xyz
2. Lender decrypts the key with their own private key.
3. Lender fetches the encrypted blobs from IPFS.
4. Lender decrypts with the subject-provided key.
5. Now lender has full detail. They verify hashes match.
6. After validUntil, lender re-requests access.

Key property: The subject grants access to specific lenders for specific scopes and time windows. A lender can’t aggregate data on non-customers. A “shadow bureau” (scraping the public chain) sees only coarse metadata, not enough to reconstruct a person’s financial life.

The social-credit defense

For social-credit prevention specifically, this privacy model is critical:

  • A government observer can see “Alice took out loans” but not the amounts, terms, or personal details.
  • The government can’t unilaterally aggregate. They’d have to get Alice’s explicit access grants, which she controls.
  • Even if a government compels disclosure from individual lenders, each lender only has the sliver of history involving them, no single entity has the full picture.

Replacing credit reporting, the flows

Subject onboarding (bring-your-own-quid)

Step 1. Individual generates their quid
────────────────────────────────────────
On their phone / device (no central registration required)
$ quidnug-credit generate
Your Subject Quid: subject-alice-chen-xyz
Your public key: 0x04abcd...
Your private key: saved locally (keep secure)
No SSN required. No forced enrollment.
Step 2. Identity verification (optional but useful)
────────────────────────────────────────────────────
To participate in regulated credit (mortgages, etc.), subject
needs identity verification. Multiple verifiers can endorse:
A DMV / KYC provider signs a trust edge:
TRUST:
truster: verifier-dmv-texas
trustee: subject-alice-chen-xyz
trustLevel: 1.0
domain: credit.identity-verification.us
attributes:
verifiedName: HASH:<name+DOB+SSN> // hash, not raw
verificationMethod: "in-person-DMV"
verificationDate: ...
validUntil: <5 years>
Multiple verifiers can endorse the same subject. Lenders
accept whichever verifier they trust. A lender who doesn't
trust the Texas DMV can require a different verifier.
Step 3. Alternative-data source linking
────────────────────────────────────────
Subject links their utility / rent / employer accounts.
Utility company: "Alice authorized me to attest."
EVENT: credit.alt-data.linked
subjectId: subject-alice-chen-xyz
payload:
dataSource: utility-con-edison-nyc
scope: "monthly-payment-history"
linkedAt: ...
signer: utility-con-edison-nyc + subject-alice-chen-xyz (co-signed)
Utility then publishes monthly:
EVENT: credit.alt-data.payment-record
payload:
month: "2026-04"
onTime: true
amount: "120-130" (coarse band)
detailCID: <encrypted for subject + verified>

Taking out a loan

Step 1. Subject applies at a lender
─────────────────────────────────────
Application is off-chain (lender's existing workflow).
Subject provides: their Subject Quid ID + grant-of-access to
relevant history.
Step 2. Lender evaluates
─────────────────────────
Pulls subject's quid, fetches accessible history + trust edges.
Runs own credit model (as described in §Core design #3).
Makes approve/decline/adjust-rate decision per their own
underwriting standards.
Step 3. Loan origination
─────────────────────────
Lender emits:
EVENT: credit.loan.originated
subjectId: subject-alice-chen-xyz
payload:
counterparty: lender-chase-bank
category: "auto-loan"
principalBand: "20k-30k"
termMonths: 60
detailCID: <encrypted blob>
detailHash: <hash>
annualRateBand: "4-6%"
signer: lender-chase-bank
Subject countersigns via an acknowledgment event:
EVENT: credit.loan.acknowledged
payload:
originationEventID: <id>
termsAccepted: true
signer: subject-alice-chen-xyz
Lender's funds disburse off-chain (bank transfer).

Ongoing payments

Monthly, lender emits:
EVENT: credit.loan.payment-received
subjectId: subject-alice-chen-xyz
payload:
loanRef: <origination event id>
paymentDate: 1713400000
onTime: true
detailCID: <encrypted>
signer: lender-chase-bank
If late:
EVENT: credit.loan.payment-late
payload:
daysLate: 7
noticeIssued: true
Subject can emit a counter-event if they dispute the "late"
claim (e.g., payment was sent on time, bank delay):
EVENT: credit.dispute.opened
payload:
contestsEventID: <the payment-late event>
evidence: <hash of bank transfer receipt>

Loan payoff and trust edge

At payoff:
EVENT: credit.loan.paid-off
payload:
loanRef: ...
finalPaymentDate: ...
summary: {
totalOnTimePayments: 58,
totalLatePayments: 2,
daysOfLatenessMax: 9,
renegotiations: 0
}
signer: lender-chase-bank
And simultaneously, lender issues the reputation trust edge:
TRUST:
truster: lender-chase-bank
trustee: subject-alice-chen-xyz
trustLevel: 0.88 // reflecting the 2 late payments
domain: credit.auto-loan.us
description: "60-month auto loan, 2 late payments (≤9 days),
no renegotiation, paid in full"
validUntil: <now + 3 years>
nonce: ...

This trust edge becomes a persistent positive reference for Alice, visible to any future auto-loan lender she authorizes.

Disputes

Scenario: Chase reports a "default" on Alice that Alice claims
is wrong (say, identity theft, or an accounting error).
Alice files a dispute event:
EVENT: credit.dispute.opened
subjectId: subject-alice-chen-xyz
payload:
contestsEventID: <the disputed event's id>
contestsLender: lender-chase-bank
contestType: "identity-theft" | "error" | "misclassification" | "other"
evidence: <IPFS CID of evidence documents>
requestedRemedy: "withdraw claim" | "correct to ..." | "other"
signer: subject-alice-chen-xyz
Chase has 30 days (per policy, or per jurisdiction regulation)
to respond:
EVENT: credit.dispute.responded
payload:
disputeRef: <id>
response: "accepted" | "denied" | "partial"
resolution: { ... }
signer: lender-chase-bank
If accepted:
Chase emits a correction event:
EVENT: credit.loan.correction
payload:
corrects: <original event id>
newSummary: { /* corrected facts */ }
signer: lender-chase-bank
If denied:
Alice can:
(a) escalate to an arbiter (a neutral third party signs
their opinion as an arbitration event)
(b) accept the outcome
(c) let the dispute stand publicly, future lenders see
both the original claim and Alice's rebuttal
Key property: the DISPUTE is always on record. An unresolved
lender-vs-subject disagreement is visible to every future
counterparty, who can weigh both sides.
Arbiter involvement (optional):
EVENT: credit.dispute.arbitration-opinion
payload:
disputeRef: <id>
arbiterQuid: arbiter-consumer-financial-watch
opinion: "lender-supported" | "subject-supported" | "inconclusive"
reasoning: <IPFS CID of arbitration report>
signer: arbiter-consumer-financial-watch
Subject retains full control: they chose to engage this arbiter.
Lenders may honor the opinion or not, weighted by their own
trust in the arbiter.

Lender reputation feedback loop

A lender that files a false default or wrongfully denies a dispute faces consequences:

  • Subject’s future lenders see the dispute + unresolved status. Sophisticated lenders factor this in as noise, but they can also see that the lender filing false claims has a pattern.
  • Cross-subject pattern analysis, a lender that has many open, subject-contested disputes has a reputation signal visible to every other lender. Other lenders may downgrade their trust in this lender in the inter-lender trust graph, reducing the weight of their attestations.
  • Subject can revoke trust edges to the lender (e.g., if the subject later becomes a lender or validator in another domain).
  • Arbitration records aggregate. A lender whose claims are consistently rejected by respected arbiters has a visible pattern.
  • Regulatory action. Fair-credit regulation (FCRA-equivalents) continues to apply. The difference is that enforcement has cryptographic evidence to work with.

Replacing the credit score, per-lender evaluation

Every lender runs their own algorithm. Examples:

Conservative mortgage lender

def evaluate_mortgage(subject_quid, requested_amount):
# Only direct mortgage history counts heavily
direct_mortgage_trust = compute_direct_trust(
self=my_quid,
subject=subject_quid,
domain="credit.mortgage.us"
)
if direct_mortgage_trust < 0.7:
return DECLINE
# Verify recent on-time payment history
events = get_events(subject_quid, domain="credit.*")
recent_lates = count_events(events, "credit.loan.payment-late",
since=now-2years)
if recent_lates > 3:
return DECLINE
# Verify identity via trusted verifier
if not has_trust_edge(verifier=my_trusted_verifiers,
to=subject_quid,
domain="credit.identity-verification.us"):
return DECLINE
# Approve with standard rate
return APPROVE, standard_rate

Progressive fintech lender (uses alternative data heavily)

def evaluate_personal_loan(subject_quid, requested_amount):
# Alternative data carries significant weight
alt_data_trust = 0
for domain in ["credit.alternative-data.utilities",
"credit.alternative-data.rent",
"credit.alternative-data.employment"]:
alt_data_trust += compute_trust(self, subject_quid, domain)
# Traditional credit is optional but additive
traditional_trust = compute_trust(self, subject_quid, "credit.*")
composite = 0.6 * alt_data_trust / 3 + 0.4 * traditional_trust
# Decline only if composite very low
if composite < 0.4:
return DECLINE
rate = map_composite_to_rate(composite)
return APPROVE, rate

Same subject quid → two lenders → two different decisions. Both are correct per their own risk models. No central score overruled or constrained either.

A subject compares their offers

$ quidnug-credit evaluate-offers
Subject: subject-alice-chen-xyz
Pulling offers from 5 lenders...
Chase (traditional): APPROVED at 6.5% ($23,451 / 60 mo)
Wells Fargo (traditional): APPROVED at 7.1%
Capital One (progressive): APPROVED at 5.8%
LendFin (alt-data-heavy): APPROVED at 5.2%
TraditionalOnly: DECLINED (thin file)
Best offer: LendFin at 5.2%.

Different lenders value different signals. The subject benefits from competition and from bringing their own alternative data.


Specific social-credit prevention measures

Beyond the structural “no central scorer,” the design includes specific protections:

1. No “total citizen” domain

The domain hierarchy is intentionally bounded to credit.*. There is no citizenship.* or trustworthiness.* top-level domain. The protocol doesn’t provide a natural home for a “citizen trust score.” A government attempting to create one would have to define a new domain, and individual lenders would have to voluntarily accept endorsements from it, which most wouldn’t if it carries political weight.

2. Per-domain scoping enforced

A lender’s trust edge in credit.auto-loan.us does NOT automatically count in credit.mortgage.us or any other domain. Each evaluator chooses which domains’ edges are relevant to their decision. There’s no aggregated “overall score.”

3. Subject-owned data access

If a government tries to mandate that all lenders publish every customer’s full history in the clear, the subject’s access-grant mechanism breaks that: encrypted details require the subject’s key. A mandate would have to compel subjects individually, which is a much harder political project than mandating centralized bureaus.

4. Multiple independent verifiers

A government requiring its own verifier to be used is met with subject choice: subjects can use multiple verifiers, and lenders can accept whichever ones they trust. No monopoly on identity.

5. Political speech does not touch credit.*

The design domain is financial-creditworthiness. A speech act on social media is not a credit.* event. Even if a state tried to shoehorn political behavior into credit domains (via a new “social-behavior” attester), lenders (and subjects) can simply refuse to honor that attester’s signatures.

6. Voluntary participation

Nothing in the protocol forces a subject to register. A person can go through life without a Subject Quid. They simply can’t get credit from lenders that require one. Compare to current social-credit systems where participation is mandatory.

7. No chain-level consequence enforcement

The protocol records signed events and trust edges. It does not enforce consequences (e.g., “this person can’t buy train tickets”). Any consequence is enforced by individual counterparties, who choose whether to honor a given endorser’s judgment. A coordinated exclusion attempt requires many independent actors to cooperate, which gives dissenters room to find refuge in non-cooperating lenders / markets.


Data sovereignty and portability

The subject’s quid is theirs

A subject migrating to a different country takes their quid with them. Their signed history is portable; foreign lenders can evaluate (weighting their trust in the attesters, which may be lower for foreign ones, but the data exists).

Guardian recovery

A subject who loses their device / private key uses guardian recovery (QDP-0002) to re-establish. Their history remains intact; only the key changes.

Right to be forgotten

A subject can:

  • Rotate their quid (old history now associated with a retired identity).
  • Revoke future access grants (existing cached data is out of their control but no new queries).
  • Require lenders to emit a credit.right-to-delete acknowledgment in jurisdictions where GDPR / CCPA apply.

The design doesn’t prevent regulations from being enforceable; it makes them enforceable cryptographically (lenders’ compliance is visible on-chain).


Key Quidnug features used

  • Bring-your-own-quid, subject owns identity.
  • Event streams, credit history on subject’s stream.
  • Trust edges with validUntil, lender endorsements expire.
  • Per-domain trust, credit type separation.
  • Relational trust computation, per-lender evaluation.
  • Push gossip (QDP-0005), fast propagation of new events / edges / disputes.
  • Guardian recovery (QDP-0002), subject key-loss recovery.
  • GuardianResignation (QDP-0006), attesters can resign roles.
  • Encrypted payloads + IPFS, privacy on-chain metadata, off-chain details.
  • Selective disclosure via access-grant events, subject controls who reads their full history.

Value delivered

DimensionCredit bureaus todayQuidnug
Data ownershipBureauSubject
Error correction time30–180 daysImmediate (dispute event)
Score customization per lenderAll use same bureauEach lender runs own formula
Alternative-data inclusionLimited, opaqueNative first-class
Breach blast radiusCatastrophic (147M at Equifax)Distributed + encrypted
Portability across bordersNoneQuid travels with you
Transparency of “score”ProprietaryEvery evaluator’s algorithm is their own, reproducible
Dispute leverageBureau mediates (slowly)On-chain dispute visible to all future lenders
Thin-file problemExcludedAlt-data + guarantors accessible
Consent to being scoredImplicit / forcedExplicit via BYOQ
Social-credit riskLow but concerningStructurally blocked by design
Historical completenessGaps commonSubject-verified, lender-signed

What’s in this folder

Runnable POC

Full end-to-end demo at examples/decentralized-credit-reputation/:

  • credit_evaluate.py, pure per-lender evaluator over the subject’s event stream: untrusted-attester filter, subject-dispute discount, alt-data lift, category filter.
  • credit_evaluate_test.py, 11 pytest cases.
  • demo.py, nine-step end-to-end flow showing three different lenders reaching different verdicts on the same subject, a fraudulent-default scenario, and the subject-filed dispute that neutralizes it.
Terminal window
cd examples/decentralized-credit-reputation
python demo.py

Comparison with prior decentralized-credit attempts

AttemptDesign differences
Blockchain credit scoring (various)Usually produce a single number (just moving the bureau on-chain). Quidnug refuses to produce a universal score.
Self-sovereign identity (SSI)Credential-based (“you have a degree”); doesn’t model credit history and payment events as first-class primitives. Quidnug does.
DeFi credit (Aave, Compound)Over-collateralized, doesn’t solve the uncollateralized trust problem. Quidnug models actual repayment history.
Credit unionsCloser in spirit (member-owned) but still centrally scored. Quidnug removes even the member-owned central scorer.

Quidnug differs from all of these by combining: (a) relational trust (no universal score), (b) rich signed event streams (detailed payment history), (c) per-domain scoping, (d) subject-controlled privacy, (e) guardian-recoverable identity. No existing system has all five.

Architecture

Data model, components, sequence diagrams.

Architecture: Decentralized Credit & Reputation

Data model, domain hierarchy, privacy mechanism, and component breakdown for a Quidnug-based credit system.

Node roles

Node typeOperatorFunction
Subject walletIndividual / businessGenerates quid, manages keys, grants access
Lender nodeBank, fintech, private lenderSubmits events, runs own trust computation
Alt-data publisherUtility, landlord, employerPublishes monthly payment attestations
Identity verifier nodeDMV, KYC providerIssues verification trust edges
Dispute arbiter nodeVoluntary third partyPublishes opinions on disputes
Observer node (r/o)Regulator, research, consumer advocacyReads public chain for analytics

Subject wallet is typically a mobile app backed by key management (secure enclave on iOS/Android). Lender nodes are full Quidnug nodes. Observer nodes are read-only Quidnug nodes.

Cryptographic primitives

PrimitiveUsed for
ECDSA P-256 (native)All identity, event, trust-edge signatures
SHA-256Quid IDs, event hashes, blob integrity
ECIES (EC P-256)Encrypting access-grant keys to specific lenders
AES-256-GCMEncrypting detail blobs
IPFS CID (sha256 default)Off-chain payload addressing
HMAC-SHA256 (existing Quidnug)Inter-node authentication

ECIES is a well-established standard and can be layered on Quidnug’s existing ECDSA keys. A subject’s single keypair can be used for both signing (Quidnug-native) and encryption (ECIES) with standard key-derivation.

Domain hierarchy (complete)

credit
├── credit.identity-verification.<country>
├── credit.reports (global cross-reference, rarely used)
├── credit.mortgage.<country>
├── credit.auto-loan.<country>
├── credit.personal-loan.<country>
├── credit.credit-card.<country>
├── credit.small-business.<country>
├── credit.student-loan.<country>
├── credit.buy-now-pay-later.<country>
├── credit.alternative-data.utilities
├── credit.alternative-data.rent
├── credit.alternative-data.employment
├── credit.alternative-data.subscriptions (Netflix-style recurring)
├── credit.disputes
├── credit.inter-lender-trust (lenders endorsing each other)
└── credit.arbiter-trust (subjects' trust in arbiters)

Cross-domain trust

Some trust is meaningful across domains. A prospective mortgage lender reviewing a subject typically cares about:

  • Direct credit.mortgage.* history (highest weight)
  • Indirect credit.auto-loan.* and credit.personal-loan.* as indicators of repayment behavior (lower weight)
  • Alt-data (credit.alternative-data.*) as supporting signal

The cross-domain weighting is the lender’s choice. Some lenders cross-count heavily; others don’t. There’s no protocol mandate.

Jurisdiction scoping

Domains include country code because regulatory regimes differ. A US lender’s endorsement in credit.auto-loan.us has different legal meaning than an endorsement in credit.auto-loan.eu. Cross-border evaluation is possible (trust edges from foreign lenders are first-class) but lenders weight them per their own policies.

Quid schemas

Subject Quid

type SubjectIdentity struct {
QuidID string // sha256 of public key, 16 hex
PublicKey []byte // ECDSA P-256
Creator string // self (BYOQ)
UpdateNonce int64
Attributes struct {
// Deliberately minimal on-chain.
// Identity facts are represented by signed trust edges
// from verifiers, not embedded here.
CreatedAt int64
}
}

No SSN, no name, no address on-chain. All those are off-chain in verifiers’ systems and in encrypted blobs.

Lender Quid

type LenderIdentity struct {
QuidID string
PublicKey []byte
Attributes struct {
LegalName string // "JPMorgan Chase Bank, N.A."
Jurisdiction string // "US"
RegulatoryID string // OCC number, FDIC cert, etc.
LenderCategory []string // ["mortgage","auto-loan",...]
LicenseVerified bool // endorsed by regulator trust edge
}
}
type LenderGuardianSet struct {
// Bank's own recovery/authority structure
Guardians []GuardianRef // CEO, CRO, compliance, regulator
Threshold uint16
RecoveryDelay time.Duration // e.g. 72h
RequireGuardianRotation: true
}

Alternative-data source quid

type AltDataSourceIdentity struct {
QuidID string
Attributes struct {
LegalName string
DataType string // "utility-payments", "rent", "employment"
Jurisdiction string
CustomerAgreedToOpt OptInTermsHash string // how they get subject consent
}
}

Identity verifier quid

type VerifierIdentity struct {
QuidID string
Attributes struct {
Type string // "DMV", "passport-office", "KYC-provider", "bank-KYC"
Jurisdiction string
Authority string // "US Department of State", "NY DMV", "Plaid-KYC"
}
}

Event schemas

Credit event: loan origination

type LoanOriginationEvent struct {
BaseEvent // standard EVENT fields
SubjectID string // borrower's quid
SubjectType string // "QUID"
EventType string // "credit.loan.originated"
Payload struct {
Counterparty string // lender's quid
Category string // "auto-loan" | "mortgage" | etc.
PrincipalBand string // "5k-10k", "10k-25k", etc. (public coarse)
TermMonths int
OriginationDate int64
AnnualRateBand string // "4-6%", "6-10%" (coarse)
DetailCID string // IPFS CID of encrypted blob
DetailHash string // sha256 of encrypted blob
AccessGrantPolicy string // "subject-approved-only"
}
Creator string // lender's quid
Signature string // lender's signature
}

Credit event: payment

type PaymentEvent struct {
EventType string // "credit.loan.payment-received" | "credit.loan.payment-late"
Payload struct {
LoanRef string // reference to origination event ID
PaymentDate int64
OnTime bool
DaysLate int // 0 if onTime
AmountBand string // coarse
DetailCID string
DetailHash string
}
Creator string // lender
}

Trust edge: lender endorses subject

type CreditTrustEdge struct {
BaseTrustEdge // standard TRUST fields
Truster string // lender's quid
Trustee string // subject's quid
TrustLevel float64 // 0.0 - 1.0, lender's discretion
Domain string // e.g., "credit.auto-loan.us"
Nonce int64
ValidUntil int64
Attributes struct {
RelationshipSummary string // human-readable
LoansInvolved []string // origination event IDs
AggregatedMetrics struct {
TotalPrincipalBand string
TotalPayments int
LatePayments int
MaxDaysLate int
Renegotiations int
}
}
}

Access grant event

type CreditAccessGrantEvent struct {
EventType string // "credit.access-grant"
Payload struct {
GrantedTo string // lender/verifier quid
Scope []string // event-type-globs to which this grants
ValidUntil int64
EncryptedKey []byte // symmetric key encrypted to grantee's pubkey via ECIES
GrantVersion int // for revocation/updates
}
Creator string // subject (self)
}

Dispute event

type CreditDisputeEvent struct {
EventType string // "credit.dispute.opened"
Payload struct {
ContestsEventID string // the disputed event's id
ContestsLender string
ContestType string // "identity-theft" | "error" | "misclassification"
EvidenceCID string // IPFS CID of evidence docs
RequestedRemedy string
}
Creator string // subject
}
type CreditDisputeResponseEvent struct {
EventType string // "credit.dispute.responded"
Payload struct {
DisputeRef string
Response string // "accepted" | "denied" | "partial"
Resolution interface{} // varies
CorrectionEventID string // if applicable
}
Creator string // lender
}

Privacy mechanism in depth

Encrypted detail blob structure

Off-chain blob (stored in IPFS or equivalent, addressed by CID):

{
"blobVersion": 1,
"encryptionAlgorithm": "AES-256-GCM",
"detail": {
"exactPrincipal": "23451.00",
"annualRate": "5.74%",
"monthlyPayment": "450.33",
"paymentSchedule": [ ... ],
"collateral": "...",
"borrowerAddress": "...",
"agreement": "<hash of full contract>",
"internalNotes": "..."
}
}

The whole blob is encrypted with a random symmetric key. The CID addresses the encrypted bytes.

Access grant mechanism

The symmetric key is distributed to authorized parties via ECIES-encrypted access grants:

  1. Subject knows the symmetric key (they approved the event, they have a copy).
  2. When the subject wants to share with a lender:
    • Generate an ephemeral ECDH keypair.
    • Compute shared secret with the lender’s public key.
    • Derive a key-encryption key (KEK).
    • Encrypt the symmetric key with the KEK.
    • Publish the encrypted key + ephemeral public point as an access-grant event.
  3. Lender receives the access grant:
    • Compute the same shared secret using their private key + the ephemeral public point.
    • Derive the KEK.
    • Decrypt the symmetric key.
    • Fetch the blob from IPFS. Decrypt. Verify hash matches.

What can a passive observer see?

The public chain reveals:

  • “Subject X has a credit event of type credit.loan.originated with counterparty Chase on date D, in category auto-loan, principal band 20k-30k, term 60 months”
  • “Subject X paid on time 58 of 60 months; 2 late payments of ≤10 days” (aggregated in the final loan.paid-off event’s summary)
  • Trust edge: Chase trusts Subject X at 0.88 for 3 years

What the public chain does NOT reveal:

  • Exact principal
  • Interest rate
  • Exact payment amounts
  • Personal details of subject
  • Reason for any late payment
  • Details of any dispute

Is this level of public info enough to cause concern? That’s a design choice. For stronger privacy, a jurisdiction can deploy with:

  • Commitment-only events, even the counterparty is hidden on-chain; only a commitment is public. Full data (including counterparty identity) is in the encrypted blob.
  • Zero-knowledge proofs, for computed evaluations (“this subject’s on-time payment ratio ≥ 90%”) without revealing the underlying events.

These would be QDPs in their own right. The baseline design gives privacy comparable to what current credit reports show a lender, while enabling far more granular consumer control.

Trust computation details

Direct trust

A direct trust edge from a specific counterparty in the queried domain:

query: evaluate Alice for an auto loan, I am lender Chase
edges: Chase --0.92--> Alice in credit.auto-loan.us (found)
direct trust = 0.92

Transitive trust

Through other lenders Chase trusts:

edges:
Chase ---0.9----> Wells-Fargo in credit.inter-lender-trust
Chase ---0.85---> Capital-One in credit.inter-lender-trust
Wells-Fargo ---0.85---> Alice in credit.auto-loan.us
Capital-One ---0.80---> Alice in credit.auto-loan.us
transitive paths:
Chase -> Wells-Fargo -> Alice = 0.9 * 0.85 = 0.765
Chase -> Capital-One -> Alice = 0.85 * 0.80 = 0.68
max transitive = 0.765

Alternative-data trust

Same shape but in alt-data domains:

edges:
Chase ---0.7----> Con-Edison in credit.inter-lender-trust (or a special alt-data trust domain)
Con-Edison ---0.88--> Alice in credit.alternative-data.utilities
transitive = 0.7 * 0.88 = 0.616

Aggregation, lender’s discretion

Simple formulas:

conservative_score = max(direct, transitive, alt_data)
progressive_score = 0.5 * direct + 0.3 * transitive + 0.2 * alt_data
weighted_by_recency = ... (weight recent edges more)

The lender writes this logic once. Queries Quidnug for all edges + events. Computes. Decides. Independent from any bureau.

Anti-collusion trust math

A subject cannot simply pay a friendly “fake lender” for a glowing endorsement because the fake lender’s edges are only worth as much as other lenders trust the fake lender.

  • Fake lender needs inter-lender trust to be useful
  • To gain inter-lender trust, it needs real loan history with subjects other lenders trust
  • Building this trust takes years and real-capital exposure
  • At scale, fraud rings are detectable (same subjects endorsing each other with no external borrowing activity)

This is structurally much harder than the current system, where a lender can simply file false data to a bureau.

Protocol flows (concise)

Subject onboarding

  1. Subject generates quid locally
  2. Subject visits identity verifier (in-person / online KYC)
  3. Verifier issues trust edge in credit.identity-verification.*
  4. Subject links alt-data sources (utility, rent, employer)
  5. Alt-data publishers publish monthly attestation events

Credit relationship

  1. Subject applies at lender (off-chain UI)
  2. Lender requests access grant from subject
  3. Subject grants access for specific scope + duration
  4. Lender fetches events + edges, runs own model
  5. Decision made; if approved, lender issues origination event
  6. Monthly payment events over loan lifetime
  7. At payoff, lender issues trust edge endorsement

Dispute

  1. Subject emits credit.dispute.opened contesting a specific event
  2. Lender responds within window
  3. Optional arbiter opinion
  4. Correction event if warranted, or dispute remains public record

Scale estimates

A mid-size national deployment:

  • 100M subjects
  • 10,000 lender nodes (banks, credit unions, fintechs)
  • 1,000 alt-data publisher nodes
  • 50 identity verifier nodes
  • 500 arbiter nodes

Per-subject activity:

  • ~5-10 active credit relationships at any time
  • ~1 credit event per active relationship per month
  • Total: 60-120 events/subject/year
  • 100M subjects × 100 events/year = 10B events/year ≈ 300M/day

Event volume is large but not unusual for a blockchain system. Push gossip’s producer rate limits cap per-lender spam.

Trust-graph size: 100M subjects × avg 10 inter-lender trust edges = 1B edges. Handleable with good indexing. Most queries are per-subject so they don’t touch the full graph at once.

Next

Implementation

Concrete API calls, pseudocode, signing shape.

Implementation: Decentralized Credit & Reputation

Concrete Quidnug API calls and code snippets for each role.

0. Node configuration

Lender node

Terminal window
ENABLE_NONCE_LEDGER=true
ENABLE_PUSH_GOSSIP=true
ENABLE_LAZY_EPOCH_PROBE=true
ENABLE_KOFK_BOOTSTRAP=true
SUPPORTED_DOMAINS=credit.*
NODE_AUTH_SECRET=<32-byte hex>
IPFS_ENABLED=true
IPFS_GATEWAY_URL=http://localhost:5001

Subject wallet

The subject runs a thin client (mobile app) that talks to any public read-only node + their own local IPFS client or a trusted IPFS pinning service.

1. Subject onboarding

1a. Generate subject quid

Terminal window
# On the subject's device (phone app, CLI, hardware wallet)
$ quidnug-credit init
Generated new subject identity.
Subject Quid ID: subject-alice-chen-xyz
Public key: 0x04a1b2c3...
Private key: saved to local secure enclave
Encryption key (derived from subject key, same curve):
- ECIES pubkey: 0x04a1b2c3...
- ECIES privkey: used for decrypting access grants to you
Terminal window
# After in-person / online KYC, the verifier emits trust edge
curl -X POST $VERIFIER_NODE/api/trust -d '{
"type":"TRUST",
"truster":"verifier-dmv-texas",
"trustee":"subject-alice-chen-xyz",
"trustLevel":1.0,
"domain":"credit.identity-verification.us",
"nonce":47283,
"validUntil":<now + 5y>,
"attributes":{
"verificationType":"in-person-DMV",
"verificationDate":"2026-04-18",
"identityHash":"<sha256 of name+DOB+SSN+address>",
"biometricVerified":true
},
"signature":"<DMV signs>"
}'

Subject can verify their verification:

Terminal window
curl "$ANY_NODE/api/v1/trust/edges?trustee=subject-alice-chen-xyz&domain=credit.identity-verification.us"

Multiple verifiers can endorse the same subject. Lenders accept whichever ones they trust.

Subject authorizes their utility company to publish attestations:

Terminal window
# Subject signs consent event
curl -X POST $NODE/api/v1/events -d '{
"type":"EVENT",
"subjectId":"subject-alice-chen-xyz",
"subjectType":"QUID",
"eventType":"credit.alt-data.consent-granted",
"payload":{
"dataSource":"utility-con-edison-nyc",
"scope":["monthly-payment-history"],
"duration":"indefinite-until-revoked",
"optInHash":"<sha256 of consent document>"
},
"creator":"subject-alice-chen-xyz",
"signature":"<subject signs>"
}'
# Utility co-signs to acknowledge
curl -X POST $UTILITY_NODE/api/v1/events -d '{
"type":"EVENT",
"subjectId":"subject-alice-chen-xyz",
"subjectType":"QUID",
"eventType":"credit.alt-data.publisher-acknowledged",
"payload":{
"consentEventRef":"<the consent event id>",
"dataSource":"utility-con-edison-nyc",
"customerVerified":true
},
"creator":"utility-con-edison-nyc",
"signature":"<utility signs>"
}'

Monthly, utility publishes attestation:

Terminal window
curl -X POST $UTILITY_NODE/api/v1/events -d '{
"type":"EVENT",
"subjectId":"subject-alice-chen-xyz",
"subjectType":"QUID",
"eventType":"credit.alt-data.payment-record",
"payload":{
"month":"2026-04",
"onTime":true,
"amountBand":"100-150",
"detailCID":"bafy...",
"detailHash":"<sha256>"
},
"creator":"utility-con-edison-nyc",
"signature":"<utility signs>"
}'

After 12 months of consecutive on-time events, the utility may issue a trust edge:

Terminal window
curl -X POST $UTILITY_NODE/api/trust -d '{
"truster":"utility-con-edison-nyc",
"trustee":"subject-alice-chen-xyz",
"trustLevel":0.88,
"domain":"credit.alternative-data.utilities",
"description":"36 months on-time payment",
"validUntil":<now + 2y>
}'

2. Lender origination flow

2a. Subject applies at lender

Off-chain: subject enters lender’s website/app with their Subject Quid ID. Lender asks for access to history.

2b. Subject grants access

package subject
import (
"crypto/aes"
"crypto/cipher"
"crypto/ecdsa"
"crypto/rand"
)
// Subject generates a symmetric key for this access grant
func (s *Subject) GenerateAccessGrant(grantee string, scopes []string, duration time.Duration) (*AccessGrant, error) {
// 1. Generate symmetric key
symKey := make([]byte, 32)
if _, err := rand.Read(symKey); err != nil {
return nil, err
}
// 2. Fetch grantee's public key from Quidnug
granteeIdentity, err := s.client.GetIdentity(ctx, grantee)
if err != nil {
return nil, err
}
// 3. Encrypt symKey with ECIES to grantee
encryptedKey, err := ecies.Encrypt(granteeIdentity.PublicKey, symKey)
if err != nil {
return nil, err
}
// 4. Publish access-grant event on subject's stream
grant := EventTransaction{
SubjectID: s.quid,
SubjectType: "QUID",
EventType: "credit.access-grant",
Payload: map[string]interface{}{
"grantedTo": grantee,
"scope": scopes,
"validUntil": time.Now().Add(duration).Unix(),
"encryptedKey": base64.StdEncoding.EncodeToString(encryptedKey),
"grantVersion": 1,
},
Creator: s.quid,
Signature: s.sign(...),
}
if err := s.client.SubmitEvent(ctx, grant); err != nil {
return nil, err
}
return &AccessGrant{
Grantee: grantee,
SymKey: symKey,
Scopes: scopes,
ValidUntil: time.Now().Add(duration),
}, nil
}

2c. Lender runs credit evaluation

package lender
type CreditEvaluator struct {
client QuidnugClient
lenderQuid string
privateKey *ecdsa.PrivateKey // for decrypting access grants
policy UnderwritingPolicy
}
func (l *CreditEvaluator) Evaluate(ctx context.Context, subjectQuid string, loanRequest LoanRequest) (*Decision, error) {
// 1. Retrieve public events
events, err := l.client.GetSubjectEvents(ctx, subjectQuid, "QUID")
if err != nil {
return nil, err
}
// 2. Retrieve access grant (if any)
grant, symKey, err := l.findAccessGrantForMe(events)
if err != nil {
return nil, err
}
// 3. Fetch and decrypt detail blobs for relevant events
decryptedEvents := l.decryptEvents(events, symKey, grant.Scope)
// 4. Retrieve relevant trust edges
edges, err := l.client.GetTrustEdges(ctx, GetEdgesFilter{
Trustee: subjectQuid,
DomainPrefix: "credit.",
})
if err != nil {
return nil, err
}
// 5. Compute trust per-domain relative to this lender
directTrust := l.computeDirectTrust(subjectQuid, loanRequest.Category)
transitiveTrust := l.computeTransitiveTrust(subjectQuid, loanRequest.Category, edges)
altDataTrust := l.computeAltDataTrust(subjectQuid, edges)
identityTrust := l.computeIdentityTrust(subjectQuid, edges)
// 6. Check verification requirements
if identityTrust < l.policy.MinIdentityTrust {
return &Decision{Approved: false, Reason: "insufficient-identity-verification"}, nil
}
// 7. Apply policy
composite := l.policy.CompositeFormula(directTrust, transitiveTrust, altDataTrust)
if composite < l.policy.MinCompositeTrust {
return &Decision{Approved: false, Reason: "insufficient-composite-trust"}, nil
}
// 8. Rate calculation
rate := l.policy.RateForTrust(composite, loanRequest)
return &Decision{
Approved: true,
Rate: rate,
Principal: loanRequest.Amount,
Term: loanRequest.Term,
CompositeTrust: composite,
DirectTrust: directTrust,
TransitiveTrust: transitiveTrust,
AltDataTrust: altDataTrust,
}, nil
}
func (l *CreditEvaluator) computeDirectTrust(subject, category string) float64 {
// Is there a trust edge from me directly to this subject in this domain?
domain := fmt.Sprintf("credit.%s.us", category)
edges, _ := l.client.GetTrustEdges(ctx, GetEdgesFilter{
Truster: l.lenderQuid,
Trustee: subject,
Domain: domain,
})
if len(edges) == 0 {
return 0
}
return maxTrustLevel(edges)
}
func (l *CreditEvaluator) computeTransitiveTrust(subject, category string, allEdges []TrustEdge) float64 {
// BFS through inter-lender trust
domain := fmt.Sprintf("credit.%s.us", category)
// Start: find all lenders who trust the subject in this domain
subjectEndorsers := filterEdges(allEdges, trustee(subject), domain(domain))
best := 0.0
for _, edge := range subjectEndorsers {
// How much does MY lender trust the endorsing lender?
myTrustInEndorser := l.computeDirectInterLenderTrust(l.lenderQuid, edge.Truster)
transitive := myTrustInEndorser * edge.TrustLevel
if transitive > best {
best = transitive
}
}
return best
}

2d. Loan origination

After approval and subject’s acceptance:

func (l *Lender) OriginateLoan(ctx context.Context, sub string, terms LoanTerms) error {
// 1. Build the detail blob
detail := map[string]interface{}{
"exactPrincipal": terms.Principal,
"annualRate": terms.Rate,
"monthlyPayment": terms.MonthlyPayment,
"paymentSchedule": terms.Schedule,
"collateral": terms.Collateral,
"borrowerAddress": terms.Address,
"agreementHash": terms.AgreementHash,
}
blob, _ := json.Marshal(detail)
// 2. Encrypt with symmetric key (shared with subject via access grant)
symKey := l.getOrGenerateSymKey(ctx, sub)
encrypted := encryptGCM(blob, symKey)
// 3. Upload to IPFS, get CID
cid, err := l.ipfs.Add(ctx, encrypted)
if err != nil {
return err
}
// 4. Emit origination event on subject's stream
event := EventTransaction{
SubjectID: sub,
SubjectType: "QUID",
EventType: "credit.loan.originated",
Payload: map[string]interface{}{
"counterparty": l.lenderQuid,
"category": terms.Category,
"principalBand": principalBand(terms.Principal),
"termMonths": terms.TermMonths,
"originationDate": time.Now().Unix(),
"annualRateBand": rateBand(terms.Rate),
"detailCID": cid,
"detailHash": sha256sum(encrypted),
"accessGrantPolicy": "subject-approved-only",
},
Creator: l.lenderQuid,
Signature: l.sign(/* canonical */),
}
return l.client.SubmitEvent(ctx, event)
}

2e. Subject acknowledges

Terminal window
curl -X POST $NODE/api/v1/events -d '{
"subjectId":"subject-alice-chen-xyz",
"subjectType":"QUID",
"eventType":"credit.loan.acknowledged",
"payload":{
"originationEventID":"<event id from step 2d>",
"termsAccepted":true,
"signedAt":1713400000
},
"creator":"subject-alice-chen-xyz",
"signature":"<subject signs>"
}'

3. Monthly payment events

Each month, lender emits a payment event:

Terminal window
# On-time
curl -X POST $NODE/api/v1/events -d '{
"subjectId":"subject-alice-chen-xyz",
"subjectType":"QUID",
"eventType":"credit.loan.payment-received",
"payload":{
"loanRef":"<origination event id>",
"paymentDate":1716048000,
"onTime":true,
"daysLate":0,
"amountBand":"400-500",
"detailCID":"bafy...",
"detailHash":"<sha256>"
},
"creator":"lender-chase-bank",
"signature":"<lender signs>"
}'
# Late
curl -X POST $NODE/api/v1/events -d '{
...
"eventType":"credit.loan.payment-late",
"payload":{
"loanRef":"...",
"daysLate":5,
"noticeIssued":true
},
...
}'

4. Dispute flow

4a. Subject files dispute

Terminal window
curl -X POST $NODE/api/v1/events -d '{
"subjectId":"subject-alice-chen-xyz",
"subjectType":"QUID",
"eventType":"credit.dispute.opened",
"payload":{
"contestsEventID":"<id of disputed late-payment event>",
"contestsLender":"lender-chase-bank",
"contestType":"error",
"evidenceCID":"bafy... (encrypted blob of bank transfer receipts)",
"evidenceHash":"<sha256>",
"requestedRemedy":"withdraw claim; payment was sent on time"
},
"creator":"subject-alice-chen-xyz",
"signature":"<subject signs>"
}'

4b. Lender responds

package lender
func (l *Lender) InvestigateDispute(ctx context.Context, disputeEventID string) error {
dispute, err := l.client.GetEvent(ctx, disputeEventID)
if err != nil {
return err
}
// Access subject's evidence blob (they shared the key via access grant)
evidence, err := l.fetchAndDecryptEvidence(ctx, dispute.Payload["evidenceCID"].(string))
if err != nil {
return err
}
// Investigate internally
decision := l.reviewClaim(evidence, dispute)
// Emit response
response := EventTransaction{
SubjectID: dispute.SubjectID,
SubjectType: "QUID",
EventType: "credit.dispute.responded",
Payload: map[string]interface{}{
"disputeRef": disputeEventID,
"response": decision.Response, // "accepted" | "denied" | "partial"
"resolution": decision.Resolution,
"correctionEventID": decision.CorrectionEventID,
},
Creator: l.lenderQuid,
Signature: l.sign(/* canonical */),
}
return l.client.SubmitEvent(ctx, response)
}

4c. Correction (if accepted)

Terminal window
curl -X POST $NODE/api/v1/events -d '{
"subjectId":"subject-alice-chen-xyz",
"subjectType":"QUID",
"eventType":"credit.loan.correction",
"payload":{
"correctsEventID":"<original late-payment event>",
"correctionReason":"Bank processing delay confirmed; payment was timely",
"newFacts":{
"onTime":true,
"daysLate":0
}
},
"creator":"lender-chase-bank",
"signature":"<lender signs>"
}'

When future evaluators read the history, they see the late- payment event + subject’s dispute + lender’s response + the correction. The correction supersedes; evaluators counting the history treat this payment as on-time.

4d. Escalation to arbiter

If subject disagrees with lender’s denial:

Terminal window
# Subject engages an arbiter (arbiter's fee handled off-chain)
curl -X POST $NODE/api/v1/events -d '{
"subjectId":"subject-alice-chen-xyz",
"subjectType":"QUID",
"eventType":"credit.dispute.arbitration-requested",
"payload":{
"disputeRef":"<original dispute event id>",
"arbiterQuid":"arbiter-consumer-financial-watch"
},
"creator":"subject-alice-chen-xyz",
"signature":"<subject signs>"
}'
# Arbiter reviews (off-chain process), then emits opinion
curl -X POST $ARBITER_NODE/api/v1/events -d '{
"subjectId":"subject-alice-chen-xyz",
"subjectType":"QUID",
"eventType":"credit.dispute.arbitration-opinion",
"payload":{
"disputeRef":"<id>",
"opinion":"subject-supported",
"reasoningCID":"bafy... (full report)"
},
"creator":"arbiter-consumer-financial-watch",
"signature":"<arbiter signs>"
}'

Lenders weigh the arbiter’s opinion per their own trust.

5. Loan payoff and endorsement

Terminal window
# Final payment event
curl -X POST $NODE/api/v1/events -d '{
"subjectId":"subject-alice-chen-xyz",
"eventType":"credit.loan.paid-off",
"payload":{
"loanRef":"<origination id>",
"finalPaymentDate":1744800000,
"summary":{
"totalPayments":60,
"onTimePayments":58,
"latePayments":2,
"maxDaysLate":9,
"renegotiations":0,
"disputesRaised":1,
"disputesResolved":1
}
},
"creator":"lender-chase-bank","signature":"..."
}'
# Lender issues reputation trust edge
curl -X POST $NODE/api/trust -d '{
"truster":"lender-chase-bank",
"trustee":"subject-alice-chen-xyz",
"trustLevel":0.88,
"domain":"credit.auto-loan.us",
"nonce":47,
"validUntil":<now + 3y>,
"description":"60mo auto loan; 58/60 on-time; dispute resolved; paid in full",
"attributes":{
"loanRef":"<origination id>",
"principalBand":"20k-30k",
"onTimePayments":58,
"maxDaysLate":9
}
}'

6. Subject-side: view and share credit history

View own history

Terminal window
$ quidnug-credit history
Your history as of 2026-04-18:
CREDIT RELATIONSHIPS:
1. Chase Bank, Auto Loan
Status: Paid in full (2026-03-15)
Principal band: 20k-30k
Payment history: 58/60 on-time, 2 late (max 9 days)
Trust level endorsed: 0.88 @ credit.auto-loan.us
Dispute history: 1 filed, resolved
2. Wells Fargo, Credit Card
Status: Active
Limit band: 5k-10k
Current: Paid in full 36/36 months
ALTERNATIVE DATA:
1. Con Edison Utility, 36 months on-time
Trust level endorsed: 0.88 @ credit.alternative-data.utilities
IDENTITY VERIFICATIONS:
1. TX DMV, verified 2024-03-15, valid until 2029-03-15
DISPUTES:
1. Chase/late-payment/2025-07, resolved in my favor

Grant access to a new lender

Terminal window
$ quidnug-credit grant-access lender-newfintech-lending \
--scope "credit.loan.*" \
--duration 30d
Granted access to lender-newfintech-lending for 30 days.
Access-grant event ID: <id>

Revoke access

Terminal window
$ quidnug-credit revoke-access lender-newfintech-lending
Access-grant revoked.
Note: lender may retain data they already cached.

7. Anyone: query public credit statistics (anonymized)

Terminal window
# Count how many loans originated in past month (public metadata)
curl "$ANY_NODE/api/v1/events/search?\
eventType=credit.loan.originated&\
since=2026-03-18&\
until=2026-04-18"
# Returns event count; specific subjects not revealed unless
# querier has access grants

8. Key rotation / recovery

Lost subject key → guardian recovery

Terminal window
# Subject's guardians (spouse, lawyer, backup-device) initiate recovery
curl -X POST $NODE/api/v2/guardian/recovery/init -d '{
"subjectQuid":"subject-alice-chen-xyz",
"fromEpoch":0,
"toEpoch":1,
"newPublicKey":"<hex of new key on new device>",
"guardianSigs":[
{"guardianQuid":"alice-spouse","keyEpoch":0,"signature":"..."},
{"guardianQuid":"alice-lawyer","keyEpoch":0,"signature":"..."}
],
...
}'

After recovery, Alice’s credit history is still linked to her Subject Quid. Lenders re-query; they see the same history. Access grants issued under the old epoch are invalidated (lenders need new access grants after rotation). This is a security property: stolen access grants don’t survive a compromise-rotation.

9. Testing

func TestCredit_BasicEvaluation(t *testing.T) {
// Subject has: 1 paid auto loan, 3 years utility history
// Lender with direct trust in utility, no direct in subject
// Expected: composite trust > 0.5 → approval at competitive rate
}
func TestCredit_AccessGrantExpiry(t *testing.T) {
// Subject grants access for 30 days
// Lender can fetch details within window
// After expiry, new access grant required
}
func TestCredit_DisputeFlow(t *testing.T) {
// Lender reports late payment
// Subject files dispute
// Lender accepts, emits correction
// Subsequent evaluations see corrected history
}
func TestCredit_DisputeUnresolved(t *testing.T) {
// Lender denies dispute
// Arbiter opinion in subject's favor
// Future lenders can weight the arbiter's view
// Lender's cross-lender trust may degrade
}
func TestCredit_CollusionDetection(t *testing.T) {
// Fake lender endorses subject at 0.99
// Real lenders don't have trust in fake lender
// Transitive trust through fake lender = 0
// Evaluation relies on real signals only
}
func TestCredit_GuardianRecovery(t *testing.T) {
// Subject's key compromised; rotated via guardians
// Post-rotation: old access grants invalid
// Subject issues new grants; lenders can re-fetch
// History remains attached to subject quid
}

Where to go next

Threat model

Adversaries, assumed capabilities, mitigations.

Threat Model: Decentralized Credit & Reputation

Assets

  1. Subject’s credit history integrity, the cryptographic record of their financial behavior.
  2. Subject’s private key + encryption key, controls identity and data access.
  3. Lender’s signing authority, attestations carry their reputation.
  4. Consumer financial autonomy, ability to participate without coercion.
  5. Absence of centralized scoring, the architectural property that prevents social-credit concentration.

Attacker inventory

AttackerCapabilityPrimary goal
Compromised subject keySubject’s signing keyOpen fraudulent credit in subject’s name
Identity thiefSSN / ID + ability to create fake quidOpen credit accounts impersonating subject
Collusive lender ringReal lenders, cooperative fraudFake credit histories
Defamatory lenderReal lender, wants to harm borrowerFile false defaults
Shadow-bureau aggregatorPublic chain observer + MLRebuild a central score from public metadata
State actor, social creditLegal/political authorityImpose universal citizen score
EmployerHas leverage over subjectCompel subject to reveal full history
Coercer / abuserPersonal relationship with subjectCompel subject to share access grants
Compromised verifierValid identity-verifier keyEnable identity theft at scale
Breach of lender’s DBOff-chain dataDisclose details despite encryption
Lender employeesLegitimate read access to customer dataExfiltrate for profit

Threats and mitigations

Category A: Identity and impersonation

A1. Stolen subject quid private key

Attack. Attacker obtains the subject’s private key and opens credit in their name.

Mitigations.

  • Lender identity verification, lenders accept new applications only for subjects with trust edges from verifiers the lender trusts (DMV, bank-KYC). Attacker with stolen key but no matching verifier endorsement fails.
  • Multi-factor at origination, lender’s off-chain flow (call, email confirm, biometric) bounds the damage.
  • Subject monitoring, subject sees any new credit events on their stream. Push gossip propagates within seconds; a watching wallet app alerts immediately. Subject can rotate via guardian recovery within hours.
  • Guardian recovery, subject rotates their quid to a new key; old key’s future signatures are invalid. Lender accepts the new epoch for new applications.

Residual risk. Small window between key theft and subject detection. Bounded by monitoring diligence; mitigated by standard fraud-detection overlays (same as current systems).

A2. Synthetic identity, attacker creates fake quid + impersonates real person

Attack. Attacker generates a fresh quid and tries to establish an identity as if they were some real person.

Mitigations.

  • Verifier trust edges are the gate. Identity verifiers (DMV, KYC providers) do their existing real-world verification. Attacker can’t synthesize a legit verifier endorsement without passing their checks.
  • Cross-verifier consistency: multiple verifiers should have consistent views. A synthetic identity verified by one verifier but not any other is suspicious.
  • Existing ID-verification industry continues to operate; Quidnug makes their endorsements cryptographically portable.

Category B: Lender misconduct

B1. False-default defamation

Attack. Lender files a late-payment or default event that the subject disputes as false.

Mitigations.

  • Dispute mechanism, subject’s counter-event is visible alongside the lender’s claim. Future evaluators see both.
  • Arbiter opinions, independent arbiter can weigh in. Lenders with multiple arbiter-rejected claims look bad.
  • Cross-lender trust degradation, lenders who observe another lender consistently over-reporting can lower their inter-lender trust edge, reducing that lender’s attestation weight.
  • Regulatory enforcement, FCRA-equivalent laws still apply, now with cryptographic evidence trail.
  • Subject narrative control, dispute events let subject document the context; future evaluators judge fairly.

Residual risk. Sophisticated lenders can file well-crafted false claims that look legitimate. Mitigated by: aggregate pattern analysis, arbiter ecosystems, regulator oversight.

B2. Hostage-taking, lender refuses to emit payoff event

Attack. Subject has paid off their loan but lender delays or refuses to emit the paid-off event, leaving the loan appearing open.

Mitigations.

  • Subject-initiated correction dispute, subject emits credit.dispute.opened with evidence of final payment. Future evaluators see the dispute.
  • Arbiter engagement, independent arbiter can verify and issue an opinion.
  • Bank-regulator involvement, regulatory systems still exist and can mandate lender compliance.
  • Switching lenders, subject can route future business away from the hostile lender, and lenders that see this pattern downgrade their trust in the original lender.

B3. Lender collusion ring, fake endorsements

Attack. Lender A and Lender B (controlled by same fraudster) issue fake trust edges back and forth endorsing fraudulent subjects. The goal: manufacture creditworthiness so those subjects can defraud legitimate Lender C.

Mitigations.

  • Inter-lender trust, Lender C’s evaluation of a subject’s endorsers is based on C’s own trust in the endorsers. Fake lenders with no legitimate banking activity have minimal inter-lender trust. Their endorsements carry little weight.
  • Regulatory registration, real lenders have OCC / FDIC / regulatory numbers. Endorsements from unregistered “lenders” are filterable.
  • Pattern detection, a ring of mutually-endorsing new entities without any external activity is a statistical anomaly. Research / consumer-protection orgs can publish warning events about such rings.
  • Regulator observation, regulators have free public read access to the chain. Regulatory action possible.

Residual risk. Well-disguised collusion at scale is possible but harder than gaming today’s bureau-based system.

B4. Hidden rate discrimination

Attack. Lenders use trust evaluations to discriminate on prohibited categories (race, age, etc.).

Mitigations.

  • Protected categories are not on-chain. Quidnug chain doesn’t store name, race, address, DOB. Discrimination based on these requires the lender accessing off-chain data, same as today.
  • Public pattern analysis, regulators can observe approval patterns per-lender (approve/decline rates by inferable demographics). Same existing regulatory oversight mechanism.
  • Lender’s algorithm is their own, but the inputs are on the chain. Regulators can demand the algorithm and replay decisions to audit discrimination.

Category C: Privacy and data-broker threats

C1. Shadow-bureau aggregator

Attack. Entity scrapes the public chain, builds a database of subjects’ coarse credit histories, sells it as a “traditional credit report” service.

Mitigations.

  • Public chain contains only coarse metadata. Exact amounts, rates, payment details are encrypted. Even with perfect scraping, the scraper gets “subject had a loan, type, approx size, on-time or not”, already comparable to what bureaus publish publicly.
  • Subject can use multiple quids, a privacy-conscious subject can maintain separate quids for different credit relationships, making cross-relationship correlation harder.
  • Legal regime, shadow bureaus compiling records without consumer consent fall under existing FCRA / GDPR frameworks.

Residual risk. A competent scraper can produce a “summary report” service. Less detailed than current bureaus, but some reconstruction is possible. Fundamental to any public-history system; solution is stronger cryptographic privacy if needed (ZK proofs; future QDP).

C2. Coerced access grants

Attack. Employer / abuser / landlord requires subject to share full credit history as a condition.

Mitigations.

  • Partial access grants, subject can share only specific scopes. Employer asking for “alt-data.rent” history doesn’t get all loan data.
  • Time-bounded access, 30-day access grant expires; coercer has to keep re-coercing.
  • Multiple quids, subject can maintain a “professional quid” with limited history and a “full quid” kept private.
  • Legal protections, jurisdictions that prohibit employer credit checks continue to do so; the protocol doesn’t enable new coercion pathways that weren’t possible before.

C3. Detail-blob leak at lender

Attack. Lender stores detail blob in plaintext on their server; server is breached.

Mitigations.

  • Design requires lender keeps blob encrypted at rest. Access grant provides a key; lender’s servers should store the encrypted blob plus the key (or decrypt on-demand and discard).
  • Lender security practices continue to apply. This is the same problem as any lender holding customer data.
  • Scope-limited grants, lender only receives the detail for specific scopes they need. Leak exposes only granted scope.

Category D: Social-credit concentration attempts

D1. State mandates a “national citizen trust score”

Attack. Government declares that all lenders must accept endorsements from a state-operated scoring agency and weight them heavily.

Mitigations (structural / operational).

  • Protocol refuses to produce a universal score. State can create a quid and issue endorsements, but can’t force private lenders to weight those endorsements.
  • Domain separation, a “citizen-trust” domain created by the state is a new domain. Lenders opt in per jurisdiction and legal requirement. Voluntary adherence is the check.
  • Parallel markets emerge, if state-score-accepting lenders become the only option, non-state-score lenders (in friendlier jurisdictions, or informal / alternative markets) become a pressure valve.
  • Civil-society resistance, consumer-advocacy orgs publish trust edges warning subjects about state-score lenders.

Residual risk. In fully-authoritarian regimes, the government can pass laws forcing participation. Quidnug can’t override law; it provides architectural defense for everywhere the law doesn’t reach.

D2. Political-behavior attestations

Attack. State or private actor publishes political-loyalty-score trust edges and pressures lenders to use them.

Mitigations.

  • Lenders can refuse, there’s no protocol requirement to honor any particular attester’s edges.
  • Regulatory environment, anti-discrimination law typically prohibits lending decisions based on political affiliation. Publishing an edge doesn’t mean lenders can lawfully use it.
  • Subject awareness, subjects can see what attesters are issuing about them and challenge via disputes.

D3. Cross-domain score aggregation

Attack. An aggregator takes trust edges from credit.mortgage.us, credit.auto-loan.us, etc., and publishes a composite “general creditworthiness” score.

Mitigations.

  • Lenders evaluate independently. An aggregator’s composite is meaningful only if lenders choose to use it, same pattern as the social-credit defense.
  • Competition in aggregation, no monopoly; many possible aggregators. Subjects can choose which they want to be summarized by.
  • Transparency, aggregator’s formula is public (on-chain event schemas); lenders can audit it.

Category E: Infrastructure attacks

E1. Network-level denial of service

Attack. DDoS on Quidnug nodes to prevent lenders or subjects from querying.

Mitigations. Standard DDoS protections; distributed node operation means multiple alternative nodes available.

E2. IPFS-dependency attacks

Attack. IPFS pinning service that holds detail blobs goes offline or is compromised.

Mitigations.

  • Subject should pin their own detail blobs (via local IPFS client or paid pinning).
  • Lender can also pin (for their own access durability).
  • Alternative storage, design is IPFS-friendly but not IPFS-required. S3, Filecoin, Arweave, or any content- addressable store works.

Category F: Adversarial economics

F1. Lender refuses to operate at a loss

Attack. A bad actor (sovereign or private) gains influence over many lenders; pressures them to refuse credit to specific demographic or political class.

Mitigations.

  • Market diversity, no single entity controls all lenders. Marginalized borrowers find alternative lenders.
  • Alternative-data-heavy lenders, fintech lenders serving underbanked populations exist; relational trust gives them first-class visibility.
  • Community lending, informal peer-to-peer lending has always existed; Quidnug gives it cryptographic tools.

F2. Predatory inclusion

Attack. Lender uses relational-trust data to identify vulnerable subjects for predatory loans.

Mitigations.

  • Regulatory frameworks against predatory lending continue to operate.
  • Subject’s dispute mechanism + visible pattern of lenders with predatory behavior.
  • Consumer-advocate trust edges warning about specific lenders.

Attack scenarios, end-to-end

Scenario 1: “Attacker wants to sabotage subject’s credit”

Required actions:

  1. File a false-default event against subject.
  2. Ensure subject can’t dispute (?).
  3. Cause future lenders to weigh the event heavily.

Defenses that defeat this:

  • Subject sees the event via monitoring → files dispute.
  • Dispute is visible to future lenders.
  • Attacker-lender’s own reputation degrades.
  • Regulator/arbiter oversight.

Compared to current bureaus, the attacker needs to be an endorsed lender to file events (regulatory barrier) AND survive subject’s dispute + arbiter scrutiny.

Scenario 2: “Government wants to build a social-credit system”

Required actions:

  1. Create a state-operated “citizen trust” issuer.
  2. Compel private lenders to weight the state’s endorsements.
  3. Compel subjects to share full history.

Defenses:

  • Private lenders’ algorithms are their own; legal compulsion required and constrained by jurisdiction constitutions.
  • Subject access grants are discretionary; compelling every subject is politically costly.
  • International lenders operate in different jurisdictions.
  • Civil society can publish counter-attestations.

Compared to a centralized government-operated database, the protocol makes the authoritarian move much harder (requires coercing many independent actors).

Scenario 3: “Thieves steal subject’s key via phishing”

Required actions:

  1. Phish private key.
  2. Apply for credit quickly before subject notices.

Defenses:

  • Lender-side identity verification (second channel).
  • Subject monitoring + push notifications.
  • Guardian recovery within hours.
  • Temporary suspension of new applications while subject investigates.

Comparable to current system’s defenses against identity theft; cryptographic guardians make recovery faster than traditional bureau freeze / unfreeze cycles.

Not defended against (explicit limits)

  1. Subject voluntarily shares all history under duress. Legal regime remains the primary defense.
  2. Regulator collusion. If all regulators coordinate to mandate state-score usage, protocol doesn’t override.
  3. Fundamental privacy vs. verifiability trade-off. The public metadata (event type, counterparty, coarse band) is inherent. Stronger privacy (ZK proofs) is future work.
  4. Lender’s off-chain decision. Lender can deny credit for any reason that isn’t specifically illegal. Protocol supports the decision; doesn’t mandate it.
  5. Full anonymization. The protocol preserves pseudonymity (quids), not full anonymity. State-level adversaries with enough data can correlate.
  6. Irrational lenders. A lender willing to lose money can ignore any trust signal and lend anyway. Market competition is the discipline.

Monitoring

Public observability / metrics:

MetricAlert condition / purpose
Subject dispute rate per lender> baseline: pattern of defamation
Lender approval-vs-denial rate by inferable demographic> threshold: possible discrimination
Trust edges issued between new quids> threshold: possible collusion
Arbiter opinions issued per lender per month> threshold: lender misconduct
Subject-lender disputes unresolved past 90 days> threshold: lender compliance issue
Cross-lender trust degradation eventsany

Incident response

Playbooks (excerpts):

  1. Subject reports identity theft.

    • Emit subject.identity-compromised event on subject’s stream.
    • Guardian-recovery rotation of subject quid.
    • All pending applications halted at new-epoch.
    • Lender notifications via push gossip.
  2. Lender has pattern of disputed claims.

    • Consumer-advocate org publishes public warning event.
    • Other lenders downgrade inter-lender trust edges.
    • Regulator can issue a formal enforcement event.
  3. Attempted state-credit-score rollout.

    • Civil-society orgs publish counter-attestations.
    • Lenders in jurisdictions with legal protections refuse to weight it.
    • Subjects advised to use multi-jurisdictional quid strategies.

References