Harness Open Source (Gitness/Drone) — API Surface#

API types#

REST/HTTP, Smart HTTP Git Protocol, Git LFS, OCI Distribution (Docker Registry), Multi-format Package Registry, CLI, Library/SDK, Server-Sent Events (SSE)


REST/HTTP API#

  • Router: go-chi/chi v5
  • Base path: /api/v1/ (mounted via APIRouter; prefix stripped before chi sees it)
  • Route registration: Explicit chi r.Route()/r.Get()/r.Post() calls in app/router/api.go; one large file registers all ~150+ endpoints grouped by domain function
  • OpenAPI: Full OpenAPI 3 spec generated programmatically via swaggest/rest reflector; available via gitness swagger generate CLI command

Middleware chain (in order)#

LayerMiddlewarePurpose
1nocache.NoCachePrevents caching of API responses
2middleware.Recoverer (chi)Panic recovery → 500
3logging.URLHandler, hlog.MethodHandler, HLogRequestIDHandler, HLogAccessLogHandlerStructured zerolog request logging
4address.HandlerPopulate request address in context
5corsHandlerCORS (configured from config.Cors.*)
6audit.MiddlewareAudit log injection into context
7middlewareauthn.Attempt(authenticator)Optional JWT authentication — populates auth.Session in context; does NOT 401 on missing auth
8middlewareprincipal.RestrictTo(enum.PrincipalTypeUser)Applied per route group to enforce user-only access (e.g., /user/)
9middlewareprincipal.RestrictToAdmin()Applied to /admin/ routes
10middlewareauthz.BlockSessionTokenApplied to git protocol routes — rejects session tokens, forces PAT/SSH
11usage.Middleware(usageSender)Applied to raw/archive/git routes for bandwidth tracking

Authentication#

Handled by JWTAuthenticator (app/auth/authn/jwt.go), which accepts tokens from:

  1. Authorization: Bearer <jwt> — primary for API calls
  2. Cookie named config.Token.CookieName — for browser sessions
  3. Authorization: RemoteAuth <jwt> — for SSH git-lfs-authenticate tokens
  4. Anonymous principal (JWT with PrincipalID == -1) — for public repo access

Token types: PAT (Personal Access Token), SAT (Service Account Token), Session (browser), Pipeline JWT (ephemeral 72h, for CI steps). Authorization checks are delegated to authz.Authorizer inside each controller (RBAC with space-level membership hierarchy).

Key endpoint groups#

Account (no auth required)#

  • POST /v1/login — authenticate, returns JWT session token
  • POST /v1/register — create account + auto-login
  • POST /v1/logout — invalidate session cookie (requires auth)

Spaces (/v1/spaces/{space_ref})#

  • POST /v1/spaces/ — create space
  • POST /v1/spaces/import — import space from external SCM
  • GET/PATCH/DELETE /v1/spaces/{ref} — find, update, soft-delete
  • POST /v1/spaces/{ref}/restore / /purge — lifecycle management
  • GET /v1/spaces/{ref}/eventsSSE stream of space-level events
  • POST /v1/spaces/{ref}/move — move/rename space
  • GET /v1/spaces/{ref}/repos / /spaces / /pipelines / /executions — list sub-resources
  • GET /v1/spaces/{ref}/secrets / /connectors / /templates / /gitspaces — list CI resources
  • GET/POST/DELETE/PATCH /v1/spaces/{ref}/members/{uid} — membership management
  • GET/POST/PATCH/DELETE /v1/spaces/{ref}/labels/{key} — label management (with nested /values)
  • GET/POST/PATCH/DELETE /v1/spaces/{ref}/webhooks/{id} — webhook CRUD + execution history
  • GET/POST/PATCH/DELETE /v1/spaces/{ref}/rules/{id} — branch protection rules
  • GET/POST/PATCH/DELETE /v1/spaces/{ref}/autolinks/{id} — autolink rules (e.g., ticket refs)
  • GET /v1/spaces/{ref}/settings/general / PATCH — space-level settings
  • GET/POST /v1/spaces/{ref}/pullreq / /pullreq/count — cross-repo PR listing

