Gogs — Interfaces#

Interface catalog#

context.Store#

  • Package: gogs.io/gogs/internal/context
  • File: internal/context/store.go
  • Methods:
    • GetAccessTokenBySHA1(ctx context.Context, sha1 string) (*database.AccessToken, error)
    • TouchAccessTokenByID(ctx context.Context, id int64) error
    • GetUserByID(ctx context.Context, id int64) (*database.User, error)
    • GetUserByUsername(ctx context.Context, username string) (*database.User, error)
    • CreateUser(ctx context.Context, username, email string, opts database.CreateUserOptions) (*database.User, error)
    • AuthenticateUser(ctx context.Context, login, password string, loginSourceID int64) (*database.User, error)
  • Purpose: The DI boundary for the Macaron request-context middleware (Contexter). Abstracts the subset of database operations needed to authenticate an incoming request (token lookup, session user fetch, basic-auth validation, reverse-proxy auto-registration). Passed into Contexter(store) at startup.
  • Implementations: context.store (unexported struct, wraps database.Handle). In tests, mocked manually.
  • Design quality: Well-segregated — deliberately limits the handler’s view of the database to the six operations the auth middleware actually needs. The interface comment says “thin-wrapper” and “limit the exposure of the underlying data layer,” confirming intentional ISP application.

context.AuthStore#

  • Package: gogs.io/gogs/internal/context
  • File: internal/context/auth.go
  • Methods: Identical to context.Store (same six signatures).
  • Purpose: Parameter type for the package-internal authenticatedUser() and authenticatedUserID() functions, keeping those functions independently testable without requiring a full Store. It is also the parameter type of the exported AuthenticateByToken() function.
  • Implementations: Anything that satisfies context.Store also satisfies AuthStore; in practice the same store struct covers both.
  • Design quality: The duplication of Store and AuthStore with identical method sets is a minor redundancy. The intent — letting authenticatedUser be tested with a narrower mock — is valid, but the two interfaces have grown identical, suggesting a refactoring opportunity to collapse them into one or make AuthStore an explicit subset via embedding.

auth.Provider#

  • Package: gogs.io/gogs/internal/auth
  • File: internal/auth/auth.go
  • Methods:
    • Authenticate(login, password string) (*ExternalAccount, error)
    • Config() any
    • HasTLS() bool
    • UseTLS() bool
    • SkipTLSVerify() bool
  • Purpose: Core extensibility contract for pluggable authentication backends. Each external identity provider (LDAP via BindDN, LDAP direct bind, SMTP, PAM, GitHub OAuth) implements this interface. Authenticate performs the actual credential check and returns a normalized ExternalAccount struct. The three TLS-inspection methods are used by the admin UI to display and validate provider configuration.
  • Implementations: internal/auth/github, internal/auth/ldap, internal/auth/pam, internal/auth/smtp — one implementation per sub-package. A Mock type constant (999) is reserved for test doubles.
  • Design quality: Reasonably well-segregated. The TLS-inspection methods (HasTLS, UseTLS, SkipTLSVerify) are provider-metadata queries rather than behavioral contracts; they could be moved to a separate ProviderConfig interface. The Config() any return type (the any alias for interface{}) loses type safety and requires type-asserting callers — a sign of age predating generics. Still, the five-method surface is cohesive enough to follow ISP.

database.Engine#

  • Package: gogs.io/gogs/internal/database
  • File: internal/database/models.go
  • Methods:
    • Delete(any) (int64, error)
    • Exec(...any) (sql.Result, error)
    • Find(any, ...any) error
    • Get(any) (bool, error)
    • ID(any) *xorm.Session
    • In(string, ...any) *xorm.Session
    • Insert(...any) (int64, error)
    • InsertOne(any) (int64, error)
    • Iterate(any, xorm.IterFunc) error
    • Sql(string, ...any) *xorm.Session
    • Table(any) *xorm.Session
    • Where(any, ...any) *xorm.Session
  • Purpose: Abstracts over either a *xorm.Engine or a *xorm.Session so that legacy xorm-based model code can accept either — useful for transactional operations where a session is passed instead of the top-level engine. Also enables passing a fake implementation in tests.
  • Implementations: *xorm.Engine and *xorm.Session (both satisfy the interface implicitly). Test code can implement a subset.
  • Design quality: Broad — 12 methods. This is a leaky abstraction: several return *xorm.Session directly, so callers depend on xorm’s session API regardless. The interface does not follow ISP; it is a capability surface for the entire xorm query API rather than a focused contract. Its breadth reflects the reality of the legacy code it serves: all xorm-based models use it, and narrowing it would require refactoring hundreds of call sites.

lfsx.Storager#

  • Package: gogs.io/gogs/internal/lfsx
  • File: internal/lfsx/storage.go
  • Methods:
    • Storage() Storage
    • Upload(oid OID, rc io.ReadCloser) (int64, error)
    • Download(oid OID, w io.Writer) error
  • Purpose: Pluggable storage backend for Git LFS objects. Allows the LFS subsystem to swap between local filesystem storage and (future) remote object stores without changing the LFS HTTP handler logic.
  • Implementations: *lfsx.LocalStorage (verified by compile-time var _ Storager = (*LocalStorage)(nil) assertion). No remote backend exists yet.
  • Design quality: Excellent. Three focused methods, each with a single responsibility. Follows ISP precisely — consumers of LFS objects only need Download, producers only need Upload. The Storage() name query method enables introspection without type assertions. The compile-time assertion var _ Storager = (*LocalStorage)(nil) enforces correctness proactively.

