The audit logger is opened at startup (exit 2 on failure) and obtained before every call, which is refused with audit_unavailable otherwise. Audit records only take validated path and sudo values and use the request id as correlation id. The rate limiter evicts idle keys, caps tracked keys, audits the first excess per key and window and uses the settings passed to the app. FORWARDED_ALLOW_IPS is now a setting, and startup warns about a missing host allow-list and unavailable OAuth.
105 lines
6.8 KiB
Markdown
105 lines
6.8 KiB
Markdown
# Configuration
|
|
|
|
All settings are environment variables. They are read case-insensitively from the process
|
|
environment and from a `.env` file in the working directory (the environment wins). Unknown
|
|
variables are ignored. A blank value (`KEY=` in `.env`) counts as unset for the optional
|
|
secrets, URLs and paths. List values accept commas or whitespace as separators.
|
|
|
|
`.env.example` is the annotated template. `uv run aegis-gitea-mcp setup` fills one in for you
|
|
(see [getting-started.md](getting-started.md)). Secret settings never appear in logs, errors or
|
|
`repr` output.
|
|
|
|
Requirements that depend on how the server is started are checked at start, not on import:
|
|
`GITEA_TOKEN` for stdio, authentication for a non-loopback HTTP bind, complete OAuth settings
|
|
when OAuth is on.
|
|
|
|
## Gitea
|
|
|
|
| Variable | Default | Rules |
|
|
|---|---|---|
|
|
| `GITEA_URL` | required | `http` or `https`, must have a host, no credentials, query or fragment. A trailing `/api/v1` is removed. |
|
|
| `GITEA_TOKEN` | unset | Token for stdio and for admin-token mode on HTTP. Its scopes bound what the server can do. Required for stdio (exit code 2 without it). |
|
|
|
|
## Transport
|
|
|
|
| Variable | Default | Rules |
|
|
|---|---|---|
|
|
| `MCP_TRANSPORT` | `stdio` | `stdio` or `http`. `serve --transport` overrides it. |
|
|
| `MCP_HOST` | `127.0.0.1` | Bind address for HTTP. A non-loopback value refuses to start unless HTTP auth is configured. |
|
|
| `MCP_PORT` | `8080` | 1 to 65535. |
|
|
| `MCP_PATH` | `/mcp` | Starts with a single `/`, no query, spaces or trailing slash, not just `/`. |
|
|
| `MCP_BEARER_TOKEN` | unset | Static bearer token for HTTP (admin identity). At least 32 characters. Generate with `openssl rand -hex 32`. |
|
|
| `PUBLIC_BASE_URL` | unset | Public URL behind a reverse proxy. Same URL rules as `GITEA_URL`, and `https` unless the host is loopback. Required for OAuth. |
|
|
| `ALLOWED_HOSTS` | empty | Extra `Host` header values accepted behind a proxy. The loopback names with the port and the host of `PUBLIC_BASE_URL` are always allowed. Other `Host` values are rejected (DNS-rebinding protection). A non-loopback `MCP_HOST` with neither `PUBLIC_BASE_URL` nor `ALLOWED_HOSTS` logs a startup warning on stderr, because remote clients then get 421. |
|
|
| `FORWARDED_ALLOW_IPS` | `127.0.0.1` | Comma list of IP addresses or networks (or `*`) whose `X-Forwarded-For` and `X-Forwarded-Proto` headers are trusted. Set it to the address of the reverse proxy when it is not on the same host, for example a Docker network. Otherwise the per-IP rate limit sees the proxy address for every client. `*` trusts every peer and logs a warning: only use it when the port is reachable solely from the proxy, since clients could otherwise spoof their IP. |
|
|
|
|
## Per-user OAuth (HTTP only)
|
|
|
|
| Variable | Default | Rules |
|
|
|---|---|---|
|
|
| `OAUTH_ENABLED` | `false` | When true, the four settings below are required, or the server refuses to start (exit code 2, with the list of what is missing). |
|
|
| `GITEA_OAUTH_CLIENT_ID` | unset | Client id of the Gitea OAuth application (created by `setup`). |
|
|
| `GITEA_OAUTH_CLIENT_SECRET` | unset | Its client secret. |
|
|
| `TOKEN_ENCRYPTION_KEY` | unset | At least 32 characters. Encrypts stored user tokens. Losing it invalidates every stored login. |
|
|
| `OAUTH_SCOPES` | all `write:<category>` scopes | Granular Gitea scopes only (`read:` or `write:` plus activitypub, admin, issue, misc, notification, organization, package, repository, user). Anything else is rejected, because Gitea grants full access for unknown scope strings. Must not be empty. |
|
|
|
|
`PUBLIC_BASE_URL` is also required for OAuth. The Gitea OAuth application's redirect URI is
|
|
`<PUBLIC_BASE_URL>/oauth/callback`.
|
|
|
|
## State
|
|
|
|
| Variable | Default | Rules |
|
|
|---|---|---|
|
|
| `DATA_DIR` | `~/.local/state/aegis-gitea-mcp` (`/var/lib/aegis-mcp` in the image) | `~` is expanded. Holds the token store (`oauth-tokens.json`, mode 0600) and the audit log. |
|
|
| `AUDIT_LOG_PATH` | `DATA_DIR/audit.log` | Path of the hash-chained audit log. See [audit-logging.md](audit-logging.md). The server opens it for append at startup and exits with code 2 and a message on stderr when it cannot. |
|
|
|
|
## Tool exposure
|
|
|
|
Everything is enabled by default. These settings only narrow it. A filtered tool is neither
|
|
listed nor callable.
|
|
|
|
| Variable | Default | Rules |
|
|
|---|---|---|
|
|
| `READ_ONLY` | `false` | Expose only read-only (GET) operations. `gitea_api_request` stays listed but accepts only GET. |
|
|
| `TOOLSETS` | `all` | Comma separated domains: `admin`, `repo`, `issue`, `pull`, `release`, `wiki`, `org`, `team`, `user`, `actions`, `hook`, `package`, `notification`, `misc`, or `all`. Unknown domains are rejected. `gitea_api_request` belongs to `misc`. |
|
|
| `DISABLED_TOOLS` | empty | Exact tool names to hide, lowercased on load. An unknown name is a startup error that lists the bad names. |
|
|
|
|
## Limits and safety
|
|
|
|
| Variable | Default | Rules |
|
|
|---|---|---|
|
|
| `REQUEST_TIMEOUT_SECONDS` | `30` | 1 to 600. Timeout of each Gitea request. |
|
|
| `MAX_TOOL_RESPONSE_ITEMS` | `200` | At least 1. Longer lists are cut and reported in `truncated`. |
|
|
| `MAX_TOOL_RESPONSE_CHARS` | `60000` | At least 1. Upper bound of one serialized result. |
|
|
| `MAX_BINARY_BYTES` | `5242880` | At least 1. Larger binary bodies fail with `payload_too_large`. |
|
|
| `SECRET_DETECTION_MODE` | `mask` | `off`, `mask` or `block`. See below. |
|
|
| `LOG_LEVEL` | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR` or `CRITICAL`. Logs go to stderr. |
|
|
| `EXPOSE_ERROR_DETAILS` | `false` | When true, the exception class name is added to internal and transport errors. Never a stack trace. Do not enable in production. |
|
|
| `RATE_LIMIT_PER_MINUTE` | `120` | At least 1. Per client on the HTTP MCP endpoint, keyed by a hash of the Authorization header, else the client IP. Excess requests get 429. Only the first rejected request per client and minute is written to the audit log. Tracking is in memory and bounded (idle clients are forgotten). |
|
|
|
|
Secret detection: `mask` replaces secret-like strings in results with a placeholder, `block`
|
|
withholds a whole result that contains one (`secret_blocked`), `off` disables pattern matching.
|
|
The server's own tokens (Gitea token, bearer token, OAuth client secret, the calling user's
|
|
token) and secret values you submit in a request body are always redacted by exact match. The
|
|
tools that exist to return a new credential (create token, runner registration token, OAuth
|
|
application, deploy token) are exempt from pattern masking so the credential stays usable.
|
|
|
|
## Profiles
|
|
|
|
Read-only assistant for issues and code:
|
|
|
|
```bash
|
|
READ_ONLY=true
|
|
TOOLSETS=repo,issue,pull,release,wiki
|
|
```
|
|
|
|
Everything except instance administration and credentials:
|
|
|
|
```bash
|
|
TOOLSETS=repo,issue,pull,release,wiki,org,team,hook,actions,package,notification,misc
|
|
DISABLED_TOOLS=gitea_user_create_token
|
|
```
|
|
|
|
(`gitea_user_create_token` must be a registered name, see [tools.md](tools.md); the server
|
|
refuses to start if a name in `DISABLED_TOOLS` does not exist.)
|