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.
Reframe the README around two transports and add a local stdio quickstart with
uvx/pip and Claude Desktop / Claude Code wiring. New docs: local-quickstart.md
and packaging.md (uv build/publish). Document resource-type-aware authorization
and classified gitea_request in security.md; stdio env vars + audit-log
fallback in configuration.md; local install in deployment.md; core+adapters in
architecture.md. Add the missing root AGENTS.md contract, update CLAUDE.md with
the core/adapter layout, fail-closed invariants, and the branching flow
(HEAD -> feature -> dev -> main). Update roadmap/todo and .env.example.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds docs/raw-api.md (two-layer policy, sensitive denylist, env vars, write-mode
warning), links it from index and api-reference, documents RAW_API_ENABLED /
RAW_API_ALLOW_SENSITIVE in .env.example, and adds commented virtual-tool-name
deny examples to policy.yaml.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two related issues made the connected MCP server return a bare "Internal
server error" for tools that need real Gitea API access (e.g.
list_repositories), while public-repo-by-path reads worked:
1. Gitea OIDC access tokens only carry openid/profile/email and cannot call
the repository REST API, so pure-OAuth mode fails for most tools. A service
PAT (GITEA_TOKEN) is required in practice; per-user permission is still
enforced before each call, so this does not weaken authorization.
2. The tool handlers caught GiteaError broadly and re-raised it as RuntimeError.
Because GiteaAuthenticationError/GiteaAuthorizationError subclass GiteaError,
a clean 401/403 was masked as a generic internal error and the server's
re-authorization guidance never fired.
Changes:
- read_tools.py / repository.py / write_tools.py: re-raise the auth/authz
subclasses before the broad GiteaError catch so server.py returns actionable
guidance instead of a generic 500.
- .env.example + README.md: document GITEA_TOKEN as a least-privilege bot PAT,
explain why it's needed and that OAuth remains authoritative, and note that
list_repositories is intentionally unavailable in service-PAT mode.
- tests: assert tool handlers propagate auth errors unwrapped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The DCR client registry created its storage directory eagerly in __init__,
and DCR_STORAGE_PATH defaulted to /var/lib/aegis-mcp — a path that is neither
created in the image nor mounted as a writable volume. Under the hardened
read-only docker-compose, every /oauth/authorize, /oauth/token, and /register
call hit `mkdir('/var/lib/aegis-mcp')` on a read-only filesystem, raising an
unhandled OSError and returning a bare "Internal Server Error" during login.
- oauth_flow.py: defer the storage-dir mkdir from __init__ to _persist (the
only write path). authorize/token only read the registry, so they no longer
require a writable filesystem and stop 500-ing.
- docker/Dockerfile: create and chown /var/lib/aegis-mcp.
- docker-compose.yml + docker/docker-compose.yml: add a persistent
aegis-mcp-data volume mounted at /var/lib/aegis-mcp so DCR registrations
survive restarts.
- .env.example: document DCR_STORAGE_PATH and set PUBLIC_BASE_URL to the real
MCP host.
- README.md: spell out exact values (Gitea host, MCP host, callback URL, MCP
URL) and add a "required writable volumes" section explaining the cause of
the login 500.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce a GiteaOAuthValidator for JWT and userinfo validation and
fallbacks, add /oauth/token proxy, and thread per-user tokens through
the
request context and automation paths. Update config and .env.example for
OAuth-first mode, add OpenAPI, extensive unit/integration tests,
GitHub/Gitea CI workflows, docs, and lint/test enforcement (>=80% cov).