Architecture
tiny assembles the combined relay host. The public tinyrelay package provides the standalone and embeddable Nostr relay, tinygit owns Git functionality, and tinyclient owns the frontend. These functional modules share one repository and Go module. See Module boundaries and protocol contracts for ownership rules, extension documentation and proposed follow-up seams.
Each tenant has a SQLite store, a policy snapshot and a set of services. The daemon constructs those services, connects their contracts and manages their lifetimes. HTTP, WebSocket and management adapters establish caller identity and enter the tenant's operation gate before calling services.
Find your starting point
cmd/ owns combined command dispatch, while cmd/ starts the standalone relay through the shared runner. daemon describes tenant assembly and event flow. Package overviews and interface, constructor and transaction comments describe the contracts at their declarations. These comments are also available through go doc and editor symbol help.
Package layout
Executable entry points live in cmd/, cmd/, cmd/ and cmd/. Host services and storage implementations live under internal/, where Go prevents outside projects from importing them.
tinyrelay, tinygit, tinyclient and protocol/ are public so other Go projects can embed them. Separate executables alone do not require public packages. These packages expose the contracts an embedder needs; host policy, database access and tenant assembly remain internal. All packages belong to the root Go module.
Responsibilities
| Packages | Responsibility |
|---|---|
protocol/ |
Public event and filter values, canonical encoding, signatures and generic wire checks. No host access policy. |
protocol/ |
Public request-proof verification and replay state. No membership or permission decisions. |
tinyrelay |
Standalone Nostr transport, durable event storage and generic relay metadata and policy. No tenant, Git or browser features. |
event, auth |
Internal compatibility aliases and feature helpers; private-kind search filtering remains host behavior. |
policy, gates |
Host policy, event admission and visibility checks. |
storage, work |
Event persistence, queries, transaction hooks and durable queue claims. |
community, communityread |
Membership, moderation, rooms, agents and the read models consumed by records. |
records |
Relay identity, signed protocol records, projections and notifications. |
blob, sites |
Content-addressed files, upload rules and sites backed by signed manifests. |
tinygit |
Repository admission, object storage, synchronization, repair and Git HTTP endpoints. |
replication, syncprotocol |
Relay synchronization plans and transports, count sketches and reconciliation sessions. |
relay, relaycmd, mcp, tinyclient |
WebSocket sessions, standalone command lifecycle, MCP requests, HTML pages and browser interactions. |
agentrunner |
The optional agent process: room mentions, durable queue recovery, ACP or command execution, and permission prompts. |
views, wiki, webpush |
Fenced-block parsing, article rendering and encrypted browser push delivery. |
seedmark |
Deterministic decorative SVG avatars for people and rooms without a profile picture. |
catalog, domains |
Tenant lifecycle records, filesystem paths and host mappings. |
templates, configport |
Built-in policy templates and configuration import, export and application. |
telemetry, daemon |
Observability, service assembly, request routing and worker ownership. |
Ownership and contracts
App owns the catalog, telemetry and active tenants. Each Tenant owns its store and service lifetimes. Services borrow the store and policy reader supplied at construction. Policy reads return a shared snapshot; management writes persist and replace that snapshot. Shutdown stops admissions, joins background work and drains active operations before closing storage.
Services own the schema and data access for their features. storage.Save and storage.SaveTx accept transaction hooks so event data, projections and work intents can commit together. APIs that receive *sql.Tx use the caller's transaction. Community event handlers that accept a persistence callback open the transaction and pass it into the callback.
Consumers declare interfaces around the operations they need. records.CommunityReader reads membership and moderation projections through communityread values. relay.Backend supplies event operations to the WebSocket router. tinyrelay.Backend is the embedding contract for a host that owns persistence, while tinyrelay.OpenServer supplies a durable standalone implementation. tinyclient.Backend supplies management results, and tinyclient.RoomsReader supplies typed room pages. The daemon implements these adapters and assembles the feature handlers used by work.Worker.
Event acceptance
protocol/ checks wire shape, event ID and signature. tinygit.ParseMetadata separately parses repository claims without establishing signer validity, repository authority or object availability. Git admission entry points perform signature and metadata checks before host admission callbacks. Generic nostr.Matches is not a visibility gate; callers must apply host access policy.
Client publication and imported events use the shared event persistence and projection path. Each entry point applies its admission rules and protocol actions. Signed repository state stays pending until its referenced Git objects are available; promotion makes it visible and plans its optional follow-up work.
Callbacks and custom views are optional. A planning intent commits with an accepted event and captures its eligible registrations or failed local lookups. Recovery rechecks the source and current registrations, with at most three worker attempts and a 15-minute age limit. Stable child intent IDs preserve completed work. Remote delivery and transforms have their own retry policies. See callbacks and custom views.
Browser boundaries
Pages render without JavaScript. page.html owns the shell and panel dispatch; each page template owns one content section. Markup uses elements and IDs for styling in style.css. Shared form and data elements live in components.js, room interactions live in rooms.js, and signed publication uses tiny.signing. The signing contract verifies returned events before publication. Browser modules describe their entry points and shared services in their source headers.
Dependencies and checks
Go module versions are declared in go.mod, with dependency checksums in go.sum. Browser dependencies are locked in package-lock.json. make verify checks the Go and JavaScript code and verifies generated browser bundles against their sources. See Testing for local and Linux checks.