Quickstart
A minimal self-host boot path for maintainers: start the service, verify readiness, and keep the first rollout safe.
1. Copy the sample env
.env.selfhost.example is the short path: required secrets plus a conservative first-boot config, with nothing about the Cloudflare Worker deploy. Copy it and fill in the placeholders — keep your real .env out of git and prefer mounted secret files for multiline values like the GitHub App private key.
cp .env.selfhost.example .env
# edit .envbashGITHUB_WEBHOOK_SECRET, GITTENSORY_API_TOKEN, GITTENSORY_MCP_TOKEN, INTERNAL_JOB_TOKEN, SELFHOST_SETUP_TOKEN) ship commented out on purpose. Generate a distinct random value for each one (e.g. openssl rand -hex 32) — never reuse the same string across more than one of them. The app refuses to boot if any of these is left at a known-placeholder or too-short value..env.selfhost.example already ships a conservative starting config — dry-run mode, a small repo allowlist, unified comments, safety, and grounding, with AI, RAG, and REES left off. Switch to live only after webhook delivery, logs, and review output match expectations. For every optional env var (observability, backup, additional AI providers) see .env.example's self-host section or the generated reference table.2. Choose your AI provider (optional)
Skip this step for a fully deterministic review (no AI). Otherwise uncomment ONE of the three blocks below in .env.selfhost.example — they're mutually exclusive, each sets its own AI_PROVIDER. The self-host image bundles both CLIs by default; credentials and provider choice are runtime-only.
AI_PROVIDER=claude-code
CLAUDE_CODE_OAUTH_TOKEN= # from `claude setup-token`AI_PROVIDER=codex
GITTENSORY_ENABLE_UNSAFE_CODEX_REVIEWER=1 # required opt-in; see Callout belowAI_PROVIDER=claude-code,codex
AI_COMBINE=synthesis
CLAUDE_CODE_OAUTH_TOKEN=
GITTENSORY_ENABLE_UNSAFE_CODEX_REVIEWER=1auth.json on the same filesystem that prompt-influenced reviews can read, so it requires explicit opt-in (GITTENSORY_ENABLE_UNSAFE_CODEX_REVIEWER=1) and a mounted /data/codex auth volume. Claude Code has no equivalent restriction. See AI providers for the full reference.3. Boot the stack
Recommended: pull the published image. No local build, no Node toolchain — the script pulls, restarts, and waits for the health check to pass.
./scripts/deploy-selfhost-image.sh
curl http://localhost:8787/health
curl http://localhost:8787/readybashPin a specific release instead of :latest, or point at your own registry:
./scripts/deploy-selfhost-image.sh ghcr.io/jsonbored/gittensory-selfhost:orb-v0.1.0
GITTENSORY_IMAGE=ghcr.io/jsonbored/gittensory-selfhost@sha256:... ./scripts/deploy-selfhost-image.shbashdocker compose up -d --build builds the gittensory service from the checkout instead of pulling a published image. Everything else in this quickstart (env, health checks, GitHub App) is identical either way. Two build-args trim the image: --build-arg INSTALL_AI_CLIS=false skips the Claude Code/Codex CLIs (default true), and --build-arg INSTALL_VISUAL_REVIEW=true adds puppeteer-core for visual capture (default false — needs a BROWSER_WS_ENDPOINT at runtime).- /health
- Liveness. It confirms the HTTP process is up.
- /ready
- Readiness. It returns 200 only after database access, migrations, and every configured backend (Redis, GitHub App auth, the AI provider, and any of Qdrant/Postgres you've enabled) are healthy.
- /metrics
- Prometheus metrics for queue, jobs, HTTP traffic, uptime, and AI usage.
4. Install or connect the GitHub App
Point your App webhook to https://your-host.example/v1/github/webhook, set the same webhook secret in GITHUB_WEBHOOK_SECRET, install the App on one test repo, and open a small PR. The direct App and Orb modes are covered in GitHub App and Orb.
ADMIN_GITHUB_LOGINS to a comma/whitespace-separated list of GitHub logins before signing in to the control panel — it's the only allowlist for the operator role (operator dashboard, drift status). No login is authorized as operator without it.5. Watch the first review
Look for these logs during boot and the first webhook:
selfhost_listening
selfhost_migrations_applied
selfhost_ai_provider # only when AI_PROVIDER is set
selfhost_job_dead # investigate immediately if present
review_context_fetch_failed # REES/RAG/grounding context failureA cold first boot on SQLite commonly logs a one-time selfhost_migrations_applied burst and a brief Redis connection retry while the sidecar finishes starting — both are expected and stop once the stack is warm. Anything else that looks wrong, or a /ready that stays unhealthy past a couple minutes, is covered in Troubleshooting.
After the deterministic path is stable, continue with Configuration and then layer in AI, REES, or RAG deliberately.
Defaults at a glance
Nothing below needs a flag to start; everything past the first row needs an explicit --profile (combine freely) or an explicit AI_PROVIDER.
ENABLED BY DEFAULT (no flags needed)
gittensory app + Redis SQLite database, dry-run-friendly, Orb telemetry (see Callout below)
RECOMMENDED FOR PRODUCTION (opt-in)
--profile postgres shared/multi-instance database (pgvector-capable)
--profile pgbouncer connection pooling in front of Postgres
--profile caddy automatic HTTPS via Let's Encrypt
--profile litestream continuous SQLite backup to S3-compatible storage
--profile observability Prometheus + Alertmanager + Loki + Grafana
OPT-IN, NOT REQUIRED FOR A TRIAL INSTANCE
--profile qdrant dedicated RAG vector store (else sqlite-vec/pgvector)
--profile ollama local model for AI review or embeddings
--profile tailscale private network sidecar
--profile runners self-hosted GitHub Actions runner
--profile backup scheduled backup + backup-exporter jobs
AI_PROVIDER=... off by default; reviews are deterministic-only until settextORB_AIR_GAP=true for an instance that sends nothing.