Harness Open Source (Drone/Gitness) — Structure#
Layout pattern#
Custom Monolith with Sub-module — The project uses a domain-driven flat-ish layout at the root (no top-level pkg/ or internal/), with a large app/ directory containing the primary application code and a separately declared Go module (registry/) as a workspace replace directive. This departs from Standard Go Layout in that all root-level directories are importable public packages, with encapsulation achieved through naming convention and domain cohesion rather than internal/ boundaries. The app/ directory itself uses a layered internal structure (handler → controller → service → store).
Directory map#
repositories/drone/
├── app/ # Core application (HTTP API, controllers, services, stores)
│ ├── api/ # HTTP layer: auth middleware, handlers, controllers, openapi
│ │ ├── auth/ # Auth middleware wrappers
│ │ ├── controller/ # Business logic per domain (30+ sub-packages)
│ │ └── handler/ # HTTP handler functions per domain (30+ sub-packages)
│ ├── auth/ # Authentication (authn) and authorization (authz)
│ ├── bootstrap/ # App bootstrap / first-run initialization
│ ├── config/ # Application configuration struct
│ ├── connector/ # External connector service
│ ├── cron/ # Cron-triggered pipeline execution
│ ├── events/ # Domain event definitions (git, pullreq, pipeline, gitspace…)
│ ├── githook/ # Git server-side hook handling
│ ├── gitspace/ # Cloud Dev Environment subsystem
│ │ ├── infrastructure/ # Gitspace infra provisioning
│ │ ├── orchestrator/ # Container / IDE orchestration
│ │ ├── platformconnector/
│ │ ├── scm/ # SCM integration for gitspaces
│ │ └── secret/
│ ├── jwt/ # JWT token creation/validation
│ ├── paths/ # Path parsing utilities (repo/space paths)
│ ├── pipeline/ # CI pipeline execution subsystem
│ │ ├── canceler/ # Pipeline cancellation
│ │ ├── commit/ # Commit status reporting
│ │ ├── converter/ # Pipeline YAML conversion (Drone → Harness spec)
│ │ ├── file/ # Pipeline YAML file loading from repo
│ │ ├── manager/ # Runner manager (assigns stages to runners)
│ │ ├── resolver/ # Plugin/template resolution
│ │ ├── runner/ # In-process runner coordination
│ │ ├── scheduler/ # Stage scheduling
│ │ └── triggerer/ # Pipeline trigger evaluation
│ ├── request/ # HTTP request context helpers
│ ├── router/ # HTTP router wiring (chi-based)
│ ├── server/ # HTTP server lifecycle
│ ├── services/ # Domain services (40+ sub-packages)
│ ├── sse/ # Server-sent events for live log streaming
│ ├── store/ # Store interfaces + implementations
│ │ ├── cache/ # Redis-backed cache for space/repo lookups
│ │ ├── database/ # SQLite/Postgres SQL store implementations
│ │ └── logs/ # Log storage abstraction
│ ├── testing/ # Integration test helpers
│ ├── token/ # Access token service
│ └── url/ # URL generation service
├── audit/ # Audit log library
├── blob/ # Blob/object storage abstraction (S3/GCS/local)
├── cache/ # Generic cache interface
├── charts/ # Helm chart for Kubernetes deployment
│ └── gitness/
├── cli/ # CLI framework wiring and sub-commands
│ ├── operations/ # CLI commands: server, account, hooks, migrate, swagger, user
│ ├── provide/ # Wire providers for CLI context
│ ├── session/ # CLI session management (local credentials)
│ └── textui/ # Terminal UI helpers
├── client/ # Go HTTP client library (for API consumers / gitness CLI)
├── cmd/
│ └── gitness/ # Single binary entry point
│ ├── main.go # CLI registration + kingpin wiring
│ ├── wire.go # Wire injection graph (build tag: wireinject)
│ └── wire_gen.go # Wire-generated DI code
├── contextutil/ # context.Context helpers
├── crypto/ # Cryptographic utilities
├── encrypt/ # Encryption-at-rest abstraction
├── errors/ # Typed error definitions and HTTP mapping
├── events/ # Generic event bus (Redis Streams backed)
├── git/ # Native git operations (wraps git CLI / libgit2)
│ ├── api/ # High-level git API (commits, refs, blobs, diffs…)
│ ├── command/ # Low-level git command execution
│ ├── diff/ # Diff parsing
│ ├── hook/ # Git hook payloads
│ ├── merge/ # Merge strategy implementations
│ ├── parser/ # Git output parsers
│ ├── storage/ # Repository on-disk storage layout
│ └── types/ # Git domain types
├── http/ # HTTP client/server utilities
├── infraprovider/ # Infrastructure provider abstraction (Docker, etc.)
├── job/ # Background job scheduler
├── langstats/ # Language statistics computation
├── livelog/ # Live log streaming (Redis Pub/Sub)
├── lock/ # Distributed lock abstraction
├── logging/ # Structured logging setup (zerolog)
├── profiler/ # pprof profiling integration
├── pubsub/ # Pub/Sub abstraction (Redis-backed)
├── registry/ # OCI artifact registry — separate Go module
│ ├── app/ # Registry HTTP API, controllers, handlers
│ ├── config/ # Registry-specific config
│ ├── gc/ # Garbage collection
│ ├── job/ # Registry background jobs
│ ├── services/ # Registry domain services
│ └── types/ # Registry-specific types
├── resources/ # Embedded resources (gitignore templates, licenses)
├── scripts/
│ ├── coverage/ # Coverage scripts
│ ├── license/ # License header scripts
│ └── wire/ # Wire generation scripts
├── secret/ # Secret storage abstraction
├── ssh/ # SSH server (gliderlabs/ssh)
├── store/ # Root-level store helpers (dbtx)
├── stream/ # Stream abstraction
├── tests/ # Top-level load/integration tests
├── types/ # Shared domain types (Config, User, Repo, Space…)
│ ├── check/ # Input validation helpers
│ └── enum/ # Enum types
├── version/ # Version info
└── web/ # React/TypeScript frontend (built into binary)Entry points#
Single binary: cmd/gitness/main.go → produces the gitness executable.
CLI commands registered via kingpin:
gitness server— starts the full HTTP server, SSH server, background workers, and all domain servicesgitness migrate— runs database migrations (dbmate-based)gitness user/gitness users— user management CLI commandsgitness account login|register|logout— account management for CLI consumersgitness hooks— git server-side hook execution (called by git itself during push/fetch)gitness swagger— generates/serves OpenAPI spec
There is no cmd/*/ for separate binaries; the runner (drone pipeline executor) runs as a separate process managed by the app/pipeline/runner/ package via the drone-runner-docker dependency.
Package organization#
Internal packages: None formally (
internal/is not used). Encapsulation is enforced via module boundaries (theregistrysub-module) and code review discipline.Public packages (root-level domains):
audit/— audit log emissionblob/— object storage (S3/GCS/local filesystem)cache/— generic cache interfaceencrypt/— encryption-at-rest interfaceerrors/— typed errors with HTTP status mappingevents/— Redis Streams event bus with typed topic routinggit/— native git operations (the platform’s core capability)http/— HTTP utilities (middleware, response writers)infraprovider/— Docker/VM infrastructure abstractionjob/— background job schedulerlivelog/— live log streaminglock/— distributed mutexpubsub/— pub/sub messagingsecret/— secret storage abstractionssh/— SSH serverstore/— database transaction helper (dbtx)stream/— byte stream abstractiontypes/— shared canonical domain types (Config, User, Repo, Space, Pipeline…)version/— version string
app/internal layering (domain-driven, 3-tier):app/api/handler/— HTTP handlers (decode request, call controller, encode response)app/api/controller/— Business logic layer (auth checks, orchestration, domain rules)app/services/— Domain services (cross-controller logic: merge, protection, webhooks…)app/store/— Data access layer (interfaces inapp/store/, SQL inapp/store/database/)
registry/sub-module: Separately versioned Go module (github.com/harness/gitness/registry) included viareplacedirective. Contains its own layeredapp/,services/,types/structure mirroring the root layout.Layering: Follows a clean layered architecture within
app/. The outer layers (handler) depend on inner layers (controller/service), but cross-domain service-to-service calls happen via injected interfaces, not direct package imports. The root-level packages (git/,events/,lock/, etc.) form a shared infrastructure layer imported byapp/.
Build system#
- Build tool:
make(primary), withwirefor DI code generation anddbmatefor migrations. - Key targets:
make build— runswirecodegen thengo build -o ./gitness ./cmd/gitnessmake test— runs all tests excluding registry conformance testsmake web-build— builds the React frontend (yarn install && yarn build)make generate— runsgo generate(wire + protobuf)make run— builds and runs the server with.local.envconfigmake tools— installs linting/formatting/codegen tools (golangci-lint,wire,dbmate, etc.)
- Docker: Yes, multi-stage:
- Stage 1 (
web): Node 16, builds React frontend with yarn - Stage 2 (
builder): Go 1.25.8 Alpine, compiles the binary with frontend dist embedded - Final stage: minimal runtime image
- Also
Dockerfile.uiv2for the v2 UI variant
- Stage 1 (
Notable structural decisions#
No
internal/package. With 2,500+ Go files, the project deliberately avoidsinternal/in favor of domain-named root packages. This makes the entire codebase importable as a library and enables theregistrysub-module to import root packages cleanly. The tradeoff is weaker access control.Single binary, maximum domain breadth. The
cmd/gitness/entry point wires 100+ WireSets into a single binary that serves as a git host, CI runner manager, artifact registry, SSH server, and cloud dev environment platform simultaneously. This is unusual even for monoliths of this size.Google Wire at scale. The
wire.gofile lists ~120 WireSets in a singlewire.Build()call — one of the largest Wire graphs in the Go open source ecosystem. The generatedwire_gen.gois hundreds of lines of auto-generated constructor call chains. This makes the DI graph explicit and compile-time verified, but the wire.go file itself is unwieldy.registry/as a workspace sub-module. The OCI artifact registry lives inregistry/with its owngo.mod, connected to the parent via areplacedirective. This provides module isolation (the registry team can evolve its API surface independently) while still compiling to the same binary. It mirrors how large monorepos use Go workspaces.Event-driven cross-domain communication. Rather than direct service-to-service calls for cross-domain operations (e.g., git push triggering CI), the platform uses a Redis Streams event bus (
events/). Domain events are defined per-domain inapp/events/<domain>/, decoupling the git subsystem from the pipeline subsystem at runtime.Embedded frontend. The React/TypeScript frontend (
web/dist/) is embedded into the Go binary at compile time using//go:embed. Theweb/directory is built separately (yarn) and the resultingdist/is copied into the Docker build context, producing a truly self-contained single-binary deployment.