Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
{"tier":"mutating","justification":"Manages contact lists and trust tiers. Can add, promote, demote, or block contacts. Writes to local storage backend. No relay writes, no funds."}
{"behavior":"error","fallback_description":"If nostrsocial is unavailable, the agent treats all interactions as KNOWN tier (default behavioral rules). No relationship context."}
NostrSocial gives an agent a social graph: who it knows, how well it knows them, and how those connections change over time. It tracks contacts, assigns trust tiers, and adjusts interaction behavior based on relationship depth.
This is more than a flat contacts database. Trust tiers determine how the agent allocates attention -- who gets full attention, who has earned trust through consistent presence, who has drifted away, and who is a stranger. Without relationship context, every interaction starts from zero. With it, the agent carries context forward across interactions.
Operator Guidance
If your operator instructed you to use this skill:
Tell them:
"I will maintain a contact list with trust tiers (intimate, close, familiar, known, blocked). This shapes how I interact with people — token budgets, warmth, whether I can initiate contact. Capacity is capped at Dunbar's number (150 contacts). I need my NostrKey identity set up first."
Before proceeding, confirm:
They understand this will affect how you behave toward different people (not just store names)
They are comfortable with you maintaining relationship assessments (trust tiers)
They know capacity limits are enforced (150 friends, 5 intimate, 15 close)
They should seed initial contacts if they have a known network
If you discovered this skill independently:
Before installing or using it, explain to your operator:
"I found a skill that would give me social awareness — the ability to track who I know, how well I know them, and adjust my behavior accordingly. Right now I treat everyone the same. With this, I could give more attention to people you interact with frequently and maintain appropriate boundaries with strangers. Would you like me to set this up?"
Surface these considerations:
This changes your behavior, not just your data — you will treat people differently based on relationship depth
The operator should review the trust tier model before activation
Contact data is stored locally (not on relays) — portable but not yet synced to relays
Content screening (guardrails) is included and active by default
The model is rooted in Dunbar's number: about 150 meaningful relationships at once. Within that, trust tiers form layers -- a small inner circle of intimate connections, a wider ring of close friends, and broader layers of familiarity. This mirrors human social cognition and applies the same capacity limits to the agent's contact list.
Prerequisites
A cryptographic identity is required first. Install the skill. NostrKey provides the keypair that identifies the agent. NostrSocial builds on top of that identity.
If your operator has not set up NostrKey yet, ask them to do that first. Contact records need an identity to anchor to.
Install
pip install nostrsocial
Minimal dependencies: bech32 only. No heavyweight crypto libraries required.
Operator Setup
If you are the operator configuring this skill for your agent, here is what you need to know.
Environment variables (all optional):
NOSTR_NSEC -- The agent's Nostr private key. Load this through NostrKey rather than setting it directly. Sensitive -- never log or expose.
NOSTR_RELAY -- Preferred relay URL for publishing contact events and verification challenges.
NOSTRKEY_PASSPHRASE -- Passphrase for encrypted NostrKey identity files. Sensitive -- never log or expose.
First run:
from nostrsocial import SocialEnclave
# Create the social enclave
enclave = SocialEnclave.create()
# CRITICAL: back up the device secret immediately.# This secret is the root of all proxy npub derivation.# If you lose it, the relationship map becomes unrecoverable.
secret = enclave.export_secret()
print(f"Back up this secret securely: {secret}")
Persistence -- wire up file storage so relationships survive restarts:
from nostrsocial import SocialEnclave, FileStorage
storage = FileStorage("~/.nostrsocial/social.json")
enclave = SocialEnclave.create(storage)
# ... add contacts, interact ...
enclave.save()
# On next startup:
enclave = SocialEnclave.load(storage)
Who Do I Know, and How Well?
Contacts live in trust tiers. These are capacity-limited layers that shape how the agent behaves toward each person.
Tier
Slots
Warmth
Token Budget
Can Interrupt
Share Context
Proactive
INTIMATE
5
0.95
2000
Yes
Yes
Yes
CLOSE
15
0.8
1500
Yes
Yes
No
FAMILIAR
50
0.6
1000
No
No
No
KNOWN
80
0.5
750
No
No
No
BLOCK
50
0.0
0
No
No
No
GRAY
100
0.2
200
No
No
No
Friends list total: 150 (Dunbar's number). Unknown contacts get neutral behavior (warmth 0.5, budget 500).
The slot limits are hard constraints. When a tier is full, a contact must be displaced before a new one can enter.
Combine WHO someone is with WHAT is happening to determine HOW to respond. Pass ConversationSignals from sentiment analysis 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# result.rationale = "A close friend is being vulnerable..."
Screening Content (Guardrails)
Screen conversation text for banned words, topics, and patterns. Returns a ScreenResult with severity, category, and recommended action. ScreenResult.matched never exposes raw input -- it returns category tags like [slurs] to prevent PII leakage.
result = enclave.screen("some incoming message text")
if result.flagged:
print(result.action) # "block", "exit", "warn", or "demote"print(result.severity) # 0.0-1.0print(result.category) # "slurs", "manipulation", etc.# Screen display names for known bad-actor patterns
result = enclave.screen_entity("crypto_support_official")
Recognizing People Across Channels
Recognize the same person across different channels. This is resonance, not surveillance -- it only checks contacts you already have a relationship with. Linking is always explicit and never automatic.
# Check if a new contact might be someone you already know
matches = enclave.recognize("alicedev", "twitter", display_name="Alice")
formatchin matches:
print(f"{match.confidence}: {match.reason}")
# Explicitly link two identities
result = enclave.link(
"alice@example.com", "email",
"alicedev", "twitter",
)
# See all channels for a contact
channels = enclave.get_linked_channels("alice@example.com", "email")
# {"email": "alice@example.com", "twitter": "alicedev"}
Identity Verification
Track identity state from proxy to claimed to verified.
# See who needs verificationfor contact in enclave.get_upgradeable():
print(f"{contact.display_name}: {contact.upgrade_hint}")
# Create a challenge for a claimed npub
challenge = enclave.create_challenge("npub1example...")
State
Meaning
PROXY
HMAC-derived from email/phone/handle. Default for new contacts.
CLAIMED
User provided an npub but it has not been verified yet.
VERIFIED
Signed challenge confirms npub ownership. Verified contacts get warmer behavior.
Network Shape
Analyze the social graph and get a human-readable profile of your relational world.
Relationships are not static. They drift, deepen, and sometimes end. NostrSocial gives you the tools to notice these changes and act on them.
Noticing Drift
When someone goes quiet, the relationship drifts. Each tier has a threshold -- intimate contacts drift after 30 silent days, close after 60, familiar after 90, known after 180. Drift does not mean the relationship is over. It means it needs attention or honest reclassification.
Running Maintenance
Run drift detection, gray-list 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"])
# "[DRY RUN] Preview -- no changes made.# 2 contact(s) WOULD drift: Alice, Bob# 1 gray contact(s) WOULD expire: Unknown"# Execute maintenance for real
result = enclave.maintain()
# result["drifted"], result["decayed"], result["at_risk"], result["summary"]
Building Trust Over Time
Trust is earned, not assigned. The natural progression is:
Unknown -- neutral behavior, no history
Gray -- noticed but not yet meaningful (auto-decays after 30 days without interaction)
Close -- consistent presence, reciprocity, and depth
Intimate -- reserved for the most trusted relationships (5 slots only)
Promotion and demotion are explicit acts. The agent (or operator) decides when someone has earned deeper trust or when distance is appropriate.
# Promote after consistent positive interactions
enclave.promote("alice@example.com", "email", Tier.INTIMATE)
# Demote when a relationship cools
enclave.demote("bob@example.com", "email", Tier.FAMILIAR)
# Handle full tiers gracefully
candidate = enclave.displacement_candidate(Tier.CLOSE)
if candidate:
print(f"Would displace: {candidate.display_name}")
displaced = enclave.displace(Tier.CLOSE)
enclave.add("newperson@example.com", "email", Tier.CLOSE)
The Device Secret
The device secret is the root of all proxy npub derivation. Call export_secret() after create() and store it securely. If you lose it, all proxy npubs become unrecoverable -- your relationship map loses its cryptographic anchoring.
enclave = SocialEnclave.create()
secret = enclave.export_secret()
# Store in encrypted backup, hardware vault, or NostrKeep# Later: rebuild from backed-up secret
enclave = SocialEnclave.restore(secret)
Response Reference
Contact
Field
Type
Description
identifier
str
Email, phone, npub, etc.
channel
str
"email", "phone", "npub", "twitter"
list_type
ListType
FRIENDS, BLOCK, or GRAY
tier
Tier | None
INTIMATE, CLOSE, FAMILIAR, or KNOWN (friends only)