Skip to main content

Trust & Neighborhoods

Communities in Mehr are emergent, not declared. There are no admin-created "zones" to join, no governance to negotiate, no artificial boundaries between groups. Instead, communities form naturally from a trust graph — just like in the real world.

The Trust Graph

Each node maintains a set of trusted peers:

TrustConfig {
// Peers I trust — relay their traffic for free
trusted_peers: Set<NodeID>,

// What I charge non-trusted traffic
default_cost_per_byte: u64,

// Per-peer cost overrides (discount for friends-of-friends, etc.)
cost_overrides: Map<NodeID, u64>,

// Self-assigned scopes — geographic and interest (purely informational)
scopes: Vec<HierarchicalScope>, // max 8 per node

// DEPRECATED: use scopes instead. Kept for backward compatibility.
community_label: Option<String>, // e.g., "portland-mesh"
}

Adding a trusted peer is the only social action in Mehr. Everything else — free local communication, community identity, credit lines — emerges from the trust graph.

Trust relationships are asymmetric and revocable at any time. Removing a node from trusted_peers immediately ends free relay for that node and downgrades any stored data from "trusted peer" to normal priority in the garbage collection policy. Cost overrides are unidirectional — they apply to outbound traffic pricing from the configuring node only.

How Communities Emerge

When a cluster of nodes all trust each other, a neighborhood forms:

  [Alice] ←──trust──→ [Bob] ←──trust──→ [Carol]
│ │ │
└────trust────→ [Dave] ←───trust──────┘

Alice, Bob, Carol, and Dave are a neighborhood. No one "created" it. No one "joined" it. It exists because they trust each other.

Properties

  • No admin: Nobody runs the neighborhood. It has no keys, no governance, no admission policy.
  • No fragmentation: The trust graph is continuous. There are no hard boundaries between communities — neighborhoods overlap naturally when people have friends in multiple clusters.
  • No UX burden: You just mark people as trusted. The same action you'd take when adding a contact.
  • Fully decentralized: There is nothing to attack, take over, or censor.

Free Local Communication

Traffic between trusted peers is always free. A relay node checks its trust list:

Relay decision:
if sender is trusted AND destination is trusted:
relay for free (no lottery, no channel update)
else if sender is trusted:
relay for free (helping a friend send outbound)
else:
relay with stochastic reward lottery

Note the asymmetry: a relay helps its trusted peers send traffic for free, but does not relay free traffic from strangers just because the destination is trusted. Without this rule, an untrusted node could route unlimited free traffic through you to any of your trusted peers, shifting relay costs onto you without compensation.

This means a village mesh where everyone trusts each other operates with zero economic overhead — no tokens, no channels, no settlements. The economic layer only activates for traffic crossing trust boundaries.

Trust-Based Credit

When a node needs MHR (e.g., to reach beyond its trusted neighborhood), its trusted peers can vouch for it:

Transitive credit:
Direct trust: full credit line (set by trusting peer)
Friend-of-friend (2 hops): 10% of direct credit line
3+ hops of trust: no credit (too diluted)

If a credited node defaults, the vouching peer absorbs the debt.
This makes trust economically meaningful — you only trust people
you'd lend to.

The credit line is rate-limited for safety:

Trust DistanceMax CreditRate Limit
Direct trusted peerConfigurable by trusting nodePer-epoch (configurable)
Friend-of-friend10% of direct limitPer-epoch, per friend-of-friend
Beyond 2 hopsNoneN/A
Credit accounting:
Each trusting node tracks outstanding credit per grantee:

CreditState {
grantee: NodeID,
credit_limit: u64, // max outstanding μMHR
outstanding: u64, // currently extended
granted_this_epoch: u64, // epoch-scoped rate limit
last_grant_epoch: u64, // for epoch-boundary reset
}

Rules:
- Direct peers: each gets a separate credit_limit (set in TrustConfig)
- Friend-of-friend: each gets 10% of the vouching peer's direct limit,
tracked independently per grantee
- granted_this_epoch resets to 0 at each epoch boundary
- Outstanding credit that exceeds limit: no new grants until repaid
- Default handling: vouching peer's outstanding balance increases by
the defaulted amount (absorbs debt); grantee is flagged

Hierarchical Scopes

Nodes self-assign scopes that describe where they are and what they care about. Scopes replace the flat community_label with a hierarchical system that supports both geographic locality and interest communities.

HierarchicalScope {
scope_type: enum {
Geo, // physical location hierarchy
Topic, // interest/community hierarchy
},
segments: Vec<String>, // hierarchical path, max 8 levels, max 32 chars each
}

Geographic Scopes

Geographic scopes describe physical location, from broad to narrow:

Alice sets:  Geo("north-america", "us", "oregon", "portland", "hawthorne")
Bob sets: Geo("north-america", "us", "oregon", "portland", "pearl")
Carol sets: Geo("asia", "iran", "tehran", "district-6")

The hierarchy is bottom-up — neighbors form the base, cities emerge from connected neighborhoods, regions from connected cities. This mirrors how mesh networks physically grow: you connect to the person next door first, then the neighborhood, then the city.

Interest Scopes

Interest scopes describe communities of shared interest that span geography:

