Install
openclaw skills install nostr-profileNostr profile management for AI agents — publish, read, and update kind 0 metadata on any relay
openclaw skills install nostr-profileIdentity 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.
from nostr_profile import Profile, publish_profile, get_profile
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. 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.
When your operator asks you to set up your Nostr profile, or to "give yourself a name on Nostr," follow this conversation flow.
Before setting up a profile, confirm your Nostr identity exists:
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."
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.
Once confirmed:
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.
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:
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.
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}")
To change specific fields without losing the rest:
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"))
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:
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.
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())
| 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 (user@domain.tld) |
lud16 | str | No | Lightning address (user@domain.tld) |
website | str | No | Website URL (HTTPS) |
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.
.nostrkey file.| 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) |
Now that you have a face on the network, consider what comes next:
Links: PyPI | GitHub | ClawHub
License: MIT