Files
amethyst/nestsClient/specs/EGG-10.md
T
Claude bd12d5ef72 docs(nestsClient/specs): close interop gaps surfaced in spec review
Edits across all 13 EGGs to remove implementer guesswork. After this an
implementer can build a listener and speaker without reading our source.

README:
- Conventions section: hex casing rule (lowercase, 64 chars, no 0x),
  NIP-01 foundations, `a`-tag form, created_at tie-break, JSON / time.
- Joining sequence: numbered end-to-end walkthrough from `kind:30312`
  to first audio frame, referencing each EGG.

EGG-01 (room event):
- `relays` tag is one tag with multiple values (not multi-tag).
- `service` URL trailing-slash normalization.
- `private` status: gate is implementation-defined, not "render as open".
- `d`-tag charset locked to [A-Za-z0-9._-] so it interpolates safely
  into the moq-auth namespace.
- Relay-discovery rule: publish to `relays` tag ∪ NIP-65 outbox.
- Tie-break on identical created_at via smallest event id.

EGG-02 (auth):
- JWT signing pinned: ES256 over P-256, JWKS at /.well-known/jwks.json,
  5-minute relay cache.
- NIP-98 tags pinned: u / method / payload, base64 RFC 4648 standard
  (not base64url).
- Error taxonomy: full HTTP status + `error` slug table for /auth, plus
  WebTransport CONNECT 200/401/403/404 table.

EGG-03 (audio):
- Pin moq-lite Lite-03 to kixelated/moq-rs `rs/moq-lite/src/lite/`.
- One Opus packet per moq Frame (no container, no timestamp).
- Pubkey hex casing reaffirmed at the suffix / broadcast slots.
- AnnouncePlease prefix="" for speaker discovery.
- Mute = stop publishing (not silence frames, not Announce Ended).
- Mid-stream join: discard pre-skip per RFC 7845.

EGG-04 (presence):
- Heartbeat jitter ±5 s required (anti-thundering-herd).
- "0"/"1" are strings, not booleans.
- Single-room rule via replaceable-event semantics.

EGG-05 (chat): 8 KB suggested, 64 KB hard cap, 3 msg/s render rate.
EGG-06 (reactions): 30s window measured against created_at, drop-on-arrival
                    if already stale.
EGG-07 (moderation): replay protection — dedupe kicks by id within 120 s.
EGG-08 (scheduling): planned rooms MUST 403 at the auth sidecar.
EGG-10 (theming): bg image caps (1 MB / 4096 px soft, 8 MB / 8192 px hard).

Deferred (per review): EGG-00 (Conventions as a standalone spec), EGG-13
(capability advertisement), EGG-14 (discovery), test vectors corpus.
The "Conventions" section in README covers EGG-00's most urgent content
inline.

https://claude.ai/code/session_01RDpuki4t8StSg1CZcXnV5b
2026-04-27 12:31:19 +00:00

4.2 KiB

EGG-10: Theming

status: draft requires: EGG-01 category: decorative

Summary

A host MAY skin their room with custom colors, font, and a background image. Theme tags are added to the existing kind:30312 event; clients without theme support ignore them and render with their default theme.

This EGG is decorative: theming MUST NOT affect protocol semantics (joining, audio, presence, chat). A non-themed renderer MUST NOT show fewer features than a themed one.

Wire format

Three optional tags on kind:30312:

["c", "<hex color>", "background" | "text" | "primary"]
["f", "<font family>", "<optional font URL>"]
["bg", "<background image URL>", "cover" | "tile"]

Color (c)

  • The hex value is six hex digits (#RRGGBB); a leading # is OPTIONAL. Three-digit shorthand (#abc) is NOT supported.
  • The third element selects the role: background, text, or primary.
  • A room MAY include up to one c tag per role. Extras MUST be ignored by receivers (palette fallbacks are reserved for a future EGG).

Font (f)

  • The family name is a free-form string. Receivers SHOULD match the four CSS-style generic names case-insensitively: sans-serif, serif, monospace, cursive. Other names fall through to the platform default unless the renderer also supports the URL.
  • The URL element is OPTIONAL. When present, it MUST be a fully-qualified HTTPS URL pointing at a single-style font file (TTF/OTF/WOFF2). HTTP URLs MUST be rejected (mixed-content + integrity concerns).

Background (bg)

  • The URL is a fully-qualified HTTPS URL pointing at an image (PNG/JPEG/ WEBP).
  • The mode is cover (scale to fill, crop overflow) or tile (repeat on both axes). Unknown modes MUST fall back to cover.
  • Hosts SHOULD keep the asset under 1 MB and 4096 px on its longest edge. Receivers MUST cap any asset they fetch at 8 MB and 8192 px on its longest edge; over-cap assets MUST be discarded with a fall-through to the background color so a malicious host cannot OOM clients.

Behavior

  1. Theme tags are PARSED ONLY by clients with rendering support. Clients without that support MUST ignore them silently.
  2. Color values MUST be parsed strictly: ^#?[0-9a-fA-F]{6}$. Anything else MUST result in the role falling back to the platform default, not in a render error.
  3. The text color is the foreground for room name, summary, and chat bodies. The background color paints behind everything except the bg image (image overlays the color). The primary color drives accent surfaces (active-speaker ring, send button).
  4. Font URL fetching MUST be opt-in to the renderer's image / asset pipeline (Tor or proxy if so configured). Renderers MUST cache the fetched font on disk keyed by URL hash; the same URL MUST NOT be re-fetched in the same session.
  5. Background image fetching MUST go through the same channel as inline images (typically the renderer's image cache).
  6. Themed renderers MUST tolerate a TIME-OF-FETCH gap: the room screen opens with platform defaults, font / background swap in once loaded. There MUST be no flash-of-blank-screen.
  7. A room theme MUST NOT alter element positions, sizes, or behavior. This EGG controls colors, font family, and a background image — no layout primitives.

Example

{
  "kind": 30312,
  "pubkey": "abc...host",
  "tags": [
    ["d", "purple-room"],
    ["room", "Purple"],
    ["status", "open"],
    ["service", "..."],
    ["endpoint", "..."],
    ["p", "abc...host", "wss://relay", "host"],
    ["c", "#1a0033", "background"],
    ["c", "#FFFFFF", "text"],
    ["c", "#a020f0", "primary"],
    ["f", "Inter", "https://fonts.example/inter.woff2"],
    ["bg", "https://example.com/stars.png", "tile"]
  ],
  "content": "",
  "...": "..."
}

Compatibility

A non-themed receiver renders the room without any of the cosmetic overrides. A themed receiver that fails to fetch one of the assets (font / background) MUST render the rest of the theme and the room is still fully functional.

Future EGGs MAY introduce per-user profile themes (e.g. kind:16767) and shared theme references (e.g. kind:36767 Ditto themes). Those specs MUST be additive: a theme defined on the room itself overrides any per-user or shared theme.