Repositories (/v1/repos/{repo_ref})#

  • POST /v1/repos/ — create; /import — import; /link — link external
  • GET/PATCH/DELETE /v1/repos/{ref} — find, update, soft-delete
  • POST /v1/repos/{ref}/fork / /fork-sync — fork management
  • GET /v1/repos/{ref}/summary — repository summary stats
  • GET /v1/repos/{ref}/content/* — browse file tree at any ref
  • GET /v1/repos/{ref}/blame/* — git blame at path
  • GET /v1/repos/{ref}/raw/* — raw file content download (usage tracked)
  • GET /v1/repos/{ref}/paths — list all file paths
  • POST /v1/repos/{ref}/path-details — batch file metadata
  • GET /v1/repos/{ref}/commits/ — list commits; POST / — commit files
  • GET /v1/repos/{ref}/commits/{sha} / /diff — single commit details
  • POST /v1/repos/{ref}/commits/calculate-divergence — branch divergence
  • GET/POST/DELETE /v1/repos/{ref}/branches/* — branch CRUD
  • GET/POST/DELETE /v1/repos/{ref}/tags/* — tag CRUD
  • GET/POST /v1/repos/{ref}/diff/* / /diff-stats/* / /merge-check/* — diff operations
  • POST /v1/repos/{ref}/rebase / /squash — git operations
  • GET /v1/repos/{ref}/archive/{gitRef}.{format} — download archive (tar/zip)
  • GET /v1/repos/{ref}/codeowners/validate — CODEOWNERS validation
  • GET/PATCH /v1/repos/{ref}/settings/security / /general — repo settings

Pull Requests (/v1/repos/{ref}/pullreq)#

  • POST/GET /pullreq — create, list
  • GET/PATCH /pullreq/{num} — find, update
  • POST /pullreq/{num}/state — change state (open/closed)
  • GET /pullreq/{num}/activities — activity feed
  • POST/PATCH/DELETE /pullreq/{num}/comments/{id} — comment CRUD
  • POST /pullreq/{num}/comments/apply-suggestions — apply code suggestions
  • GET/PUT/DELETE /pullreq/{num}/reviewers/{id} / /usergroups — reviewer management
  • GET /pullreq/{num}/reviewers/combined — merged reviewer list
  • POST /pullreq/{num}/reviews — submit review decision
  • POST /pullreq/{num}/merge — merge PR
  • POST /pullreq/{num}/revert — create revert PR
  • PUT/DELETE /pullreq/{num}/automerge — auto-merge management
  • GET /pullreq/{num}/diff / /commits / /metadata / /codeowners / /checks
  • POST/DELETE /pullreq/{num}/branch — manage PR source branch
  • PUT /pullreq/{num}/target-branch — change target branch
  • GET/PUT/DELETE /pullreq/{num}/file-views — track which files have been reviewed
  • PUT/GET/DELETE /pullreq/{num}/labels — PR label management

Pipelines and CI (/v1/repos/{ref}/pipelines)#

  • GET/POST/DELETE/PATCH /pipelines/{id} — pipeline CRUD
  • GET /repos/{ref}/pipelines — list; GET /generate — AI-generate pipeline YAML
  • GET/POST/DELETE/PATCH /pipelines/{id}/executions/{num} — execution CRUD + cancel
  • GET /executions/{num}/logs/{stage}/{step} — fetch stored log
  • GET /executions/{num}/logs/{stage}/{step}/streamSSE live log tail
  • GET/POST/PATCH/DELETE /pipelines/{id}/triggers/{id} — trigger management

User (/v1/user)#

  • GET/PATCH /user — self profile
  • GET /user/memberships — space memberships
  • GET/POST/DELETE /user/tokens/{id} — PAT management
  • GET/DELETE /user/sessions/{id} — session token management
  • GET/POST/DELETE/PATCH /user/keys/{id} — SSH public key management
  • POST/DELETE /user/favorite/{id} — favorite repos

Admin (/v1/admin, admin principal only)#

  • GET/POST /admin/users — list, create users
  • GET/PATCH/DELETE /admin/users/{uid} — user management
  • PATCH /admin/users/{uid}/admin — grant/revoke admin

CI Infrastructure#

  • GET/POST/PATCH/DELETE /v1/connectors/{ref} + POST /test — SCM/infra connectors
  • GET/POST/PATCH/DELETE /v1/secrets/{ref} — secret store
  • GET/POST/PATCH/DELETE /v1/templates/{type}/{ref} — reusable step/stage templates
  • GET /v1/plugins — list available CI plugins

Gitspaces (/v1/gitspaces)#

  • POST /gitspaces/lookup-repo — validate repo URL for gitspace creation
  • POST/GET /gitspaces — create, list all
  • GET/PATCH/DELETE /gitspaces/{id} — find, update, delete
  • POST /gitspaces/{id}/actions — start/stop/reset container
  • GET /gitspaces/{id}/eventsSSE lifecycle events
  • GET /gitspaces/{id}/logs/streamSSE provisioning log stream

Infrastructure Providers#

  • POST/GET/DELETE /v1/infraproviders/{id} — Docker/k8s infra config management

System#

  • GET /v1/system/health — health check (no auth)
  • GET /v1/system/version — version info
  • GET /v1/system/config — public configuration (feature flags, SCM providers)

Internal (git hook callbacks)#

  • POST /v1/internal/git-hooks/pre-receive
  • POST /v1/internal/git-hooks/update
  • POST /v1/internal/git-hooks/post-receive

These are called by the server-side git hook process back to the gitness HTTP server during push operations. Authenticated with ephemeral pipeline JWT tokens.

Migration#

  • POST /v1/migrate/repos/ — create repo via migration
  • POST /v1/migrate/repos/{ref}/pullreqs / /webhooks / /rules — batch import sub-resources
  • POST /v1/migrate/spaces/{ref}/labels — migrate label definitions

Git Protocol (Smart HTTP)#

  • Router: GitRouter (app/router/git_router.go) — handles all traffic that isn’t prefixed with /api/ or registry paths
  • Base path: /{repo_ref}/ (where repo_ref is a slash-encoded space/repo path)
MethodPathDescription
POST/{ref}/git-upload-packServe fetch/clone (smart protocol)
POST/{ref}/git-receive-packReceive push (smart protocol)
GET/{ref}/info/refs?service=git-*Smart protocol capability discovery
GET/{ref}/Browser redirect to UI repo page
GET/{ref}/HEAD, /objects/*Dumb protocol stubs — intentionally return 502 with message

Middleware additions for git routes:

  • goget.Middleware — serves ?go-get=1 responses for go get support
  • middlewareauthz.BlockSessionToken — rejects browser session cookies; forces PAT or basic-auth
  • usage.Middleware — tracks bandwidth consumption

Git LFS (under git router)#

MethodPathDescription
POST/{ref}/info/lfs/objects/batchLFS batch API (negotiate transfer)
PUT/{ref}/info/lfs/objects/Upload LFS object
GET/{ref}/info/lfs/objects/Download LFS object

OCI Registry API (/v2/...)#

Implements the OCI Distribution Specification.

  • Router: RegistryRouter — picks up traffic at /v2/ prefix
  • Handler: registry/app/api/router/oci/route.go — dispatch table keyed by route type + HTTP method
OperationMethodPath
Auth tokenGET/v2/token
API checkGET/v2/
Get manifestGET/HEAD/v2/{registry}/manifests/{reference}
Push manifestPUT/v2/{registry}/manifests/{reference}
Delete manifestDELETE/v2/{registry}/manifests/{reference}
Get blobGET/v2/{registry}/blobs/{digest}
Head blobHEAD/v2/{registry}/blobs/{digest}
Delete blobDELETE/v2/{registry}/blobs/{digest}
Initiate uploadPOST/v2/{registry}/blobs/uploads/
Upload chunkPATCH/v2/{registry}/blobs/uploads/{session}
Complete uploadPUT/v2/{registry}/blobs/uploads/{session}
Cancel uploadDELETE/v2/{registry}/blobs/uploads/{session}
Get upload statusGET/v2/{registry}/blobs/uploads/{session}
List tagsGET/v2/{registry}/tags/list
Get referrersGET/v2/{registry}/referrers/{digest}

Registry-specific middleware:

  • middleware.OciCheckAuth — OCI-specific auth challenge (WWW-Authenticate)
  • middleware.BlockNonOciSourceToken — rejects tokens not issued for OCI access
  • middleware.TrackDownloadStat — download count tracking
  • middleware.TrackBandwidthStat — bandwidth tracking
  • middleware.CheckQuarantineStatusOCI — blocks quarantined artifacts

Package Registry API#

Multi-format artifact registry under /{rootIdentifier}/{registryName}/<type>/.

  • Router: RegistryRouter handles /registry/, /maven/, /generic/, /pkg/ prefixes
  • Package types supported (each has its own handler package in registry/app/api/handler/):
Package TypeProtocolUse Case
npmnpm registry protocolNode.js packages
mavenMaven/Gradle repository protocolJava artifacts
pythonPyPI/PEP 503 simple indexPython packages
nugetNuGet v3 feed.NET packages
rpmYUM/DNF repositoryLinux RPM packages
cargoCargo sparse indexRust crates
goGo module proxy protocolGo modules
genericSimple upload/downloadArbitrary binaries
huggingfaceHuggingFace Hub protocolML model weights

Registry Management API (/api/v1/registry/, /api/v1/spaces/.../registries)#

Manages registry lifecycle (create, configure, GC policies, replication, webhooks).

  • Handler: Generated from OpenAPI contract at registry/app/api/openapi/contracts/artifact/ via oapi-codegen; uses artifact.NewStrictHandler wrapping metadata.APIController
  • Key operations: CRUD for registries, cleanup policies, replication rules, upstream proxies, artifact listing, tag management, webhook CRUD for registry events

CLI#

  • Framework: gopkg.in/alecthomas/kingpin.v2
  • Binary: gitness

Command structure#

gitness
├── server                  # Start HTTP/SSH server (primary command)
│   └── --config env vars   # All configuration via environment variables
├── migrate
│   ├── current             # Show current DB migration version
│   └── to <version>        # Migrate DB to target version
├── user
│   ├── self                # Print info about authenticated user
│   └── create-pat          # Create Personal Access Token
├── users                   # Admin user management
│   ├── list
│   ├── create
│   ├── find
│   ├── update
│   └── delete
├── account
│   ├── login               # Authenticate and store session
│   ├── register            # Create new user account
│   └── logout              # Invalidate session
├── hooks                   # Git hook helpers (for server-side hooks)
└── swagger                 # OpenAPI spec management
    └── generate            # Print OpenAPI 3 JSON spec to stdout

Flag patterns#

  • Global: no global flags; config is environment-variable only for server command
  • CLI commands (user, users, account) use --host / --token flags or session file stored at ~/.gitness (via cli/session/ package)
  • Commands reading from the session file use cli/provide/ helpers to inject credentials

Library/SDK API#

The client/ package exposes a Go HTTP client for the REST API:

type Client interface {
    Login(ctx, *user.LoginInput) (*types.TokenResponse, error)
    Register(ctx, *user.RegisterInput) (*types.TokenResponse, error)
    Self(ctx) (*types.User, error)
    User(ctx, key string) (*types.User, error)
    UserList(ctx, types.UserFilter) ([]types.User, error)
    UserCreate(ctx, *types.User) (*types.User, error)
    UserUpdate(ctx, key string, *types.UserInput) (*types.User, error)
    UserDelete(ctx, key string) error
    UserCreatePAT(ctx, user.CreateTokenInput) (*types.TokenResponse, error)
}

This is a minimal client — only covers user/auth operations. Not a full API SDK. The registry sub-module has a separate generated client from the OpenAPI contract.


Notable API design decisions#

  1. Four-router dispatch avoids conflicting patterns. The outer Router checks IsEligibleTraffic() in priority order (Git → Registry → API → Web). This prevents the SPA catch-all (WebRouter) from swallowing git or registry traffic, and keeps each sub-system’s routing logic independent. The tradeoff: a new traffic type requires a new Interface implementation and a priority insertion decision.

  2. Path encoding middleware. The encode.GitPathBefore and encode.TerminatedPathBefore wrappers pre-process URLs before chi sees them. Git paths contain slash-encoded repository references (e.g., myspace%2Fmyrepo); this is decoded into the correct URL segment. Similarly, terminated paths ending in / are normalized. This keeps chi’s routing simple at the cost of a custom URL transformation layer.

  3. Optional authentication model. middlewareauthn.Attempt() does NOT require authentication — it populates the session if credentials exist, but continues without them. Authorization is enforced inside each controller method via authz.Authorizer.Check(). This enables public repo access without separate unauthenticated route groups — every route can serve both authenticated and anonymous traffic.

  4. Internal git-hook callbacks. Push operations trigger server-side hooks that call back to POST /api/v1/internal/git-hooks/pre-receive etc. These are protected with ephemeral pipeline JWT tokens injected into the git hook environment. This design keeps the git hook logic in Go (not shell scripts) and enables hook results to influence the push response, while staying within the same HTTP server.

  5. No gRPC. Despite integrating with the Drone CI ecosystem (which uses gRPC in some versions), Gitness is pure HTTP. The pipeline runner executes as an in-process goroutine using the embedded client (adapter pattern over ExecutionManager). External Drone runner support is available via the drone/runner-go client library, but the server does not expose a dedicated runner RPC endpoint — runners would use the same API surface.

  6. SSE for real-time streaming. Live log tailing, gitspace events, and space-level events use Server-Sent Events rather than WebSockets. SSE is simpler (HTTP/1.1 compatible, automatic reconnect in browsers), sufficient for unidirectional server→client streaming, and avoids the connection upgrade overhead of WebSockets.

  7. OpenAPI generated from code, not from YAML. The API spec is created programmatically by the app/api/openapi/ package using swaggest/rest reflector. The registry sub-module uses the opposite approach: an OpenAPI contract YAML (artifact/openapi.yaml) drives code generation via oapi-codegen, producing typed request/response structs and a StrictHandler interface. This contrast shows divergent team philosophies within the same binary.