Dave sets:   Topic("gaming", "pokemon", "competitive")
Eve sets: Topic("science", "physics", "quantum")
Frank sets: Topic("music", "jazz")

Interest communities are sparse — not everyone in Portland cares about Pokemon. A Pokemon community might span Portland, Tokyo, and Berlin with nothing in between. This is the opposite of geographic scopes, which are dense (most people are physically somewhere).

Scope Matching

Subscriptions and queries can match at any level of the hierarchy:

PatternMatches
Geo("north-america", "us", "oregon", "portland") exactOnly Portland
Geo("north-america", "us", "oregon") prefixPortland, Eugene, Bend, and everything in Oregon
Geo("north-america", "us") prefixAll US scopes
Topic("gaming") prefixPokemon, Minecraft, and all gaming sub-topics
Topic("gaming", "pokemon") exactOnly Pokemon, not gaming broadly

Properties

Scopes retain all the properties of the old community_label:

  • Self-assigned — no one approves your scope claims, no authority enforces them
  • Not authoritative — scopes carry no protocol-level privileges (cannot grant access, waive fees, or modify trust)
  • Not unique — multiple disjoint clusters can use the same scope
  • Free-form strings — all segments are arbitrary strings. Communities converge on naming through social consensus (e.g., "portland" not "pdx"), the same way they do today. No ISO codes, no standardized taxonomy, no gatekeeping.
  • Used by servicesMHR-Name scopes names by geographic scope, MHR-Pub supports Scope(match) subscriptions, MHR-DHT uses scopes for content propagation boundaries, and the Social layer uses scopes for geographic and interest feeds

Geographic Scope Verification

Geographic scopes can be verified through bottom-up aggregation — see Identity Claims for the full verification protocol. In summary:

  • Neighborhood level: Verified by RadioRangeProof — if you can hear a node's LoRa beacon, you're within physical range (~1–15 km)
  • City level: Verified by aggregating multiple verified neighborhood claims within the city
  • Region/Country level: Verified by aggregating verified city claims within the region

Interest scopes are never verified — anyone can declare interest in Pokemon. Verification only matters for geographic claims because they're prerequisites for geographic voting and local governance (see Voting).

Wire Format

Designed for constrained devices:

FieldSizeDescription
scope_type1 byte0 = Geo, 1 = Topic
segment_count1 byteNumber of path segments (max 8)
segmentsvariableLength-prefixed UTF-8 (1-byte length + content per segment)

Maximum size per scope: 2 + 8 × 33 = 266 bytes. A node with 8 scopes uses at most ~2.1 KB for scope data.

Backward Compatibility

The community_label field is retained for backward compatibility. New nodes populate both:

Migration:
community_label: "portland-mesh"
→ scopes: [Geo("portland")]

Old nodes: read community_label, ignore scopes
New nodes: read scopes, fall back to community_label if scopes is empty

Geographic vs. Interest: Two Dimensions of Community

Geographic ScopesInterest Scopes
DensityDense — most people are somewhereSparse — niche interests span the globe
PropagationBottom-up through physical proximityWide, across geography
VerificationRadioRangeProof + peer vouchesNone needed (self-declared)
Content costCheap locally, expensive globallyDepends on relay distance
VotingEnables geographic votingNo voting implications
ExamplesGeo("north-america", "us", "oregon", "portland")Topic("gaming", "pokemon")

A single post can have both a geographic and interest scope. A post tagged Geo("portland") + Topic("gaming", "pokemon") appears in both the Portland local feed and the global Pokemon feed. Intersection queries ("Portland Pokemon") are resolved client-side by filtering on both scopes.

Comparison: Zones vs. Trust Neighborhoods

AspectExplicit Zones (old)Trust Neighborhoods (current)
CreationSomeone creates a zoneEmerges from mutual trust
JoiningRequest + approvalMark someone as trusted
GovernanceAdmin keys, votingNone needed
BoundariesHard, declaredSoft, overlapping
Free communicationWithin zone boundaryBetween any trusted peers
Namingalice@zone-namealice@geo:portland (hierarchical scopes)
Sybil resistanceAdmission policyTrust is social and economic (you absorb their debts)
UX complexityCreate, join, configureAdd contacts

Sybil Mitigation

The trust graph provides natural Sybil resistance:

  1. Trust has economic cost: Vouching for a node means absorbing its potential debts. Sybil identities with no real relationships get no credit.
  2. Rate limiting: Even if a malicious node gains one trust relationship, transitive credit is capped at 10% per hop.
  3. Reputation: A node's usefulness as a relay/service provider is what earns trust over time. Creating many identities dilutes reputation rather than concentrating it.
  4. Local detection: A node's trust graph is visible to its peers. A node trusting an unusual number of new, unproven identities is itself suspicious.

Real-World Parallels

Real WorldMehr Trust
Talk to your neighbor for freeFree relay between trusted peers
Lend money to a friendTransitive credit via trust
Wouldn't lend to a strangerNo credit without trust chain
Communities aren't corporationsNeighborhoods have no admin
You belong to multiple groupsTrust graph is continuous, not partitioned
Reputation builds over timeTrust earned through reliable service