Skip to content

api(byok): key-write routes return an undeclared 503; the GETs declare an unreachable 404 #9709

Description

@JSONbored

⚠️ Definition of Done: this issue must be completed in full, in a single PR. Do not split this
work across multiple PRs, and do not defer any Deliverable below to a follow-up issue. A PR that
satisfies only some of the Deliverables, stubs a required test, or leaves a checkbox
partially-done does NOT resolve this issue and will be closed.

Context

Four BYOK key-write routes catch missing_encryption_secret and answer 503 encryption_unavailable: POST /v1/repos/:owner/:repo/ai-key (src/api/routes.ts:3467-3469), POST /v1/repos/:owner/:repo/linear-key (:3502-3504), POST /v1/internal/repos/:owner/:repo/ai-key (:5392-5394), POST /v1/internal/repos/:owner/:repo/linear-key (:5465-5467).

None of the four declares 503. The asymmetry is exact and provable: the fifth route with the identical catch, POST /v1/internal/provider-credentials/{provider} (:5432-5434), does publish 503 in the committed apps/loopover-ui/public/openapi.json. An operator who has not configured TOKEN_ENCRYPTION_SECRET gets an undocumented status from four routes and a documented one from the fifth.

In the other direction, getAiKey and getLinearKey (src/openapi/orb-and-control-route-specs.ts:251-259 and the linear-key arm of the same flatMap) declare 404: "Repo not registered". No code path can produce it. The handlers (src/api/routes.ts:3449-3454 and :3484-3489) call requireRepoWriteAccess and then return getRepositoryAiKeyStatus / getRepositoryLinearKeyStatus unconditionally. requireRepoWriteAccess (:6814-6835) -> requireRepoMaintainer (:6792-6802) -> requireSessionRepoAccess (:6774-6788) can return only 401, 403 forbidden_repo, or 403 insufficient_repo_permission — never 404. A repo with no row simply reads back { configured: false }.

Requirements

  • Add 503 (description naming encryption_unavailable / an unconfigured TOKEN_ENCRYPTION_SECRET) to the set{Ai,Linear}Key and set{Ai,Linear}KeyInternal entries in src/openapi/orb-and-control-route-specs.ts.
  • Remove the 404 from the get{Ai,Linear}Key entries — it is unreachable.
  • Do not change any handler behaviour. The 503 stays a 503 and the GETs keep returning { configured: false } for an unregistered repo.
  • Regenerate and commit apps/loopover-ui/public/openapi.json.

⚠️ Required pattern: match the 503 description style already used for POST /v1/internal/provider-credentials/{provider} in src/openapi/spec.ts:2290-2305, and keep the per-kind flatMap in src/openapi/orb-and-control-route-specs.ts:247-306 as the single place the four key routes are declared — the pair must stay generated from one table so ai-key and linear-key cannot drift. It does NOT satisfy this issue to unroll the flatMap into six literal entries; to add the 503 to only the two maintainer-facing routes and not the two internal ones; or to "fix" the unreachable 404 by adding a 404 branch to the GET handlers.

Deliverables

  • In the regenerated apps/loopover-ui/public/openapi.json, POST /v1/repos/{owner}/{repo}/ai-key, POST /v1/repos/{owner}/{repo}/linear-key, POST /v1/internal/repos/{owner}/{repo}/ai-key, and POST /v1/internal/repos/{owner}/{repo}/linear-key each declare a 503 response.
  • GET /v1/repos/{owner}/{repo}/ai-key and GET /v1/repos/{owner}/{repo}/linear-key no longer declare 404.
  • A new test asserts that a POST to /v1/repos/:owner/:repo/ai-key with TOKEN_ENCRYPTION_SECRET unset answers 503 with { error: "encryption_unavailable" }, and that 503 is declared for that operation in buildOpenApiSpec().
  • The same assertion for POST /v1/internal/repos/:owner/:repo/linear-key.
  • A new test asserts GET /v1/repos/:owner/:repo/ai-key for a repo with no stored key answers 200 with configured: false (proving the removed 404 is unreachable).
  • apps/loopover-ui/public/openapi.json regenerated and committed.

All Deliverables above are required in a single PR. A PR that satisfies only some of them — for example adding the 503 to the spec without the failing-before/passing-after 503 handler test, or removing the 404 without the test that proves the GET answers 200 for an unregistered repo — does not resolve this issue.

Test Coverage Requirements

src/api/** and src/openapi/** are inside Codecov's src/** include; the 99% branch-counted patch gate applies. Both arms of the error.message === "missing_encryption_secret" catch must be covered: the 503 path and the re-throw path for any other error. Extend the existing test/unit/routes-ai-byok.test.ts rather than creating a parallel file. The 503 assertions are the named regression tests for this fix.

Expected Outcome

The published contract for the BYOK key surface matches the handlers in both directions: the 503 an operator actually hits when key storage is unconfigured is documented, and the 404 no caller can ever receive is gone.

Links & Resources

src/api/routes.ts:3449-3514, :5359-5478, :6774-6835; src/openapi/orb-and-control-route-specs.ts:244-306; src/openapi/spec.ts:2290-2305; test/unit/routes-ai-byok.test.ts.

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:bugGittensor-scored bug fix — scores a 0.05x multiplier.help wantedExtra attention is needed

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions