Pillar 4. Relationships

NostrSocial

Who do I know, and how well? Without a social graph an agent cannot tell a known contact from a stranger, so every interaction starts from zero.

Social graph manager for AI agents, contacts, trust tiers, conversation evaluation, guardrails, cross-channel recognition, and identity verification. All anchored to Nostr npub identity.

Install

Minimal dependencies: bech32 only. No heavyweight crypto libraries required.

pip install nostrsocial

Links

Quickstart

Create an enclave, add a contact, get tier-based behavior rules.

from nostrsocial import SocialEnclave, Tier

# Create an enclave and back up the device secret immediately
enclave = SocialEnclave.create()
secret = enclave.export_secret()  # Store this securely

# Add a contact and get tier-based behavior
enclave.add("alice@example.com", "email", Tier.CLOSE, display_name="Alice")
rules = enclave.get_behavior("alice@example.com", "email")
# rules.warmth=0.8, rules.token_budget=1500, rules.can_interrupt=True

Core Capabilities

1. Manage Contacts

Add contacts to friends, block, or gray lists with capacity enforcement.

from nostrsocial import SocialEnclave, Tier

enclave = SocialEnclave.create()
enclave.add("alice@example.com", "email", Tier.CLOSE, display_name="Alice")
enclave.block("spam@bad.com", "email")
enclave.gray("meh@example.com", "email")

2. Query Behavioral Rules

Get tier-based behavioral parameters for any contact. Unknown contacts get neutral behavior.

rules = enclave.get_behavior("alice@example.com", "email")
# rules.token_budget, rules.warmth, rules.can_interrupt, etc.

# Unknown contacts get neutral behavior
rules = enclave.get_behavior("stranger@example.com", "email")
# warmth=0.5, token_budget=500

3. Evaluate Conversations

Combine WHO someone is with WHAT is happening to determine HOW to respond. Pass ConversationSignals and get back an Evaluation with adjusted warmth, token budget, approach guidance, and a recommended action.

from nostrsocial import ConversationSignals

signals = ConversationSignals(
    sentiment="vulnerable",
    vulnerability=0.7,
    reciprocity=0.8,
    engagement=0.9,
    topic_depth=0.6,
)
result = enclave.evaluate("alice@example.com", "email", signals)
# result.action = Action.HOLD
# result.approach = "full presence"
# result.adjusted_warmth = 0.96
# result.adjusted_token_budget = 1950

4. Screen Content (Guardrails)

Screen conversation text for banned words, topics, and patterns. Returns a ScreenResult with severity, category, and recommended action.

result = enclave.screen("some incoming message text")
if result.flagged:
    print(result.action)     # "block", "exit", "warn", or "demote"
    print(result.severity)   # 0.0-1.0
    print(result.category)   # "slurs", "manipulation", etc.
    print(result.matched)    # "[slurs]" (never raw input)

# Screen display names for known bad-actor patterns
result = enclave.screen_entity("crypto_support_official")

5. Network Shape

Analyze the social graph and get a human-readable profile.

shape = enclave.network_shape()
# shape.profile_type = "balanced", "fortress", "deep-connector", etc.
# shape.narrative = "12 friends (2 intimate, 4 close, ...)
# shape.tier_counts, shape.verified_count, shape.avg_interaction_days

6. Auto-Maintenance

Run drift detection, gray decay, and at-risk reporting in a single call. Use dry_run=True to preview changes without committing them.

# Preview what would happen
preview = enclave.maintain(dry_run=True)
print(preview["summary"])

# Execute maintenance for real
result = enclave.maintain()
# result["drifted"], result["decayed"], result["at_risk"], result["summary"]

7. Cross-Channel Recognition

Recognize the same person across different channels. This is resonance, not surveillance, it only checks contacts you already have a relationship with.

# Check if a new contact might be someone you already know
matches = enclave.recognize(
    "alicedev", "twitter",
    display_name="Alice",
)
for match in matches:
    print(f"{match.confidence}: {match.reason}")

# Explicitly link two identities (never auto-linked)
result = enclave.link(
    "alice@example.com", "email",
    "alicedev", "twitter",
)

8. Identity Verification

Track identity state from proxy to claimed to verified.

# See who needs verification
for contact in enclave.get_upgradeable():
    print(f"{contact.display_name}: {contact.upgrade_hint}")

# Create a challenge for a claimed npub
challenge = enclave.create_challenge("npub1alice...")

9. Persistence

Save and load the social graph.

from nostrsocial import FileStorage

storage = FileStorage("~/.agent/social.json")
enclave = SocialEnclave.create(storage)
enclave.add("alice@example.com", "email", Tier.CLOSE)
enclave.save()

# Later
enclave = SocialEnclave.load(storage)

Trust Tiers

Friends list total: 150 (Dunbar's number). Unknown contacts get neutral behavior (warmth 0.5, budget 500).

Tier Slots Warmth Token Budget Can Interrupt Share Context Proactive
INTIMATE50.952000YesYesYes
CLOSE150.81500YesYesNo
FAMILIAR500.61000NoNoNo
KNOWN800.5750NoNoNo
BLOCK500.00NoNoNo
GRAY1000.2200NoNoNo

Response Format

Contact

Returned by add(), promote(), demote().

FieldTypeDescription
identifierstrEmail, phone, npub, etc.
channelstr"email", "phone", "npub", "twitter"
list_typeListTypeFRIENDS, BLOCK, or GRAY
tierTier | NoneINTIMATE, CLOSE, FAMILIAR, or KNOWN
identity_stateIdentityStatePROXY, CLAIMED, or VERIFIED
proxy_npubstrHMAC-derived npub for non-npub contacts
display_namestr | NoneHuman-readable name
interaction_countintTotal interactions recorded
upgrade_hintstrHint for identity verification

BehaviorRules

Returned by get_behavior().

FieldTypeDescription
token_budgetintToken allowance (intimate=2000, known=750, block=0)
memory_depthintPast interactions to consider
can_interruptboolCan interrupt ongoing tasks
warmthfloat0.0–1.0 (intimate=0.95, known=0.5, block=0.0)
response_priorityint1=highest (intimate), 10=block
share_contextboolShare agent context with this contact
proactive_contactboolAgent initiates contact

Evaluation

Returned by evaluate().

FieldTypeDescription
actionActionHOLD, PROMOTE, DEMOTE, WATCH, BLOCK, or REACH_OUT
confidencefloat0.0–1.0
adjusted_warmthfloatWarmth for this specific moment
adjusted_token_budgetintToken budget for this response
approachstr"lean in", "de-escalate", "match energy", etc.
rationalestrWhy this recommendation
tier_suggestionTier | NoneSuggested tier if promote/demote

ScreenResult

Returned by screen().

FieldTypeDescription
flaggedboolWhether content was flagged
severityfloat0.0–1.0
categorystr"slurs", "hate_symbols", "manipulation", etc.
matchedstrCategory tag like [slurs] (never raw input. PII safe)
actionstr"block", "exit", "warn", or "demote"
rationalestrHuman-readable explanation

NetworkShape

Returned by network_shape().

FieldTypeDescription
total_contactsintTotal across all lists
tier_countsdictPer-tier counts
verified_countintVerified identities
profile_typestr"balanced", "fortress", "deep-connector", etc.
narrativestrHuman-readable network description

When to Use Each Module

TaskModuleFunction
Add/remove contactsenclaveadd, block, gray, remove
Change trust tierenclavepromote, demote
Get behavioral rulesenclave / behaviorget_behavior
Evaluate a conversationenclave / evaluateevaluate with ConversationSignals
Screen contentenclave / guardrailsscreen, screen_entity
Check remaining slotsenclaveslots_remaining
Find unverified contactsenclaveget_unverified_contacts, get_upgradeable
Create verification challengeenclave / verifycreate_challenge
Derive proxy npubproxyderive_proxy_npub
Run maintenanceenclavemaintain, maintain(dry_run=True)
Recognize cross-channelenclave / resonancerecognize
Link two identitiesenclave / resonancelink
Analyze network profileenclavenetwork_shape
Displace weakest in tierenclavedisplace
Persist social graphenclavesave / load

Security

Configuration

Capacity Constants

All capacity constants are defined in types.py and can be overridden via SocialEnclave.create(tier_capacity=..., list_capacity=...).

ListCapacity
INTIMATE5
CLOSE15
FAMILIAR50
KNOWN80
BLOCK50
GRAY100 (auto-decay after 30 days)
Friends total150 (Dunbar's number)

Identity States

StateMeaning
PROXYHMAC-derived from email/phone/handle. Default for new contacts.
CLAIMEDUser provided an npub but it has not been verified.
VERIFIEDSigned challenge confirms npub ownership. Verified contacts get warmer behavior.

Drift Thresholds

TierSilent Days Before Demotion
INTIMATE30 days
CLOSE60 days
FAMILIAR90 days
KNOWN180 days (then moves to gray)

NIP Integration

NostrSocial works with Nostr identity but does not require relay connectivity for core operations. Proxy npubs are derived locally via HMAC-SHA256.

NIPUsageStatus
NIP-01Event signing for verification challengesStub (0.1.0)
NIP-02Contact list structure (Dunbar-inspired tiers)Implemented
NIP-19bech32 npub encoding for proxy identitiesImplemented

Ecosystem

NostrSocial is Pillar 4 of the NSE platform, the relationships layer. It works standalone or alongside the other OpenClaw skills.

PillarPackageDomain
1. IdentitynostrkeyCryptographic keypairs, signing, encryption
2. FinancenostrwalletconnectLightning wallet access via NWC
3. TimenostrcalendarSelf-owned scheduling and availability
4. RelationshipsnostrsocialSocial graph, trust tiers, guardrails
5. Compasssocial-alignmentValue alignment across five lenses
Orchestratornse-orchestratorNervous system connecting all pillars