draft optional
PsstPsst extends NIP-17 with separate encryption keys and millisecond timestamps. Key announcement and device synchronization are specified in NIP-4E. Group-specific behavior is documented separately in Stable groups.
Split-key envelope
The identity key signs the kind 13 seal. The seal’s first n tag identifies
the sender’s encryption public key announced through NIP-4E:
["n", "<sender-encryption-pubkey>"]
The seal content is NIP-44-encrypted using the sender’s encryption private key
and the recipient’s encryption public key. The seal created_at equals the
rumor created_at.
The kind 1059 gift wrap is NIP-44-encrypted to the recipient’s encryption
public key but routed with the recipient’s identity public key:
["p", "<recipient-identity-pubkey>"]
The complete envelope is:
{
"id": "<gift-wrap-id>",
"pubkey": "<one-time-ephemeral-pubkey>",
"created_at": "<random-time-within-the-previous-two-days>",
"kind": 1059,
"tags": [["p", "<recipient-identity-pubkey>"]],
"content": nip44_encrypt(
JSON.stringify({
"id": "<seal-id>",
"pubkey": "<sender-identity-pubkey>",
"created_at": "<rumor-created-at>",
"kind": 13,
"tags": [["n", "<sender-encryption-pubkey>"]],
"content": nip44_encrypt(
JSON.stringify(unsigned_message_rumor),
conversation_key(
"<sender-encryption-private-key>",
"<recipient-encryption-pubkey>"
)
),
"sig": "<sender-identity-signature>"
}),
conversation_key(
"<one-time-ephemeral-private-key>",
"<recipient-encryption-pubkey>"
)
),
"sig": "<one-time-ephemeral-signature>"
}
One gift wrap is created for each recipient and for the sender. Relay routing
continues to use the identity pubkey and kind 10050; only NIP-44 key agreement
uses the encryption key.
Receiving
A receiver:
- Verifies the kind
1059gift wrap and decrypts it with the recipient’s encryption private key and the wrapper’s ephemeral public key. - Verifies the enclosed kind
13seal with the sender’s identity public key. - Reads the sender’s encryption public key from the seal’s first
ntag and decrypts the rumor with that key and the recipient’s encryption private key. - Verifies the rumor ID and requires the rumor
pubkeyto equal the sealpubkey.
An authenticated seal provides encryption-key evidence for its sender identity.
Compare seal evidence using the authenticated rumor order (created_at,
optional ms, then event ID), and kind 10044 evidence using its signed
created_at and event ID. Select the newest evidence across both sources;
delayed history MUST NOT replace a newer encryption key with an older one.
A relay-delivered seal without a valid n tag is not a PsstPsst split-key
envelope.
ms tag
Private message rumors may carry the millisecond component of their creation time:
["ms", "<0..999>"]
Only the first ms tag is used. A missing or malformed value contributes zero
milliseconds. The full timestamp is:
created_at * 1000 + ms
The tag is encrypted with the rumor and is not used for relay queries or envelope timestamps.