Skip to content

feat(ai-review): config-as-code provider/model + maintainer self-serve BYOK (backend, closes #662) - #664

Merged
JSONbored merged 1 commit into
mainfrom
feat/byok-maintainer-config
Jun 14, 2026
Merged

feat(ai-review): config-as-code provider/model + maintainer self-serve BYOK (backend, closes #662)#664
JSONbored merged 1 commit into
mainfrom
feat/byok-maintainer-config

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Backend for #662 — makes BYOK AI review usable end-to-end by maintainers. (UI is #663.)

Config-as-code provider/model

  • RepositorySettings gains aiReviewProvider (anthropic|openai|null) + aiReviewModel (migration 0029), settable from .gittensory.yml:
    gate:
      aiReview: { mode: block, byok: true, provider: anthropic, model: claude-3-5-sonnet-latest }
    The secret key is never in the yml (world-readable) — only the non-secret provider/model.
  • The engine resolves the effective provider/model: the stored key's provider is authoritative; a declared provider must match it or BYOK is skipped (→ free Workers-AI fallback); a declared model overrides the stored/default model.

Maintainer self-serve (the missing enabler)

Keys can't live in repo secrets — a GitHub App can't read them — so maintainers configure them through gittensory:

  • PUT /v1/repos/:owner/:repo/ai-review — set mode/byok/provider/model (merges onto current settings; preserves unrelated settings).
  • POST/GET/DELETE /v1/repos/:owner/:repo/ai-key — write-only encrypted key; GET returns only {configured, provider, last4, model} (never the key).
  • All session-authenticated and scoped to repos the maintainer owns/maintains (requireRepoMaintainer helper, mirroring the focus-manifest routes).

Real bug fixed

The new repo subpaths had to be added to canSessionAccessPath's allowlist — without that, the global protected middleware 403 insufficient_roles every maintainer before the handler runs (caught by a session-auth test). The internal-token routes from #652 remain for operator/backend use.

Tests

Config-as-code parse + round-trip + resolve; engine provider-match / mismatch-skip / model-override; the maintainer routes (operator-token + session owner success + non-owner 403 on every route + cross-repo 403 + 401 unauth + invalid-body 400 + 503 when key storage is unconfigured). Coverage holds at the 97% gate; alignment + OpenAPI + Workers checks pass.

Part of #525.

…e BYOK routes

Makes BYOK AI review usable end-to-end by maintainers (backend for #662).

Config-as-code provider/model:
- RepositorySettings gains aiReviewProvider (anthropic|openai|null) + aiReviewModel
  (migration 0029), settable via .gittensory.yml gate.aiReview { provider, model }
  and the dashboard. The secret key is never in the yml.
- The engine uses the effective provider/model: the stored key's provider is
  authoritative; a declared provider must match it (else BYOK is skipped →
  Workers-AI fallback); a declared model overrides the stored/default model.

Maintainer self-serve (the missing enabler — keys can't live in repo secrets,
since a GitHub App can't read them):
- PUT /v1/repos/:owner/:repo/ai-review — set mode/byok/provider/model (merges
  onto current settings; preserves unrelated settings).
- POST/GET/DELETE /v1/repos/:owner/:repo/ai-key — write-only encrypted key;
  GET returns only {configured, provider, last4, model}.
- All session-authenticated + scoped to repos the maintainer owns/maintains
  (requireRepoMaintainer helper) and allowlisted in canSessionAccessPath.
  Fixes a real gap: the new repo subpaths must be added to the session path
  allowlist or every maintainer is wrongly 403'd.

The internal-token routes from #652 remain for operator/backend use. Coverage
holds above the 97% gate; alignment + OpenAPI + Workers checks pass.

Closes #662
@ghost

ghost commented Jun 14, 2026

Copy link
Copy Markdown

Note

Gittensory Gate skipped

PR closed before full evaluation. No late first comment was created.

Signal Result Evidence Action
Gate result ⚠️ Skipped #664 is no longer open. No action.

💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.

@ghost ghost added the gittensory:reviewed label Jun 14, 2026
@ghost

ghost commented Jun 14, 2026

Copy link
Copy Markdown

gittensory · advisory review

Reviewed 12 changed file(s) — two independent AI reviewers.

Suggested action:Safe to merge — both reviewers found no blocking issues.

Reviewer A · gpt-oss-120b — recommends ✅ merge
This PR introduces configurable BYOK provider and model settings for AI review, adds maintainer self‑serve routes for configuring those settings and managing encrypted keys, updates DB schema and focus‑manifest parsing, and adds comprehensive tests. The changes are well‑scoped, respect the public/private boundaries, and include proper authorization and error handling.

Suggestions

  • Update any API documentation or OpenAPI spec to include the new /ai-review and /ai-key endpoints.
  • Consider adding a migration test to verify the new columns are added without data loss on existing databases.
  • Document the expected shape of the providerKey object in runGittensoryAiReview to avoid future mismatches.

Worth double-checking

  • Migration must be applied in production; ensure the migration runs before any code that expects the new columns.
  • The upsert logic resets provider/model to null when omitted – verify this is the intended behavior for all callers.

Reviewer B · nemotron-3-120b-a12b — recommends ✅ merge
The PR implements config-as-code for AI review provider/model and maintainer self-serve BYOK backend, adding new settings fields, routes, DB schema updates, and tests. Changes respect the public/private boundary by keeping secret keys encrypted and non-secret in public outputs. Implementation is consistent with existing patterns and includes comprehensive unit tests.

Suggestions

  • Add a comment in normalizeAiReviewProvider to clarify it returns null for non-matching values.
  • In upsertRepositorySettings, ensure aiReviewModel trimming aligns with Zod schema validation (though internal callers benefit from the safeguard).

Worth double-checking

  • Verify encryption secret handling in production (503 when missing) aligns with deployment docs.
  • Ensure provider/model validation in focus manifest parsing doesn't reintroduce deprecated values.

@ghost ghost added the gittensory-review label Jun 14, 2026
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@JSONbored
JSONbored merged commit b9bab40 into main Jun 14, 2026
10 checks passed
@JSONbored
JSONbored deleted the feat/byok-maintainer-config branch June 14, 2026 01:00
@github-project-automation github-project-automation Bot moved this from Todo to Done in gittensory - v1 roadmap Jun 14, 2026
JSONbored added a commit that referenced this pull request Jun 14, 2026
…-only panel guard, key validation) (#670)

Follow-up hardening from a security/completeness audit of the BYOK feature
(PRs #664/#665). The panel was already maintainer-only via the backend gate;
these changes close the confirmed gaps.

Backend:
- Bound the BYOK provider fetch with AbortSignal.timeout (20s), mirroring the
  github/gittensor timeoutFetch convention, so a hung Anthropic/OpenAI
  connection can't stall the queue worker. Surface the failure reason
  (timeout/http_error/exception) in the ai_usage_events audit metadata for
  observability — never any key material.
- Classify POST/DELETE /ai-key and PUT /ai-review as "expensive" in
  routeClassForPath (POST /ai-key runs PBKDF2 100k iters + an encrypted upsert).
- Enforce the provider/key prefix check the schema comment already described:
  Anthropic keys must start with sk-ant-, OpenAI with sk- (and not sk-ant-),
  so a mismatched key is rejected with a 400 instead of failing silently later.

Frontend:
- Add an explicit maintainer/owner/operator role gate to MaintainerPanel via
  useSession, so the dashboard query and the BYOK form never mount for a
  non-maintainer (the backend 403 remains the source of truth) and a clean
  "Maintainer access required" message replaces the raw 403.
- Mirror the provider/key prefix validation client-side for instant feedback.

Tests: provider-fetch timeout signal + recorded failure reason, rate-limit
classification, and provider/key mismatch rejection (400). Branch coverage 97%.
@github-actions github-actions Bot mentioned this pull request Jun 14, 2026
12 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant