Interactive Templated Content Frames
Motivation
Current content display in Nostr is either unstructured (plain text/markdown) or client-dependent. This makes it difficult to create consistent, interactive experiences across different clients. Hyperframes solve this by providing:
-
Guaranteed rendering consistency through HTML/CSS templates
-
Interactive capabilities through Nostr-native callbacks
-
Flexible sizing for different use cases (stories, cards, whiteboard elements, …)
-
Template variables using Mustache-like syntax with Nostr extensions (Nostache)
Specification
Frame Event
A Hyperframe is created using a kind:1234
event with the following structure:
{
"kind": 1234,
"content": "<HTML template with Nostache syntax and embedded styles>",
"tags": [
["dim", "<width>x<height>"],
["type", "<frame-type>"],
["preview", "<image url>"]
]
}
Tag definitions:
-
dim
- Required frame dimensions in pixels (e.g. "360x640" for stories) -
type
- Frame type identifier (e.g. "story", "card", "whiteboard-element") -
preview
- Optional preview image URL for clients and/or use cases where rendering the frame doesn’t make sense
Nostache Template Syntax:
The content field uses an extended version of Mustache JS (Wikistr, Wikifreedia) syntax that includes Nostr-specific callbacks. These callbacks follow the NIP-07 (Wikistr, Wikifreedia) window.nostr
pattern, where the hosting client acts as the signer/handler for the frame:
Standard Mustache tags:
-
{{variable}}
- Variable substitution -
{{#section}}…{{/section}}
- Section blocks -
{{^section}}…{{/section}}
- Inverted sections -
{{!comment}}
- Comments -
{{>partial}}
- Partials
Nostr-specific callbacks (Nostache):
-
{{#nostr.getPublicKey}}…{{/nostr.getPublicKey}}
- Get the user’s public key -
{{#nostr.signEvent}}…{{/nostr.signEvent}}
- Request event signing -
{{#nostr.zap}}…{{/nostr.zap}}
- Request zap payment (NIP-57 (Wikistr, Wikifreedia)) -
{{#nostr.navigate}}…{{/nostr.navigate}}
- Navigate to another frame or position
Navigation can be done using
-
Event ID:
{{#nostr.navigate}}event:{{event_id}}{{/nostr.navigate}}
-
Other navigation methods TBD
Example: Story Frame
{
"kind": 1234,
"content": `
<style>
.story {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.story img {
width: 100%;
height: auto;
}
.cta {
padding: 16px;
text-align: center;
}
.zap-button {
background: #ff9500;
color: white;
border: none;
padding: 8px 16px;
border-radius: 8px;
}
</style>
<div class="story">
<img src="{{image_url}}" alt="{{alt_text}}">
<div class="cta">
{{#nostr.zap}}
<button class="zap-button" data-amount="{{amount}}">
Zap {{amount}} sats!
</button>
{{/nostr.zap}}
</div>
</div>
`,
"tags": [
["dim", "360x640"],
["type", "story"],
["preview", "https://example.com/story-preview.jpg"]
]
}
Example: Simple Whiteboard Note
{
"kind": 1234,
"content": `
<style>
.note {
width: 100%;
height: 100%;
font-family: Inter, system-ui;
}
.note-header {
font-size: 16px;
font-weight: 600;
margin-bottom: 8px;
}
.note-content {
font-size: 14px;
white-space: pre-wrap;
}
</style>
<div class="note">
<div class="note-header">{{title}}</div>
<div class="note-content">{{content}}</div>
</div>
`,
"tags": [
["dim", "200x200"],
["type", "whiteboard-element"],
["pos", "100", "150"]
]
}
Implementation Notes
-
Clients MUST render Hyperframes exactly as specified by the HTML/CSS, maintaining the given dimensions
-
Clients MUST sandbox Hyperframe content to prevent malicious code execution (NIP-94 (Wikistr, Wikifreedia))
-
Clients SHOULD implement the standard Nostache callbacks following NIP-07 (Wikistr, Wikifreedia) patterns
When a callback is triggered:
-
The client acts as a NIP-07 (Wikistr, Wikifreedia) provider for the frame
-
All callbacks are async and return promises
-
Callbacks should follow the same security model as NIP-07 (Wikistr, Wikifreedia)
-
User confirmation is required for sensitive operations
Security Considerations
-
Clients MUST sanitize HTML content to prevent XSS attacks
-
All callbacks MUST require user confirmation before execution
-
Frame dimensions MUST be respected to prevent layout manipulation
-
CSS MUST be scoped to the frame to prevent style leakage
-
Clients MUST implement resource limits (CPU, memory, network)
-
Frames MUST NOT have access to the parent window’s DOM
-
Callbacks MUST follow the same security model as NIP-07 (Wikistr, Wikifreedia)
Benefits
-
Consistent rendering across all clients
-
Native Nostr interactivity through standardized NIP-07 (Wikistr, Wikifreedia) style callbacks
-
Flexible sizing for different use cases
-
Reusable templates with variable substitution
-
Secure sandboxed execution
-
Perfect for stories, cards, interactive posts, and whiteboard elements
Client Recommendations
-
Implement a robust HTML/CSS sanitizer
-
Provide clear UI indicators for interactive elements
-
Cache commonly used templates
-
Support template sharing between frames
-
Implement graceful fallbacks for unsupported features
-
Follow NIP-07 (Wikistr, Wikifreedia) security best practices for callback handling