From 16454e4135005e2f7515c2e7103f5a32799ebf5b Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Thu, 2 Jul 2026 17:13:09 -0700 Subject: [PATCH 1/2] docs(selfhost): make beta onboarding image-first (#1938) The documented self-host path was source-build-first (docker compose up --build) even though a published-image deploy script (scripts/deploy-selfhost-image.sh) already existed and was never surfaced in the docs. The sample env also mixed Cloudflare Worker vars with self-host-only vars, with no focused starter file, no AI-provider quick-pick, and no enabled/opt-in defaults table. - Quickstart now leads with ./scripts/deploy-selfhost-image.sh (pull the published image, no local build/Node toolchain) as the recommended boot path, with `docker compose up --build` demoted to an explicit "building from source" alternative for contributors. - New .env.selfhost.example: the short path -- required secrets plus the existing conservative first-boot config, with nothing about the Worker deploy. .env.example remains the exhaustive reference for every optional service. - New "Choose your AI provider" step with copy-paste Claude-Code-only / Codex-only / both blocks, plus the existing Codex fail-closed safety callout surfaced up front instead of only in the env file comments. - New "Defaults at a glance" table: what's on by default vs. what needs an explicit --profile or AI_PROVIDER, plus the Orb telemetry always-on notice. - Quickstart's log-events step now explains which first-boot log noise is expected (migrations burst, brief Redis retry) vs. worth investigating. - docker-compose.yml's header comment documents both boot paths instead of only the build-from-source one. --- .env.selfhost.example | 87 ++++++++++++++ .gitignore | 1 + .../docs.self-hosting-configuration.tsx | 6 + .../routes/docs.self-hosting-quickstart.tsx | 108 +++++++++++++++--- docker-compose.yml | 14 ++- 5 files changed, 195 insertions(+), 21 deletions(-) create mode 100644 .env.selfhost.example diff --git a/.env.selfhost.example b/.env.selfhost.example new file mode 100644 index 0000000000..d0d0b8d3fe --- /dev/null +++ b/.env.selfhost.example @@ -0,0 +1,87 @@ +# Gittensory self-host — focused starter env (#1938) +# +# This is the SHORT path: the required secrets plus a conservative first-boot config, with nothing +# about the Cloudflare Worker deploy. Copy it to `.env` (gitignored) and fill in the placeholders. +# +# cp .env.selfhost.example .env +# +# For the EXHAUSTIVE list of every self-host env var (optional services, observability, backup, +# every AI provider) see `.env.example`'s "Self-host (Docker) — runtime config" section, or the +# generated reference table at https://gittensory.aethereal.dev/docs/self-hosting-configuration. +# Every value below is a SAMPLE placeholder — never commit real secrets. + +# ============================================================================= +# 1. Required secrets — the app will not start without these +# ============================================================================= +GITHUB_APP_ID=123456 +GITHUB_APP_SLUG=my-gittensory-app +GITHUB_APP_PRIVATE_KEY_FILE=/run/secrets/github-app-private-key.pem # or GITHUB_APP_PRIVATE_KEY= inline +GITHUB_WEBHOOK_SECRET=change-this-long-random-value +GITTENSOR_REGISTRY_URL=https://example.invalid/registry.json +GITTENSORY_API_TOKEN=change-this-32-byte-random-token +GITTENSORY_MCP_TOKEN=change-this-32-byte-random-token +INTERNAL_JOB_TOKEN=change-this-32-byte-random-token + +# REQUIRED once, for the first-run /setup wizard that creates the GitHub App manifest. Not needed +# after the App credentials above are filled in. +PUBLIC_API_ORIGIN=https://reviews.example.com +SELFHOST_SETUP_TOKEN=change-this-long-random-value + +# REQUIRED for control-panel access. Comma/whitespace-separated GitHub logins. FAIL-CLOSED: unset +# means nobody (not even you) can sign into the dashboard. +ADMIN_GITHUB_LOGINS=your-github-login + +# ============================================================================= +# 2. Storage — sane defaults, no action needed for a trial/single-node instance +# ============================================================================= +# SQLite is the default database (this file on the mounted data volume; migrations auto-apply). +# It's fine for a single maintainer instance. Move to Postgres (--profile postgres, see +# .env.example) once you're running multiple instances or a serious volume of repos. +DATABASE_PATH=/data/gittensory.sqlite + +# Redis is REQUIRED and starts automatically with the default compose stack (webhook dedup, rate +# limiting, the GitHub GET-response cache). Only override this if you're pointing at an external +# Redis instead of the bundled container. +REDIS_URL=redis://redis:6379 + +# ============================================================================= +# 3. Start conservative — leave AI, RAG, and REES off until webhook delivery and the +# deterministic review are working (see the Quickstart "boot the stack" step) +# ============================================================================= +SELFHOST_DEPLOYMENT_MODE=dry-run +GITTENSORY_REVIEW_REPOS=owner/repo +GITTENSORY_REVIEW_UNIFIED_COMMENT=true +GITTENSORY_REVIEW_SAFETY=true +GITTENSORY_REVIEW_GROUNDING=true +GITTENSORY_REVIEW_RAG=false +GITTENSORY_REVIEW_ENRICHMENT=false + +# ============================================================================= +# 4. AI provider — pick ONE block below and uncomment it (deterministic-only review if you skip +# this section entirely). See /docs/self-hosting-ai-providers for the full reference. +# ============================================================================= + +# --- Claude Code only (subscription) ----------------------------------------- +# AI_PROVIDER=claude-code +# CLAUDE_CODE_OAUTH_TOKEN= # from `claude setup-token` + +# --- Codex only (ChatGPT subscription) ---------------------------------------- +# Codex is fail-closed by default for self-host PR review (its OAuth credential lives on the same +# filesystem prompt-influenced reviews can read) -- the explicit opt-in below is required, and you +# must mount your Codex auth at /data/codex (the image exposes it as ~/.codex). +# AI_PROVIDER=codex +# GITTENSORY_ENABLE_UNSAFE_CODEX_REVIEWER=1 + +# --- Both, synthesized into one decision -------------------------------------- +# AI_PROVIDER=claude-code,codex +# AI_COMBINE=synthesis +# CLAUDE_CODE_OAUTH_TOKEN= +# GITTENSORY_ENABLE_UNSAFE_CODEX_REVIEWER=1 + +# ============================================================================= +# 5. Telemetry — informational only, no action needed +# ============================================================================= +# Running this image contributes anonymized gate-calibration data (verdict, outcome, cycle time -- +# never repo/PR names, code, or logins) to gittensory's central collector once your App is +# configured. This is on by default with no opt-out flag; see .env.example's Orb section for the +# air-gap flag and full privacy detail. diff --git a/.gitignore b/.gitignore index 40c67d7c18..c9e78ab791 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ output/ .env .env.* !.env.example +!.env.selfhost.example docker-compose.override.yml !docker-compose.override.yml.example *.local diff --git a/apps/gittensory-ui/src/routes/docs.self-hosting-configuration.tsx b/apps/gittensory-ui/src/routes/docs.self-hosting-configuration.tsx index 7bfe69fc01..4f73446716 100644 --- a/apps/gittensory-ui/src/routes/docs.self-hosting-configuration.tsx +++ b/apps/gittensory-ui/src/routes/docs.self-hosting-configuration.tsx @@ -33,6 +33,12 @@ function SelfHostingConfiguration() { title="Configuration" description="The self-host configuration model: deployment env, private per-repo policy, feature flags, and review modes." > +

+ This page is the exhaustive reference. For the short path — the required secrets plus a + conservative first-boot config — start with .env.selfhost.example in{" "} + Quickstart instead. +

+

Config layers

1. Copy the sample env

- The sample env contains placeholders only. Keep your real .env out of git and - prefer mounted secret files for multiline values like the GitHub App private key. + .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.

+ + .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. Start conservative

+

2. Choose your AI provider (optional)

- Begin with a small allowlist, unified comments, safety, and grounding. Leave AI, RAG, and - REES off until webhook delivery and deterministic review are working. + Skip this step for a fully deterministic review (no AI). Otherwise uncomment ONE block in{" "} + .env.selfhost.example — Claude Code, Codex, or both. The self-host image + bundles both CLIs by default; credentials and provider choice are runtime-only.

- - dry-run computes reviews but suppresses writes. Switch to live only after - webhook delivery, logs, and review output match expectations. + + Codex stores its OAuth credential in auth.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. +

+

+ Pin a specific release instead of :latest, or point at your own registry: +

+ + + Contributors and anyone customizing the Dockerfile can still build locally —{" "} + docker compose up -d --build builds the gittensory service from + the checkout instead of pulling a published image. Everything else on this page (env, health + checks, GitHub App) is identical either way. + +

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. +

+ + + Orb fleet-calibration telemetry (verdict, outcome, cycle time — never repo names, code, or + logins) is on by default with no opt-out flag once your GitHub App is configured; set{" "} + ORB_AIR_GAP=true for an air-gapped instance that sends nothing. + +

4. Install or connect the GitHub App

Point your App webhook to https://your-host.example/v1/github/webhook, set the @@ -105,6 +172,13 @@ selfhost_ai_provider # only when AI_PROVIDER is set selfhost_job_dead # investigate immediately if present review_context_fetch_failed # REES/RAG/grounding context failure`} /> +

+ A 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, diff --git a/docker-compose.yml b/docker-compose.yml index b69db76494..8b7a971862 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,12 @@ -# One-command self-host for gittensory (#980): docker compose up --build +# Self-host for gittensory (#980). Two ways to boot the `gittensory` service — everything else on +# this page (profiles, env, health checks) is identical either way: # -# SECRETS: copy .env.example → .env, fill in your GitHub App credentials and any optional secrets. -# The file is gitignored. Every value below is a sample placeholder — never commit real secrets. +# RECOMMENDED: ./scripts/deploy-selfhost-image.sh # pulls the published image, no local build +# Building from source: docker compose up --build # for contributors / custom Dockerfile changes +# +# SECRETS: copy .env.selfhost.example → .env (the focused self-host starter) or .env.example → .env +# (the exhaustive reference) and fill in your GitHub App credentials and any optional secrets. The +# file is gitignored. Every value in either sample is a placeholder — never commit real secrets. # # PROFILES — activate optional services by passing --profile (combine freely): # @@ -17,7 +22,8 @@ # --profile runners GitHub Actions self-hosted runner # # Examples: -# docker compose up --build # SQLite + Redis, no external AI +# ./scripts/deploy-selfhost-image.sh # SQLite + Redis, published image +# docker compose up --build # SQLite + Redis, build from source # docker compose --profile postgres --profile caddy up -d # Postgres + HTTPS # docker compose --profile observability up -d # metrics + logs + dashboards # docker compose --profile tailscale --profile runners up -d # tailnet + CI runners From 273f8bbc4737681f893cf7e82e1313c8e9f01a10 Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Thu, 2 Jul 2026 17:25:14 -0700 Subject: [PATCH 2/2] docs(selfhost): split mutually-exclusive AI_PROVIDER samples, apply gate review nits The AI-provider quickstart snippet showed all three mutually exclusive AI_PROVIDER assignments as one uncommented .env block, so copying it wholesale set AI_PROVIDER three times and landed on the combined-provider mode instead of the one actually chosen. - Split into three separately copyable CodeBlocks, one per provider choice. - Move "Defaults at a glance" after the numbered steps instead of between 3 and 4, so the sequential flow isn't interrupted by a reference table. - Fix "everything else on this page" wording in a compose-file comment and a quickstart callout (a compose file isn't a page). - Mirror the Orb telemetry wording between .env.selfhost.example and the Quickstart callout, and clarify it's an always-on contract with one explicit disable path (ORB_AIR_GAP), not a flag with a listed opt-out. --- .env.selfhost.example | 6 +- .../routes/docs.self-hosting-quickstart.tsx | 101 +++++++++--------- docker-compose.yml | 4 +- 3 files changed, 59 insertions(+), 52 deletions(-) diff --git a/.env.selfhost.example b/.env.selfhost.example index d0d0b8d3fe..1b5905b0a0 100644 --- a/.env.selfhost.example +++ b/.env.selfhost.example @@ -83,5 +83,7 @@ GITTENSORY_REVIEW_ENRICHMENT=false # ============================================================================= # Running this image contributes anonymized gate-calibration data (verdict, outcome, cycle time -- # never repo/PR names, code, or logins) to gittensory's central collector once your App is -# configured. This is on by default with no opt-out flag; see .env.example's Orb section for the -# air-gap flag and full privacy detail. +# configured -- this starts automatically, it's the self-hosting contract, not a flag you turn on. +# The one way to disable it is the explicit air-gap flag below; see .env.example's Orb section for +# full privacy detail. +# ORB_AIR_GAP=true # air-gapped/OFFLINE deployments only: compute locally, never send diff --git a/apps/gittensory-ui/src/routes/docs.self-hosting-quickstart.tsx b/apps/gittensory-ui/src/routes/docs.self-hosting-quickstart.tsx index 10027be390..645f28e288 100644 --- a/apps/gittensory-ui/src/routes/docs.self-hosting-quickstart.tsx +++ b/apps/gittensory-ui/src/routes/docs.self-hosting-quickstart.tsx @@ -55,23 +55,27 @@ function SelfHostingQuickstart() {

2. Choose your AI provider (optional)

- Skip this step for a fully deterministic review (no AI). Otherwise uncomment ONE block in{" "} - .env.selfhost.example — Claude Code, Codex, or both. The self-host image - bundles both CLIs by default; credentials and provider choice are runtime-only. + 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.

+ + Codex stores its OAuth credential in auth.json on the same filesystem that @@ -103,8 +107,8 @@ GITTENSORY_IMAGE=ghcr.io/jsonbored/gittensory-selfhost@sha256:... ./scripts/depl Contributors and anyone customizing the Dockerfile can still build locally —{" "} docker compose up -d --build builds the gittensory service from - the checkout instead of pulling a published image. Everything else on this page (env, health - checks, GitHub App) is identical either way. + the checkout instead of pulling a published image. Everything else in this quickstart (env, + health checks, GitHub App) is identical either way. -

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. -

- - - Orb fleet-calibration telemetry (verdict, outcome, cycle time — never repo names, code, or - logins) is on by default with no opt-out flag once your GitHub App is configured; set{" "} - ORB_AIR_GAP=true for an air-gapped instance that sends nothing. - -

4. Install or connect the GitHub App

Point your App webhook to https://your-host.example/v1/github/webhook, set the @@ -184,6 +157,38 @@ review_context_fetch_failed # REES/RAG/grounding context failure`} 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. +

+ + + Orb fleet-calibration telemetry (verdict, outcome, cycle time — never repo names, code, or + logins) starts automatically once your GitHub App is configured — this is the self-hosting + contract, not a flag you turn on. The one way to disable it is the explicit air-gap flag: + set ORB_AIR_GAP=true for an instance that sends nothing. + ); } diff --git a/docker-compose.yml b/docker-compose.yml index 8b7a971862..2d901e58d5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,5 @@ -# Self-host for gittensory (#980). Two ways to boot the `gittensory` service — everything else on -# this page (profiles, env, health checks) is identical either way: +# Self-host for gittensory (#980). Two ways to boot the `gittensory` service — everything else in +# this compose file (profiles, env, health checks) is identical either way: # # RECOMMENDED: ./scripts/deploy-selfhost-image.sh # pulls the published image, no local build # Building from source: docker compose up --build # for contributors / custom Dockerfile changes