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) errorGetUserByID(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 intoContexter(store)at startup. - Implementations:
context.store(unexported struct, wrapsdatabase.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()andauthenticatedUserID()functions, keeping those functions independently testable without requiring a fullStore. It is also the parameter type of the exportedAuthenticateByToken()function. - Implementations: Anything that satisfies
context.Storealso satisfiesAuthStore; in practice the samestorestruct covers both. - Design quality: The duplication of
StoreandAuthStorewith identical method sets is a minor redundancy. The intent — lettingauthenticatedUserbe tested with a narrower mock — is valid, but the two interfaces have grown identical, suggesting a refactoring opportunity to collapse them into one or makeAuthStorean 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() anyHasTLS() boolUseTLS() boolSkipTLSVerify() 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.
Authenticateperforms the actual credential check and returns a normalizedExternalAccountstruct. 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. AMocktype 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 separateProviderConfiginterface. TheConfig() anyreturn type (theanyalias forinterface{}) 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) errorGet(any) (bool, error)ID(any) *xorm.SessionIn(string, ...any) *xorm.SessionInsert(...any) (int64, error)InsertOne(any) (int64, error)Iterate(any, xorm.IterFunc) errorSql(string, ...any) *xorm.SessionTable(any) *xorm.SessionWhere(any, ...any) *xorm.Session
- Purpose: Abstracts over either a
*xorm.Engineor a*xorm.Sessionso 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.Engineand*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.Sessiondirectly, 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() StorageUpload(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-timevar _ 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 needUpload. TheStorage()name query method enables introspection without type assertions. The compile-time assertionvar _ 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) errorDiffNameOnly(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) errorRepoTags(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-modulepackage behind an interface so that Git operations can be mocked in unit tests without spawning real git processes. The package-levelvar Module ModuleStore = module{}variable is replaced in tests with a fake. - Implementations:
gitx.module(unexported zero-size struct), wrapping the corresponding top-level functions fromgithub.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
Moduleis 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.Storeplus: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 ofcontext.Storewith repository resolution and 2FA status, which are needed to authorize a git push or fetch over HTTPS. - Implementations:
repo.store(unexported struct), wrapsdatabase.Handlelike 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 sharedAuthStoreinterface, the duplication would disappear.
Interface patterns#
- Size distribution: Small-to-medium. Most interfaces have 3–8 methods. The
database.Engineoutlier 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, androute/repo.Storeare copy-pasted rather than expressed asAuthStoreembedded inStore. 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.Storeis defined ininternal/context, not ininternal/database.gitx.ModuleStoreis defined ininternal/gitx, not ingithub.com/gogs/git-module. This is idiomatic Go — the consumer declares the minimum it needs. - stdlib interfaces used:
io.ReadCloserandio.Writerappear inlfsx.Storager.Upload/Download.sql.Resultappears indatabase.Engine.Exec. The email package defines lightweightUser,Repository, andIssueinterfaces ininternal/emailto avoid importinginternal/database— a standard Go dependency-inversion trick.
Key abstractions#
auth.Provider— The most architecturally important interface. It is the only true extensibility point where third-party identity systems plug in. The entireconf/auth.d/INI-file-based login source system exists to let operators configure whichProviderimplementation eachLoginSourceuses.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 todatabase.Handle.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)).gitx.ModuleStore— Enables unit testing of git-dependent business logic (pull request metadata, tag listing) without spawning subprocesses. The globalModulevariable pattern is the project’s chosen testability mechanism for the git layer.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 eitherEngineor 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.