Gitea — API Surface#
API types#
Gitea exposes five distinct API surfaces to different classes of callers:
- REST API (
/api/v1) — public Swagger-documented REST API for all external integrations - Package Manager APIs (
/api/packages,/v2) — 20+ protocol-native package registry endpoints - Actions Runner API (
/api/actions) — Connect-RPC (protobuf-over-HTTP) for CI runner communication - Private/Internal API (
/api/internal) — HTTP IPC between the git hook subprocess and the web server - 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/v5via a custommodules/web.Routerwrapper - Route registration: All routes are registered explicitly in
routers/api/v1/api.go:Routes(), a ~900-line function - Documentation: Swaggo-style godoc
// swagger:operationcomments on handler functions; spec served at/api/swagger(when enabled)
Middleware chain#
Applied in order via m.BeforeRouting / m.AfterRouting:
securityHeaders()— setsX-Frame-Options,X-Content-Type-Options,X-XSS-Protection, etc.- CORS handler (
github.com/go-chi/cors) — optional, configurable allowed origins/methods context.APIContexter()— createsAPIContext(extendsBasewithAPIOrganization,APIRepo, etc.), sets up request data storecheckDeprecatedAuthMethods— warns via header when legacy?token=or?access_token=query params are usedapiAuth(buildAuthGroup())— resolves the caller identity; auth methods tried in order: OAuth2 → HTTPSign → Basic (→ ReverseProxy if enabled)verifyAuthWithOptions— enforcesRequireSignInViewStrictif configured- Per-route:
tokenRequiresScopes(...)— enforces fine-grained scope on the API token (read/write per category) - 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 token —
Authorization: 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-Useror configured header - Session cookie — inherited from web context (for same-origin browser requests)
- Sudo — site admins can impersonate users via
?sudo=<username>orSudo: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#
| Group | Prefix | Scope | Description |
|---|---|---|---|
| Misc | /api/v1/ | none | version, swagger, signing keys, gitignore/license/label templates, markdown rendering, settings |
| Notifications | /api/v1/notifications | notification | list, read, thread management |
| Users | /api/v1/users, /api/v1/user | user | profile, SSH/GPG keys, followers, starred repos, OAuth2 apps, access tokens, hooks, blocks |
| Repositories | /api/v1/repos | repository | full repo lifecycle, branches, tags, commits, raw content, archive, forks, LFS |
| Code review | /api/v1/repos/{owner}/{repo}/pulls | repository | PRs, reviews, merge, auto-merge, status checks |
| Issues | /api/v1/repos/{owner}/{repo}/issues | issue | issues, comments, labels, milestones, reactions, time tracking, dependencies |
| Releases | /api/v1/repos/{owner}/{repo}/releases | repository | releases, tags, assets |
| Webhooks | /api/v1/repos/{owner}/{repo}/hooks | admin | per-repo and global webhook CRUD |
| Gitea Actions | /api/v1/repos/{owner}/{repo}/actions | repository | workflows, runs, jobs, artifacts, secrets, variables, runners |
| Organizations | /api/v1/orgs, /api/v1/org/{org} | organization | org CRUD, members, teams, hooks, labels, activity feeds |
| Packages | /api/v1/packages/{username} | package | Gitea package management meta-API (list, get, delete — not protocol-specific) |
| Admin | /api/v1/admin | admin | cron tasks, users, emails, repos, hooks, Actions runners, badges |
| ActivityPub | /api/v1/activitypub | activitypub | federation endpoints (currently NotImplemented stubs) |
Notable endpoints:
GET /api/v1/repos/{owner}/{repo}/contents/{filepath}— file contentsPOST /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 strategyPOST /api/v1/repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches— trigger workflowGET /api/v1/repos/{owner}/{repo}/git/trees/{sha}— git object treeGET /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()andContainerRoutes()entry points inrouters/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+)#
| Format | Protocol base | Notable |
|---|---|---|
| Alpine APK | Custom APK index protocol | .tar.gz index, APKINDEX |
| Arch Linux | pacman repository format | — |
| Cargo (Rust) | Crates.io sparse index API | yank/unyank support |
| Chef Cookbooks | Supermarket API v1 | cookbook universe endpoint |
| Composer (PHP) | Packagist API | packages.json, p2/ metadata |
| Conan (C++) | Conan v1 + v2 API | recipe & package snapshots |
| Conda | conda channel format | — |
| Container (OCI/Docker) | OCI Distribution Spec 1.0 | full OCI push/pull/delete; mounted at /v2/ |
| CRAN (R) | CRAN repository format | — |
| Debian | APT repository format | .deb upload, Packages index |
| Generic | Simple file store | raw file upload/download |
| Go Module Proxy | GOPROXY protocol | @v/list, @v/{version}.info/.mod/.zip |
| Helm | Helm repository format | index.yaml, chart download |
| Maven / Gradle | Maven repository protocol | metadata XML, artifact download |
| npm | npm registry API | package.json metadata, tarballs, dist-tags |
| NuGet | NuGet v2 + v3 API | OData feed and JSON endpoints |
| Pub (Dart) | pub.dev API | advisories, version listing |
| PyPI | PyPI simple API + JSON API | simple/ index, file downloads |
| RPM | YUM/DNF repository | repomd.xml, RPM spec |
| RubyGems | RubyGems API | .gemspec + .gem download |
| Swift (SPM) | SPM package registry API | Package.swift, .zip download |
| Vagrant | Vagrant Cloud API | box 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.Routerwith OCI-specific error format (JSONerrorsarray)- Auth:
WWW-Authenticate: Bearerchallenge 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 inartifact.pb.go - Services registered:
PingService— health check for runner ↔ server connectivityRunnerService— job lifecycle: register, fetch job, update job status, submit logs
- Framework:
connectrpc.com/connect(formerlybufbuild/connect-go) - Route pattern: All POSTs to
/api/actions/{service}/{method}viam.Post(path+"*", handler.ServeHTTP)— Connect uses HTTP/1.1 POST withContent-Type: application/connect+proto - Artifact handling: Additional handlers in
artifacts.go/artifactsv4.gofor 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)#
| Method | Path | Purpose |
|---|---|---|
| GET | /dummy | Health probe |
| POST | /ssh/authorized_keys | Look up public key by content for authorized_keys generation |
| POST | /ssh/{id}/update/{repoid} | Update key-to-repo association |
| POST | /ssh/log | Log 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/shutdown | Graceful shutdown |
| POST | /manager/restart | Graceful restart (SIGUSR1 equivalent) |
| POST | /manager/reload-templates | Hot-reload HTML templates |
| POST | /manager/flush-queues | Drain all background queues |
| POST | /manager/pause-logging | Pause log output |
| POST | /manager/resume-logging | Resume log output |
| POST | /manager/release-and-reopen-logging | Log rotation |
| POST | /manager/set-log-sql | Toggle SQL query logging at runtime |
| POST | /manager/add-logger | Add a log writer at runtime |
| POST | /manager/remove-logger/{logger}/{writer} | Remove a log writer |
| GET | /manager/processes | List all tracked goroutines/processes |
| POST | /mail/send | Send email via the configured mailer |
| POST | /restore_repo | Restore a repository from a bundle |
| POST | /actions/generate_actions_runner_token | Generate 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— setsAppWorkPath-c / --config— path toapp.ini(default:{WorkPath}/custom/conf/app.ini)-C / --custom-path— setsCustomPath
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):
| Command | Purpose |
|---|---|
web | Start the HTTP/HTTPS/FCGI web server |
serv | SSH git service dispatcher (called by authorized_keys command=) |
hook | Git hook bridge (pre-receive, post-receive, update, proc-receive) |
keys | Output authorized_keys entry for a given SSH public key |
dump | Export instance data to a zip/tar archive |
admin | Administrative operations (see sub-tree below) |
migrate | Run pending database migrations |
doctor | Health checks and repair operations |
manager | Runtime management via private API (shutdown, restart, flush-queues, etc.) |
embedded | Manage embedded static assets (extract, list) |
migrate-storage | Migrate attachments/LFS between storage backends |
dump-repo | Dump a repository to a bundle |
restore-repo | Restore a repository from a bundle |
actions | Actions runner management (generate tokens) |
admin sub-tree:
| Subcommand | Purpose |
|---|---|
admin user create | Create a user account |
admin user list | List all users |
admin user delete | Delete a user |
admin user change-password | Change a user’s password |
admin user generate-access-token | Create an API token for a user |
admin user must-change-password | Force password reset on next login |
admin auth add-ldap | Add LDAP authentication source |
admin auth update-ldap | Modify an LDAP source |
admin auth add-oauth | Add OAuth2 authentication source |
admin auth update-oauth | Modify an OAuth2 source |
admin auth add-smtp | Add SMTP authentication source |
admin auth list | List all auth sources |
admin auth delete | Delete an auth source |
admin regenerate hooks | Regenerate git hooks for all repos |
admin regenerate keys | Rewrite authorized_keys file |
Standalone subcommands (no config required):
| Command | Purpose |
|---|---|
config | Show/edit config values |
cert | Generate TLS certificates |
generate | Generate secret strings (SECRET_KEY, INTERNAL_TOKEN, etc.) |
docs | Output documentation |
Flag patterns#
- Global flags propagate via
Beforehook to all subcommands insubCmdWithConfig - urfave/cli v3 uses typed flag structs (
&cli.StringFlag{Name: ..., Aliases: ..., TakesFile: ...}) - Environment variable binding: CLI does not bind env vars directly;
modules/settingreadsGITEA_CUSTOM,GITEA__SECTION__KEYpatterns separately
Notable API design decisions#
Five isolated API surfaces, one binary. The same
giteabinary 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.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 bytokenRequiresScopes(...)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.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.
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, anddockercan be pointed at Gitea without any client modification.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.Swagger generated from godoc comments. Route handler functions carry
// swagger:operationgodoc annotations. The swagger spec is generated at build time withswaggoand served as a static file. No runtime code generation. This keeps the spec in sync with the code via CI.