Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions .env.selfhost.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# 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 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ output/
.env
.env.*
!.env.example
!.env.selfhost.example
docker-compose.override.yml
!docker-compose.override.yml.example
*.local
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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."
>
<p>
This page is the exhaustive reference. For the short path — the required secrets plus a
conservative first-boot config — start with <code>.env.selfhost.example</code> in{" "}
<Link to="/docs/self-hosting-quickstart">Quickstart</Link> instead.
</p>

<h2>Config layers</h2>
<FeatureRow
items={[
Expand Down
115 changes: 97 additions & 18 deletions apps/gittensory-ui/src/routes/docs.self-hosting-quickstart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,42 +34,82 @@ function SelfHostingQuickstart() {
>
<h2>1. Copy the sample env</h2>
<p>
The sample env contains placeholders only. Keep your real <code>.env</code> out of git and
prefer mounted secret files for multiline values like the GitHub App private key.
<code>.env.selfhost.example</code> 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 <code>.env</code> out of git and prefer mounted secret files
for multiline values like the GitHub App private key.
</p>
<CodeBlock
lang="bash"
code={`cp .env.example .env
code={`cp .env.selfhost.example .env
# edit .env`}
/>
<Callout variant="note">
<code>.env.selfhost.example</code> already ships a conservative starting config —{" "}
<code>dry-run</code> 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 <code>.env.example</code>'s self-host section or the{" "}
<Link to="/docs/self-hosting-configuration">generated reference table</Link>.
</Callout>

<h2>2. Start conservative</h2>
<h2>2. Choose your AI provider (optional)</h2>
<p>
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 of the
three blocks below in <code>.env.selfhost.example</code> — they're mutually exclusive, each
sets its own <code>AI_PROVIDER</code>. The self-host image bundles both CLIs by default;
credentials and provider choice are runtime-only.
</p>
<CodeBlock
filename=".env"
code={`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`}
filename=".env — Claude Code only"
code={`AI_PROVIDER=claude-code
CLAUDE_CODE_OAUTH_TOKEN= # from \`claude setup-token\``}
/>
<Callout variant="note">
<code>dry-run</code> computes reviews but suppresses writes. Switch to live only after
webhook delivery, logs, and review output match expectations.
<CodeBlock
filename=".env — Codex only"
code={`AI_PROVIDER=codex
GITTENSORY_ENABLE_UNSAFE_CODEX_REVIEWER=1 # required opt-in; see Callout below`}
/>
<CodeBlock
filename=".env — both, synthesized into one decision"
code={`AI_PROVIDER=claude-code,codex
AI_COMBINE=synthesis
CLAUDE_CODE_OAUTH_TOKEN=
GITTENSORY_ENABLE_UNSAFE_CODEX_REVIEWER=1`}
/>
<Callout variant="warn" title="Codex is fail-closed by default">
Codex stores its OAuth credential in <code>auth.json</code> on the same filesystem that
prompt-influenced reviews can read, so it requires explicit opt-in (
<code>GITTENSORY_ENABLE_UNSAFE_CODEX_REVIEWER=1</code>) and a mounted{" "}
<code>/data/codex</code> auth volume. Claude Code has no equivalent restriction. See{" "}
<Link to="/docs/self-hosting-ai-providers">AI providers</Link> for the full reference.
</Callout>

<h2>3. Boot the stack</h2>
<p>
<strong>Recommended: pull the published image.</strong> No local build, no Node toolchain —
the script pulls, restarts, and waits for the health check to pass.
</p>
<CodeBlock
lang="bash"
code={`docker compose up -d --build
code={`./scripts/deploy-selfhost-image.sh
curl http://localhost:8787/health
curl http://localhost:8787/ready`}
/>
<p>
Pin a specific release instead of <code>:latest</code>, or point at your own registry:
</p>
<CodeBlock
lang="bash"
code={`./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.sh`}
/>
<Callout variant="note" title="Building from source instead">
Contributors and anyone customizing the Dockerfile can still build locally —{" "}
<code>docker compose up -d --build</code> builds the <code>gittensory</code> service from
the checkout instead of pulling a published image. Everything else in this quickstart (env,
health checks, GitHub App) is identical either way.
</Callout>
<FeatureRow
items={[
{
Expand Down Expand Up @@ -105,11 +145,50 @@ 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`}
/>
<p>
A cold first boot on SQLite commonly logs a one-time{" "}
<code>selfhost_migrations_applied</code> 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 <code>/ready</code> that stays unhealthy past a couple minutes, is
covered in <Link to="/docs/self-hosting-troubleshooting">Troubleshooting</Link>.
</p>
<p>
After the deterministic path is stable, continue with{" "}
<Link to="/docs/self-hosting-configuration">Configuration</Link> and then layer in AI, REES,
or RAG deliberately.
</p>

<h2>Defaults at a glance</h2>
<p>
Nothing below needs a flag to start; everything past the first row needs an explicit{" "}
<code>--profile</code> (combine freely) or an explicit <code>AI_PROVIDER</code>.
</p>
<CodeBlock
lang="text"
code={`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 set`}
/>
<Callout variant="safety">
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 <code>ORB_AIR_GAP=true</code> for an instance that sends nothing.
</Callout>
</DocsPage>
);
}
14 changes: 10 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -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 in
# this compose file (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 <name> (combine freely):
#
Expand All @@ -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
Expand Down
Loading