tinyrelayfile
tinyrelay/docs/view-definitions.md/

docs/view-definitions.md raw
1# Reusable view definitions
2
3Status: Proposal. These definitions are not accepted by the current management API. [Custom views](views.md) describes the block transforms available today.
4
5A view selects relay data and presents a result. It can list events, summarize a set of events or render one event differently. A diagram renderer is one application of the third mode.
6
7## Three modes
8
9| Mode | Input and result | Examples |
10| --- | --- | --- |
11| `list` | A bounded event query becomes an ordered collection. | Articles by an author, open repository issues, a room feed. |
12| `aggregate` | A bounded set of events becomes one derived result. | Counts by kind, a calendar with RSVP totals, a weekly activity report. |
13| `render` | One event or an extracted part becomes a replacement or companion result. | Diagram blocks, a formatted event card, a rendered README. |
14
15The modes share selection, access, refresh and presentation concepts. They have different result identities and update rules. A scheduled render remains a set of independent renders; running it hourly does not make it an aggregate.
16
17## Shared fields
18
19| Field | Meaning |
20| --- | --- |
21| `version` | Definition format version. |
22| `name` | Stable owner-selected name. |
23| `mode` | `list`, `aggregate` or `render`. |
24| `source` | A Nostr filter and a bounded selection policy. |
25| `refresh` | Refresh on matching writes or on an interval. Manual refresh is always available. |
26| `audience` | Who can read the view: `public` or `members`. |
27| `presentation` | How the result appears: events, a table, structured data or an inline rendering. |
28
29`source.filter` uses the relay's existing Nostr filter semantics, including kinds, authors, IDs and tag filters, through its access-controlled query path. A source limit and deterministic ordering are explicit; equal timestamps use the relay's event ID ordering. A time window is separate from the refresh interval. `refresh.on` and `refresh.every_seconds` are mutually exclusive.
30
31Mode-specific fields are validated for that mode. A list does not require a transform URL. An aggregate does not require a language. A render does not have to produce an image.
32
33## Event list
34
35An article view needs a query and a presentation:
36
37```json
38{
39 "version": 1,
40 "name": "articles",
41 "mode": "list",
42 "source": {
43 "filter": { "kinds": [30023] },
44 "limit": 100,
45 "order": "newest"
46 },
47 "refresh": { "on": "write" },
48 "audience": "public",
49 "presentation": { "type": "events" }
50}
51```
52
53The result retains event references. Existing event components can render them without an external service or generated HTML. Event replacement, deletion and expiration update membership in the list.
54
55## Aggregate
56
57A report counts notes and articles written in the last seven days:
58
59```json
60{
61 "version": 1,
62 "name": "weekly-activity",
63 "mode": "aggregate",
64 "source": {
65 "filter": { "kinds": [1, 30023] },
66 "window_seconds": 604800,
67 "limit": 10000,
68 "order": "newest"
69 },
70 "transform": {
71 "type": "builtin",
72 "name": "count",
73 "group_by": ["kind"]
74 },
75 "refresh": { "every_seconds": 3600 },
76 "audience": "members",
77 "presentation": { "type": "table" }
78}
79```
80
81This produces one result for the selected set. Its response records the time range, source count and whether the selection was truncated. A partial selection must not be presented as a complete total. A table and a chart can present the same structured result without rerunning the aggregation.
82
83Built-in reducers cover common reports. An HTTP reducer can use the same mode only with an explicit, bounded event projection defining the fields sent to the service. It must not inherit the block endpoint's request contract.
84
85## Event rendering
86
87A diagram view selects events, extracts their fenced blocks and renders those blocks:
88
89```json
90{
91 "version": 1,
92 "name": "diagrams",
93 "mode": "render",
94 "source": {
95 "filter": { "kinds": [1, 30023, 30818] },
96 "limit": 500,
97 "order": "newest"
98 },
99 "input": {
100 "type": "fenced-blocks",
101 "from": "content",
102 "languages": ["mermaid", "dot", "d2"]
103 },
104 "transform": {
105 "type": "http",
106 "url": "https://diagram.zip/transform/blocks",
107 "connection": "diagram-renderer",
108 "options": { "appearance": "auto-transparent" }
109 },
110 "output": {
111 "types": ["image/svg+xml", "image/png"],
112 "max_bytes": 1048576
113 },
114 "refresh": { "on": "write" },
115 "audience": "public",
116 "presentation": {
117 "type": "inline",
118 "target": "input",
119 "fallback": "source"
120 }
121}
122```
123
124Languages belong to the fenced-block extractor. Image types belong to this renderer's output contract. Neither is a requirement for every view.
125
126Whole-event rendering uses an explicit field projection as its input and can return structured data or text for a registered presenter. Repository rendering uses an explicit `repo-readme` source adapter. Kind 30618 alone should not silently change event content into a Git file.
127
128Transform options belong to the chosen adapter. Diagram appearance and frame settings do not become relay-wide view fields. Credentials live in the named connection and are excluded from exported definitions.
129
130## Results and access
131
132Each mode has a distinct result identity:
133
134- A list identifies a query result and the referenced events.
135- An aggregate identifies the definition revision and the selected source snapshot or window.
136- A render identifies the definition revision, the input and the renderer revision. Reuse across events is allowed only when the renderer depends solely on the extracted input.
137
138Changing a filter, transform option, output type or renderer revision invalidates the affected results. Refresh replaces a result atomically only after the new result passes validation. A completed run cannot overwrite a newer definition or source snapshot. Readers can see when a result was produced and whether it is stale or failed.
139
140The view's audience cannot grant access to source data a reader could not otherwise read. Selection, presentation and HTTP serving enforce that rule. Public summaries of restricted data need a separate, explicit policy; choosing an aggregate mode must not publish private inputs. The source fields exported to an external transform are declared independently from the result's audience.
141
142Each presentation has a server-rendered baseline. Lists use event components, tables use defined columns, structured results use the JSON viewer, and inline renders retain source access. Arbitrary HTML, scripts and executable templates are outside this proposal.
143
144## Migration
145
146The existing flat custom-view definition maps to `mode: "render"`: `kinds` becomes the source filter, `languages` moves into the fenced-block input, and the URL becomes an HTTP transform. Existing API calls and artifact URLs remain valid during migration. An old definition that includes repository state needs an explicit README adapter when exported to the new format.
147
148The built-in article view is a candidate for `list`; calendar, zap and moderation summaries are candidates for `aggregate`. Their existing audience rules must remain intact. Presence is live state and needs a live source adapter rather than being forced into a stored event query.
149
150Use one catalog and one definition validator for the three modes. Keep execution and result handling specific to each mode. Validate the shared design with an event list, an aggregate report and a block render before replacing the current APIs.

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs