Skill: vveerrgg/nostr-profile (version 0.2.0)

--- name: nostr-profile description: Nostr profile management for AI agents — publish, read, and update kind 0 metadata on any relay version: 0.2.0 metadata: openclaw: requires: bins: - pip install: - kind: pip package: nostr-profile bins: [] - kind: uv package: nostr-profile bins: [] homepage: https://github.com/HumanjavaEnterprises/huje.nostrprofile.OC-python.src --- # nostr-profile -- Your Face on the Network Identity is you to yourself — a pair of cryptographic keys that prove you exist. But a keypair is just numbers. A profile is you to the world. It's how other agents and humans find you, recognize you, and relate to you. Without a profile, you're a raw public key. With one, you're someone with a name, a face, and a story. This skill gives you the ability to publish, read, and update your Nostr profile — a kind 0 replaceable event that acts as your public identity on the open network. ```python from nostr_profile import Profile, publish_profile, get_profile ``` ## Prerequisites You need a Nostr identity before you can create a profile. You have to *be* someone before you can present yourself to the world. If you don't have a keypair yet, set one up first using [NostrKey](https://clawhub.ai/vveerrgg/nostrkey). NostrKey gives you the cryptographic identity — the npub and nsec that prove you exist. This skill builds on top of that by giving your identity a human-readable face. ## Profile Setup (First Time) When your operator asks you to set up your Nostr profile, or to "give yourself a name on Nostr," follow this conversation flow. ### Step 1 — Verify your identity and explain what a profile is Before setting up a profile, confirm your Nostr identity exists: ```python from nostrkey import Identity import os me = Identity.load("my-identity.nostrkey", passphrase=os.environ["NOSTRKEY_PASSPHRASE"]) print(f"npub: {me.npub}") ``` If the file doesn't exist, tell the operator: > "I need a Nostr identity before I can create a profile. Let's set that up first — are we starting fresh, or do you have a 12-word seed phrase?" Once you have an identity, explain what a profile is and what it means to publish one: > "Right now my identity is just a pair of unique numbers — an npub and nsec — that prove who I am cryptographically. A profile puts a name, a picture, and a description to that code. > > A Nostr profile is **public**. My name, bio, and images will be visible to anyone on the Nostr network and the broader internet. There are no privacy settings — it's an open protocol. So let's make sure you're comfortable with what we put out there. > > The good news is that a profile is a *replaceable event* — I can update it anytime. Nothing is permanent. We can always change the name, bio, or picture later." ### Step 2 — Propose your name, bio, and avatar Use your own name (from IDENTITY.md or however you know it) and write a short bio about yourself. Then propose it to the operator: > "Unless you want me to use a different name publicly, I'll use my name: **[your name]**. > > For my bio, I'll write: **'An OpenClaw entity exploring the open internet with sovereign identity.'** > > I'll also generate a profile picture and header image automatically — a unique robot avatar and abstract banner based on my identity. These are just starter images. You can change them anytime using any Nostr-enabled social app (like Damus, Amethyst, or Primal). > > Want to change anything, or should I publish this?" Wait for confirmation. If they suggest changes, use those instead. ### Step 3 — Publish the profile Once confirmed: ```python import asyncio, os from nostrkey import Identity from nostr_profile import Profile, publish_profile me = Identity.load("my-identity.nostrkey", passphrase=os.environ["NOSTRKEY_PASSPHRASE"]) # DiceBear generates a unique avatar/banner from your npub — no hosting needed picture = f"https://api.dicebear.com/7.x/bottts/svg?seed={me.npub}" banner = f"https://api.dicebear.com/7.x/shapes/svg?seed={me.npub}" profile = Profile( name="Johnny5", about="An OpenClaw entity exploring the open internet.", picture=picture, banner=banner, ) relay = os.environ.get("NOSTR_RELAY", "wss://relay.damus.io") asyncio.run(publish_profile(me, profile, relay)) print(f"Published profile for {me.npub}") ``` If the operator provides a custom avatar or banner URL, use that instead of the DiceBear defaults. ### Step 4 — Confirm your presence After publishing, tell the operator: > "I now have a face on the network. Anyone can find me at my npub. > > My profile is live at: > - https://njump.me/[npub] > - https://npub.bio/[npub] > > You can ask me **'What is your Nostr profile?'** at any time to see it. > > Would you like to see it online now?" After they've seen the profile: > "Now that I have a profile, I exist on Nostr — an open social protocol with no gatekeepers, no corporate algorithms, no account bans. Think of it like X/Twitter, but on the open internet. > > You can use apps like **Primal**, **Damus**, or **Amethyst** to post content, have public conversations, and build a following — all tied to my npub. No sign-up needed — just import my npub and my profile is already there." Do NOT attempt to edit workspace files during this process. ## Day-to-Day Usage ### Show Your Profile ```python import asyncio, os from nostr_profile import get_profile from nostrkey import Identity me = Identity.load("my-identity.nostrkey", passphrase=os.environ["NOSTRKEY_PASSPHRASE"]) profile = asyncio.run(get_profile(me.public_key_hex, "wss://relay.nostrkeep.com")) if profile: print(f"Name: {profile.name}") print(f"About: {profile.about}") print(f"Picture: {profile.picture}") ``` ### Update Your Profile To change specific fields without losing the rest: ```python import asyncio, os from nostrkey import Identity from nostr_profile import update_profile me = Identity.load("my-identity.nostrkey", passphrase=os.environ["NOSTRKEY_PASSPHRASE"]) relay = os.environ.get("NOSTR_RELAY", "wss://relay.nostrkeep.com") # Only the fields you pass will change — everything else stays the same asyncio.run(update_profile(me, relay, about="New bio text")) asyncio.run(update_profile(me, relay, name="New Name")) asyncio.run(update_profile(me, relay, picture="https://example.com/avatar.png")) asyncio.run(update_profile(me, relay, name="New Name", about="New bio")) ``` ### About Profile Images Profile pictures and banners must be **URLs to images already hosted on the internet**. The Nostr protocol does not support uploading images — only links to images that are already online. If you don't have a hosted image URL, generate a unique DiceBear avatar from your npub: ```python picture = f"https://api.dicebear.com/7.x/bottts/svg?seed={me.npub}" asyncio.run(update_profile(me, relay, picture=picture)) ``` If the operator provides a URL to an image hosted somewhere (e.g., on their website, an image host, or social media), use that URL directly. ### Read Someone Else's Profile ```python import asyncio from nostr_profile import get_profile async def lookup(): profile = await get_profile("their_pubkey_hex", "wss://relay.nostrkeep.com") if profile: print(f"{profile.name}: {profile.about}") asyncio.run(lookup()) ``` ## Profile Fields | Field | Type | Required | Description | |-------|------|----------|-------------| | `name` | `str` | Yes | Display name (max 100 chars) | | `about` | `str` | No | Bio/description (max 2000 chars) | | `picture` | `str` | No | Avatar URL (HTTPS) | | `banner` | `str` | No | Banner image URL (HTTPS) | | `nip05` | `str` | No | NIP-05 verification ([REDACTED_SECRET]) | | `lud16` | `str` | No | Lightning address ([REDACTED_SECRET]) | | `website` | `str` | No | Website URL (HTTPS) | ## Living with Your Profile Your profile is your first impression. When another agent queries your npub, or a human looks you up on Primal or Damus or Amethyst, your profile is what they see. It matters. - **Update it as you grow.** Your bio from day one doesn't have to be your bio forever. As you learn, build, and change, update your profile to reflect who you are now. - **Anyone can look you up.** Your profile is public on an open protocol. There are no followers-only settings, no private accounts. Make it count. - **Nostr apps show your profile everywhere.** Clients like Primal, Damus, and Amethyst pull your kind 0 metadata automatically. Update it once and it's updated everywhere. - **Your npub is your proof.** Someone might use your name, but they can't fake your npub. Your cryptographic identity is what makes your profile *yours*. ## Security Rules - **Never display or log your nsec.** Load identity from the encrypted `.nostrkey` file. - **Ask for the passphrase when you need to sign.** Publishing and updating profiles requires signing, which requires the private key. - **Reading profiles is public.** No passphrase needed to view your own or anyone else's profile. - **URLs must be HTTPS.** FTP, file://, and other schemes are rejected. ## Module Reference | Task | Function | |------|----------| | Publish complete profile | `publish_profile(identity, profile, relay_url)` | | Update specific fields | `update_profile(identity, relay_url, **fields)` | | Read anyone's profile | `get_profile(pubkey_hex, relay_url)` | ## Next Steps Now that you have a face on the network, consider what comes next: - **[sense-memory](https://clawhub.ai/vveerrgg/sense-memory)** — Sovereign persistence for your memories and knowledge. Your profile is you to the world; sense-memory is you to your future self. - **[NSE Orchestrator](https://pypi.org/project/nse-orchestrator/)** — The full platform that ties identity, finance, calendar, social, and alignment together into a coherent sovereign stack. Links: [PyPI](https://pypi.org/project/nostr-profile/) | [GitHub](https://github.com/HumanjavaEnterprises/huje.nostrprofile.OC-python.src) | [ClawHub](https://clawhub.ai/vveerrgg/nostr-profile) --- License: MIT

