Skip to main content

Multi-Module Structure

The core repository contains three Go modules and uses build tags to control what gets compiled into different binaries. Understanding this structure is important for knowing where to put new code and why certain imports fail.

The Three Modules

core (root module)

Module path: github.com/theopenlane/core

The main API server. Contains the HTTP server, Ent schemas, GraphQL resolvers, hooks, interceptors, middleware, and all business logic. This module is not designed to be imported by external repositories (except code inside of pkg) -- its internal/ directory is compiler-enforced as non-importable.

The root go.mod contains a replace directive pointing common to the local ./common directory, so the two modules stay in sync without publishing.

common (shared types)

Module path: github.com/theopenlane/core/common

A lightweight module containing types that external repositories need to consume. This is intentionally kept thin -- it has minimal dependencies and no server logic.

PackageContents
common/enumsEnumeration types (OAuth providers, user statuses, roles, entity states)
common/modelsDomain models (compliance, audit, IP address types)
common/openapiREST API request/response structs
common/storagetypesStorage provider enums and configuration types
common/integrationsThird-party integration configuration types
common/jobspecJob queue job specification models
common/helpersUtility functions

If you are building a service that needs to interact with Openlane's API types without importing the entire server, common is a viable path.

cli (command-line tool)

Module path: github.com/theopenlane/core/cli

The Openlane CLI binary. It lives in the same repository for development convenience but compiles as a separate binary. It imports both core and common as dependencies.

The CLI uses Cobra for command structure and the generated GraphQL client for API communication.

Where to Put New Code

Code TypeLocationReason
Server business logicinternal/Not importable externally, keeps API surface controlled
Reusable utilities (used by other repos)pkg/Importable by external Go code
Types shared with CLI or external reposcommon/Separate module, minimal dependencies
CLI commandscli/Separate binary, separate module
Ent schemasinternal/ent/schema/Server-only, drives code generation
REST handlersinternal/httpserve/handlers/Server-only
GraphQL resolversinternal/graphapi/Server-only
tip

If you are unsure whether something belongs in pkg/ or internal/, ask: "Will any repository other than core ever import this?" If no, use internal/. If yes, use pkg/. If you are unsure, start with internal/ -- it is easier to move code outward than to retract a public API.

Build Tags

The codebase uses //go:build tags for conditional compilation. This keeps the main server binary lean by excluding tooling and test infrastructure.

Active Build Tags

TagPurposeWhere Used
testTest-only code (test utilities, fixtures, mock implementations)internal/testutils/, test files
cliCLI-specific codecli/, CLI command implementations
codegenCode generation scripts (not compiled into any binary)internal/ent/generate/, internal/graphapi/generate/
clistripeStripe webhook management CLI toolcmd/stripe-webhook/
climanagedgroupsManaged groups CLI toolcmd/managedgroups/
ignoreGeneration scripts that should never be compiledVarious generate.go files

Impact on Development

Your IDE needs to know about these tags to resolve symbols correctly. Without cli,test,codegen in your build tags, functions gated behind those tags will appear undefined.

VSCode -- add to .vscode/settings.json:

{
"go.buildTags": "cli,test,codegen"
}

GoLand -- Settings > Go > Build Tags: cli,test,codegen

Command line -- pass tags explicitly:

go test -tags test ./...
go build -tags cli ./cli/...

Adding a New Build Tag

If you need to gate new functionality behind a build tag:

  1. Add //go:build yourtag at the top of the file (before the package declaration)
  2. Ensure the corresponding non-tagged files do not define conflicting symbols
  3. Document the tag in the table above
  4. Update IDE settings if the tag is needed for development

Build tags are appropriate for optional CLI tools and test infrastructure. They are not appropriate for feature flags in production code -- use the entitlements/module system for that.