Configuration
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
- Environment
- Deployment-wide infrastructure, secrets, feature kill switches, and service URLs. Requires restart or recreate when changed.
- Private repo config
- Mounted GITTENSORY_REPO_CONFIG_DIR files for private per-repo policy. Read fresh each review.
- Public repo config
- The repo .gittensory.yml. Useful for transparent policy, but not for thresholds or rules you need to keep private.
- Built-in defaults
- Safe fallback when nothing is configured. Gate off, AI off, and no repo runs per-PR features until allowlisted.
Precedence
Where policy for a given repo can live is one question; which layer wins when more than one is set is another. Most specific wins, in this order:
- the repo's
.gittensory.yml(public repo config, or the mounted private per-repo config file below ifGITTENSORY_REPO_CONFIG_DIRis set), then - the per-repo database settings (the dashboard), then
- built-in safe defaults.
Within .gittensory.yml itself, the typed gate: block is an alias for the gate-related fields and wins over the generic settings: block for those same fields — so a value written under both gate.duplicates and settings.duplicates resolves to whatever gate.duplicates says. One exception to the whole precedence chain: hard path guardrails (settings.hardGuardrailGlobs) are config-as-code only — omitted or empty means no path guardrails, never a hidden engine fallback, regardless of what the database row or defaults would otherwise imply.
This page covers the environment layer and the shape of the config file. For the full field list — every gate: and settings: key, its default, and what it does — see Tuning your reviews, and for a complete, commented, copy-pasteable manifest see .gittensory.yml.example in the repo — the authoritative reference for every field, including several documented only in its comments (see below).
Required baseline env
PUBLIC_API_ORIGIN=https://reviews.example.com
GITHUB_APP_ID=123456
GITHUB_APP_SLUG=my-gittensory-app
GITHUB_APP_PRIVATE_KEY_FILE=/run/secrets/github-app-private-key.pem
GITHUB_WEBHOOK_SECRET=<random-webhook-secret>
GITTENSOR_REGISTRY_URL=https://example.invalid/registry.json
GITTENSORY_API_TOKEN=<random-32-byte-token>
GITTENSORY_MCP_TOKEN=<random-32-byte-token>
INTERNAL_JOB_TOKEN=<random-32-byte-token>Any FOO_FILE is loaded into FOO at startup. Explicit FOO wins over the file variant.
Every command example on these docs pages hardcodes :8787 — that's the default, not a fixed port. Set PORT to listen on something else; update your compose port mapping and any curl/health-check commands to match.
GITTENSORY_MCP_TOKEN is a shared, end-user-obtainable CLI credential (the normal alternative to gittensory-mcp login), so it must not implicitly stage actions (merges, closes, approvals) on every repo the App happens to be installed on. MCP_ACTUATION_REPO_ALLOWLIST scopes it to an explicit, comma/whitespace-separated owner/repo list — unset denies all actuation for this token. Set it to * or all to opt back into the pre-scoping, any-repo behavior. If you already rely on GITTENSORY_MCP_TOKEN for approval-queue actuation, set this variable after upgrading or MCP actuation stops working.# Deny-by-default: unset means the static MCP token cannot stage or decide any action.
MCP_ACTUATION_REPO_ALLOWLIST=owner/repo-one, owner/repo-two
# Restore pre-upgrade any-repo behavior:
# MCP_ACTUATION_REPO_ALLOWLIST=*MCP_READ_REPO_ALLOWLIST is the same fail-closed/wildcard model, kept as a separate allowlist so read-only MCP tools (repo context, issue quality, watch subscriptions) can be granted independently of actuation trust. The full */all wildcard additionally unlocks the non-repo-scoped contributor/operator tools.
Data paths
MIGRATIONS_DIR(defaultmigrations) — where the self-host runtime looks for SQL migration files to auto-apply at boot. Only relevant for a custom build that ships migrations somewhere other than the default in-image location.REVIEW_AUDIT_DIR— when set, persists visual-review screenshot PNGs to this filesystem path so they're served from cache instead of re-rendered on every request. Unset means each screenshot is re-rendered on demand. Only relevant whenBROWSER_WS_ENDPOINT(see REES enrichment) is also set — visual review is fully inert without it.CODEX_HOME— do not set this for the app container. The Codex provider rejects a container-setCODEX_HOMEoutright (fails closed withcodex_credential_isolation_required) becausecodex execreads attacker-controlled PR title/body/diff text, and a mounted OAuth home on the same filesystem could otherwise leak into review output via prompt injection. This is why the Codex subscription path additionally requires the explicitGITTENSORY_ENABLE_UNSAFE_CODEX_REVIEWER=1opt-in — see AI providers.
GitHub API cache
Redis backs shared caching for stable GitHub GET responses, including repeated installation, repo/user metadata, and branch-protection required-status reads. Keys include the caller identity and response-shaping headers, and cold misses are single-flighted so concurrent jobs do not stampede GitHub.
GITHUB_CACHE_TTL_SECONDS=20
GITHUB_BRANCH_PROTECTION_CACHE_TTL_SECONDS=1200
GITHUB_METADATA_CACHE_TTL_SECONDS=600GITHUB_CACHE_TTL_SECONDS is the short default for repeated safe GitHub GETs. Stable repo/user metadata and branch-protection required-status reads use the per-class TTLs above so operators can keep repeated policy reads hot without broadening stale cache risk. Live CI status, check-run, check-suite, pull/issue subresources, pull mergeability, token minting, rate-limit, and collaborator-permission endpoints are never served from this cache. Prometheus exports gittensory_github_response_cache_total, and the bundled self-host Grafana dashboard includes the hit/miss/coalesced/error breakdown.Queue cadence and startup
CRON_INTERVAL_MS (default 120000, ~2 minutes) is the tick that drives the maintain/sweep and sync cadence — contributor evidence, burden forecasts, RAG re-indexing, drift scans, and notifications all fan out from it. QUEUE_BACKGROUND_CONCURRENCY (default 1) caps how many low-priority background jobs may occupy a QUEUE_CONCURRENCY slot at once, independent of live webhook/review work.
QUEUE_STARTUP_JITTER_MIN_JOBS (default 8) sets the pending-job count below which the queue skips its startup jitter delay — useful on a small instance where you'd rather a handful of jobs start processing immediately after boot than wait out a jitter window meant to stagger many instances restarting at once.
Maintenance and installation backpressure
Two independent, opt-out admission checks run at queue-claim time, on top of GitHub rate-limit deferral, so background work never starves live PR review or overloads the host. Both grew out of real production incidents — an un-jittered cron enqueue and an unbounded per-installation background fan-out — and every value below is optional with a sane default.
- MAINTENANCE_ADMISSION_* — host/lane pressure
- Defers a periodic maintenance job (contributor evidence, burden forecasts, RAG re-index, drift scans, notifications) when live jobs are backed up or host load is high, so maintenance never competes with live webhook/review work for CPU or DB time. A denied job is deferred with jitter, never dropped.
- GITHUB_INSTALLATION_CONCURRENCY_* — per-install fan-out
- Caps how many GitHub-fetching background jobs one installation may run at once, so one installation's sweep or backfill can't claim every background slot and starve every other installation, even when neither is near GitHub's own rate limit.
Tune MAINTENANCE_ADMISSION_MAX_LIVE_PENDING (default 5), MAINTENANCE_ADMISSION_MAX_LIVE_AGE_MS (default 120000), MAINTENANCE_ADMISSION_MAX_PENDING (default 15), MAINTENANCE_ADMISSION_MAX_HOST_LOAD (default 1.5, a 1-minute load-average-per-core ceiling), and MAINTENANCE_ADMISSION_MAX_BACKLOG_CONVERGENCE_PENDING (default 10) if you register many repos or run a busy instance and see maintenance sweeps lagging behind where you'd like. A denial backs off by MAINTENANCE_ADMISSION_DEFER_MS (default 180000, 3 minutes) before jitter, but two escape hatches stop a deferral from becoming a starve: MAINTENANCE_ADMISSION_MAX_DEFER_AGE_MS (default 14400000, 4 hours) force-admits any maintenance job that has waited this long regardless of pressure, and the shorter MAINTENANCE_ADMISSION_DRAIN_AGE_MS (default 600000, 10 minutes, clamped to the 4-hour ceiling) specifically drains the oldest jobs in a backed-up maintenance_pending_high lane so it can actually shrink instead of denying every claim for hours. Set MAINTENANCE_ADMISSION_ENABLED=false to fully disable the policy and return to the old always-run behavior.
GITHUB_INSTALLATION_CONCURRENCY_LIMIT (default 2) is the per- installation ceiling; GITHUB_INSTALLATION_CONCURRENCY_DEFER_MS (default 15000, 15 seconds) is its base backoff before jitter. Raise the limit if a single large installation's background work is being throttled and you have GitHub rate-limit and host headroom to spare; set GITHUB_INSTALLATION_CONCURRENCY_ENABLED=false to disable the check entirely. This check only applies to background jobs that call GitHub — live PR review (github-webhook/agent-regate-pr) is never subject to it.
FOREGROUND_LIVENESS_MAX_DEFER_MS (default 600000, 10 minutes), checked every FOREGROUND_LIVENESS_CHECK_INTERVAL_MS (default 60000, 1 minute — deliberately not the 1-second poll tick, so a job that is still genuinely rate-limited waits for the next sweep instead of busy-looping), releasing at most FOREGROUND_LIVENESS_MAX_RELEASE_PER_SWEEP jobs per tick (default 25, oldest first, so a large inherited backlog ramps up gradually instead of every released job re-tripping the same rate-limit bucket at once). It also runs once at boot, so a restart self-heals inherited over-deferral. Set FOREGROUND_LIVENESS_ENABLED=false to disable the sweep.Tracing and telemetry env
OTEL_EXPORTER_OTLP_ENDPOINT overrides the OpenTelemetry collector target only if you're routing to an external collector instead of the bundled one (default http://otel-collector:4318 under the observability profile). OTEL_SERVICE_NAME (default gittensory-selfhost) is the service name traces and metrics are tagged with — set a distinct value per instance if you run more than one and want to tell them apart in Grafana/Tempo. OTEL_TRACES_SAMPLER (default parentbased_traceidratio) picks the sampling strategy for app job/provider traces; pair it with OTEL_TRACES_SAMPLER_ARG (for example 0.05 to sample 5% of root traces).
Generated env reference
This table is generated from process.env.NAME reads in src/selfhost/** and src/server.ts. It intentionally includes names and first source references only, never example values.
| Name | First reference |
| --- | --- |
| `AI_COMBINE` | `src/selfhost/ai.ts:1160` |
| `AI_DUAL_REVIEW` | `src/selfhost/ai.ts:1135` |
| `AI_EMBED_API_KEY` | `src/server.ts:441` |
| `AI_EMBED_BASE_URL` | `src/server.ts:438` |
| `AI_EMBED_MODEL` | `src/selfhost/ai.ts:1032` |
| `AI_ON_MERGE` | `src/selfhost/ai.ts:1162` |
| `AI_PROVIDER` | `src/selfhost/ai-config.ts:43` |
| `ANTHROPIC_AI_BASE_URL` | `src/selfhost/ai.ts:1036` |
| `ANTHROPIC_AI_MODEL` | `src/selfhost/ai.ts:96` |
| `ANTHROPIC_API_KEY` | `src/selfhost/ai.ts:1035` |
| `BACKUP_ACKNOWLEDGED` | `src/server.ts:380` |
| `BROWSER_WS_ENDPOINT` | `src/selfhost/stubs/puppeteer.ts:11` |
| `CLAUDE_AI_EFFORT` | `src/selfhost/ai.ts:147` |
| `CLAUDE_AI_MODEL` | `src/selfhost/ai.ts:88` |
| `CLAUDE_AI_TIMEOUT_MS` | `src/selfhost/ai.ts:147` |
| `CODEX_AI_EFFORT` | `src/selfhost/ai.ts:151` |
| `CODEX_AI_FIRST_OUTPUT_TIMEOUT_MS` | `src/selfhost/ai.ts:171` |
| `CODEX_AI_MODEL` | `src/selfhost/ai.ts:92` |
| `CODEX_AI_TIMEOUT_MS` | `src/selfhost/ai.ts:151` |
| `CODEX_HOME` | `src/selfhost/ai.ts:340` |
| `CRON_INTERVAL_MS` | `src/server.ts:919` |
| `DATABASE_PATH` | `src/server.ts:250` |
| `DATABASE_URL` | `src/selfhost/preflight.ts:201` |
| `DISCORD_REPO_WEBHOOKS` | `src/services/notify-discord.ts:41` |
| `DISCORD_WEBHOOK_URL` | `src/services/notify-discord.ts:78` |
| `FOREGROUND_LIVENESS_CHECK_INTERVAL_MS` | `src/selfhost/foreground-liveness.ts:52` |
| `FOREGROUND_LIVENESS_ENABLED` | `src/selfhost/foreground-liveness.ts:41` |
| `FOREGROUND_LIVENESS_MAX_DEFER_MS` | `src/selfhost/foreground-liveness.ts:51` |
| `FOREGROUND_LIVENESS_MAX_RELEASE_PER_SWEEP` | `src/selfhost/foreground-liveness.ts:53` |
| `GITHUB_APP_ID` | `src/selfhost/orb-collector.ts:59` |
| `GITHUB_APP_PRIVATE_KEY` | `src/selfhost/orb-collector.ts:166` |
| `GITHUB_CACHE_TTL_SECONDS` | `src/server.ts:510` |
| `GITHUB_INSTALLATION_CONCURRENCY_DEFER_MS` | `src/selfhost/installation-concurrency-admission.ts:47` |
| `GITHUB_INSTALLATION_CONCURRENCY_ENABLED` | `src/selfhost/installation-concurrency-admission.ts:34` |
| `GITHUB_INSTALLATION_CONCURRENCY_LIMIT` | `src/selfhost/installation-concurrency-admission.ts:43` |
| `GITTENSORY_REPO_CONFIG_DIR` | `src/server.ts:289` |
| `GITTENSORY_VERSION` | `src/selfhost/otel.ts:62` |
| `HOME` | `src/selfhost/ai.ts:340` |
| `MAINTENANCE_ADMISSION_DEFER_MS` | `src/selfhost/maintenance-admission.ts:171` |
| `MAINTENANCE_ADMISSION_DRAIN_AGE_MS` | `src/selfhost/maintenance-admission.ts:145` |
| `MAINTENANCE_ADMISSION_ENABLED` | `src/selfhost/maintenance-admission.ts:126` |
| `MAINTENANCE_ADMISSION_MAX_BACKLOG_CONVERGENCE_PENDING` | `src/selfhost/maintenance-admission.ts:167` |
| `MAINTENANCE_ADMISSION_MAX_DEFER_AGE_MS` | `src/selfhost/maintenance-admission.ts:141` |
| `MAINTENANCE_ADMISSION_MAX_LIVE_AGE_MS` | `src/selfhost/maintenance-admission.ts:155` |
| `MAINTENANCE_ADMISSION_MAX_LIVE_PENDING` | `src/selfhost/maintenance-admission.ts:151` |
| `MAINTENANCE_ADMISSION_MAX_PENDING` | `src/selfhost/maintenance-admission.ts:159` |
| `MIGRATIONS_DIR` | `src/server.ts:393` |
| `OBSERVABILITY_SMOKE_POLL_MS` | `scripts/smoke-observability-traces.mjs:8` |
| `OBSERVABILITY_SMOKE_TIMEOUT_MS` | `scripts/smoke-observability-traces.mjs:6` |
| `OLLAMA_AI_API_KEY` | `src/selfhost/ai.ts:1029` |
| `OLLAMA_AI_BASE_URL` | `src/selfhost/ai.ts:1025` |
| `OLLAMA_AI_MODEL` | `src/selfhost/ai.ts:100` |
| `OPENAI_AI_BASE_URL` | `src/selfhost/ai.ts:1027` |
| `OPENAI_AI_MODEL` | `src/selfhost/ai.ts:101` |
| `OPENAI_API_KEY` | `src/selfhost/ai.ts:1029` |
| `OPENAI_COMPATIBLE_AI_API_KEY` | `src/selfhost/ai.ts:1029` |
| `OPENAI_COMPATIBLE_AI_BASE_URL` | `src/selfhost/ai.ts:1028` |
| `OPENAI_COMPATIBLE_AI_MODEL` | `src/selfhost/ai.ts:102` |
| `ORB_AIR_GAP` | `src/selfhost/orb-collector.ts:161` |
| `ORB_ANONYMIZE` | `src/selfhost/orb-collector.ts:174` |
| `ORB_APP_ID` | `src/selfhost/orb-collector.ts:59` |
| `ORB_BROKER_URL` | `src/server.ts:974` |
| `ORB_COLLECTOR_TOKEN` | `src/selfhost/orb-collector.ts:205` |
| `ORB_COLLECTOR_URL` | `src/selfhost/orb-collector.ts:172` |
| `ORB_ENROLLMENT_SECRET` | `src/selfhost/orb-collector.ts:165` |
| `ORB_RELAY_MODE` | `src/server.ts:962` |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | `src/selfhost/otel.ts:47` |
| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | `src/selfhost/otel.ts:45` |
| `OTEL_SERVICE_ENVIRONMENT` | `src/selfhost/otel.ts:60` |
| `OTEL_SERVICE_NAME` | `src/selfhost/otel.ts:59` |
| `OTEL_TRACES_EXPORTER` | `src/selfhost/otel.ts:40` |
| `OTEL_TRACES_SAMPLER` | `src/selfhost/otel.ts:74` |
| `OTEL_TRACES_SAMPLER_ARG` | `src/selfhost/otel.ts:76` |
| `PGPOOL_MAX` | `src/selfhost/queue-common.ts:713` |
| `PGVECTOR_ENABLED` | `src/server.ts:230` |
| `PORT` | `src/server.ts:718` |
| `PUBLIC_API_ORIGIN` | `src/selfhost/preflight.ts:192` |
| `QDRANT_API_KEY` | `src/selfhost/qdrant-vectorize.ts:50` |
| `QDRANT_DIM` | `src/selfhost/qdrant-vectorize.ts:71` |
| `QDRANT_URL` | `src/server.ts:529` |
| `QUEUE_BACKGROUND_CONCURRENCY` | `src/selfhost/queue-common.ts:130` |
| `QUEUE_CONCURRENCY` | `src/selfhost/pg-queue.ts:286` |
| `QUEUE_DEAD_LETTER_AUTO_RETRY_MAX_EXTRA_ATTEMPTS` | `src/selfhost/queue-common.ts:721` |
| `QUEUE_STARTUP_JITTER_MIN_JOBS` | `src/selfhost/queue-common.ts:702` |
| `REDIS_URL` | `src/selfhost/preflight.ts:144` |
| `REVIEW_AUDIT_DIR` | `src/server.ts:574` |
| `SELFHOST_BUNDLE_ALL` | `scripts/build-selfhost.mjs:13` |
| `SELFHOST_SERVICE` | `scripts/smoke-observability-traces.mjs:5` |
| `SELFHOST_SETUP_TOKEN` | `src/selfhost/preflight.ts:186` |
| `SENTRY_DSN` | `src/selfhost/sentry.ts:389` |
| `SENTRY_ENVIRONMENT` | `src/selfhost/otel.ts:60` |
| `SENTRY_RELEASE` | `src/selfhost/otel.ts:62` |
| `SENTRY_SERVER_NAME` | `src/selfhost/sentry.ts:407` |
| `SENTRY_TRACES_SAMPLE_RATE` | `src/selfhost/sentry.ts:195` |
| `SETUP_OUTPUT_PATH` | `src/server.ts:835` |
| `SLACK_WEBHOOK_URL` | `src/services/notify-discord.ts:173` |Per-PR feature flags
Most review capabilities need both their own flag and the repo in GITTENSORY_REVIEW_REPOS. This gives you a global kill switch and a per-repo rollout switch.
GITTENSORY_REVIEW_REPOS=owner/repo,owner/another
GITTENSORY_REVIEW_UNIFIED_COMMENT=true
GITTENSORY_REVIEW_INLINE_COMMENTS=false
GITTENSORY_REVIEW_SAFETY=true
GITTENSORY_REVIEW_GROUNDING=true
GITTENSORY_REVIEW_RAG=false
GITTENSORY_REVIEW_ENRICHMENT=false
GITTENSORY_REVIEW_REPUTATION=falseGITTENSORY_REVIEW_REPOS means no repos run the per-PR feature path, regardless of the individual flags.Private per-repo config
Mount a gitignored directory and point GITTENSORY_REPO_CONFIG_DIR at it. If either a per-repo file or the dir-root global default (.gittensory.yml at the mount root) exists, the public repo .gittensory.yml is never fetched for that review. With only one of the two present, its contents are used as-is; with both present, they are deep-merged — the per-repo file overlaid onto the global default, nested mappings merging key by key and arrays replacing wholesale.
gittensory-config/
owner__repo/.gittensory.yml
repo-name/.gittensory.yml
owner__repo.yml
.gittensory.ymlgate:
enabled: true
aiReview:
mode: advisory
allAuthors: true
settings:
commentMode: all_prs
includeMaintainerAuthors: true
autonomy:
merge: observe
close: observe
agentDryRun: false
features:
safety: true
unifiedComment: true
rag: false
reputation: falseThe features: block above overrides a deployment-wide GITTENSORY_REVIEW_* flag (rag, reputation, unifiedComment, safety) for this one repo, with three states per key: true forces the capability on for this repo (still subject to the env flag itself being enabled — it can never turn on a capability the operator has fully disabled at the deployment level); false forces it off for this repo regardless of the env flag; and omitting the key entirely falls back to the GITTENSORY_REVIEW_REPOS allowlist default, i.e. today's behavior for an operator who hasn't set anything here. See Tuning your reviews for the full GITTENSORY_REVIEW_* flag list this overrides.
Config-as-code blocks with no dashboard equivalent
Everything above has a dashboard row it mirrors. The fields below exist only in .gittensory.yml — there is no DB column or dashboard toggle for them, so a self-host operator who never reads the example file may not know they exist.
gate.checkMode
Controls only whether/how the required Gittensory Orb Review Agent check-run is published — it never affects gate evaluation, comments, labels, audit records, or autonomous merge/close, all of which run identically in every mode. Takes precedence over the legacy gate.enabled boolean when both are set.
- required
- Publish/update the check exactly as before. Use if you keep it as a required branch-protection status check.
- visible
- Publish/update the same check-run for UI visibility only. Never add it to branch protection as required.
- disabled
- Never create/update the check-run. Recommended for high-volume autonomous self-hosting — avoids a perpetually-pending status and cuts GitHub API calls.
disabled, remove Gittensory Orb Review Agent from this repo's branch-protection or ruleset required-status-checks list — Gittensory cannot do this on your behalf, and leaving it required with nothing to satisfy it means GitHub shows a pending status forever. Keep your real CI/Codecov/security checks required; this setting only ever affects Gittensory's own check-run.For a repo that has never been configured, the default is disabled; an already-configured repo keeps its current effective behavior. Self-hosters running high-volume autonomous review should prefer visible or disabled over required — Gittensory's own merge/close decisions never depend on this check either way.
Other gate-only fields
gate.cla— sub-object for the CLA gate (gate.claMode, documented on Tuning your reviews):consentPhrase(a case-insensitive substring gittensory looks for in the PR description),checkRunName(an existing CLA-bot check-run name that also satisfies consent), andcheckRunAppSlug(the trusted App slug required to have produced that check-run, so a contributor-controlled same-name check can't satisfy a blocking legal gate). Either detection method is enough; both may be set. All default tonull(not configured).gate.expectedCiContexts— CI check/status context names to treat as required when GitHub branch protection returns no readable required-status-checks (unconfigured, or a 403 from a token lackingadministration:read— common for GitHub App installations, especially self-host). Merged with branch-protection contexts when both are readable; used alone when branch protection is null/empty. Default: not configured, which keeps the fold-all fail-closed behavior when branch protection is also unreadable.gate.premergeContentRecheck— whentrue, a PR touchingmigrations/**gets a fresh GitHub read of the base branch's current migration filenames immediately before an agent-driven merge, catching a different PR that merged a same-numbered migration in the meantime. A live collision holds the PR instead of merging blind. Defaultfalse— costs one extra GitHub API call per migrations-touching PR.gate.requireFreshRebaseWindow— when the base branch has advanced within this many minutes of the actual merge decision, forces anupdate_branch+ fresh CI recheck before merging, instead of trusting a possibly-stalemergeable_state: cleanread. A bounded retry cap prevents a fast-moving base from live-locking the PR. Defaultnull(never force).gate.dryRun— whentrue, the posted check conclusion remains the real non-enforcing verdict while comments/check text may also show the would-be stricter verdict for AI-review blocker mode. It does not disable downstream merge/close planning for failures from already-enforced gates. Defaultfalse.gate.firstTimeContributorGrace— reserved and currently inert: parsed and stored, but the gate does not read it. A first-time contributor with a real blocker is one-shot closed the same as a repeat contributor. Kept for potential future use.
settings.closeOwnerAuthors and blockedPaths
settings.closeOwnerAuthors — by default, the repo owner's own PRs (and ADMIN_GITHUB_LOGINS fleet-operator PRs) are never auto-closed; they may still auto-merge when clean and passing, or fall to a manual hold. Set true to make owner/admin-authored PRs eligible for auto-close like a contributor's, still gated by the close autonomy class and adverse-signal conditions. Automation-bot PRs stay exempt regardless of this setting. Default false.
blockedPaths (top-level, alongside wantedPaths) is contributor-facing guidance only — it never blocks, holds, or produces a gate finding. A touched path surfaces in contributor onboarding guidance and in gittensory's own risk-reason commentary, but the gate itself never enforces it. The only mechanism that actually holds a PR for a touched path is settings.hardGuardrailGlobs (config-as-code only, described above) — a would-merge PR that touches a configured guardrail glob is held for manual review regardless of blockedPaths. A legacy top-level blockedPaths that once acted as an enforcement mechanism is retired; setting it produces a migration warning pointing at settings.hardGuardrailGlobs. Default [] (nothing listed).
settings anti-abuse block
A cluster of contributor-abuse guardrails, all config-as-code only, all off/unset by default:
- Open-item caps —
contributorOpenPrCapandcontributorOpenIssueCapbound how many PRs/issues a single non-owner/ non-admin/non-bot contributor may have open at once; a contributor's newest item above the cap is closed with a clear reason, their oldest items up to the cap stay open. Both are unset (no cap) by default.contributorCapLabel(defaultover-contributor-limit) is the label applied on close — set it to explicitnullto close silently.contributorCapCancelCicancels in-flight CI runs on a cap-triggered close (requires theactions: writeApp permission; degrades gracefully without it) and falls back to theCONTRIBUTOR_CAP_CANCEL_CI_DEFAULTenv var when unset. - Review-nag cooldown —
reviewNagPolicy(off/hold/close, defaultoff) throttles a contributor who repeatedly pings@gittensoryfor review on the same PR/issue, once they exceedreviewNagMaxPings(default3) withinreviewNagCooldownDays(default5).reviewNagLabel(defaultreview-nag-cooldown) is applied alongside the hold/close action.reviewNagMonitoredMentionsextends the same cooldown to specific maintainer logins a contributor keeps tagging directly instead of (or in addition to)@gittensory. - Exemptions and account age —
autoCloseExemptLoginsis a shared, repo-scoped list of logins never throttled or closed by these deterministic mechanisms, on top of the standing owner/admin/bot exemption.accountAgeThresholdDays(defaultnull, off) appliesnewAccountLabel(defaultnew-account) to a PR from a below-threshold-age account — friction/visibility only, never an automatic close on account age alone, and never for the owner, admins, or bots. - Command rate limit —
commandRateLimitPolicy(off/hold, defaultoff) generalizes the review-nag pattern to every@gittensorycommand, not just review-request pings.commandRateLimitMaxPerWindow(default20) bounds cheap, cache-only commands;commandRateLimitAiMaxPerWindow(default5) is the tighter limit for AI-cost-bearing commands (ask/blockers/preflight/etc.);commandRateLimitWindowHours(default24) is the rolling window both limits count against.
contentLane
Lets a self-hosted maintainer point Gittensory at their own structured registry (a subnet/plugin/package catalog, for example) without a Gittensory code change — reviewing additions to a data file the same way it reviews code. Unconfigured by default; uncomment and set at least entryFileGlob and collectionField (both required — the whole block is ignored with a warning if either is missing).
contentLane:
entryFileGlob: registry/*.json # Glob for the structured entry files this lane reviews. Required.
collectionField: entries # The JSON field holding the collection this lane diffs. Required.
providerFileGlob: providers/*.ts # Optional glob for source files the entries are validated against.
artifactGlob: dist/registry.json # Optional glob for a generated/build artifact to cross-check.
maxAppendedEntries: 1 # Positive integer cap on new entries per PR. Default: unbounded.
duplicateKeyFields: [slug] # Field name(s) used to detect a duplicate entry. Default: [] (no dedup check).
validatorId: my-registry-validator # Optional identifier for a custom per-entry validator. Default: none.repoDocGeneration
Lets gittensory open a pull request that refreshes this repo's own AGENTS.md/CLAUDE.md (and, additively, a skill file) on a schedule — never a direct commit. Disabled by default: an unconfigured repo, or an explicit enabled: false, means no repo-doc refresh ever runs for it.
repoDocGeneration:
enabled: true # Opt in. Default: false (fully disabled).
scope: [agents] # "agents" (AGENTS.md/CLAUDE.md) and/or "skills". Default: [agents].
allowOverwriteExisting: false # Refresh a file that needs manual review to change. Default: false.
refreshIntervalDays: 7 # Minimum days between refreshes. Default: 7.Instance-wide write switches (SELFHOST_DEPLOYMENT_MODE)
SELFHOST_DEPLOYMENT_MODE forces write suppression for the whole instance, regardless of per-repo autonomy — useful for running a self-host in parallel with the live cloud App on the same webhooks, provably posting nothing until an explicit cutover.
- Unset (default)
- Normal mode. Per-repo autonomy and GitHub permissions decide what can be written.
- dry-run
- Compute reviews and audit as shadow, but suppress comments, checks, labels, merges, and closes.
- disabled
- Suppress writes as denied. Use when you need a hard instance-wide stop.
Next steps
Configure the GitHub integration in GitHub App and Orb, then add optional context through AI providers, REES, or RAG. For the full gate-mode and per-repo settings reference — including the AI-review combine modes and a complete worked manifest — see Tuning your reviews.