client

package
v0.7.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 3, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Admin dashboard: server-rendered page at /admin for the relay owner to tune every config knob live via NIP-86.

The gate here is the cookie session, not NIP-98 — NIP-98 is what the dispatcher uses for the per-action grain_* writes the page issues from the browser. We render the shell only if the cookie-session pubkey matches the relay_metadata.json owner; non-owners get a 303 to "/" with no content leak.

Lives in the client package (not server/api) because rendering goes through RenderTemplate, which is defined here. Putting the handler in server/api would create an import cycle.

Owner-gated proxy that lets the admin dashboard preview the keys at a domain's .well-known/nostr.json without giving the browser direct cross-origin fetch access. Used by the whitelist section to show "this domain admits N keys: name1 hex1, name2 hex2…" after an operator adds a domain.

Curated table of well-known Nostr event kinds → human-readable labels. Sourced from the Event Kinds section of github.com/nostr-protocol/nips/blob/master/README.md.

Used by the admin dashboard's list-of-kinds inputs (event_purge's kinds_to_purge today; whitelist/blacklist kind lists later) so an operator sees "7 — Reaction (NIP-25)" instead of a bare integer.

This is intentionally not exhaustive — only labels we can stand behind. An operator can still add any non-negative integer; the UI just shows "(no description)" for kinds not in this table. Update by reading the upstream README, not by inventing labels.

client/registerEndpoints.go

First-run owner provisioning. /setup is the click-through claim page for operators who haven't set GRAIN_OWNER_PUBKEY in their env.

Security model (deliberately tiny): first POST to /setup while the relay is unowned wins. A signed NIP-98 envelope would add nothing — before ownership exists the relay has no notion of "which pubkeys may claim." The cost of losing the race is "redeploy a fresh relay" so the loud red banner + the post-claim "claimed by <npub>" page are the operator's safety net. See plans/floating-imagining-tome.md.

Index

Constants

This section is empty.

Variables

View Source
var KindLabels = map[int]string{
	0:     "User Metadata (NIP-01)",
	1:     "Short Text Note (NIP-01)",
	2:     "Recommend Relay (deprecated)",
	3:     "Follow List (NIP-02)",
	4:     "Encrypted Direct Message (NIP-04, deprecated)",
	5:     "Event Deletion Request (NIP-09)",
	6:     "Repost (NIP-18)",
	7:     "Reaction (NIP-25)",
	8:     "Badge Award (NIP-58)",
	9:     "Chat Message (NIP-C7)",
	13:    "Seal (NIP-59)",
	14:    "Direct Message (NIP-17)",
	15:    "File Message (NIP-17)",
	16:    "Generic Repost (NIP-18)",
	17:    "Reaction to a Website (NIP-25)",
	20:    "Picture-First Feed (NIP-68)",
	21:    "Video Event (NIP-71)",
	22:    "Short-form Portrait Video (NIP-71)",
	40:    "Channel Creation (NIP-28)",
	41:    "Channel Metadata (NIP-28)",
	42:    "Channel Message (NIP-28)",
	43:    "Channel Hide Message (NIP-28, deprecated)",
	44:    "Channel Mute User (NIP-28, deprecated)",
	1059:  "Gift Wrap (NIP-59)",
	1063:  "File Metadata (NIP-94)",
	1311:  "Live Chat Message (NIP-53)",
	1984:  "Reporting (NIP-56)",
	1985:  "Label (NIP-32)",
	9734:  "Zap Request (NIP-57)",
	9735:  "Zap Receipt (NIP-57)",
	10000: "Mute List (NIP-51)",
	10001: "Pin List (NIP-51)",
	10002: "Relay List Metadata (NIP-65)",
	10003: "Bookmark List (NIP-51)",
	10004: "Communities List (NIP-51)",
	10005: "Public Chats List (NIP-51)",
	10006: "Blocked Relays List (NIP-51)",
	10007: "Search Relays List (NIP-51)",
	10015: "Interests List (NIP-51)",
	10030: "User Emoji List (NIP-51)",
	30000: "Follow Sets (NIP-51)",
	30002: "Relay Sets (NIP-51)",
	30003: "Bookmark Sets (NIP-51)",
	30004: "Curation Sets (NIP-51)",
	30008: "Profile Badges (NIP-58)",
	30009: "Badge Definition (NIP-58)",
	30015: "Interest Sets (NIP-51)",
	30017: "Stall (NIP-15)",
	30018: "Product (NIP-15)",
	30023: "Long-form Content (NIP-23)",
	30024: "Draft Long-form Content (NIP-23)",
	30030: "Emoji Sets (NIP-51)",
	30078: "Application-specific Data (NIP-78)",
	30311: "Live Event (NIP-53)",
	30315: "User Statuses (NIP-38)",
	30402: "Classified Listing (NIP-99)",
	30403: "Draft Classified Listing (NIP-99)",
	31922: "Date-Based Calendar Event (NIP-52)",
	31923: "Time-Based Calendar Event (NIP-52)",
	31924: "Calendar (NIP-52)",
	31925: "Calendar Event RSVP (NIP-52)",
	31989: "Handler Recommendation (NIP-89)",
	31990: "Handler Information (NIP-89)",
	34550: "Community Definition (NIP-72)",
}

