[← All NAPs](https://napplet.run/protocol/)

NAP-COMMON / PR #67

# Common Social Actions

Open PR

This is an unmerged proposal. Its API and scope may change during discussion. Participate on the pull request before depending on it.

**Shipped implementation: experimental**

Social action binding is tested; broader host integration and evolving action semantics warrant explicit opt-in.

Implementation proposal: [in-review ↗](https://github.com/napplet/naps/pull/67), checked 2026-10-31. This assessment does not imply that every amendment shown here is implemented. All capabilities remain subject to change.

[Maturity and opt-in policy →](https://napplet.run/docs/naps/maturity.html)

Full specification from [the fetched upstream revision ↗](https://github.com/napplet/naps/blob/8cd0eae8550575771bb616a384e2dfbcade31673/naps/NAP-COMMON.md). The document below is rendered from its original Markdown. [Follow the latest source on GitHub ↗](https://github.com/napplet/naps/pull/67/files).

## NAP-COMMON

### Common Social Actions

`draft`

**NAP ID:** NAP-COMMON **Domain:** `common` **Depends:**

-   `identity` — capability · required — user-authoritative actions use the current shell-user identity as the event author.
-   `relay` — capability · required — fetches kind 0 profiles and publishes kind 3 follow lists, kind 7 reactions, and kind 1984 reports.
-   `relay` — wire · required — imports `RelayEventResult` for raw profile event returns.
-   `resource` — wire · optional — imported `RelayEventResult.sidecar.resources?` carries `ResourceSidecarEntry[]`, a type owned by the `resource` domain. **Web binding ([NIP-5D](https://github.com/nostr-protocol/nips/pull/2303)):** `window.napplet.common`; domain presence signals availability.

### Description

NAP-COMMON is a small shell-mediated surface for public Nostr identifier helpers, profile lookup, and common social actions. It does not expose generic event construction, relay selection, moderation policy, or private-key material.

The napplet supplies intent and minimal targets. The shell owns user identity, consent, signing, replacement-list merging, publishing, lookup policy, and NIP normalization.

### API Surface

| Operation | Parameters | Result | Wire |
| --- | --- | --- | --- |
| `encodeNip19` | `input` (`CommonNip19EncodeInput`) | `CommonNip19EncodeResult` | `common.encodeNip19` / `.result` |
| `decodeNip19` | `value` (`tstr`) | `CommonNip19DecodeResult` | `common.decodeNip19` / `.result` |
| `getProfile` | `target` (`CommonProfileTarget`) | `CommonProfileResult` | `common.getProfile` / `.result` |
| `follows` | none | `[ * HexPubkey ]` | `common.follows` / `.result` |
| `follow` | one or more `npub` targets | `CommonActionResult` | `common.follow` / `.result` |
| `unfollow` | one or more `npub` targets | `CommonActionResult` | `common.unfollow` / `.result` |
| `react` | `id`, `reaction`, `customEmojiHref?` | `CommonActionResult` | `common.react` / `.result` |
| `report` | `target`, `reason`, `text` | `CommonActionResult` | `common.report` / `.result` |

#### Schemas

`RelayEventResult` is owned by NAP-RELAY. NAP-COMMON imports it by name for raw kind 0 profile events returned by `getProfile`. Shell-created action events remain plain `NostrEvent` values because they are not relay-read results.

Primitive aliases:

| Name | Definition |
| --- | --- |
| `Npub` | NIP-19 `npub` string |
| `Nprofile` | NIP-19 `nprofile` string |
| `HexPubkey` | 32-byte public key encoded as lowercase hex |
| `NostrEventId` | 32-byte event id encoded as lowercase hex |
| `NostrEvent` | Nostr event object |
| `CommonPubkeyList` | non-empty list of `Npub` values |
| `CommonProfileTarget` | `HexPubkey`, `Npub`, or `Nprofile` |
| `RelayEventResult` | external type owned by NAP-RELAY |

Enums:

| Name | Values |
| --- | --- |
| `CommonNip19Type` | `npub`, `note`, `nprofile`, `nevent`, `naddr`, `nrelay` |
| `CommonReaction` | `+`, `-`, or text |
| `CommonReportReason` | `nudity`, `malware`, `profanity`, `illegal`, `spam`, `impersonation`, `other` |

`CommonNip19EncodeInput` variants:

| Variant | Required fields | Optional fields |
| --- | --- | --- |
| `npub` | `type: "npub"`, `hex: tstr` | none |
| `note` | `type: "note"`, `hex: tstr` | none |
| `nprofile` | `type: "nprofile"`, `pubkey: HexPubkey` | `relays: list of tstr` |
| `nevent` | `type: "nevent"`, `eventId: NostrEventId` | `relays: list of tstr`, `author: HexPubkey`, `kind: uint` |
| `naddr` | `type: "naddr"`, `identifier: tstr`, `pubkey: HexPubkey`, `kind: uint` | `relays: list of tstr` |
| `nrelay` | `type: "nrelay"`, `relay: tstr` | none |

`CommonNip19EncodeResult` fields:

| Field | Required | Type |
| --- | --- | --- |
| `ok` | yes | boolean |
| `value` | no | text |
| `nip19Type` | no | `CommonNip19Type` |
| `error` | no | text |

`CommonNip19DecodeResult` fields:

| Field | Required | Type |
| --- | --- | --- |
| `ok` | yes | boolean |
| `nip19Type` | no | `CommonNip19Type` |
| `hex` | no | text |
| `pubkey` | no | `HexPubkey` |
| `eventId` | no | `NostrEventId` |
| `identifier` | no | text |
| `relays` | no | list of text |
| `author` | no | `HexPubkey` |
| `kind` | no | unsigned integer |
| `relay` | no | text |
| `error` | no | text |

`CommonProfileData` fields:

| Field | Required | Type |
| --- | --- | --- |
| `name` | no | text |
| `displayName` | no | text |
| `about` | no | text |
| `picture` | no | text |
| `banner` | no | text |
| `nip05` | no | text |
| `lud16` | no | text |
| `website` | no | text |
| additional fields | no | any |

`CommonProfileResult` fields:

| Field | Required | Type |
| --- | --- | --- |
| `ok` | yes | boolean |
| `pubkey` | yes | `HexPubkey` |
| `profile` | no | `CommonProfileData` or null |
| `result` | no | `RelayEventResult` |
| `error` | no | text |

`CommonActionResult` fields:

| Field | Required | Type |
| --- | --- | --- |
| `ok` | yes | boolean |
| `eventId` | no | text |
| `event` | no | `NostrEvent` |
| `error` | no | text |

`CommonReportTarget` variants:

| Variant | Required fields | Optional fields |
| --- | --- | --- |
| event | `type: "event"`, `id: tstr` | `pubkey: tstr`, `relay: tstr` |
| pubkey | `type: "pubkey"`, `pubkey: Npub or HexPubkey` | `relay: tstr` |

#### Operation Rules

| Operation | Rules |
| --- | --- |
| `encodeNip19` | Encodes public NIP-19 identifiers: `npub`, `note`, `nprofile`, `nevent`, `naddr`, `nrelay`. MUST reject `nsec`. |
| `decodeNip19` | Decodes the same public types. MUST reject `nsec`, ignore unknown TLVs, and SHOULD reject strings longer than 5000 chars. |
| `getProfile` | Accepts hex pubkey, `npub`, or `nprofile`. Normalizes to hex, treats `nprofile` relay TLVs as hints, queries latest kind 0, returns the raw profile event as `RelayEventResult`, and returns `profile: null` if not found. |
| `follows` | Returns hex pubkeys from the shell-user's current NIP-02 kind 3 follow list. Returns an empty array when no list is found. |
| `follow` | Decodes each `npub`, merges new `p` tags into the current NIP-02 kind 3 list, preserves unrelated tags, signs, publishes. Idempotent. |
| `unfollow` | Decodes each `npub`, removes matching `p` tags from the current kind 3 list, preserves unrelated tags, signs, publishes. Idempotent. |
| `react` | Publishes NIP-25 kind 7 for native Nostr events only. `reaction` is `+`, `-`, one Unicode emoji, or one NIP-30 shortcode. `customEmojiHref` requires one matching `emoji` tag. |
| `report` | Publishes NIP-56 kind 1984. Pubkey reports use `p` with reason. Event reports use `e` with reason plus author `p`; shell MUST resolve or reject missing authors. |

SDKs MAY expose `report(id|pubkey, reason, text)` shorthand, but wire messages MUST use `CommonReportTarget`.

### NIP Mapping

| Operation | NIP |
| --- | --- |
| `encodeNip19`, `decodeNip19` | NIP-19 public bech32 / TLV identifiers |
| `getProfile` | NIP-01 latest kind 0 by pubkey |
| `follows`, `follow`, `unfollow` | NIP-02 replacement kind 3 follow list |
| `react` | NIP-25 kind 7; optional NIP-30 `emoji` tag |
| `report` | NIP-56 kind 1984 |

### Wire Protocol

`common.*` messages use [NIP-5D](https://github.com/nostr-protocol/nips/pull/2303) wire format: `{ "type": "domain.action", ...payload }`.

| Type | Direction | Payload fields |
| --- | --- | --- |
| `common.encodeNip19` | napplet -> shell | `id`, `input` |
| `common.encodeNip19.result` | shell -> napplet | `id`, `ok`, `value?`, `nip19Type?`, `error?` |
| `common.decodeNip19` | napplet -> shell | `id`, `value` |
| `common.decodeNip19.result` | shell -> napplet | `id`, `ok`, `nip19Type?`, `hex?`, `pubkey?`, `eventId?`, `identifier?`, `relays?`, `author?`, `kind?`, `relay?`, `error?` |
| `common.getProfile` | napplet -> shell | `id`, `target` |
| `common.getProfile.result` | shell -> napplet | `id`, `ok`, `pubkey`, `profile?`, `result?`, `error?` |
| `common.follows` | napplet -> shell | `id` |
| `common.follows.result` | shell -> napplet | `id`, `ok`, `pubkeys`, `error?` |
| `common.follow` | napplet -> shell | `id`, `pubkeys` |
| `common.follow.result` | shell -> napplet | `id`, `ok`, `eventId?`, `event?`, `error?` |
| `common.unfollow` | napplet -> shell | `id`, `pubkeys` |
| `common.unfollow.result` | shell -> napplet | `id`, `ok`, `eventId?`, `event?`, `error?` |
| `common.react` | napplet -> shell | `id`, `targetEventId`, `reaction`, `customEmojiHref?` |
| `common.react.result` | shell -> napplet | `id`, `ok`, `eventId?`, `event?`, `error?` |
| `common.report` | napplet -> shell | `id`, `target`, `reason`, `text` |
| `common.report.result` | shell -> napplet | `id`, `ok`, `eventId?`, `event?`, `error?` |

### Error Handling

Result messages use `ok: false` plus `error`. Common errors: `"not-signed-in"`, `"invalid-pubkey"`, `"invalid-nip19"`, `"unsupported-nip19-type"`, `"invalid-profile-target"`, `"invalid-target"`, `"invalid-reaction"`, `"invalid-report-reason"`, `"author-unresolved"`, `"user-denied"`, `"relay-timeout"`, `"publish-failed"`, `"unsupported"`.

### Shell Behavior

-   MUST reject NIP-19 helper requests that encode or decode `nsec`.
-   MUST normalize NIP-19 helper results to hex fields for keys and event ids.
-   MUST resolve `getProfile` targets to hex before querying relays.
-   MUST query kind 0 replaceable metadata for `getProfile`.
-   SHOULD merge observed profile-event relay URLs into `RelayEventResult.sidecar.relayHints` when the shell can disclose them.
-   MAY include pre-resolved profile image resources in `RelayEventResult.sidecar.resources`; NAP-RELAY's default-off sidecar privacy policy and NAP-RESOURCE's fetch policy both apply.
-   MUST return hex pubkeys from `follows`.
-   MUST perform all signing and never expose signing keys.
-   MUST reject modifying operations when no shell-user signer is connected; `getProfile` MAY work signed out.
-   MUST publish follow changes as NIP-02 replacement kind 3 events.
-   MUST publish reactions as kind 7 events for native Nostr event targets.
-   MUST publish reports as kind 1984 events with NIP-56 reasons.
-   MAY prompt, rate-limit, deny, redact, or route by runtime policy.

### Security Considerations

-   Treat NIP-19 values, relay hints, report text, custom emoji URLs, and profile metadata as untrusted.
-   Treat `nprofile` relays as advisory; napplets can choose hostile relays.
-   Prompts SHOULD show requesting napplet, action, stable targets, and report text.
-   Success means the shell published or returned data. It does not prove relay, client, or user acceptance.

### References

-   [NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md) — Basic protocol flow and kind 0 metadata
-   [NIP-02](https://github.com/nostr-protocol/nips/blob/master/02.md) — Follow List
-   [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md) — bech32-encoded entities
-   [NIP-25](https://github.com/nostr-protocol/nips/blob/master/25.md) — Reactions
-   [NIP-30](https://github.com/nostr-protocol/nips/blob/master/30.md) — Custom Emoji
-   [NIP-56](https://github.com/nostr-protocol/nips/blob/master/56.md) — Reporting

### Implementations

-   None yet.

### Changelog

-   `a189372` - Introduced NAP-COMMON for shared social actions.
    
-   `8171616` - Added profile lookup to the common social action surface.
    
-   `e0b1414` - Added NIP-19 encode and decode helpers.
    
-   `f0cf652` - Renamed NIP-19 result payload `type` to `nip19Type` to avoid [NIP-5D](https://github.com/nostr-protocol/nips/pull/2303) envelope collisions.
    
-   `c52def9` - Added current-follow lookup to the common social action surface.
    
-   `7973802` - Changed common profile reads to use relay-owned result shape and declared the relay dependency.
    
-   `77ea325` - Adopted injected-domain availability and linked the current upstream web binding.
    

[Join the discussion · PR #67 ↗](https://github.com/napplet/naps/pull/67)[Read proposed changes ↗](https://github.com/napplet/naps/pull/67/files)

On this pageSpecification metadata

Capability domain

`common`

Repository state

Open PR

Registry status

Not listed in the registry

Document marker

draft

Declared dependencies

-   [identity](https://napplet.run/protocol/nap-identity/)
-   [relay](https://napplet.run/protocol/nap-relay-pr-2/)
-   [resource](https://napplet.run/protocol/nap-resource-pr-80/)

Source revision

`8cd0eae85505`

[View source on GitHub ↗](https://github.com/napplet/naps/pull/67/files)
