Gitea — API Surface#

API types#

Gitea exposes five distinct API surfaces to different classes of callers:

  1. REST API (/api/v1) — public Swagger-documented REST API for all external integrations
  2. Package Manager APIs (/api/packages, /v2) — 20+ protocol-native package registry endpoints
  3. Actions Runner API (/api/actions) — Connect-RPC (protobuf-over-HTTP) for CI runner communication
  4. Private/Internal API (/api/internal) — HTTP IPC between the git hook subprocess and the web server
  5. Web UI (/) — Full HTML rendering surface (same chi router, not analyzed in depth here)

REST/HTTP API (/api/v1)#

Router#

  • Framework: github.com/go-chi/chi/v5 via a custom modules/web.Router wrapper
  • Route registration: All routes are registered explicitly in routers/api/v1/api.go:Routes(), a ~900-line function
  • Documentation: Swaggo-style godoc // swagger:operation comments on handler functions; spec served at /api/swagger (when enabled)

Middleware chain#

Applied in order via m.BeforeRouting / m.AfterRouting:

  1. securityHeaders() — sets X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, etc.
  2. CORS handler (github.com/go-chi/cors) — optional, configurable allowed origins/methods
  3. context.APIContexter() — creates APIContext (extends Base with APIOrganization, APIRepo, etc.), sets up request data store
  4. checkDeprecatedAuthMethods — warns via header when legacy ?token= or ?access_token= query params are used
  5. apiAuth(buildAuthGroup()) — resolves the caller identity; auth methods tried in order: OAuth2 → HTTPSign → Basic (→ ReverseProxy if enabled)
  6. verifyAuthWithOptions — enforces RequireSignInViewStrict if configured
  7. Per-route: tokenRequiresScopes(...) — enforces fine-grained scope on the API token (read/write per category)
  8. Per-route: repoAssignment() / orgAssignment() — loads the repo/org from path params, checks visibility

Authentication#

Six supported methods (evaluated by the auth.Group pipeline):

  • OAuth2 Bearer tokenAuthorization: token <TOKEN> header (preferred)
  • HTTPSign — HTTP Signatures with SSH key (for SSH-based clients)
  • Basic auth — deprecated for removal in v1.23; still accepted
  • Reverse proxy — optional, reads user from X-Forwarded-User or configured header
  • Session cookie — inherited from web context (for same-origin browser requests)
  • Sudo — site admins can impersonate users via ?sudo=<username> or Sudo: header

Token scopes (introduced in Gitea 1.19): tokenRequiresScopes enforces minimum scope per route group. Categories: repository, issue, notification, user, organization, package, admin, activitypub. Each category has read/write granularity, and tokens can be restricted to public resources only.

Key endpoint groups#

GroupPrefixScopeDescription
Misc/api/v1/noneversion, swagger, signing keys, gitignore/license/label templates, markdown rendering, settings
Notifications/api/v1/notificationsnotificationlist, read, thread management
Users/api/v1/users, /api/v1/useruserprofile, SSH/GPG keys, followers, starred repos, OAuth2 apps, access tokens, hooks, blocks
Repositories/api/v1/reposrepositoryfull repo lifecycle, branches, tags, commits, raw content, archive, forks, LFS
Code review/api/v1/repos/{owner}/{repo}/pullsrepositoryPRs, reviews, merge, auto-merge, status checks
Issues/api/v1/repos/{owner}/{repo}/issuesissueissues, comments, labels, milestones, reactions, time tracking, dependencies
Releases/api/v1/repos/{owner}/{repo}/releasesrepositoryreleases, tags, assets
Webhooks/api/v1/repos/{owner}/{repo}/hooksadminper-repo and global webhook CRUD
Gitea Actions/api/v1/repos/{owner}/{repo}/actionsrepositoryworkflows, runs, jobs, artifacts, secrets, variables, runners
Organizations/api/v1/orgs, /api/v1/org/{org}organizationorg CRUD, members, teams, hooks, labels, activity feeds
Packages/api/v1/packages/{username}packageGitea package management meta-API (list, get, delete — not protocol-specific)
Admin/api/v1/adminadmincron tasks, users, emails, repos, hooks, Actions runners, badges
ActivityPub/api/v1/activitypubactivitypubfederation endpoints (currently NotImplemented stubs)

Notable endpoints:

  • GET /api/v1/repos/{owner}/{repo}/contents/{filepath} — file contents
  • POST /api/v1/repos/{owner}/{repo}/contents/{filepath} — create file (creates a commit)
  • PUT /api/v1/repos/{owner}/{repo}/pulls/{index}/merge — merge PR with configurable strategy
  • POST /api/v1/repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches — trigger workflow
  • GET /api/v1/repos/{owner}/{repo}/git/trees/{sha} — git object tree
  • GET /api/v1/repos/{owner}/{repo}/compare/* — diff between refs

Package Manager APIs (/api/packages, /v2)#

Gitea doubles as a full-featured package registry. Each package type speaks its native protocol; these routes are separate from the REST /api/v1 routes and are mounted at /api/packages/{username}/{type}/....

Router#

  • Same chi wrapper as the REST API
  • Separate CommonRoutes() and ContainerRoutes() entry points in routers/api/packages/api.go
  • Authentication: OAuth2, Basic, plus format-specific auth helpers (nuget.Auth, chef.Auth, packages/auth.go)
  • Container routes live at /v2/ (OCI Distribution Spec root)

Supported package formats (20+)#

FormatProtocol baseNotable
Alpine APKCustom APK index protocol.tar.gz index, APKINDEX
Arch Linuxpacman repository format
Cargo (Rust)Crates.io sparse index APIyank/unyank support
Chef CookbooksSupermarket API v1cookbook universe endpoint
Composer (PHP)Packagist APIpackages.json, p2/ metadata
Conan (C++)Conan v1 + v2 APIrecipe & package snapshots
Condaconda channel format
Container (OCI/Docker)OCI Distribution Spec 1.0full OCI push/pull/delete; mounted at /v2/
CRAN (R)CRAN repository format
DebianAPT repository format.deb upload, Packages index
GenericSimple file storeraw file upload/download
Go Module ProxyGOPROXY protocol@v/list, @v/{version}.info/.mod/.zip
HelmHelm repository formatindex.yaml, chart download
Maven / GradleMaven repository protocolmetadata XML, artifact download
npmnpm registry APIpackage.json metadata, tarballs, dist-tags
NuGetNuGet v2 + v3 APIOData feed and JSON endpoints
Pub (Dart)pub.dev APIadvisories, version listing
PyPIPyPI simple API + JSON APIsimple/ index, file downloads
RPMYUM/DNF repositoryrepomd.xml, RPM spec
RubyGemsRubyGems API.gemspec + .gem download
Swift (SPM)SPM package registry APIPackage.swift, .zip download
VagrantVagrant Cloud APIbox metadata + file download

Each format has its own sub-package under routers/api/packages/<format>/, typically with a *.go file implementing the format-specific handlers.

Container (OCI) routes#

  • Mounted at /v2/ (not /api/packages)
  • Full OCI Distribution Spec 1.0: push (PUT blob, POST upload, PUT manifest), pull (GET manifest, GET blob), delete, referrers, catalog, list tags
  • ContainerRoutes() returns a *web.Router with OCI-specific error format (JSON errors array)
  • Auth: WWW-Authenticate: Bearer challenge flow (token service built into Gitea)

Actions Runner API (/api/actions)#

Gitea Actions uses a Connect-RPC (protobuf over HTTP/1.1) protocol to communicate with self-hosted runners.

  • Proto definition: routers/api/actions/artifact.proto; generated code in artifact.pb.go
  • Services registered:
    • PingService — health check for runner ↔ server connectivity
    • RunnerService — job lifecycle: register, fetch job, update job status, submit logs
  • Framework: connectrpc.com/connect (formerly bufbuild/connect-go)
  • Route pattern: All POSTs to /api/actions/{service}/{method} via m.Post(path+"*", handler.ServeHTTP) — Connect uses HTTP/1.1 POST with Content-Type: application/connect+proto
  • Artifact handling: Additional handlers in artifacts.go / artifactsv4.go for uploading/downloading CI artifacts (GitHub Actions-compatible artifact API v3 + v4)

Private/Internal API (/api/internal)#

This surface is not public — it is used exclusively by Gitea’s own subprocess invocations (gitea serv, gitea hook). It runs on the same HTTP listener but is protected by a shared InternalToken checked via X-Gitea-Internal-Auth: Bearer <token>.

Authentication#

authInternal middleware does a subtle.ConstantTimeCompare of the Bearer token against setting.InternalToken. No public access is possible without this secret.

Route table (routers/private/internal.go)#

MethodPathPurpose
GET/dummyHealth probe
POST/ssh/authorized_keysLook up public key by content for authorized_keys generation
POST/ssh/{id}/update/{repoid}Update key-to-repo association
POST/ssh/logLog SSH activity from gitea serv
POST/hook/pre-receive/{owner}/{repo}Pre-receive git hook enforcement (branch protection, quota)
POST/hook/post-receive/{owner}/{repo}Post-receive processing (update repo state, fire webhooks, update PRs)
POST/hook/proc-receive/{owner}/{repo}Proc-receive for push-to-PR (Gitea extension)
POST/hook/set-default-branch/{owner}/{repo}/{branch}Update default branch after push
GET/serv/none/{keyid}SSH key lookup (no git command, just check auth)
GET/serv/command/{keyid}/{owner}/{repo}Authorize and resolve git-over-SSH command
POST/manager/shutdownGraceful shutdown
POST/manager/restartGraceful restart (SIGUSR1 equivalent)
POST/manager/reload-templatesHot-reload HTML templates
POST/manager/flush-queuesDrain all background queues
POST/manager/pause-loggingPause log output
POST/manager/resume-loggingResume log output
POST/manager/release-and-reopen-loggingLog rotation
POST/manager/set-log-sqlToggle SQL query logging at runtime
POST/manager/add-loggerAdd a log writer at runtime
POST/manager/remove-logger/{logger}/{writer}Remove a log writer
GET/manager/processesList all tracked goroutines/processes
POST/mail/sendSend email via the configured mailer
POST/restore_repoRestore a repository from a bundle
POST/actions/generate_actions_runner_tokenGenerate a runner registration token
Various/repo/{owner}/{repo}/...LFS object serving (internal Git LFS smart protocol)

CLI (gitea binary)#

Framework#

github.com/urfave/cli/v3 (not Cobra). The main app is constructed in cmd/main.go:NewMainApp().

Global flags#

  • -w / --work-path — sets AppWorkPath
  • -c / --config — path to app.ini (default: {WorkPath}/custom/conf/app.ini)
  • -C / --custom-path — sets CustomPath

All three flags are inherited by subcommands via a Before hook that calls prepareWorkPathAndCustomConf.

Command structure#

Subcommands requiring config (inherit global flags, call InitWebInstalled or subset):

CommandPurpose
webStart the HTTP/HTTPS/FCGI web server
servSSH git service dispatcher (called by authorized_keys command=)
hookGit hook bridge (pre-receive, post-receive, update, proc-receive)
keysOutput authorized_keys entry for a given SSH public key
dumpExport instance data to a zip/tar archive
adminAdministrative operations (see sub-tree below)
migrateRun pending database migrations
doctorHealth checks and repair operations
managerRuntime management via private API (shutdown, restart, flush-queues, etc.)
embeddedManage embedded static assets (extract, list)
migrate-storageMigrate attachments/LFS between storage backends
dump-repoDump a repository to a bundle
restore-repoRestore a repository from a bundle
actionsActions runner management (generate tokens)

admin sub-tree:

SubcommandPurpose
admin user createCreate a user account
admin user listList all users
admin user deleteDelete a user
admin user change-passwordChange a user’s password
admin user generate-access-tokenCreate an API token for a user
admin user must-change-passwordForce password reset on next login
admin auth add-ldapAdd LDAP authentication source
admin auth update-ldapModify an LDAP source
admin auth add-oauthAdd OAuth2 authentication source
admin auth update-oauthModify an OAuth2 source
admin auth add-smtpAdd SMTP authentication source
admin auth listList all auth sources
admin auth deleteDelete an auth source
admin regenerate hooksRegenerate git hooks for all repos
admin regenerate keysRewrite authorized_keys file

Standalone subcommands (no config required):

CommandPurpose
configShow/edit config values
certGenerate TLS certificates
generateGenerate secret strings (SECRET_KEY, INTERNAL_TOKEN, etc.)
docsOutput documentation

Flag patterns#

  • Global flags propagate via Before hook to all subcommands in subCmdWithConfig
  • urfave/cli v3 uses typed flag structs (&cli.StringFlag{Name: ..., Aliases: ..., TakesFile: ...})
  • Environment variable binding: CLI does not bind env vars directly; modules/setting reads GITEA_CUSTOM, GITEA__SECTION__KEY patterns separately

Notable API design decisions#

  1. Five isolated API surfaces, one binary. The same gitea binary simultaneously serves the web UI, the public REST API, 20+ native package protocols, the Connect-RPC Actions protocol, and the internal git-hook IPC. Each surface is chi-mounted at a distinct prefix and has its own middleware stack and context type.

  2. Scope-based token authorization. The REST API uses a two-dimensional permission model: {category}:{read|write} (e.g., repository:write, issue:read). This is enforced by tokenRequiresScopes(...) middleware — HTTP method determines read vs. write, route group determines category. Public-only tokens are also supported (scope.PublicOnly()), preventing access to private repos with a single token scope restriction.

  3. Private IPC via HTTP, not pipes. The git hook subprocess communicates with the running web server over an HTTP Unix socket rather than stdout/environment variables. This enables typed, version-safe data exchange and lets the server enforce branch protection rules synchronously during the git push, before git accepts the pack.

  4. Package manager protocol fidelity. Each package registry sub-router replicates the exact protocol of the target ecosystem (e.g., OCI spec for containers, Cargo sparse index for Rust, PyPI Simple API for Python). This means package managers like pip, cargo, npm, helm, and docker can be pointed at Gitea without any client modification.

  5. Connect-RPC (not REST) for the runner protocol. The Gitea Actions runner uses connectrpc.com/connect (protobuf-over-HTTP) rather than a REST API. This means strong schema evolution guarantees and efficient binary encoding for the tight runner ↔ server loop.

  6. Swagger generated from godoc comments. Route handler functions carry // swagger:operation godoc annotations. The swagger spec is generated at build time with swaggo and served as a static file. No runtime code generation. This keeps the spec in sync with the code via CI.