Artifact 5b83062f4d3e34b925de8fc33f83a51cf3be45e47961c2df6fcf7a7433191408:
- File
hub/commit.go
— part of check-in
[67ea682587]
at
2026-05-04 18:34:31
on branch trunk
— feat(hub): new package for hub-fossil management and HTTP serving (#109)
Adds a top-level hub/ package that hosts an EdgeSync hub in-process: fossil hub repo bootstrap, embedded NATS server with JetStream, auto-port allocation, libfossil HTTP server (timeline UI + xfer endpoint), user management, and hub-side commits/reads. All libfossil and nats-server types are wrapped internally — the public surface exposes only stdlib types and types defined here.
Public API: - Config + NewHub + ServeHTTP + Stop - NATSURL / LeafUpstream / HTTPAddr accessors - AddUser / GetUser / HasUser / ListUsers / RemoveUser (User type) - Commit / Read / ReadAt (RevID, FileToCommit, CommitOpts types)
Auto port allocation: passing 0 for FossilHTTPPort, NATSClientPort, or NATSLeafPort yields three distinct bound ports. The HTTP listener is bound during NewHub so HTTPAddr() is immediately usable, even before ServeHTTP is called.
SQLite tuning (SetMaxOpenConns(1) + busy_timeout=30s) applied at hub bootstrap, matching the leaf agent's tuning.
Lives inside the EdgeSync root module (not a separate go.mod) so it ships in lockstep with the leaf agent. Can be promoted to its own module later if downstream consumers need to depend on it independently.
Closes #102 (user: [email protected], size: 1572) [annotate] [blame] [check-ins using]
package hub import ( "context" "errors" "fmt" libfossil "github.com/danmestas/libfossil" ) // RevID is an opaque commit handle. type RevID string // FileToCommit names a file and its content for inclusion in a commit. type FileToCommit struct { Name string Content []byte } // CommitOpts configures Hub.Commit. type CommitOpts struct { Files []FileToCommit Message string Author string // fossil user; required } // Commit commits a set of files to the hub repo. func (h *Hub) Commit(ctx context.Context, opts CommitOpts) (RevID, error) { if opts.Author == "" { return "", errors.New("hub: Commit: Author is required") } files := make([]libfossil.FileToCommit, len(opts.Files)) for i, f := range opts.Files { files[i] = libfossil.FileToCommit{Name: f.Name, Content: f.Content} } _, uuid, err := h.repo.Commit(libfossil.CommitOpts{ Files: files, Comment: opts.Message, User: opts.Author, }) if err != nil { return "", fmt.Errorf("hub: commit: %w", err) } return RevID(uuid), nil } // Read returns the contents of path at the current trunk tip. func (h *Hub) Read(ctx context.Context, path string) ([]byte, error) { return h.repo.ReadFileAt("trunk", path) } // ReadAt returns the contents of path at rev. rev may be a branch name // (e.g. "trunk"), a tag, or a RevID returned from Commit. func (h *Hub) ReadAt(ctx context.Context, rev RevID, path string) ([]byte, error) { if rev == "" { return nil, errors.New("hub: ReadAt: rev is required (use Read for current tip)") } return h.repo.ReadFileAt(string(rev), path) }