| 1 | # Module boundaries and protocol contracts |
| 2 | |
| 3 | The project is a monorepo. `tinyrelay` is the relay host, `tinygit` is the Git engine and standalone Git service, and `tinyclient` is the browser interface and standalone frontend. Separate executables do not imply separate Go modules, databases or network services for every package. |
| 4 | |
| 5 | The Git and frontend components, public `protocol/nostr` and `protocol/auth` packages, and `tinygit.ParseMetadata` establish the current boundaries. Blob, room and narrower community/records services below remain proposed follow-up work. |
| 6 | |
| 7 | ## Keep protocol rules separate from access policy |
| 8 | |
| 9 | Apply checks in this order: |
| 10 | |
| 11 | 1. Parse the wire representation and verify its signature or request proof. |
| 12 | 2. Validate the feature's protocol structure and semantics. |
| 13 | 3. Apply host policy: membership, grants, visibility, quotas and enabled features. |
| 14 | 4. Commit the event, feature projection and required work together. |
| 15 | 5. Expose the result through the relevant transport and read model. |
| 16 | |
| 17 | A valid Nostr event can still be refused by host policy. A policy refusal is not evidence of an invalid signature or a protocol violation. Conversely, an owner or maintainer role must never make a malformed signed event valid. |
| 18 | |
| 19 | The client can validate a draft for feedback and verify a signer's result, but server-side validation remains authoritative. HTTP, WebSocket and MCP adapters must use the same feature operation rather than implement separate admission rules. |
| 20 | |
| 21 | ## Recommended seams |
| 22 | |
| 23 | | Boundary | Owns | Must not own | |
| 24 | | --- | --- | --- | |
| 25 | | Public `protocol/nostr` | Event and filter types, canonical serialization, event IDs, signatures and common encoding rules. | Tenant configuration, membership, SQL or feature-specific tags. | |
| 26 | | Public `protocol/auth` | Verification for HTTP, WebSocket and service-specific proof profiles; authenticated actor and proof scope. | Deciding what that actor may do. | |
| 27 | | Host access policy | Membership, agent grants, visibility, quotas and admission decisions. | Reinterpreting signed fields or weakening feature protocol validation. | |
| 28 | | `tinygit` | Git repository metadata, signed refs, object storage, smart HTTP, repair and Git-specific protocol rules. | Rooms, browser sessions or general relay management. | |
| 29 | | `tinyclient` | Server-rendered pages, browser interactions, draft construction and signer-result verification. | Authoritative permissions, repository mutation or event persistence. | |
| 30 | | Blob service, a candidate for a later `tinyblob` extraction | Content-addressed storage, upload and download contracts, integrity and applicable Blossom behavior. | Site manifests, room membership or a frontend. | |
| 31 | | Site service | Signed site manifests, routing and site lifecycle over a blob-store contract. | Owning another copy of blob storage or upload authorization. | |
| 32 | | Room service | Room state and message projections, with the room protocols it implements. | General agent grants or all community administration. | |
| 33 | | Host assembly | Tenant lifecycle, transaction coordination, worker ownership and adapter wiring. | A second implementation of feature rules. | |
| 34 | |
| 35 | Keep domain-specific protocol rules with their feature. Do not create a universal `nips` package containing every numbered NIP: that would recreate the coupling under a new name. A NIP that crosses authentication, transport and persistence boundaries should have one documented ownership map and shared conformance fixtures, not one oversized implementation package. |
| 36 | |
| 37 | `records` and `community` are candidates for narrower internal services before another standalone executable. Their existing contracts span several responsibilities; splitting by durable state and operation ownership is more useful than renaming the packages. |
| 38 | |
| 39 | ## Define the contract before moving dependencies |
| 40 | |
| 41 | Each public feature boundary should document: |
| 42 | |
| 43 | - Inputs, outputs and stable error categories. |
| 44 | - The authenticated actor and authorization context it accepts. |
| 45 | - Which protocol checks it performs and which policy decisions the host supplies. |
| 46 | - Whether it owns a transaction or joins one supplied by the host. |
| 47 | - Idempotency, ordering and retry behavior. |
| 48 | - Cancellation, resource ownership and shutdown. |
| 49 | - Side effects and the point at which they become visible. |
| 50 | - Capability differences between standalone and integrated operation. |
| 51 | |
| 52 | Use small, consumer-owned interfaces for access decisions, persistence and optional transports. Prefer feature-specific read models to a universal backend interface. Moving packages must not break the atomicity of event persistence, projections and durable work. |
| 53 | |
| 54 | `tinygit.Event` and the frontend room event type now originate in public `protocol/nostr` . Public `protocol/auth` depends on those primitives, not host services. Internal aliases preserve one implementation, type identity, verifier replay state and error sentinels. Feature kind constants, room-reply helpers and private-kind search exclusions remain internal rather than becoming generic protocol rules. |
| 55 | |
| 56 | `tinygit` owns its small policy and persistence contracts. Its default SQLite implementation lives in `internal/gitstore` , and tenant assembly adapts the existing host store and policy. The host retains transaction ownership; the engine does not close a supplied store. External-consumer tests exercise the public store interface, while dependency tests guard the protocol packages. `tinygit.ParseMetadata` validates metadata shape without asserting signature validity or authority; its admission entry points combine these checks before consulting host policy. |
| 57 | |
| 58 | ## Describe standards and project extensions separately |
| 59 | |
| 60 | Protocol documentation should classify each behavior as one of: |
| 61 | |
| 62 | - **Upstream protocol:** Behavior specified by a named NIP, GRASP specification, Blossom document or other upstream specification. Cite the document and the revision used by conformance tests. |
| 63 | - **Supported profile:** The subset or combination implemented by a particular executable, with omissions and required options stated explicitly. Do not imply full compliance when required behavior is absent. |
| 64 | - **Project extension:** Additional wire behavior defined by this project. Describe how an ordinary client behaves without it and how peers discover support. |
| 65 | - **Local implementation detail:** Storage paths, queues, indexes or internal APIs that are not an interoperability contract. |
| 66 | |
| 67 | For each project extension, create a specification under `docs/extensions/` when its behavior has been audited. Each specification should include a stable identifier, status, version, owning module, upstream references, exact event kinds and tag shapes or endpoint schemas, validation rules, authorization requirements, errors, discovery, security considerations and compatibility rules. Include valid and invalid fixtures and link the implementation and tests. |
| 68 | |
| 69 | The [extension catalog](extensions/README.md) indexes specifications for agent grants and grant requests, callbacks, custom-view transforms and the tinyclient HTTP read adapter. These describe implemented behavior and its audited limits; they are not full upstream conformance certificates. Continue auditing remaining custom bindings without repeating each specification in the catalog. Existing behavior is not automatically standardized, and an implementation detail should not be promoted into a wire extension merely because it is unusual. |
| 70 | |
| 71 | Keep transport-specific method names in their adapters. A feature operation may have HTTP, MCP and browser bindings, but its domain contract and validation must have one owner. Generate discovery and schema checks from the same definitions where practical; do not create a parallel hand-maintained registry with different defaults. |
| 72 | |
| 73 | ## Compatibility and terminology |
| 74 | |
| 75 | Use `tinyrelay` for the host, `tinygit` for Git functionality and `tinyclient` for frontend functionality in package documentation, command help and architecture descriptions. Reserve “module” for a functional boundary unless explicitly discussing a Go module. |
| 76 | |
| 77 | Do not apply blanket replacements to protocol or persisted identifiers. Existing event kinds, tags, endpoints, database schemas, Git hooks, `.tinyrelay` journal paths and `tiny.*` browser APIs remain compatibility surfaces until an explicit migration is designed and tested. A historical storage name can remain while surrounding documentation identifies its current owner accurately. |
| 78 | |
| 79 | ## Verification |
| 80 | |
| 81 | Keep the existing feature tests when moving implementation files. Add external-consumer build checks, standalone service tests and dependency checks that reject importing the host assembly from a standalone component. Use shared protocol fixtures for server validation and browser construction where their responsibilities overlap. |
| 82 | |
| 83 | For each extraction, compare the integrated service with the baseline on the same fixtures. Cover valid and invalid events, authorization failures, persistence and restart, HTTP behavior and rendered content. Test the standalone profile separately and state unsupported features; a passing standalone subset is not full integrated parity. |