KindLabels maps kind → "Display Name (NIP-XX)" or "Display Name (deprecated)" where applicable. Keep ordered by kind for diffability; ranges (10000+) are NOT enumerated because most are addressable / replaceable buckets without specific well-known assignments.

Functions

func GetCoreClient

func GetCoreClient() interface{}

GetCoreClient returns the core client instance for advanced usage

func GetEmbeddedWWW added in v0.5.0

func GetEmbeddedWWW() fs.FS

GetEmbeddedWWW returns the embedded www filesystem.

func GetSessionStats

func GetSessionStats() map[string]interface{}

GetSessionStats returns current session statistics

func HandleAdmin added in v0.7.0

func HandleAdmin(w http.ResponseWriter, r *http.Request)

HandleAdmin renders the dashboard for the relay owner only.

Gate: session cookie -> SessionMgr.GetCurrentUser -> compare to GetRelayOwnerPubkey (case-insensitive). Non-owner / no session -> 303 redirect to "/".

func HandleAdminDomainKeys added in v0.7.0

func HandleAdminDomainKeys(w http.ResponseWriter, r *http.Request)

HandleAdminDomainKeys returns the parsed names map for a single domain's .well-known/nostr.json. Owner-gated via the cookie session — same gate /admin uses — so an unauthenticated visitor can't use grain as a SSRF proxy.

func HandleSetup added in v0.7.0

func HandleSetup(w http.ResponseWriter, r *http.Request)

HandleSetup renders the first-run claim page (GET) or processes a claim attempt (POST). See package doc for the threat-model rationale.

func InitializeClient

func InitializeClient(ctx context.Context, serverCfg *cfgType.ServerConfig) error

