Prerequisites
Before running docsfy, make sure your environment has Python, uv, git, one supported AI CLI with credentials, and a valid ADMIN_KEY.
Python and uv
docsfy requires Python 3.12+.
[project]
name = "docsfy"
version = "0.1.0"
description = "AI-powered documentation generator - generates polished static HTML docs from GitHub repos"
requires-python = ">=3.12"
dependencies = [
"ai-cli-runner",
"fastapi",
"uvicorn",
"pydantic-settings",
"python-simple-logger",
"aiosqlite",
"jinja2",
"markdown",
"pygments",
"python-multipart>=0.0.22",
]
The lock file enforces the same minimum Python version:
version = 1
revision = 3
requires-python = ">=3.12"
The project workflow uses uv for install, running, and tests:
RUN uv sync --frozen --no-dev
ENTRYPOINT ["uv", "run", "--no-sync", "uvicorn", "docsfy.main:app", "--host", "0.0.0.0", "--port", "8000"]
[env.unittests]
deps = ["uv"]
commands = [["uv", "run", "--extra", "dev", "pytest", "-n", "auto", "tests"]]
Note:
Settingsloads environment variables from.env, so your local config must be present there.
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
)
git is required
docsfy uses git to clone repositories and resolve commit SHAs:
result = subprocess.run(
["git", "clone", "--depth", "1", "--", repo_url, str(repo_path)],
capture_output=True,
text=True,
timeout=300,
)
if result.returncode != 0:
msg = f"Clone failed: {result.stderr or result.stdout}"
raise RuntimeError(msg)
sha_result = subprocess.run(
["git", "rev-parse", "HEAD"],
cwd=repo_path,
capture_output=True,
text=True,
)
Local-path generation also requires a real git repo (.git must exist):
if not (repo_p / ".git").exists():
raise HTTPException(
status_code=400,
detail=f"Not a git repository (no .git directory): '{gen_request.repo_path}'",
)
Supported AI providers, CLIs, and credentials
Supported providers are fixed to claude, gemini, and cursor:
ai_provider: Literal["claude", "gemini", "cursor"] | None = None
assert VALID_AI_PROVIDERS == frozenset({"claude", "gemini", "cursor"})
AI_CLI_TIMEOUT must be greater than zero:
ai_cli_timeout: int = Field(default=60, gt=0)
The container image installs all three AI CLIs:
# Install Claude Code CLI (installs to ~/.local/bin)
RUN /bin/bash -o pipefail -c "curl -fsSL https://claude.ai/install.sh | bash"
# Install Cursor Agent CLI (installs to ~/.local/bin)
RUN /bin/bash -o pipefail -c "curl -fsSL https://cursor.com/install | bash"
# Configure npm for non-root global installs and install Gemini CLI
RUN mkdir -p /home/appuser/.npm-global \
&& npm config set prefix '/home/appuser/.npm-global' \
&& npm install -g @google/gemini-cli
Credential/config variables expected in .env:
AI_PROVIDER=claude
AI_MODEL=claude-opus-4-6[1m]
AI_CLI_TIMEOUT=60
# Claude - Option 1: API Key
# ANTHROPIC_API_KEY=
# Claude - Option 2: Vertex AI
# CLAUDE_CODE_USE_VERTEX=1
# CLOUD_ML_REGION=
# ANTHROPIC_VERTEX_PROJECT_ID=
# Gemini
# GEMINI_API_KEY=
# Cursor
# CURSOR_API_KEY=
The app checks provider CLI readiness before generation:
cli_flags = ["--trust"] if ai_provider == "cursor" else None
available, msg = await check_ai_cli_available(
ai_provider, ai_model, cli_flags=cli_flags
)
if not available:
await update_project_status(
project_name,
ai_provider,
ai_model,
status="error",
owner=owner,
error_message=msg,
)
return
Tip: You only need credentials for the provider selected in
AI_PROVIDER, but that provider’s CLI must be installed and authenticated.
Mandatory ADMIN_KEY setup
ADMIN_KEY is required and must be at least 16 characters.
# REQUIRED - Admin key for user management (minimum 16 characters)
ADMIN_KEY=your-secure-admin-key-here-min-16-chars
Startup fails fast if ADMIN_KEY is missing or too short:
settings = get_settings()
if not settings.admin_key:
logger.error("ADMIN_KEY environment variable is required")
raise SystemExit(1)
if len(settings.admin_key) < 16:
logger.error("ADMIN_KEY must be at least 16 characters long")
raise SystemExit(1)
ADMIN_KEY is also used as the admin login secret:
if username == "admin" and api_key == settings.admin_key:
is_admin = True
authenticated = True
And as the HMAC secret for API key hashing:
secret = hmac_secret or os.getenv("ADMIN_KEY", "")
if not secret:
msg = "ADMIN_KEY environment variable is required for key hashing"
raise RuntimeError(msg)
Warning: Rotating
ADMIN_KEYinvalidates existing API key hashes, andADMIN_KEYusers cannot rotate this through the API ("ADMIN_KEY users cannot rotate keys. Change the ADMIN_KEY env var instead.").
Minimal .env baseline
ADMIN_KEY=<your-min-16-char-secret>
AI_PROVIDER=claude
AI_MODEL=claude-opus-4-6[1m]
AI_CLI_TIMEOUT=60
LOG_LEVEL=INFO
For local HTTP (non-HTTPS) development, this optional setting is available:
# Set to false for local HTTP development
# SECURE_COOKIES=false
docker-compose also expects .env:
services:
docsfy:
env_file: .env