Skip to content

Commit 6ff28c4

Browse files
authored
docs(selfhost): make beta onboarding image-first (#2650)
* 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. * 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.
1 parent ea5da2a commit 6ff28c4

5 files changed

Lines changed: 203 additions & 22 deletions

File tree

.env.selfhost.example

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Gittensory self-host — focused starter env (#1938)
2+
#
3+
# This is the SHORT path: the required secrets plus a conservative first-boot config, with nothing
4+
# about the Cloudflare Worker deploy. Copy it to `.env` (gitignored) and fill in the placeholders.
5+
#
6+
# cp .env.selfhost.example .env
7+
#
8+
# For the EXHAUSTIVE list of every self-host env var (optional services, observability, backup,
9+
# every AI provider) see `.env.example`'s "Self-host (Docker) — runtime config" section, or the
10+
# generated reference table at https://gittensory.aethereal.dev/docs/self-hosting-configuration.
11+
# Every value below is a SAMPLE placeholder — never commit real secrets.
12+
13+
# =============================================================================
14+
# 1. Required secrets — the app will not start without these
15+
# =============================================================================
16+
GITHUB_APP_ID=123456
17+
GITHUB_APP_SLUG=my-gittensory-app
18+
GITHUB_APP_PRIVATE_KEY_FILE=/run/secrets/github-app-private-key.pem # or GITHUB_APP_PRIVATE_KEY= inline
19+
GITHUB_WEBHOOK_SECRET=change-this-long-random-value
20+
GITTENSOR_REGISTRY_URL=https://example.invalid/registry.json
21+
GITTENSORY_API_TOKEN=change-this-32-byte-random-token
22+
GITTENSORY_MCP_TOKEN=change-this-32-byte-random-token
23+
INTERNAL_JOB_TOKEN=change-this-32-byte-random-token
24+
25+
# REQUIRED once, for the first-run /setup wizard that creates the GitHub App manifest. Not needed
26+
# after the App credentials above are filled in.
27+
PUBLIC_API_ORIGIN=https://reviews.example.com
28+
SELFHOST_SETUP_TOKEN=change-this-long-random-value
29+
30+
# REQUIRED for control-panel access. Comma/whitespace-separated GitHub logins. FAIL-CLOSED: unset
31+
# means nobody (not even you) can sign into the dashboard.
32+
ADMIN_GITHUB_LOGINS=your-github-login
33+
34+
# =============================================================================
35+
# 2. Storage — sane defaults, no action needed for a trial/single-node instance
36+
# =============================================================================
37+
# SQLite is the default database (this file on the mounted data volume; migrations auto-apply).
38+
# It's fine for a single maintainer instance. Move to Postgres (--profile postgres, see
39+
# .env.example) once you're running multiple instances or a serious volume of repos.
40+
DATABASE_PATH=/data/gittensory.sqlite
41+
42+
# Redis is REQUIRED and starts automatically with the default compose stack (webhook dedup, rate
43+
# limiting, the GitHub GET-response cache). Only override this if you're pointing at an external
44+
# Redis instead of the bundled container.
45+
REDIS_URL=redis://redis:6379
46+
47+
# =============================================================================
48+
# 3. Start conservative — leave AI, RAG, and REES off until webhook delivery and the
49+
# deterministic review are working (see the Quickstart "boot the stack" step)
50+
# =============================================================================
51+
SELFHOST_DEPLOYMENT_MODE=dry-run
52+
GITTENSORY_REVIEW_REPOS=owner/repo
53+
GITTENSORY_REVIEW_UNIFIED_COMMENT=true
54+
GITTENSORY_REVIEW_SAFETY=true
55+
GITTENSORY_REVIEW_GROUNDING=true
56+
GITTENSORY_REVIEW_RAG=false
57+
GITTENSORY_REVIEW_ENRICHMENT=false
58+
59+
# =============================================================================
60+
# 4. AI provider — pick ONE block below and uncomment it (deterministic-only review if you skip
61+
# this section entirely). See /docs/self-hosting-ai-providers for the full reference.
62+
# =============================================================================
63+
64+
# --- Claude Code only (subscription) -----------------------------------------
65+
# AI_PROVIDER=claude-code
66+
# CLAUDE_CODE_OAUTH_TOKEN= # from `claude setup-token`
67+
68+
# --- Codex only (ChatGPT subscription) ----------------------------------------
69+
# Codex is fail-closed by default for self-host PR review (its OAuth credential lives on the same
70+
# filesystem prompt-influenced reviews can read) -- the explicit opt-in below is required, and you
71+
# must mount your Codex auth at /data/codex (the image exposes it as ~/.codex).
72+
# AI_PROVIDER=codex
73+
# GITTENSORY_ENABLE_UNSAFE_CODEX_REVIEWER=1
74+
75+
# --- Both, synthesized into one decision --------------------------------------
76+
# AI_PROVIDER=claude-code,codex
77+
# AI_COMBINE=synthesis
78+
# CLAUDE_CODE_OAUTH_TOKEN=
79+
# GITTENSORY_ENABLE_UNSAFE_CODEX_REVIEWER=1
80+
81+
# =============================================================================
82+
# 5. Telemetry — informational only, no action needed
83+
# =============================================================================
84+
# Running this image contributes anonymized gate-calibration data (verdict, outcome, cycle time --
85+
# never repo/PR names, code, or logins) to gittensory's central collector once your App is
86+
# configured -- this starts automatically, it's the self-hosting contract, not a flag you turn on.
87+
# The one way to disable it is the explicit air-gap flag below; see .env.example's Orb section for
88+
# full privacy detail.
89+
# ORB_AIR_GAP=true # air-gapped/OFFLINE deployments only: compute locally, never send

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ output/
1212
.env
1313
.env.*
1414
!.env.example
15+
!.env.selfhost.example
1516
docker-compose.override.yml
1617
!docker-compose.override.yml.example
1718
*.local

apps/gittensory-ui/src/routes/docs.self-hosting-configuration.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ function SelfHostingConfiguration() {
3333
title="Configuration"
3434
description="The self-host configuration model: deployment env, private per-repo policy, feature flags, and review modes."
3535
>
36+
<p>
37+
This page is the exhaustive reference. For the short path — the required secrets plus a
38+
conservative first-boot config — start with <code>.env.selfhost.example</code> in{" "}
39+
<Link to="/docs/self-hosting-quickstart">Quickstart</Link> instead.
40+
</p>
41+
3642
<h2>Config layers</h2>
3743
<FeatureRow
3844
items={[

apps/gittensory-ui/src/routes/docs.self-hosting-quickstart.tsx

Lines changed: 97 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,42 +34,82 @@ function SelfHostingQuickstart() {
3434
>
3535
<h2>1. Copy the sample env</h2>
3636
<p>
37-
The sample env contains placeholders only. Keep your real <code>.env</code> out of git and
38-
prefer mounted secret files for multiline values like the GitHub App private key.
37+
<code>.env.selfhost.example</code> is the short path: required secrets plus a conservative
38+
first-boot config, with nothing about the Cloudflare Worker deploy. Copy it and fill in the
39+
placeholders — keep your real <code>.env</code> out of git and prefer mounted secret files
40+
for multiline values like the GitHub App private key.
3941
</p>
4042
<CodeBlock
4143
lang="bash"
42-
code={`cp .env.example .env
44+
code={`cp .env.selfhost.example .env
4345
# edit .env`}
4446
/>
47+
<Callout variant="note">
48+
<code>.env.selfhost.example</code> already ships a conservative starting config —{" "}
49+
<code>dry-run</code> mode, a small repo allowlist, unified comments, safety, and grounding,
50+
with AI, RAG, and REES left off. Switch to live only after webhook delivery, logs, and
51+
review output match expectations. For every optional env var (observability, backup,
52+
additional AI providers) see <code>.env.example</code>'s self-host section or the{" "}
53+
<Link to="/docs/self-hosting-configuration">generated reference table</Link>.
54+
</Callout>
4555

46-
<h2>2. Start conservative</h2>
56+
<h2>2. Choose your AI provider (optional)</h2>
4757
<p>
48-
Begin with a small allowlist, unified comments, safety, and grounding. Leave AI, RAG, and
49-
REES off until webhook delivery and deterministic review are working.
58+
Skip this step for a fully deterministic review (no AI). Otherwise uncomment ONE of the
59+
three blocks below in <code>.env.selfhost.example</code> — they're mutually exclusive, each
60+
sets its own <code>AI_PROVIDER</code>. The self-host image bundles both CLIs by default;
61+
credentials and provider choice are runtime-only.
5062
</p>
5163
<CodeBlock
52-
filename=".env"
53-
code={`SELFHOST_DEPLOYMENT_MODE=dry-run
54-
GITTENSORY_REVIEW_REPOS=owner/repo
55-
GITTENSORY_REVIEW_UNIFIED_COMMENT=true
56-
GITTENSORY_REVIEW_SAFETY=true
57-
GITTENSORY_REVIEW_GROUNDING=true
58-
GITTENSORY_REVIEW_RAG=false
59-
GITTENSORY_REVIEW_ENRICHMENT=false`}
64+
filename=".env — Claude Code only"
65+
code={`AI_PROVIDER=claude-code
66+
CLAUDE_CODE_OAUTH_TOKEN= # from \`claude setup-token\``}
6067
/>
61-
<Callout variant="note">
62-
<code>dry-run</code> computes reviews but suppresses writes. Switch to live only after
63-
webhook delivery, logs, and review output match expectations.
68+
<CodeBlock
69+
filename=".env — Codex only"
70+
code={`AI_PROVIDER=codex
71+
GITTENSORY_ENABLE_UNSAFE_CODEX_REVIEWER=1 # required opt-in; see Callout below`}
72+
/>
73+
<CodeBlock
74+
filename=".env — both, synthesized into one decision"
75+
code={`AI_PROVIDER=claude-code,codex
76+
AI_COMBINE=synthesis
77+
CLAUDE_CODE_OAUTH_TOKEN=
78+
GITTENSORY_ENABLE_UNSAFE_CODEX_REVIEWER=1`}
79+
/>
80+
<Callout variant="warn" title="Codex is fail-closed by default">
81+
Codex stores its OAuth credential in <code>auth.json</code> on the same filesystem that
82+
prompt-influenced reviews can read, so it requires explicit opt-in (
83+
<code>GITTENSORY_ENABLE_UNSAFE_CODEX_REVIEWER=1</code>) and a mounted{" "}
84+
<code>/data/codex</code> auth volume. Claude Code has no equivalent restriction. See{" "}
85+
<Link to="/docs/self-hosting-ai-providers">AI providers</Link> for the full reference.
6486
</Callout>
6587

6688
<h2>3. Boot the stack</h2>
89+
<p>
90+
<strong>Recommended: pull the published image.</strong> No local build, no Node toolchain —
91+
the script pulls, restarts, and waits for the health check to pass.
92+
</p>
6793
<CodeBlock
6894
lang="bash"
69-
code={`docker compose up -d --build
95+
code={`./scripts/deploy-selfhost-image.sh
7096
curl http://localhost:8787/health
7197
curl http://localhost:8787/ready`}
7298
/>
99+
<p>
100+
Pin a specific release instead of <code>:latest</code>, or point at your own registry:
101+
</p>
102+
<CodeBlock
103+
lang="bash"
104+
code={`./scripts/deploy-selfhost-image.sh ghcr.io/jsonbored/gittensory-selfhost:orb-v0.1.0
105+
GITTENSORY_IMAGE=ghcr.io/jsonbored/gittensory-selfhost@sha256:... ./scripts/deploy-selfhost-image.sh`}
106+
/>
107+
<Callout variant="note" title="Building from source instead">
108+
Contributors and anyone customizing the Dockerfile can still build locally —{" "}
109+
<code>docker compose up -d --build</code> builds the <code>gittensory</code> service from
110+
the checkout instead of pulling a published image. Everything else in this quickstart (env,
111+
health checks, GitHub App) is identical either way.
112+
</Callout>
73113
<FeatureRow
74114
items={[
75115
{
@@ -105,11 +145,50 @@ selfhost_ai_provider # only when AI_PROVIDER is set
105145
selfhost_job_dead # investigate immediately if present
106146
review_context_fetch_failed # REES/RAG/grounding context failure`}
107147
/>
148+
<p>
149+
A cold first boot on SQLite commonly logs a one-time{" "}
150+
<code>selfhost_migrations_applied</code> burst and a brief Redis connection retry while the
151+
sidecar finishes starting — both are expected and stop once the stack is warm. Anything else
152+
that looks wrong, or a <code>/ready</code> that stays unhealthy past a couple minutes, is
153+
covered in <Link to="/docs/self-hosting-troubleshooting">Troubleshooting</Link>.
154+
</p>
108155
<p>
109156
After the deterministic path is stable, continue with{" "}
110157
<Link to="/docs/self-hosting-configuration">Configuration</Link> and then layer in AI, REES,
111158
or RAG deliberately.
112159
</p>
160+
161+
<h2>Defaults at a glance</h2>
162+
<p>
163+
Nothing below needs a flag to start; everything past the first row needs an explicit{" "}
164+
<code>--profile</code> (combine freely) or an explicit <code>AI_PROVIDER</code>.
165+
</p>
166+
<CodeBlock
167+
lang="text"
168+
code={`ENABLED BY DEFAULT (no flags needed)
169+
gittensory app + Redis SQLite database, dry-run-friendly, Orb telemetry (see Callout below)
170+
171+
RECOMMENDED FOR PRODUCTION (opt-in)
172+
--profile postgres shared/multi-instance database (pgvector-capable)
173+
--profile pgbouncer connection pooling in front of Postgres
174+
--profile caddy automatic HTTPS via Let's Encrypt
175+
--profile litestream continuous SQLite backup to S3-compatible storage
176+
--profile observability Prometheus + Alertmanager + Loki + Grafana
177+
178+
OPT-IN, NOT REQUIRED FOR A TRIAL INSTANCE
179+
--profile qdrant dedicated RAG vector store (else sqlite-vec/pgvector)
180+
--profile ollama local model for AI review or embeddings
181+
--profile tailscale private network sidecar
182+
--profile runners self-hosted GitHub Actions runner
183+
--profile backup scheduled backup + backup-exporter jobs
184+
AI_PROVIDER=... off by default; reviews are deterministic-only until set`}
185+
/>
186+
<Callout variant="safety">
187+
Orb fleet-calibration telemetry (verdict, outcome, cycle time — never repo names, code, or
188+
logins) starts automatically once your GitHub App is configured — this is the self-hosting
189+
contract, not a flag you turn on. The one way to disable it is the explicit air-gap flag:
190+
set <code>ORB_AIR_GAP=true</code> for an instance that sends nothing.
191+
</Callout>
113192
</DocsPage>
114193
);
115194
}

docker-compose.yml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
# One-command self-host for gittensory (#980): docker compose up --build
1+
# Self-host for gittensory (#980). Two ways to boot the `gittensory` service — everything else in
2+
# this compose file (profiles, env, health checks) is identical either way:
23
#
3-
# SECRETS: copy .env.example → .env, fill in your GitHub App credentials and any optional secrets.
4-
# The file is gitignored. Every value below is a sample placeholder — never commit real secrets.
4+
# RECOMMENDED: ./scripts/deploy-selfhost-image.sh # pulls the published image, no local build
5+
# Building from source: docker compose up --build # for contributors / custom Dockerfile changes
6+
#
7+
# SECRETS: copy .env.selfhost.example → .env (the focused self-host starter) or .env.example → .env
8+
# (the exhaustive reference) and fill in your GitHub App credentials and any optional secrets. The
9+
# file is gitignored. Every value in either sample is a placeholder — never commit real secrets.
510
#
611
# PROFILES — activate optional services by passing --profile <name> (combine freely):
712
#
@@ -17,7 +22,8 @@
1722
# --profile runners GitHub Actions self-hosted runner
1823
#
1924
# Examples:
20-
# docker compose up --build # SQLite + Redis, no external AI
25+
# ./scripts/deploy-selfhost-image.sh # SQLite + Redis, published image
26+
# docker compose up --build # SQLite + Redis, build from source
2127
# docker compose --profile postgres --profile caddy up -d # Postgres + HTTPS
2228
# docker compose --profile observability up -d # metrics + logs + dashboards
2329
# docker compose --profile tailscale --profile runners up -d # tailnet + CI runners

0 commit comments

Comments
 (0)