InitializeClient sets up the client package with server configuration. ctx bounds the background goroutines (session cleanup, cache cleanup, relay health check) to the lifetime of the server instance that started them, so a config-reload restart cancels them instead of leaking a fresh set (#93).

func KindLabel added in v0.7.0

func KindLabel(k int) string

KindLabel returns the human-readable label for a known kind, or "" if the kind isn't in the table. Callers that want a fallback string should `if l := KindLabel(k); l != "" { ... }`.

func RegisterEndpoints

func RegisterEndpoints(mux *http.ServeMux)

RegisterEndpoints registers all endpoints on the given mux

func RegisterPWARoutes

func RegisterPWARoutes(mux *http.ServeMux)

RegisterPWARoutes registers PWA-related routes

func RenderTemplate

func RenderTemplate(w http.ResponseWriter, data PageData, view string)

RenderTemplate renders a template with the standard layout using the embedded FS

func SetAssetVersion added in v0.7.0

func SetAssetVersion(v string)

SetAssetVersion wires the build version in so {{assetVersion}} in the templates resolves to it. Called from main once the version is known.

func SetEmbeddedWWW added in v0.5.0

func SetEmbeddedWWW(fsys fs.FS)

SetEmbeddedWWW sets the embedded filesystem containing www/ static assets. The FS should have "www" as the root prefix (e.g., files at "www/views/app.html").

func ShutdownClient

func ShutdownClient() error

ShutdownClient gracefully shuts down the client package

Types

type AdminPageData added in v0.7.0

type AdminPageData struct {
	Title      string
	Theme      string
	Owner      string
	Sections   []AdminSection
	KindLabels map[int]string
}

AdminPageData is what admin.html renders against.

type AdminSection added in v0.7.0

type AdminSection struct {
	ID     string
	Title  string
	Icon   string
	Method string // grain_* write method this section targets (empty for ops)
	Config any
}

AdminSection is one panel in the accordion. Config is the typed config blob (or nil for ops) — the template renders it inside the stub <pre> in Phase 1, and Phase 2+ commits read individual fields off the typed struct as they replace each stub with a real form.

type BlacklistSectionData added in v0.7.0

type BlacklistSectionData struct {
	Config                cfgType.BlacklistConfig
	UnifiedPubkeys        []UnifiedPubkey
	BrokenPubkeys         []string // bad entries from blacklist.yml's pubkeys/npubs
	MutelistAuthors       []UnifiedPubkey
	BrokenMutelistAuthors []string

	// Owner's private mute list sync status (#60). Rendered so the owner
	// knows when they last synced and how many pubkeys it contributed.
	// Zero values mean "never synced".
	OwnerMutelistCount  int
	OwnerMutelistSynced int64 // unix seconds; 0 = never

	// The owner's synced private-mute pubkeys, for in-panel display with
	// profiles. Owner-only page, so showing the actual keys is fine here
	// (the public endpoint stays count-only).
	OwnerMutelistPubkeys []UnifiedPubkey
}

BlacklistSectionData wraps BlacklistConfig with the same unified pubkey treatment the whitelist gets: hex + npub merge into one display list, mutelist authors render with profile previews. IP scalars ride through the bulk save now that UpdateBlacklistConfig writes them to config.yml. The IP LIST is edited live (per-row blockip/unblockip) rather than via the section Save, so it has no snapshot-replay path.

type ClientInitError

type ClientInitError struct {
	Message string
}

ClientInitError represents initialization errors

func (*ClientInitError) Error

func (e *ClientInitError) Error() string

type EventPurgeSectionData added in v0.7.0

type EventPurgeSectionData struct {
	Config      cfgType.EventPurgeConfig
	Categories  []string
	CommonKinds []QuickKind
	// KindLabels duplicated here from the page-level data because
	// Go templates lose access to the outer dot once a sub-template
	// is invoked. Cheap to pass — it's a single map reference.
	KindLabels map[int]string
}

EventPurgeSectionData is the per-section template data for the event_purge form. The form renders one checkbox per known purge category (the v0.4-compat names from server/db/nostrdb/purge.go:purgeCategoryForKind); rather than teach the template to construct a literal slice, we hand it the list directly. CommonKinds drives the quick-add chip row above the kinds_to_purge textarea.

type KindRatePreset added in v0.7.0

type KindRatePreset struct {
	Kind  int
	Limit float64
	Burst int
}

KindRatePreset is one suggested kind→{limit, burst} on the per-kind rate quick-add row.

type KindSizePreset added in v0.7.0

type KindSizePreset struct {
	Kind  int
	Bytes int
}

KindSizePreset is one suggested kind→max_size pairing on the per-kind size limits quick-add row. Sizes here are operator- friendly defaults — operators can edit before clicking Add.

type LoggingSectionData added in v0.7.0

type LoggingSectionData struct {
	Config        cfgType.LogConfig
	AllComponents []string
}

LoggingSectionData is the per-section template data for the logging form. We can't render the suppress-components UI from just LogConfig — the form needs the full set of known component names (so an operator gets checkboxes instead of typing names they have to guess), and that catalog lives in server/utils/log/components.go. Bundling the two here keeps the template clean and the catalog discoverable.

type OpsSectionData added in v0.7.0

type OpsSectionData struct {
	RelayName           string
	RelayDescription    string
	RelayIcon           string
	RelayBanner         string
	RelayContact        string
	RelayPrivacyPolicy  string
	RelayTermsOfService string
	RelayPostingPolicy  string
}

OpsSectionData is the per-section template data for ops. The dashboard renders the current relay identity (name/description/ icon/banner/contact + policy URLs) so an operator can edit them in place, plus a stats area fetched live via grain_stats_overview. Cache refresh and config reload run via separate buttons.

type PWAIcon

type PWAIcon struct {
	Src     string `json:"src"`
	Sizes   string `json:"sizes"`
	Type    string `json:"type"`
	Purpose string `json:"purpose,omitempty"`
}

PWAIcon represents an icon in the manifest

type PWAManifest

type PWAManifest struct {
	Name            string          `json:"name"`
	ShortName       string          `json:"short_name"`
	Description     string          `json:"description"`
	StartURL        string          `json:"start_url"`
	Display         string          `json:"display"`
	BackgroundColor string          `json:"background_color"`
	ThemeColor      string          `json:"theme_color"`
	Orientation     string          `json:"orientation"`
	Scope           string          `json:"scope"`
	Lang            string          `json:"lang"`
	Categories      []string        `json:"categories"`
	Screenshots     []PWAScreenshot `json:"screenshots,omitempty"`
	Icons           []PWAIcon       `json:"icons"`
}

PWAManifest represents the web app manifest structure

type PWAScreenshot

type PWAScreenshot struct {
	Src        string `json:"src"`
	Sizes      string `json:"sizes"`
	Type       string `json:"type"`
	FormFactor string `json:"form_factor,omitempty"`
	Label      string `json:"label,omitempty"`
}

PWAScreenshot represents a screenshot in the manifest

type PageData

type PageData struct {
	Title string
	Theme string
}

type QuickKind added in v0.7.0

type QuickKind struct {
	Kind  int
	Label string
}

QuickKind is one entry in the kinds_to_purge quick-add chip row.

type RateLimitSectionData added in v0.7.0

type RateLimitSectionData struct {
	Config              cfgType.RateLimitConfig
	RateLimitCategories []string
	CategoryDefaults    map[string]map[string]float64 // category → {limit, burst}
	KindSizePresets     []KindSizePreset
	KindRatePresets     []KindRatePreset
}

RateLimitSectionData is the per-section template data for the rate_limit form. Carries the typed config plus reference catalogs the form needs (category list for deterministic order, suggested per-kind size and rate presets for the quick-add chip rows).

type SetupPageData added in v0.7.0

type SetupPageData struct {
	Title     string
	OwnerHex  string
	OwnerNpub string
}

SetupPageData feeds setup.html (claim form) and setup-claimed.html (already-owned info panel). OwnerNpub is empty in the unowned state; populated with the bech32 form when rendering the claimed panel so the operator can eyeball whether the claimant is them.

type UnifiedPubkey added in v0.7.0

type UnifiedPubkey struct {
	Hex  string
	Npub string
}

UnifiedPubkey is one row of the merged hex+npub display. Both forms are precomputed server-side so the page doesn't have to fan out N convert-API calls on first render.

type WhitelistSectionData added in v0.7.0

type WhitelistSectionData struct {
	Config         cfgType.WhitelistConfig
	UnifiedPubkeys []UnifiedPubkey
	BrokenPubkeys  []string // raw entries from the YAML that didn't parse
	KindLabels     map[int]string
	KindPresets    []QuickKind
}

WhitelistSectionData wraps WhitelistConfig with a unified pubkey view + the kind catalog needed by the form. The dashboard renders one row per UnifiedPubkey showing both hex and npub regardless of which form the operator originally entered; the wire shape (Pubkeys vs Npubs) is collapsed on save into Pubkeys-only by the JS submit path.

Directories

Path Synopsis
client/core/eventSerializer.go
client/core/eventSerializer.go

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL