Documentation
¶
Overview ¶
Package tinygit coordinates signed Nostr repository events with native Git object storage and smart HTTP. Signed events establish repository refs, metadata and maintainer authority.
GitRelay stores bare repositories under Config.Root. Journal files under .tinyrelay/journal make event-to-ref transitions recoverable, while pending state files keep a signed ref announcement out of the visible repository until every advertised tip object is present. New creates the root, recovers journals and reloads repository metadata from the event store. Synchronization runs through Service.Tick or transport callbacks invoked by the caller.
ParseMetadata checks repository announcement and state shape without a store, signature check or host policy. Its Metadata result contains claims, not resolved repository authority. Use protocol/nostr.Validate for the separate wire, event ID and signature check. Neither check establishes maintainer authority, hosting permission or Git object availability, and the metadata parser does not claim full NIP or GRASP compliance.
Validate and ValidateImported check signatures and metadata shape before resolving repository authority and applying host admission. A valid shape may still be denied by Config.Authorize or lack a current announcement. After the caller durably stores an admitted event, CommitAfterStore stages its Git transition. CommitAfterStoreNoNotify serves callers already holding the host publication fence. PromotePending completes a receive-pack repair and invokes OnPromote after state becomes visible.
ServeHTTP exposes Git smart HTTP for public and private repositories. Private requests pass through AuthorizeHTTP. GRASP-08 private peer traffic is limited to Config.PrivatePeers and is signed by HTTPAuth. GitSync and EventSync supply synchronization transports; the daemon supplies the relay client and WebSocket lifecycle.
For a standalone public host, OpenServer owns event persistence and exposes POST /events plus Git smart HTTP. It requires an owner public key, admits only that owner's public repositories and their announced maintainers, and rejects private announcements. Publish signed kind 30617 metadata first, then signed kind 30618 refs before pushing their objects. Receive-pack promotes pending refs; unsigned ref updates are rejected by the same native hook used by the embedded engine. GET /healthz reports process readiness.
New is the lower-level integration boundary. Supply an absolute Config.Root and an OpenStore result, or provide a Store implementation for an embedded host. Policy, Event and Store are public contracts; the standard SQLite adapter and the host's richer policy remain behind those contracts. Neither constructor starts the daemon, relay protocol, UI or replication workers. Native Git and a POSIX shell are required for smart HTTP and receive hooks.
Index ¶
- Variables
- func RepairSources(r Repository) []string
- type BackupObject
- type BackupRepository
- type BrowseCommit
- type BrowseEntry
- type BrowsePage
- type BrowseRequest
- type CollaborationDiff
- type Config
- type Event
- type GitBackup
- type GitRelay
- func (g *GitRelay) AdmitSource(raw string) (string, error)
- func (g *GitRelay) Browse(ctx context.Context, q BrowseRequest) (BrowsePage, error)
- func (g *GitRelay) BrowseRepository(owner, identifier string) (Repository, error)
- func (g *GitRelay) Capabilities() []string
- func (g *GitRelay) CommitAfterStore(ctx context.Context, e event.Event, repo Repository) error
- func (g *GitRelay) CommitAfterStoreNoNotify(ctx context.Context, e event.Event, repo Repository) error
- func (g *GitRelay) ConfigurePrivatePeers(peers []string, signer HTTPAuthSigner)
- func (g *GitRelay) DeletePRRef(ctx context.Context, identity, ref string) error
- func (g *GitRelay) ExpirePending(ctx context.Context, eventIDs []string) error
- func (g *GitRelay) FetchMissing(ctx context.Context, r Repository, sources []string, ...) error
- func (g *GitRelay) GRASPService() *Service
- func (g *GitRelay) ImportGitBackup(ctx context.Context, input []byte) error
- func (g *GitRelay) IsMaintainer(ctx context.Context, r Repository, pubkey string) bool
- func (g *GitRelay) IsPending(eventID string) bool
- func (g *GitRelay) PRDiff(ctx context.Context, r Repository, base, tip string) CollaborationDiff
- func (g *GitRelay) PromotePending(ctx context.Context, repo Repository) error
- func (g *GitRelay) PruneRef(ctx context.Context, repository, ref string) error
- func (g *GitRelay) Publish(ctx context.Context, e event.Event) error
- func (g *GitRelay) Reload(ctx context.Context) error
- func (g *GitRelay) RepairPullRequestObjects(ctx context.Context, hosted Repository, set PullRequestRepair) error
- func (g *GitRelay) Repositories() []Repository
- func (g *GitRelay) ServeHTTP(w http.ResponseWriter, req *http.Request)
- func (g *GitRelay) StorageInventory(ctx context.Context, owner, identifier string) (map[string]any, error)
- func (g *GitRelay) SupportedGRASPs() []string
- func (g *GitRelay) Validate(ctx context.Context, e event.Event) (Repository, error)
- func (g *GitRelay) ValidateEvent(e event.Event) error
- func (g *GitRelay) ValidateImported(ctx context.Context, e event.Event) (Repository, error)
- func (g *GitRelay) WriteSource(ctx context.Context, q BrowseRequest, dst io.Writer) error
- type HTTPAuthSigner
- type Maintainer
- type MaintainerSource
- type Metadata
- type Policy
- type PolicyFeatures
- type PullRequestRepair
- type Repository
- type RepositorySyncStatus
- type Server
- type ServerConfig
- type Service
- type Store
- type SyncStatus
Constants ¶
This section is empty.
Variables ¶
var ErrDuplicate = gitstore.ErrDuplicate
ErrDuplicate indicates that an event was already persisted.
var ErrIncomplete = errors.New("synchronization incomplete")
ErrIncomplete reports a synchronization pass that saved progress and has more history to fetch. The scheduler continues it soon without counting a failure.
Functions ¶
func RepairSources ¶
func RepairSources(r Repository) []string
RepairSources gives a daemon a deterministic source order for a missing object. Clone URLs are preferred, followed by relay URLs; callers must still pass each URL through AdmitSource and verify the object graph.
Types ¶
type BackupObject ¶
type BackupObject struct {
OID string `json:"oid"`
Type string `json:"type"`
Size int `json:"size"`
Chunks []string `json:"chunks"`
}
BackupObject is one validated Git object and its compressed content chunks.
type BackupRepository ¶
type BackupRepository struct {
Key string `json:"key"`
Owner string `json:"owner"`
Identifier string `json:"identifier"`
Alternative bool `json:"alternative"`
Objects []BackupObject `json:"objects"`
Refs map[string]string `json:"refs"`
}
BackupRepository contains the objects and refs for one imported repository.
type BrowseCommit ¶
type BrowseEntry ¶
type BrowsePage ¶
type BrowsePage struct {
Owner string `json:"owner"`
Identifier string `json:"identifier"`
Head string `json:"head"`
Private bool `json:"private"`
Clone []string `json:"clone"`
Relays []string `json:"relays"`
// Maintainers is filled by the host, which knows about agent grants.
Maintainers []Maintainer `json:"maintainers,omitempty"`
Refs map[string]string `json:"refs"`
Commit string `json:"commit"`
Path string `json:"path"`
View string `json:"view"`
Entries []BrowseEntry `json:"entries"`
Content string `json:"content"`
Binary bool `json:"binary"`
Truncated bool `json:"truncated"`
Commits []BrowseCommit `json:"commits"`
Diff string `json:"diff"`
NextOffset int `json:"next_offset"`
}
type BrowseRequest ¶
type CollaborationDiff ¶
type CollaborationDiff struct {
Status string `json:"status"`
Base string `json:"base,omitempty"`
Tip string `json:"tip,omitempty"`
MergeBase string `json:"merge_base,omitempty"`
Diff string `json:"diff,omitempty"`
Truncated bool `json:"truncated,omitempty"`
Reason string `json:"reason,omitempty"`
}
CollaborationDiff is the result of rendering a pull request's local Git objects. Status is "ok" when Diff is authoritative and "diff_unavailable" when one or both commits are not present locally.
type Config ¶
type Config struct {
Store Store
Root string
Policy func() Policy
Authorize func(context.Context, Event, Repository) error
// Maintainers extends maintainer checks with host-side authority. It is
// consulted only after the owner and the maintainers tag.
Maintainers MaintainerSource
// AuthorizeHTTP is called for private repositories and for operators that
// want signed/NIP-98 authorization at the Git HTTP boundary.
AuthorizeHTTP func(context.Context, *http.Request, Repository) error
// ServiceURL is used to reject recursive GRASP sources and to construct
// advertised clone URLs. It is optional for an embedded relay.
ServiceURL string
// PublicURL is the canonical HTTPS URL used in repository advertisements;
// ServiceURL is used when PublicURL is empty.
PublicURL string
// EnableGRASP06 enables the alternative PR repository surface. It is kept
// opt-in because publishing a capability creates an interoperability
// promise beyond the base GRASP-01 HTTP endpoint.
EnableGRASP06 bool
// AllowMissingObjects is ignored.
// Missing objects enter the durable pending path and stay hidden until
// receive-pack or repair supplies every advertised tip.
AllowMissingObjects bool
// AllowPrivateRelays permits operator-controlled private DNS targets for
// self-hosted networks. Public deployments should leave it disabled.
AllowPrivateRelays bool
// PrivatePeers are operator-configured GRASP-08 origins. Only these
// sources may receive signed outbound Git requests.
PrivatePeers []string
// HTTPAuth signs requests to configured private peers. It is never used for
// sources that are merely present in a repository announcement.
HTTPAuth HTTPAuthSigner
// GitSync and EventSync are operator-owned transport callbacks. Keeping
// transport outside this package allows native websocket/NIP-77, HTTP, or
// an internal queue without making GRASP depend on one client library.
GitSync func(context.Context, Repository) error
EventSync func(context.Context, Repository) error
// OnPromote is called after a pending state becomes object-complete and
// visible. The host relay uses it to release event visibility/live fanout.
OnPromote func(context.Context, string, Repository) error
}
Config supplies the event store, Git root, authorization and transport hooks.
type Event ¶
Event is the signed Nostr wire event accepted by the Git engine. Clients sign events themselves; the server never needs their private keys.
type GitBackup ¶
type GitBackup struct {
Format string `json:"format"`
Repositories []BackupRepository `json:"repositories"`
}
GitBackup is the complete bind.ws/git-sqlite/1 import document.
func ValidateGitBackup ¶
ValidateGitBackup validates an import document without writing Git state.
type GitRelay ¶
type GitRelay struct {
// contains filtered or unexported fields
}
GitRelay coordinates signed repository events with native bare repositories.
func (*GitRelay) AdmitSource ¶
AdmitSource enforces the source restrictions from GRASP's Git sync model. Only HTTPS sources are accepted; localhost, private IPs, credentials, query/fragment data, and this relay itself are rejected.
func (*GitRelay) Browse ¶
func (g *GitRelay) Browse(ctx context.Context, q BrowseRequest) (BrowsePage, error)
Browse only reads objects reachable from materialized, signed public refs. It never checks out a working tree, invokes a shell, or follows a symlink.
func (*GitRelay) BrowseRepository ¶
func (g *GitRelay) BrowseRepository(owner, identifier string) (Repository, error)
func (*GitRelay) Capabilities ¶
Capabilities returns only protocol surfaces that this instance can serve.
func (*GitRelay) CommitAfterStore ¶
CommitAfterStore applies the Git-side transition after the caller has durably saved the event. It intentionally does not call Store.Save.
func (*GitRelay) CommitAfterStoreNoNotify ¶
func (g *GitRelay) CommitAfterStoreNoNotify(ctx context.Context, e event.Event, repo Repository) error
CommitAfterStoreNoNotify stages the Git transition without invoking the host fanout callback. Use this while already inside the relay publish fence; the durable work intent will release visibility afterward.
func (*GitRelay) ConfigurePrivatePeers ¶
func (g *GitRelay) ConfigurePrivatePeers(peers []string, signer HTTPAuthSigner)
ConfigurePrivatePeers refreshes operator-controlled private transport after a tenant policy update without reopening the Git store.
func (*GitRelay) DeletePRRef ¶
DeletePRRef removes a temporary GRASP-06 ref before its Nostr metadata is deleted. Identity accepts the source coordinates used by delivery jobs: pr:<owner>:<identifier> or 30617:<owner>:<identifier>.
func (*GitRelay) ExpirePending ¶
ExpirePending removes the file and in-memory authorities for state events whose retained deadline has elapsed. The SQL event sweep calls this before deleting the corresponding event so a stale journal cannot promote it after a restart.
func (*GitRelay) FetchMissing ¶
func (g *GitRelay) FetchMissing(ctx context.Context, r Repository, sources []string, expected map[string]string) error
FetchMissing downloads only the expected refs from an admitted source. Git verifies pack checksums and object connectivity; refs are installed only after every expected object exists locally.
func (*GitRelay) GRASPService ¶
func (*GitRelay) ImportGitBackup ¶
ImportGitBackup installs a validated backup into empty native repositories. It refuses pre-existing refs or repositories to avoid silently mixing namespaces; callers can stage an import into a fresh Git root and swap it.
func (*GitRelay) IsMaintainer ¶
IsMaintainer reports whether pubkey may sign state and push refs for a repository: the owner, a key in the announcement's maintainers tag, or a key the configured MaintainerSource vouches for. Every maintainer check in this package goes through here so the host's answer applies uniformly.
func (*GitRelay) IsPending ¶
IsPending lets the event admission/read layer hide a metadata event until its signed Git tips are verified locally.
func (*GitRelay) PRDiff ¶
func (g *GitRelay) PRDiff(ctx context.Context, r Repository, base, tip string) CollaborationDiff
PRDiff renders a bounded diff from commits already present in the repository. It never fetches or resolves a remote ref. A supplied base is used only after it is verified as a local commit; when it is empty, Git's merge-base with the repository target is used.
func (*GitRelay) PromotePending ¶
func (g *GitRelay) PromotePending(ctx context.Context, repo Repository) error
PromotePending verifies and exposes a previously accepted state event. It is useful for local receive-pack integrations; ServeHTTP invokes it after a smart push as well.
func (*GitRelay) PruneRef ¶
PruneRef removes an expired native Git ref before its retained metadata is deleted. repository may be the storage key or its identifier.
func (*GitRelay) Publish ¶
Publish validates and admits a signed repository announcement or state before saving it and staging the Git transition. Enabled GRASP-06 events use their separate admission path.
func (*GitRelay) Reload ¶
Reload rebuilds repository and pending indexes after a restore has inserted events and native Git repositories on disk.
func (*GitRelay) RepairPullRequestObjects ¶
func (g *GitRelay) RepairPullRequestObjects(ctx context.Context, hosted Repository, set PullRequestRepair) error
RepairPullRequestObjects fetches the commits named by a NIP-34 PR and its updates into the supplied hosted repository.
Updates are accepted only from the PR author and must point to the root PR. This prevents an unrelated event from supplying a ref to a hosted PR.
func (*GitRelay) Repositories ¶
func (g *GitRelay) Repositories() []Repository
Repositories returns detached snapshots. Callers must enforce tenant and repository read permissions before revealing these records.
func (*GitRelay) ServeHTTP ¶
func (g *GitRelay) ServeHTTP(w http.ResponseWriter, req *http.Request)
ServeHTTP exposes Git smart HTTP at /npub/<identifier>.git and /prs/... . The subprocess boundary keeps pack parsing and object integrity in Git.
func (*GitRelay) StorageInventory ¶
func (g *GitRelay) StorageInventory(ctx context.Context, owner, identifier string) (map[string]any, error)
StorageInventory reports retained bytes for one accepted repository. It is deliberately derived from the local object store so the self-hosted result has no hosted fuel or quota fields.
func (*GitRelay) SupportedGRASPs ¶
SupportedGRASPs is the NIP-11-facing capability list. It deliberately mirrors Capabilities so callers cannot advertise preview protocols by hand.
func (*GitRelay) Validate ¶
Validate checks the signature and metadata shape, resolves repository authority, then applies host admission. Shape failures precede host callbacks. The caller must associate the returned authority with its event transaction. Validate has no store side effects and does not require Git objects locally.
func (*GitRelay) ValidateEvent ¶
ValidateEvent checks the wire shape, canonical ID and signature, not repository metadata shape or host admission. Use ParseMetadata for the pure metadata check, or Validate for the complete admission boundary.
func (*GitRelay) ValidateImported ¶
ValidateImported admits metadata received through an explicitly synchronized relay. Local announcements continue through Validate.
func (*GitRelay) WriteSource ¶
WriteSource streams a published source blob directly to the destination.
type HTTPAuthSigner ¶
HTTPAuthSigner returns the authorization header for a private peer request. GRASP-08 callers receive the repository root, GET and an empty payload hash; it is deliberately a callback so GitRelay never owns private keys.
type Maintainer ¶
type Maintainer struct {
PubKey string `json:"pubkey"`
Role string `json:"role"`
Name string `json:"name,omitempty"`
}
Maintainer is one key that may sign repository state, push refs and change status. Role is "owner", "maintainer" for a key in the announcement's maintainers tag, or "agent" for a key the host vouches for under an agent grant; Name carries the grant's label when it has one.
type MaintainerSource ¶
type MaintainerSource interface {
IsMaintainer(ctx context.Context, r Repository, pubkey string) bool
}
MaintainerSource answers whether a key maintains a repository beyond the owner and the announcement's maintainers tag. The host relay supplies it so authority it records elsewhere, such as an agent grant, counts in every place this package checks maintainers.
type Metadata ¶
type Metadata struct {
Kind int
Author string
Identifier string
EventID string
Private bool
Clone []string
Relays []string
Maintainers []string
Refs map[string]string
Head string
}
Metadata contains the claims parsed from a repository announcement (kind 30617) or state (kind 30618). Author is the event's claimed signer, not a resolved repository owner. Announcement fields are not inherited by states. A Metadata value conveys neither signature validity nor hosting authority.
func ParseMetadata ¶
ParseMetadata checks and parses tinygit's supported repository metadata shape: identifier, state ref names and nonzero SHA-1 tips, and symbolic HEAD syntax. HEAD may name a branch or tag not present in Refs, including an unborn branch. Unknown tags and supported extension fields are ignored.
This is a pure shape/semantic check, not full NIP or GRASP compliance. It does not validate the event ID or signature, consult announcements or maintainers, authorize hosting, check clone or relay endpoints, or inspect Git objects. Use nostr.Validate separately before trusting Author. GitRelay.Validate and ValidateImported combine signature validation, this parser and host admission; missing Git objects remain a separate pending-state concern.
type Policy ¶
type Policy struct {
Owner string
Reads string
PrivatePeers []string
Features PolicyFeatures
}
Policy contains the policy values used by the Git engine. Hosts may keep a richer policy internally and adapt these values at the integration boundary.
func DefaultPolicy ¶
DefaultPolicy returns the defaults for an embedded engine.
func (Policy) PrivateServiceEnabled ¶
type PolicyFeatures ¶
type PolicyFeatures struct {
Grasp bool
Grasp02 bool
Grasp03 bool
Grasp05 bool
Grasp06 bool
Grasp08 bool
}
PolicyFeatures selects optional Git protocol capabilities.
type PullRequestRepair ¶
PullRequestRepair groups a PR with its later tip updates. The event store remains the authority for these events; this value only describes the Git objects that GRASP-02 should fetch for them.
type Repository ¶
type Repository struct {
Owner string
Identifier string
EventID string
Private bool
Clone []string
Relays []string
Maintainers []string
Refs map[string]string
Head string
Alternative bool
}
Repository is the resolved authority and Git state for one hosted project.
type RepositorySyncStatus ¶
type RepositorySyncStatus struct {
AttemptedAt int64 `json:"attempted_at"`
SucceededAt int64 `json:"succeeded_at,omitempty"`
NextAt int64 `json:"next_at,omitempty"`
Fingerprint string `json:"fingerprint,omitempty"`
Attempts int `json:"attempts"`
Error string `json:"error,omitempty"`
}
RepositorySyncStatus is the durable status of one GRASP reconciliation job.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server owns a Git engine and durable SQLite metadata, without a relay daemon, WebSocket service, UI or replication workers. POST /events accepts signed repository metadata. Other paths are handled by Git smart HTTP.
func OpenServer ¶
func OpenServer(ctx context.Context, cfg ServerConfig) (*Server, error)
OpenServer opens the standalone host. Stop all HTTP requests before Close.
func (*Server) Close ¶
Close releases metadata storage. The caller must first stop serving requests.
type ServerConfig ¶
ServerConfig configures the standalone public Git host. Use a dedicated DataDir, not a running tinyrelay tenant's database or Git directory.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service is the durable GRASP scheduler boundary. Tick is safe to call from a daemon ticker and runs one pass over the currently accepted repositories. Callback progress is persisted so a restart does not lose the last pass.
func (*Service) SyncStatus ¶
func (s *Service) SyncStatus(ctx context.Context) (SyncStatus, error)
type Store ¶
type Store interface {
// Query returns matching events by descending timestamp, then ascending ID,
// excluding expired, hidden and pending events without applying reader ACLs.
// now is the Unix expiry cutoff. A positive limit caps the result; f.Limit
// can impose a smaller cap. With neither limit, all matches are returned.
Query(context.Context, nostr.Filter, int64, int) ([]Event, error)
// Save durably records e using now for storage timestamps. Duplicate events
// return ErrDuplicate.
Save(context.Context, Event, int64) error
// Latest returns the newest addressable event for kind, pubkey and d.
// Ties use the lowest event ID. This includes expired, hidden and pending
// rows so recovery can check durable state independently of read visibility.
Latest(context.Context, int, string, string) (Event, bool, error)
// Exists reports whether id is present with the requested kind.
Exists(context.Context, string, int) (bool, error)
// Close releases resources owned by the store.
Close() error
}
Store is the persistence contract required by GitRelay. Implementations may use any durable or in-memory representation of Nostr events. The caller owns the store; New never closes it.
type SyncStatus ¶
type SyncStatus struct {
At int64 `json:"at"`
LastKey string `json:"last_key"`
Repositories map[string]RepositorySyncStatus `json:"repositories,omitempty"`
}
SyncStatus is a read-only snapshot suitable for relay status pages.