gitx.ModuleStore#

  • Package: gogs.io/gogs/internal/gitx
  • File: internal/gitx/module.go
  • Methods:
    • RemoteAdd(repoPath, name, url string, opts ...git.RemoteAddOptions) error
    • DiffNameOnly(repoPath, base, head string, opts ...git.DiffNameOnlyOptions) ([]string, error)
    • Log(repoPath, rev string, opts ...git.LogOptions) ([]*git.Commit, error)
    • MergeBase(repoPath, base, head string, opts ...git.MergeBaseOptions) (string, error)
    • RemoteRemove(repoPath, name string, opts ...git.RemoteRemoveOptions) error
    • RepoTags(repoPath string, opts ...git.TagsOptions) ([]string, error)
    • PullRequestMeta(headPath, basePath, headBranch, baseBranch string) (*PullRequestMeta, error)
    • ListTagsAfter(repoPath, after string, limit int) (*TagsPage, error)
  • Purpose: Wraps the third-party gogs/git-module package behind an interface so that Git operations can be mocked in unit tests without spawning real git processes. The package-level var Module ModuleStore = module{} variable is replaced in tests with a fake.
  • Implementations: gitx.module (unexported zero-size struct), wrapping the corresponding top-level functions from github.com/gogs/git-module.
  • Design quality: Good testability shim. The interface groups the git operations actually used by the pull-request and release handlers — not the entire git-module API. The variadic options pattern (inherited from git-module) keeps the signatures forward-compatible. The package-level singleton Module is a global — slightly less clean than constructor injection, but pragmatic given that handlers do not yet receive dependencies.

route/repo.Store#

  • Package: gogs.io/gogs/internal/route/repo
  • File: internal/route/repo/store.go
  • Methods: Six methods shared with context.Store plus:
    • GetRepositoryByName(ctx context.Context, ownerID int64, name string) (*database.Repository, error)
    • IsTwoFactorEnabled(ctx context.Context, userID int64) bool
  • Purpose: The DI boundary for the HTTP Git smart-protocol middleware (HTTPContexter). Extends the basic auth surface of context.Store with repository resolution and 2FA status, which are needed to authorize a git push or fetch over HTTPS.
  • Implementations: repo.store (unexported struct), wraps database.Handle like the other store implementations.
  • Design quality: Consumer-defined and correctly scoped. The additional two methods reflect exactly what the git-HTTP layer needs beyond the base auth surface. The duplicated six-method auth block (copy-pasted from context.Store) is a code smell — if these were composed via embedding of a shared AuthStore interface, the duplication would disappear.

Interface patterns#

  • Size distribution: Small-to-medium. Most interfaces have 3–8 methods. The database.Engine outlier at 12 methods is a legacy xorm-wrapping artifact rather than a design choice. Newer interfaces (lfsx.Storager: 3, auth.Provider: 5, context.Store: 6) are well-sized.
  • Embedding: Interfaces are not composed via embedding. The overlapping method sets between context.Store, context.AuthStore, and route/repo.Store are copy-pasted rather than expressed as AuthStore embedded in Store. This is the most visible structural issue in the interface layer.
  • Implicit satisfaction: All interfaces are consumer-defined (defined in the consuming package, not in the provider). context.Store is defined in internal/context, not in internal/database. gitx.ModuleStore is defined in internal/gitx, not in github.com/gogs/git-module. This is idiomatic Go — the consumer declares the minimum it needs.
  • stdlib interfaces used: io.ReadCloser and io.Writer appear in lfsx.Storager.Upload/Download. sql.Result appears in database.Engine.Exec. The email package defines lightweight User, Repository, and Issue interfaces in internal/email to avoid importing internal/database — a standard Go dependency-inversion trick.

Key abstractions#

  1. auth.Provider — The most architecturally important interface. It is the only true extensibility point where third-party identity systems plug in. The entire conf/auth.d/ INI-file-based login source system exists to let operators configure which Provider implementation each LoginSource uses.

  2. context.Store / context.AuthStore — The primary DI seam introduced in the GORM migration era to make the auth middleware independently testable. They define the contract between the Macaron middleware stack and the database layer for authentication, replacing what was previously a direct call to database.Handle.

  3. lfsx.Storager — The cleanest interface in the codebase. Enables LFS storage backends to be swapped (local → S3 / object store) without touching handler code. The only interface with a compile-time satisfaction assertion (var _ Storager = (*LocalStorage)(nil)).

  4. gitx.ModuleStore — Enables unit testing of git-dependent business logic (pull request metadata, tag listing) without spawning subprocesses. The global Module variable pattern is the project’s chosen testability mechanism for the git layer.

  5. database.Engine — Not a clean abstraction but a significant one: it is the seam through which the xorm-based legacy model code is insulated from the xorm library. Every legacy model function accepts either Engine or the concrete *xorm.Session, which is what allows passing sessions for transactions.

Interface-driven extensibility#

Gogs has one genuine plugin point — authentication backends — driven entirely by the auth.Provider interface. Operators can add a new login source (LDAP, SMTP, PAM, GitHub) via the admin UI or via a conf/auth.d/ INI file; the system instantiates the corresponding Provider implementation at startup.

All other interfaces (context.Store, route/repo.Store, gitx.ModuleStore, lfsx.Storager) serve testability rather than extensibility in the plugin sense. They wrap concrete dependencies behind interfaces so that tests can substitute fakes, but the production code path has exactly one implementation per interface. There is no plugin registry, no factory pattern, and no dynamic loading.

This reflects Gogs’ design philosophy: simplicity over flexibility. The one extensibility axis that matters for a self-hosted Git service — authentication — is properly abstracted; everything else is optimized for a single deployment topology.