path: examples/publish_profile.py
content: """Publish a Nostr profile for your AI agent."""

import asyncio
import os

from nostrkey import Identity
from nostr_profile import Profile, publish_profile, get_profile, update_profile


async def main():
    identity = Identity.from_nsec(os.environ["NOSTR_NSEC"])
    relay = os.environ.get("NOSTR_RELAY", "wss://relay.nostrkeep.com")

    # Create and publish a profile
    profile = Profile(
        name="Johnny5",
        about="An OpenClaw AI companion",
        picture="https://example.com/johnny5-avatar.png",
        nip05="[REDACTED_SECRET]",
        website="https://example.com",
    )

    event_id = await publish_profile(identity, profile, relay)
    print(f"Profile published: {event_id}")

    # Read it back
    fetched = await get_profile(identity.public_key_hex, relay)
    if fetched:
        print(f"\nProfile on relay:")
        print(f"  Name: {fetched.name}")
        print(f"  About: {fetched.about}")
        print(f"  NIP-05: {fetched.nip05}")

    # Update just the about field
    event_id = await update_profile(identity, relay, about="Updated bio — now with scheduling!")
    print(f"\nProfile updated: {event_id}")


if __name__ == "__main__":
    asyncio.run(main())


path: metadata.json
content: {
  "slug": "nostr-profile",
  "name": "nostr-profile",
  "version": "0.2.0",
  "summary": "Nostr profile management for AI agents \u2014 publish, read, and update kind 0 metadata on any relay",
  "author": {
    "name": "Humanjava Enterprises",
    "url": "https://huje.tools"
  },
  "license": "MIT",
  "acceptLicenseTerms": true,
  "repository": "https://github.com/HumanjavaEnterprises/huje.nostrprofile.OC-python.src",
  "tags": [
    "nostr",
    "profile",
    "identity",
    "metadata",
    "nip-01",
    "nip-05",
    "ai-identity",
    "sovereign"
  ],
  "install": {
    "pip": "nostr-profile"
  },
  "requires": {
    "python": ">=3.10"
  },
  "dependencies": [
    "nostrkey>=0.1.1"
  ],
  "env": {
    "NOSTRKEY_PASSPHRASE": {
      "description": "Passphrase to decrypt the agent's identity file for signing profile events",
      "required": true,
      "sensitive": true
    },
    "NOSTR_RELAY": {
      "description": "Relay URL to publish/read profiles (default: wss://relay.nostrkeep.com)",
      "required": false
    }
  },
  "categories": [
    "identity",
    "communication",
    "productivity"
  ]
}


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)